<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>你说的灰</title><link href="https://ihehehe.com/feed.xml" rel="self"/><link href="https://ihehehe.com/"/>
  <updated>2020-09-20T18:34:36.000Z</updated><id>https://ihehehe.com/</id>

  <entry><title>wkw</title><link href="https://ihehehe.com/posts/生活-wkw/"/><id>https://ihehehe.com/posts/生活-wkw/</id><updated>2020-09-20T18:34:36.000Z</updated><content type="html">&lt;p&gt;// cover(white_color=#ffffff , try_auto_background=yes , light_color=#3399ff , layout=default , text_color=#333333)&lt;/p&gt;
&lt;h2&gt;【危凯雯】培训&lt;/h2&gt;
&lt;p&gt;欢迎参加培训&lt;/p&gt;
&lt;p&gt;// pic-text(left_right_rate=0.5 , link_font_color=#ffffff , link_border_color=#cccccc , style=left , text_align=auto , shape=no , bg_fixed=no , background_color=#ffffff , link_background_color=#000000)&lt;/p&gt;
&lt;h2&gt;凯雯&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://bitcron.com&quot;&gt;AWESOME BITCRON&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;时间：最好是11.7号周六下午&lt;/p&gt;
&lt;p&gt;地点：杭州某个酒店暂时还没确定&lt;/p&gt;
&lt;p&gt;对象：领航36个代理商老板+操盘手+大区经理&lt;/p&gt;
</content></entry>

  <entry><title>日志走一波</title><link href="https://ihehehe.com/posts/生活-日志走一波/"/><id>https://ihehehe.com/posts/生活-日志走一波/</id><updated>2018-11-07T07:01:40.000Z</updated><content type="html">&lt;p&gt;这是我第一篇日志博客&lt;/p&gt;
&lt;h2&gt;先来个自拍&lt;/h2&gt;
&lt;p&gt;这是我在泰国的一张照片
&lt;img src=&quot;/assets/media/IMG_0185.jpg&quot; alt=&quot;IMG_0185&quot;&gt;&lt;/p&gt;
&lt;h2&gt;这是我的家乡&lt;/h2&gt;
&lt;p&gt;这是大安化
&lt;img src=&quot;/assets/media/IMG_0984-1.jpg&quot; alt=&quot;IMG_0984&quot;&gt;&lt;/p&gt;
&lt;h2&gt;为什么改一下就有多个&lt;/h2&gt;
&lt;p&gt;这是什么鬼👻
&lt;img src=&quot;/assets/media/Xnip2018-08-21_14-33-34.jpg&quot; alt=&quot;Xnip2018-08-21_14-33-34&quot;&gt;&lt;/p&gt;
</content></entry>

  <entry><title>kill 用法，某个进程杀不掉</title><link href="https://ihehehe.com/posts/kill 用法，某个进程杀不掉/"/><id>https://ihehehe.com/posts/kill 用法，某个进程杀不掉/</id><updated>2018-03-21T20:18:12.000Z</updated><content type="html">&lt;p&gt;kill -9发送SIGKILL信号将其终止，但是以下两种情况不起作用：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;该进程处于&amp;quot;Zombie&amp;quot;状态（使用ps命令返回defunct的进程）。此时进程已经释放所有资源，但还未得到其父进程的确认。&amp;quot;Zombie&amp;quot;进程要等到下次重启时才会消失，但它的存在不会影响系统性能。&lt;/li&gt;
&lt;li&gt;该进程处于&amp;quot;kernel mode&amp;quot;（核心态）且在等待不可获得的资源。处于核心态的进程忽略所有信号处理，因此对于这些一直处于核心态的进程只能通过重启系统实现。进程在AIX 中会处于两种状态，即用户态和核心态。只有处于用户态的进程才可以用“kill”命令将其终止。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;kill命令&lt;/strong&gt;用来删除执行中的程序或工作。kill可将指定的信息送至程序。预设的信息为SIGTERM(15),可将指定程序终止。若仍无法终止该程序，可使用SIGKILL(9)信息尝试强制删除程序。程序或工作的编号可利用&lt;a href=&quot;http://man.linuxde.net/ps&quot;&gt;ps&lt;/a&gt;指令或job指令查看。&lt;/p&gt;
&lt;h3&gt;语法&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;kill(选项)(参数)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;选项&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;-a：当处理当前进程时，不限制命令名和进程号的对应关系；
-l &amp;lt;信息编号&amp;gt;：若不加&amp;lt;信息编号&amp;gt;选项，则-l参数会列出全部的信息名称；
-p：指定kill 命令只打印相关进程的进程号，而不发送任何信号；
-s &amp;lt;信息名称或编号&amp;gt;：指定要送出的信息；
-u：指定用户。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;实例&lt;/h3&gt;
&lt;p&gt;列出所有信号名称：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt; kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGSTKFLT
17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU
25) SIGXFSZ     26) SIGVTALRM   27) SIGPROF     28) SIGWINCH
29) SIGIO       30) SIGPWR      31) SIGSYS      34) SIGRTMIN
35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3  38) SIGRTMIN+4
39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7  58) SIGRTMAX-6
59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只有第9种信号(SIGKILL)才可以无条件终止进程，其他信号进程都有权利忽略，&lt;strong&gt;下面是常用的信号：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;HUP     1    终端断线
INT     2    中断（同 Ctrl + C）
QUIT    3    退出（同 Ctrl + &#92;）
TERM   15    终止
KILL    9    强制终止
CONT   18    继续（与STOP相反， fg/bg命令）
STOP   19    暂停（同 Ctrl + Z）
&lt;/code&gt;&lt;/pre&gt;
</content></entry>

  <entry><title>单点登录-SSO</title><link href="https://ihehehe.com/posts/单点登录-SSO/"/><id>https://ihehehe.com/posts/单点登录-SSO/</id><updated>2018-01-10T20:18:12.000Z</updated><content type="html">&lt;blockquote&gt;
&lt;p&gt;转载自：https://www.cnblogs.com/ywlaker/p/6113927.html&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;单点登录&lt;/h2&gt;
&lt;p&gt;　　什么是单点登录？单点登录全称Single Sign On（以下简称SSO），是指在多系统应用群中登录一个系统，便可在其他所有系统中得到授权而无需再次登录，包括单点登录与单点注销两部分&lt;/p&gt;
&lt;h3&gt;1、登录&lt;/h3&gt;
&lt;p&gt;　　相比于单系统登录，sso需要一个独立的认证中心，只有认证中心能接受用户的用户名密码等安全信息，其他系统不提供登录入口，只接受认证中心的间接授权。间接授权通过令牌实现，sso认证中心验证用户的用户名密码没问题，创建授权令牌，在接下来的跳转过程中，授权令牌作为参数发送给各个子系统，子系统拿到令牌，即得到了授权，可以借此创建局部会话，局部会话登录方式与单系统的登录方式相同。这个过程，也就是单点登录的原理，用下图说明&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ws1.sinaimg.cn/large/006tKfTcgy1ftv5xxz9epj30kh0pv75y.jpg&quot; alt=&quot;img&quot;&gt;&lt;/p&gt;
&lt;p&gt;　　下面对上图简要描述&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用户访问系统1的受保护资源，系统1发现用户未登录，跳转至sso认证中心，并将自己的地址作为参数&lt;/li&gt;
&lt;li&gt;sso认证中心发现用户未登录，将用户引导至登录页面&lt;/li&gt;
&lt;li&gt;用户输入用户名密码提交登录申请&lt;/li&gt;
&lt;li&gt;sso认证中心校验用户信息，创建用户与sso认证中心之间的会话，称为全局会话，同时创建授权令牌&lt;/li&gt;
&lt;li&gt;sso认证中心带着令牌跳转会最初的请求地址（系统1）&lt;/li&gt;
&lt;li&gt;系统1拿到令牌，去sso认证中心校验令牌是否有效&lt;/li&gt;
&lt;li&gt;sso认证中心校验令牌，返回有效，注册系统1&lt;/li&gt;
&lt;li&gt;系统1使用该令牌创建与用户的会话，称为局部会话，返回受保护资源&lt;/li&gt;
&lt;li&gt;用户访问系统2的受保护资源&lt;/li&gt;
&lt;li&gt;系统2发现用户未登录，跳转至sso认证中心，并将自己的地址作为参数&lt;/li&gt;
&lt;li&gt;sso认证中心发现用户已登录，跳转回系统2的地址，并附上令牌&lt;/li&gt;
&lt;li&gt;系统2拿到令牌，去sso认证中心校验令牌是否有效&lt;/li&gt;
&lt;li&gt;sso认证中心校验令牌，返回有效，注册系统2&lt;/li&gt;
&lt;li&gt;系统2使用该令牌创建与用户的局部会话，返回受保护资源&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;　　用户登录成功之后，会与sso认证中心及各个子系统建立会话，用户与sso认证中心建立的会话称为全局会话，用户与各个子系统建立的会话称为局部会话，局部会话建立之后，用户访问子系统受保护资源将不再通过sso认证中心，全局会话与局部会话有如下约束关系&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;局部会话存在，全局会话一定存在&lt;/li&gt;
&lt;li&gt;全局会话存在，局部会话不一定存在&lt;/li&gt;
&lt;li&gt;全局会话销毁，局部会话必须销毁&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;　　你可以通过博客园、百度、csdn、淘宝等网站的登录过程加深对单点登录的理解，注意观察登录过程中的跳转url与参数&lt;/p&gt;
&lt;h3&gt;2、注销&lt;/h3&gt;
&lt;p&gt;　　单点登录自然也要单点注销，在一个子系统中注销，所有子系统的会话都将被销毁，用下面的图来说明&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ws1.sinaimg.cn/large/006tKfTcgy1ftv5y5ut0qj30je0dvwf3.jpg&quot; alt=&quot;3b139d2e-0b83-4a69-b4f2-316adb8997ce&quot;&gt;&lt;/p&gt;
&lt;p&gt;　　sso认证中心一直监听全局会话的状态，一旦全局会话销毁，监听器将通知所有注册系统执行注销操作&lt;/p&gt;
&lt;p&gt;　　下面对上图简要说明&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用户向系统1发起注销请求&lt;/li&gt;
&lt;li&gt;系统1根据用户与系统1建立的会话id拿到令牌，向sso认证中心发起注销请求&lt;/li&gt;
&lt;li&gt;sso认证中心校验令牌有效，销毁全局会话，同时取出所有用此令牌注册的系统地址&lt;/li&gt;
&lt;li&gt;sso认证中心向所有注册系统发起注销请求&lt;/li&gt;
&lt;li&gt;各注册系统接收sso认证中心的注销请求，销毁局部会话&lt;/li&gt;
&lt;li&gt;sso认证中心引导用户至登录页面&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;四、部署图&lt;/h2&gt;
&lt;p&gt;　　单点登录涉及sso认证中心与众子系统，子系统与sso认证中心需要通信以交换令牌、校验令牌及发起注销请求，因而子系统必须集成sso的客户端，sso认证中心则是sso服务端，整个单点登录过程实质是sso客户端与服务端通信的过程，用下图描述&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ws2.sinaimg.cn/large/006tKfTcgy1ftv5zgomw5j30jn0f4mxy.jpg&quot; alt=&quot;fb29685c-487c-42b9-9ceb-6c7ee29e98c9&quot;&gt;&lt;/p&gt;
&lt;p&gt;　　sso认证中心与sso客户端通信方式有多种，这里以简单好用的httpClient为例，web service、rpc、restful api都可以&lt;/p&gt;
&lt;h2&gt;五、实现&lt;/h2&gt;
&lt;p&gt;　　只是简要介绍下基于java的实现过程，不提供完整源码，明白了原理，我相信你们可以自己实现。sso采用客户端/服务端架构，我们先看sso-client与sso-server要实现的功能（下面：sso认证中心=sso-server）&lt;/p&gt;
&lt;p&gt;　　sso-client&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;拦截子系统未登录用户请求，跳转至sso认证中心&lt;/li&gt;
&lt;li&gt;接收并存储sso认证中心发送的令牌&lt;/li&gt;
&lt;li&gt;与sso-server通信，校验令牌的有效性&lt;/li&gt;
&lt;li&gt;建立局部会话&lt;/li&gt;
&lt;li&gt;拦截用户注销请求，向sso认证中心发送注销请求&lt;/li&gt;
&lt;li&gt;接收sso认证中心发出的注销请求，销毁局部会话&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;　　sso-server&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;验证用户的登录信息&lt;/li&gt;
&lt;li&gt;创建全局会话&lt;/li&gt;
&lt;li&gt;创建授权令牌&lt;/li&gt;
&lt;li&gt;与sso-client通信发送令牌&lt;/li&gt;
&lt;li&gt;校验sso-client令牌有效性&lt;/li&gt;
&lt;li&gt;系统注册&lt;/li&gt;
&lt;li&gt;接收sso-client注销请求，注销所有会话&lt;/li&gt;
&lt;/ol&gt;
</content></entry>

  <entry><title>Zookeeper的选举机制</title><link href="https://ihehehe.com/posts/Zookeeper的选举机制/"/><id>https://ihehehe.com/posts/Zookeeper的选举机制/</id><updated>2017-12-21T20:18:12.000Z</updated><content type="html"></content></entry>

  <entry><title>2PC-2PC</title><link href="https://ihehehe.com/posts/2PC-3PC/"/><id>https://ihehehe.com/posts/2PC-3PC/</id><updated>2017-12-10T20:18:14.000Z</updated><content type="html">&lt;h2&gt;2PC&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;二阶段提交(Two-phaseCommit)是指，在计算机网络以及数据库领域内，为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法(Algorithm)。通常，二阶段提交也被称为是一种协议(Protocol))。在分布式系统中，每个节点虽然可以知晓自己的操作时成功或者失败，却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时，为了保持事务的ACID特性，需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。因此，&lt;strong&gt;二阶段提交的算法思路可以概括为：参与者将操作成败通知协调者，再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所谓的两个阶段是指：第一阶段：**准备阶段(投票阶段)**和第二阶段：&lt;strong&gt;提交阶段（执行阶段）&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;准备阶段&lt;/h3&gt;
&lt;p&gt;事务协调者(事务管理器)给每个参与者(资源管理器)发送Prepare消息，每个参与者要么直接返回失败(如权限验证失败)，要么在本地执行事务，写本地的redo和undo日志，但不提交，到达一种“万事俱备，只欠东风”的状态。&lt;/p&gt;
&lt;p&gt;可以进一步将准备阶段分为以下三个步骤：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1）协调者节点向所有参与者节点询问是否可以执行提交操作(vote)，并开始等待各参与者节点的响应。&lt;/p&gt;
&lt;p&gt;2）参与者节点执行询问发起为止的所有事务操作，并将Undo信息和Redo信息写入日志。（注意：若成功这里其实每个参与者已经执行了事务操作）&lt;/p&gt;
&lt;p&gt;3）各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功，则它返回一个”同意”消息；如果参与者节点的事务操作实际执行失败，则它返回一个”中止”消息。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;提交阶段&lt;/h3&gt;
&lt;p&gt;如果协调者收到了参与者的失败消息或者超时，直接给每个参与者发送回滚(Rollback)消息；否则，发送提交(Commit)消息；参与者根据协调者的指令执行提交或者回滚操作，释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源)&lt;/p&gt;
&lt;p&gt;接下来分两种情况分别讨论提交阶段的过程。&lt;/p&gt;
&lt;p&gt;当协调者节点从所有参与者节点获得的相应消息都为”同意”时:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/assets/imported-images/ec7000694920f8c6.png&quot;&gt;&lt;img src=&quot;/assets/imported-images/ec7000694920f8c6.png&quot; alt=&quot;success&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1）协调者节点向所有参与者节点发出”正式提交(commit)”的请求。&lt;/p&gt;
&lt;p&gt;2）参与者节点正式完成操作，并释放在整个事务期间内占用的资源。&lt;/p&gt;
&lt;p&gt;3）参与者节点向协调者节点发送”完成”消息。&lt;/p&gt;
&lt;p&gt;4）协调者节点受到所有参与者节点反馈的”完成”消息后，完成事务。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果任一参与者节点在第一阶段返回的响应消息为”中止”，或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/assets/imported-images/2a5159bd4fab938c.png&quot;&gt;&lt;img src=&quot;/assets/imported-images/2a5159bd4fab938c.png&quot; alt=&quot;fail&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1）协调者节点向所有参与者节点发出”回滚操作(rollback)”的请求。&lt;/p&gt;
&lt;p&gt;2）参与者节点利用之前写入的Undo信息执行回滚，并释放在整个事务期间内占用的资源。&lt;/p&gt;
&lt;p&gt;3）参与者节点向协调者节点发送”回滚完成”消息。&lt;/p&gt;
&lt;p&gt;4）协调者节点受到所有参与者节点反馈的”回滚完成”消息后，取消事务。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;　　不管最后结果如何，第二阶段都会结束当前事务。&lt;/p&gt;
&lt;p&gt;二阶段提交看起来确实能够提供原子性的操作，但是不幸的事，二阶段提交还是有几个&lt;strong&gt;缺点&lt;/strong&gt;的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1、&lt;strong&gt;同步阻塞问题&lt;/strong&gt;。执行过程中，所有参与节点都是事务阻塞型的。当参与者占有公共资源时，其他第三方节点访问公共资源不得不处于阻塞状态。&lt;/p&gt;
&lt;p&gt;2、&lt;strong&gt;单点故障&lt;/strong&gt;。由于协调者的重要性，一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段，协调者发生故障，那么所有的参与者还都处于锁定事务资源的状态中，而无法继续完成事务操作。（如果是协调者挂掉，可以重新选举一个协调者，但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题）&lt;/p&gt;
&lt;p&gt;3、&lt;strong&gt;数据不一致&lt;/strong&gt;。在二阶段提交的阶段二中，当协调者向参与者发送commit请求之后，发生了局部网络异常或者在发送commit请求过程中协调者发生了故障，这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。&lt;/p&gt;
&lt;p&gt;4、二阶段无法解决的问题：协调者再发出commit消息之后宕机，而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者，这条事务的状态也是不确定的，没人知道事务是否被已经提交。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由于二阶段提交存在着诸如同步阻塞、单点问题、脑裂等缺陷，所以，研究者们在二阶段提交的基础上做了改进，提出了三阶段提交。&lt;/p&gt;
&lt;h2&gt;3PC&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;三阶段提交（Three-phase commit），也叫三阶段提交协议（Three-phase commit protocol），是二阶段提交（2PC）的改进版本。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;/assets/imported-images/982a3a4ab6ababc0.png&quot;&gt;&lt;img src=&quot;/assets/imported-images/982a3a4ab6ababc0.png&quot; alt=&quot;3&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;与两阶段提交不同的是，三阶段提交有两个改动点。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1、引入超时机制。同时在协调者和参与者中都引入超时机制。
2、在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也就是说，除了引入超时机制之外，3PC把2PC的准备阶段再次一分为二，这样三阶段提交就有&lt;code&gt;CanCommit&lt;/code&gt;、&lt;code&gt;PreCommit&lt;/code&gt;、&lt;code&gt;DoCommit&lt;/code&gt;三个阶段。&lt;/p&gt;
&lt;h3&gt;CanCommit阶段&lt;/h3&gt;
&lt;p&gt;3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求，参与者如果可以提交就返回Yes响应，否则返回No响应。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;1.事务询问&lt;/strong&gt; 协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.响应反馈&lt;/strong&gt; 参与者接到CanCommit请求之后，正常情况下，如果其自身认为可以顺利执行事务，则返回Yes响应，并进入预备状态。否则反馈No&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;PreCommit阶段&lt;/h3&gt;
&lt;p&gt;协调者根据参与者的反应情况来决定是否可以继续事务的PreCommit操作。根据响应情况，有以下两种可能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;假如协调者从所有的参与者获得的反馈都是Yes响应，那么就会执行事务的预执行。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;1.发送预提交请求&lt;/strong&gt; 协调者向参与者发送PreCommit请求，并进入Prepared阶段。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.事务预提交&lt;/strong&gt; 参与者接收到PreCommit请求后，会执行事务操作，并将undo和redo信息记录到事务日志中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.响应反馈&lt;/strong&gt; 如果参与者成功的执行了事务操作，则返回ACK响应，同时开始等待最终指令。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;假如有任何一个参与者向协调者发送了No响应，或者等待超时之后，协调者都没有接到参与者的响应，那么就执行事务的中断。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;1.发送中断请求&lt;/strong&gt; 协调者向所有参与者发送abort请求。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.中断事务&lt;/strong&gt; 参与者收到来自协调者的abort请求之后（或超时之后，仍未收到协调者的请求），执行事务的中断。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;doCommit阶段&lt;/h3&gt;
&lt;p&gt;该阶段进行真正的事务提交，也可以分为以下两种情况。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;执行提交&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;1.发送提交请求&lt;/strong&gt; 协调接收到参与者发送的ACK响应，那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.事务提交&lt;/strong&gt; 参与者接收到doCommit请求之后，执行正式的事务提交。并在完成事务提交之后释放所有事务资源。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.响应反馈&lt;/strong&gt; 事务提交完之后，向协调者发送Ack响应。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.完成事务&lt;/strong&gt; 协调者接收到所有参与者的ack响应之后，完成事务。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;中断事务&lt;/strong&gt; 协调者没有接收到参与者发送的ACK响应（可能是接受者发送的不是ACK响应，也可能响应超时），那么就会执行中断事务。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;1.发送中断请求&lt;/strong&gt; 协调者向所有参与者发送abort请求&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.事务回滚&lt;/strong&gt; 参与者接收到abort请求之后，利用其在阶段二记录的undo信息来执行事务的回滚操作，并在完成回滚之后释放所有的事务资源。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.反馈结果&lt;/strong&gt; 参与者完成事务回滚之后，向协调者发送ACK消息&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.中断事务&lt;/strong&gt; 协调者接收到参与者反馈的ACK消息之后，执行事务的中断。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;在doCommit阶段，如果参与者无法及时接收到来自协调者的doCommit或者rebort请求时，会在等待超时之后，会继续进行事务的提交。（其实这个应该是基于概率来决定的，当进入第三阶段时，说明参与者在第二阶段已经收到了PreCommit请求，那么协调者产生PreCommit请求的前提条件是他在第二阶段开始之前，收到所有参与者的CanCommit响应都是Yes。（一旦参与者收到了PreCommit，意味他知道大家其实都同意修改了）所以，一句话概括就是，当进入第三阶段时，由于网络超时等原因，虽然参与者没有收到commit或者abort响应，但是他有理由相信：成功提交的几率很大。 ）
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2PC与3PC的区别&lt;/h2&gt;
&lt;p&gt;相对于2PC，3PC主要解决的&lt;strong&gt;单点故障问题，并减少阻塞&lt;/strong&gt;，因为一旦参与者无法及时收到来自协调者的信息之后，他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会导致数据一致性问题，因为，由于网络原因，协调者发送的abort响应没有及时被参与者接收到，那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;了解了2PC和3PC之后，我们可以发现，无论是二阶段提交还是三阶段提交都无法彻底解决分布式的一致性问题。Google Chubby的作者Mike Burrows说过， &lt;code&gt;there is only one consensus protocol, and that’s Paxos” – all other approaches are just broken versions of Paxos.&lt;/code&gt; 意即&lt;strong&gt;世上只有一种一致性算法，那就是Paxos&lt;/strong&gt;，所有其他一致性算法都是Paxos算法的不完整版。后面的文章会介绍这个公认为难于理解但是行之有效的Paxos算法。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;转载 ： http://www.hollischuang.com/archives/681&lt;/p&gt;
&lt;/blockquote&gt;
</content></entry>

  <entry><title>如何减少碰撞概率</title><link href="https://ihehehe.com/posts/2017-12-10-如何减少碰撞概率/"/><id>https://ihehehe.com/posts/2017-12-10-如何减少碰撞概率/</id><updated>2017-12-10T20:18:12.000Z</updated><content type="html">&lt;p&gt;如何减少碰撞概率，我觉得应该可以从三个方面考虑的：&lt;/p&gt;
&lt;h4&gt;1、增大 映射空间/原空间 的大小&lt;/h4&gt;
&lt;p&gt;这个很明显，映射空间/原空间 的比值大，碰撞的概率就可能小了。 前面我们也提到过，当这个比值大于等于1时，就可以实现无碰撞。&lt;/p&gt;
&lt;h4&gt;2、尽可能把原数据集均匀映射到较小空间&lt;/h4&gt;
&lt;p&gt;简单举个例子说明。比如有10个数，映射到5个位置。
如果每个位置均匀对应2个数，那么任意选2个数，碰撞概率为0.2.
如果1个位置对应6个数，其余4个位置各对应1个数，那么任意选两个数，碰撞概率为0.36.&lt;/p&gt;
&lt;h4&gt;3、结合原空间数据的数据特征&lt;/h4&gt;
&lt;p&gt;我们在处理原空间的数据集时，数据集一般都会有特征，而且不会是原空间的全集。换句话说，我们要处理的数据一般只会是原空间所有数据的一部分，而且数据集有一定特征（数据元素出现的概率的不同）。&lt;/p&gt;
&lt;p&gt;一般哈希函数用质数取模这就是为了使得有特征的数据集也能均匀映射到映射空间。&lt;/p&gt;
&lt;p&gt;在这种情况下，有时hash函数还要结合数据特征，让&lt;strong&gt;出现概率较大的数据集有较小的碰撞概率，出现概率较小的数据集有较大的碰撞概率&lt;/strong&gt;。这样，就可以减少整体数据集的碰撞概率。&lt;/p&gt;
</content></entry>

  <entry><title>分布式锁</title><link href="https://ihehehe.com/posts/分布式锁/"/><id>https://ihehehe.com/posts/分布式锁/</id><updated>2017-12-10T20:18:12.000Z</updated><content type="html">&lt;p&gt;&lt;strong&gt;不同节点的线程访问同步代码块，就需要分布式锁&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;分布式锁的实现有哪些？&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1.Memcached分布式锁&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;利用Memcached的&lt;strong&gt;add命令&lt;/strong&gt;。此命令是原子性操作，只有在key不存在的情况下，才能add成功，也就意味着线程得到了锁。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.Redis分布式锁&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;和Memcached的方式类似，利用Redis的&lt;strong&gt;setnx命令&lt;/strong&gt;。此命令同样是原子性操作，只有在key不存在的情况下，才能set成功。（setnx命令并不完善，后续会介绍替代方案）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.Zookeeper分布式锁&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;利用Zookeeper的&lt;strong&gt;顺序临时节点&lt;/strong&gt;，来实现分布式锁和等待队列。Zookeeper设计的初衷，就是为了实现分布式锁服务的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.Chubby&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Google公司实现的粗粒度分布式锁服务，底层利用了Paxos一致性算法。&lt;/p&gt;
&lt;h4&gt;使用Redis实现分布式锁&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1.加锁&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;s&gt;&lt;strong&gt;setnx（key，1）&lt;/strong&gt;&lt;/s&gt;→&lt;strong&gt;set（key，1，30，NX）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;redis命令：SET key 1 EX 30 NX，&lt;strong&gt;不用setnx 是因为setnx和expire设置超时时不是原子操作&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当一个线程执行set返回1，说明key原本不存在，该线程成功得到了锁；当一个线程执行set返回0，说明key已经存在，该线程抢锁失败。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.解锁&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;有加锁就得有解锁。当得到锁的线程执行完任务，需要释放锁，以便其他线程可以进入。释放锁的最简单方式是执行del指令，伪代码如下：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;del（key）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;释放锁之后，其他线程就可以继续执行set命令来获得锁。&lt;/p&gt;
&lt;p&gt;**防止因为操作缓慢，导致误删，**可以将value 改为threadID，在删除前进行判断。判断和删除也不是原子操作，可以使用lua脚本进行操作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.锁超时&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;锁超时是什么意思呢？如果一个得到锁的线程在执行任务的过程中挂掉，来不及显式地释放锁，这块资源将会永远被锁住，别的线程再也别想进来。&lt;/p&gt;
&lt;p&gt;所以，setnx的key必须设置一个超时时间，以保证即使没有被显式释放，这把锁也要在一定时间后自动释放。setnx不支持超时参数，所以需要额外的指令，伪代码如下：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;expire（key， 30）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;增加守护线程，给锁加时间，有风险。&lt;/p&gt;
&lt;h4&gt;使用&lt;strong&gt;Zookeeper&lt;/strong&gt;实现分布式锁（顺序临时节点）&lt;/h4&gt;
&lt;p&gt;Zookeeper节点的概念：&lt;/p&gt;
&lt;p&gt;​	Zookeeper的数据存储结构就像一棵树，这棵树由节点组成，这种节点叫做Znode。&lt;/p&gt;
&lt;p&gt;Znode分为四种类型：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.持久节点 （PERSISTENT）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;默认的节点类型。创建节点的客户端与zookeeper断开连接后，该节点依旧存在 。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.持久节点顺&lt;/strong&gt;序节点（PERSISTENT_SEQUENTIAL）&lt;/p&gt;
&lt;p&gt;所谓顺序节点，就是在创建节点时，Zookeeper根据创建的时间顺序给该节点名称进行编号：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.临时节点（EPHEMERAL）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;和持久节点相反，当创建节点的客户端与zookeeper断开连接后，临时节点会被删除：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.临时顺序节点（EPHEMERAL_SEQUENTIAL）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;顾名思义，临时顺序节点结合和临时节点和顺序节点的特点：在创建节点时，Zookeeper根据创建的时间顺序给该节点名称进行编号；当创建节点的客户端与zookeeper断开连接后，临时节点会被删除。&lt;/p&gt;
&lt;h5&gt;Zookeeper分布式锁的原理&lt;/h5&gt;
&lt;p&gt;Zookeeper分布式锁恰恰应用了临时顺序节点。具体如何实现呢？让我们来看一看详细步骤：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.获取锁&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;首先，在Zookeeper当中创建一个持久节点ParentLock。当第一个客户端想要获得锁时，需要在ParentLock这个节点下面创建一个&lt;strong&gt;临时顺序节点&lt;/strong&gt; Lock1。&lt;/p&gt;
&lt;p&gt;之后，Client1查找ParentLock下面所有的临时顺序节点并排序，判断自己所创建的节点Lock1是不是顺序最靠前的一个。如果是第一个节点，则成功获得锁。&lt;/p&gt;
&lt;p&gt;这时候，如果再有一个客户端 Client2 前来获取锁，则在ParentLock下载再创建一个临时顺序节点Lock2。&lt;/p&gt;
&lt;p&gt;Client2查找ParentLock下面所有的临时顺序节点并排序，判断自己所创建的节点Lock2是不是顺序最靠前的一个，结果发现节点Lock2并不是最小的。&lt;/p&gt;
&lt;p&gt;于是，Client2向排序仅比它靠前的节点Lock1注册&lt;strong&gt;Watcher&lt;/strong&gt;，用于监听Lock1节点是否存在。这意味着Client2抢锁失败，进入了等待状态。&lt;/p&gt;
&lt;p&gt;这时候，如果又有一个客户端Client3前来获取锁，则在ParentLock下载再创建一个临时顺序节点Lock3。&lt;/p&gt;
&lt;p&gt;Client3查找ParentLock下面所有的临时顺序节点并排序，判断自己所创建的节点Lock3是不是顺序最靠前的一个，结果同样发现节点Lock3并不是最小的。&lt;/p&gt;
&lt;p&gt;于是，Client3向排序仅比它靠前的节点&lt;strong&gt;Lock2&lt;/strong&gt;注册Watcher，用于监听Lock2节点是否存在。这意味着Client3同样抢锁失败，进入了等待状态。&lt;/p&gt;
&lt;p&gt;这样一来，Client1得到了锁，Client2监听了Lock1，Client3监听了Lock2。这恰恰形成了一个等待队列，很像是Java当中ReentrantLock所依赖的&lt;strong&gt;AQS&lt;/strong&gt;（AbstractQueuedSynchronizer）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.释放锁&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;释放锁分为两种情况：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.1.任务完成，客户端显示释放&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当任务完成时，Client1会显示调用删除节点Lock1的指令。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.2.任务执行过程中，客户端崩溃&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;获得锁的Client1在任务执行过程中，如果Duang的一声崩溃，则会断开与Zookeeper服务端的链接。根据临时节点的特性，相关联的节点Lock1会随之自动删除。&lt;/p&gt;
&lt;p&gt;由于Client2一直监听着Lock1的存在状态，当Lock1节点被删除，Client2会立刻收到通知。这时候Client2会再次查询ParentLock下面的所有节点，确认自己创建的节点Lock2是不是目前最小的节点。如果是最小，则Client2顺理成章获得了锁。&lt;/p&gt;
&lt;p&gt;同理，如果Client2也因为任务完成或者节点崩溃而删除了节点Lock2，那么Client3就会接到通知。&lt;/p&gt;
&lt;p&gt;最终，Client3成功得到了锁。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zookeeper和Redis分布式锁的比较&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;下面的表格总结了Zookeeper和Redis分布式锁的优缺点：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ws4.sinaimg.cn/large/006tNc79gy1fsuf0c1v6nj30u00dzgnp.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
</content></entry>

  <entry><title>分布式系统—幂等性设计</title><link href="https://ihehehe.com/posts/分布式系统—幂等性设计/"/><id>https://ihehehe.com/posts/分布式系统—幂等性设计/</id><updated>2017-11-25T20:18:12.000Z</updated><content type="html">&lt;h2&gt;常见用来保证幂等的手段：&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1.MVCC方案&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;　　多版本并发控制，该策略主要使用update with condition（更新带条件来防止）来保证多次外部请求调用对系统的影响是一致的。在系统设计的过程中，合理的使用乐观锁，通过version或者updateTime（timestamp）等其他条件，来做乐观锁的判断条件，这样保证更新操作即使在并发的情况下，也不会有太大的问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.去重表&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;　　在插入数据的时候，插入去重表，利用数据库的唯一索引特性，保证唯一的逻辑。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.悲观锁&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;​    select for update，整个执行过程中锁定该订单对应的记录。注意：这种在DB读大于写的情况下尽量少用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. select + insert&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;　　并发不高的后台系统，或者一些任务JOB，为了支持幂等，支持重复执行，简单的处理方法是，先查询下一些关键数据，判断是否已经执行过，在进行业务处理，就可以了。注意：核心高并发流程不要用这种方法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5.状态机幂等&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;　　在设计单据相关的业务，或者是任务相关的业务，肯定会涉及到状态机，就是业务单据上面有个状态，状态在不同的情况下会发生变更，一般情况下存在有限状态机，这时候，如果状态机已经处于下一个状态，这时候来了一个上一个状态的变更，理论上是不能够变更的，这样的话，保证了有限状态机的幂等。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. token机制，防止页面重复提交&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;　　业务要求：页面的数据只能被点击提交一次
　　发生原因：由于重复点击或者网络重发，或者nginx重发等情况会导致数据被重复提交
解决办法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集群环境：采用token加redis（redis单线程的，处理需要排队）&lt;/li&gt;
&lt;li&gt;单JVM环境：采用token加redis或token加jvm内存&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;处理流程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据提交前要向服务的申请token，token放到redis或jvm内存，token有效时间&lt;/li&gt;
&lt;li&gt;提交后后台校验token，同时删除token，生成新的token返回&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;　　token特点:要申请，一次有效性，可以限流&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. 对外提供接口的api如何保证幂等&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;　　如银联提供的付款接口：需要接入商户提交付款请求时附带：source来源，seq序列号。source+seq在数据库里面做唯一索引，防止多次付款，(并发时，只能处理一个请求)&lt;/p&gt;
&lt;p&gt;　　总结： 幂等性应该是合格程序员的一个基因，在设计系统时，是首要考虑的问题，尤其是在像支付宝，银行，互联网金融公司等涉及的都是钱的系统，既要高效，数据也要准确，所以不能出现多扣款，多打款等问题，这样会很难处理，用户体验也不好 。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;转载至： http://www.cnblogs.com/wxgblogs/p/6639272.html&lt;/p&gt;
&lt;/blockquote&gt;
</content></entry>

  <entry><title>什么是CAS</title><link href="https://ihehehe.com/posts/什么是CAS/"/><id>https://ihehehe.com/posts/什么是CAS/</id><updated>2017-11-21T20:18:12.000Z</updated><content type="html">&lt;h3&gt;什么是CAS&lt;/h3&gt;
&lt;p&gt;CAS是英文单词&lt;strong&gt;Compare And Swap&lt;/strong&gt;的缩写，翻译过来就是比较并替换。&lt;/p&gt;
&lt;p&gt;CAS机制当中使用了3个基本操作数：内存地址V，旧的预期值A，要修改的新值B。&lt;/p&gt;
&lt;p&gt;更新一个变量的时候，只有当变量的预期值A和内存地址V当中的实际值相同时，才会将内存地址V对应的值修改为B。&lt;/p&gt;
&lt;p&gt;如果期望值A和实际内存地址V的值不同时，这个重新尝试的过程被称为&lt;strong&gt;自旋&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;从思想上来说，Synchronized属于&lt;strong&gt;悲观锁&lt;/strong&gt;，悲观地认为程序中的并发情况严重，所以严防死守。CAS属于&lt;strong&gt;乐观锁&lt;/strong&gt;，乐观地认为程序中的并发情况不那么严重，所以让线程不断去尝试更新。&lt;/p&gt;
&lt;p&gt;应用场景：Atomic类、Lock的底层实现、jdk1.6以前的版本，synchronized 转变为重量级锁之前，底层也使用了CAS&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CAS的缺点：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.CPU开销较大&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在并发量比较高的情况下，如果许多线程反复尝试更新某一个变量，却又一直更新不成功，循环往复，会给CPU带来很大的压力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.不能保证代码块的原子性&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CAS机制所保证的&lt;strong&gt;只是一个变量的原子性操作&lt;/strong&gt;，而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新，就不得不使用Synchronized了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.ABA问题&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是CAS机制最大的问题所在。&lt;/p&gt;
&lt;p&gt;首先看一看AtomicInteger当中常用的自增方法 &lt;strong&gt;incrementAndGet：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;private volatile int value;
public final int get() {
    return value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码是一个无限循环，也就是CAS的自旋。循环体当中做了三件事：
1.获取当前值。
2.当前值+1，计算出目标值。
3.进行CAS操作，如果成功则跳出循环，如果失败则重复上述步骤。&lt;/p&gt;
&lt;p&gt;这里需要注意的重点是 get 方法，这个方法的作用是获取变量的当前值。&lt;/p&gt;
&lt;p&gt;如何保证获得的当前值是内存中的最新值呢？很简单，用&lt;strong&gt;volatile&lt;/strong&gt;关键字来保证。有关volatile关键字的知识，我们之前有介绍过，这里就不详细阐述了。&lt;/p&gt;
&lt;p&gt;接下来看一看compareAndSet方法的实现，以及方法所依赖对象的来历：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ws3.sinaimg.cn/large/006tKfTcgy1fsucgdpbxkj30gj09a0tu.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;compareAndSet方法的实现很简单，只有一行代码。这里涉及到两个重要的对象，一个是&lt;strong&gt;unsafe&lt;/strong&gt;，一个是&lt;strong&gt;valueOffset&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;什么是unsafe呢？Java语言不像C，C++那样可以直接访问底层操作系统，但是JVM为我们提供了一个后门，这个后门就是unsafe。unsafe为我们提供了&lt;strong&gt;硬件级别的原子操作&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;至于valueOffset对象，是通过unsafe.objectFieldOffset方法得到，所代表的是&lt;strong&gt;AtomicInteger对象value成员变量在内存中的偏移量&lt;/strong&gt;。我们可以简单地把valueOffset理解为value变量的内存地址。&lt;/p&gt;
&lt;p&gt;CAS机制当中使用了3个基本操作数：&lt;strong&gt;内存地址V，旧的预期值A，要修改的新值B&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而unsafe的compareAndSwapInt方法参数包括了这三个基本元素：valueOffset参数代表了V，expect参数代表了A，update参数代表了B。&lt;/p&gt;
&lt;p&gt;正是unsafe的compareAndSwapInt方法保证了Compare和Swap操作之间的原子性操作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;什么意思呢？真正要做到严谨的CAS机制，我们在Compare阶段不仅要比较期望值A和地址V中的实际值，还要比较变量的版本号是否一致。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在Java当中，&lt;strong&gt;AtomicStampedReference&lt;/strong&gt;类就实现了用版本号做比较的CAS机制。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Java语言CAS底层如何实现？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;利用unsafe提供了原子性操作方法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 什么是ABA问题？怎么解决？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当一个值从A更新成B，又更新会A，普通CAS机制会误判通过检测。&lt;/p&gt;
&lt;p&gt;利用版本号比较可以有效解决ABA问题。&lt;/p&gt;
</content></entry>

  <entry><title>字节码指令简介</title><link href="https://ihehehe.com/posts/字节码指令简介/"/><id>https://ihehehe.com/posts/字节码指令简介/</id><updated>2017-09-29T11:01:00.000Z</updated><content type="html">&lt;p&gt;指令由一个字节长度的、代表着某种特定操作含义的数字（称为操作 码，Opcode）以及跟随其后的零至多个代表此操作所需参数（称为操作数，Operands）而构 成。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;6.4.1 字节码与数据类型&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;操作码助记符中都有特殊的字符来表 明专门为哪种数据类型服务：i代表对int类型的数据操作，l代表long,s代表short,b代表byte,c代 表char,f代表float,d代表double,a代表reference。&lt;/li&gt;
&lt;li&gt;byte、char和short、boolean --&amp;gt;int 处理
大部分的指令都没有支持整数类型byte、char和short，甚至 没有任何指令支持boolean类型。编译器会在编译期或运行期将byte和short类型的数据带符号 扩展（Sign-Extend）为相应的int类型数据，将boolean和char类型数据零位扩展（ZeroExtend）为相应的int类型数据。与之类似，在处理boolean、byte、short和char类型的数组时， 也会转换为使用对应的int类型的字节码指令来处理。因此，大多数对于boolean、byte、short 和char类型数据的操作，实际上都是使用相应的int类型作为运算类型（Computational Type）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;6.4.2 加载和存储指令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将一个局部变量加载到操作栈：iload、iload_＜n＞、lload、lload_＜n＞、fload、fload_ ＜n＞、dload、dload_＜n＞、aload、aload_＜n＞&lt;/li&gt;
&lt;li&gt;将一个数值从操作数栈存储到局部变量表：istore、istore_＜n＞、lstore、lstore_＜n＞、 fstore、fstore_＜n＞、dstore、dstore_＜n＞、astore、astore_＜n＞。&lt;/li&gt;
&lt;li&gt;将一个常量加载到操作数栈：bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、 iconst_m1、iconst_＜i＞、lconst_＜l＞、fconst_＜f＞、dconst_＜d＞。&lt;/li&gt;
&lt;li&gt;扩充局部变量表的访问索引的指令：wide。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;6.4.3 运算指令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;加法指令：iadd、ladd、fadd、dadd。&lt;/li&gt;
&lt;li&gt;减法指令：isub、lsub、fsub、dsub。&lt;/li&gt;
&lt;li&gt;乘法指令：imul、lmul、fmul、dmul。&lt;/li&gt;
&lt;li&gt;除法指令：idiv、ldiv、fdiv、ddiv。&lt;/li&gt;
&lt;li&gt;求余指令：irem、lrem、frem、drem。&lt;/li&gt;
&lt;li&gt;取反指令：ineg、lneg、fneg、dneg。&lt;/li&gt;
&lt;li&gt;位移指令：ishl、ishr、iushr、lshl、lshr、lushr。&lt;/li&gt;
&lt;li&gt;按位或指令：ior、lor。&lt;/li&gt;
&lt;li&gt;按位与指令：iand、land。&lt;/li&gt;
&lt;li&gt;按位异或指令：ixor、lxor。&lt;/li&gt;
&lt;li&gt;局部变量自增指令：iinc。&lt;/li&gt;
&lt;li&gt;比较指令：dcmpg、dcmpl、fcmpg、fcmpl、lcmp。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;6.4.4 类型转换指令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;宽化类型转换（安全）
&lt;ul&gt;
&lt;li&gt;int类型到long、float或者double类型。&lt;/li&gt;
&lt;li&gt;long类型到float、double类型。&lt;/li&gt;
&lt;li&gt;float类型到double类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;窄化类型转换（产生不同的正负号、不同的数量级）
&lt;ul&gt;
&lt;li&gt;必须显式地使用转换 指令来完成，这些转换指令包括：i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l和d2f。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;6.4.5 对象创建与访问指令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建类实例的指令：new。&lt;/li&gt;
&lt;li&gt;创建数组的指令：newarray、anewarray、multianewarray。&lt;/li&gt;
&lt;li&gt;访问类字段（static字段，或者称为类变量）和实例字段（非static字段，或者称为实例变 量）的指令：getfield、putfield、getstatic、putstatic。&lt;/li&gt;
&lt;li&gt;把一个数组元素加载到操作数栈的指令：baload、caload、saload、iaload、laload、 faload、daload、aaload。&lt;/li&gt;
&lt;li&gt;将一个操作数栈的值存储到数组元素中的指令：bastore、castore、sastore、iastore、 fastore、dastore、aastore。&lt;/li&gt;
&lt;li&gt;取数组长度的指令：arraylength。&lt;/li&gt;
&lt;li&gt;检查类实例类型的指令：instanceof、checkcast。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;6.4.6 操作数栈管理指令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将操作数栈的栈顶一个或两个元素出栈：pop、pop2。&lt;/li&gt;
&lt;li&gt;复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶：dup、dup2、 dup_x1、dup2_x1、dup_x2、dup2_x2。&lt;/li&gt;
&lt;li&gt;将栈最顶端的两个数值互换：swap。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;6.4.7 控制转移指令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;条件分支：ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull、if_icmpeq、if_icmpne、 if_icmplt、if_icmpgt、if_icmple、if_icmpge、if_acmpeq和if_acmpne。&lt;/li&gt;
&lt;li&gt;复合条件分支：tableswitch、lookupswitch。&lt;/li&gt;
&lt;li&gt;无条件分支：goto、goto_w、jsr、jsr_w、ret。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;6.4.8 方法调用和返回指令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;invokevirtual指令用于调用对象的实例方法，根据对象的实际类型进行分派（虚方法分 派），这也是Java语言中最常见的方法分派方式。&lt;/li&gt;
&lt;li&gt;invokeinterface指令用于调用接口方法，它会在运行时搜索一个实现了这个接口方法的对 象，找出适合的方法进行调用。&lt;/li&gt;
&lt;li&gt;invokespecial指令用于调用一些需要特殊处理的实例方法，包括实例初始化方法、私有 方法和父类方法。&lt;/li&gt;
&lt;li&gt;invokestatic指令用于调用类方法（static方法）。&lt;/li&gt;
&lt;li&gt;invokedynamic指令用于在运行时动态解析出调用点限定符所引用的方法，并执行该方 法，前面4条调用指令的分派逻辑都固化在Java虚拟机内部，而invokedynamic指令的分派逻 辑是由用户所设定的引导方法决定的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;6.4.9 异常处理指令&lt;/p&gt;
&lt;p&gt;在Java程序中显式抛出异常的操作（throw语句）都由athrow指令来实现，除了用throw语 句显式抛出异常情况之外，Java虚拟机规范还规定了许多运行时异常会在其他Java虚拟机指 令检测到异常状况时自动抛出。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在Java虚拟机中，处理异常（catch语句）不是由字节码指令来实现的（很久之前曾经 使用jsr和ret指令来实现，现在已经不用了），而是采用异常表来完成的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;6.4.10 同步指令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步，这两种同步结构都 是使用管程（Monitor）来支持的。&lt;/li&gt;
&lt;li&gt;方法级的同步是隐式的，即无须通过字节码指令来控制，它实现在方法调用和返回操作 之中。&lt;/li&gt;
&lt;li&gt;同步一段指令集序列通常是由Java语言中的synchronized语句块来表示的，Java虚拟机的 指令集中有monitorenter和monitorexit两条指令来支持synchronized关键字的语义，正确实现 synchronized关键字需要Javac编译器与Java虚拟机两者共同协作支持&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content></entry>

  <entry><title>Class类文件的结构</title><link href="https://ihehehe.com/posts/Class类文件的结构/"/><id>https://ihehehe.com/posts/Class类文件的结构/</id><updated>2017-09-27T11:01:00.000Z</updated><content type="html">&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Class文件是一组以8位字节为基础单位的二进制流
各个数据项目严格按照顺序紧凑地 排列在Class文件之中，中间没有添加任何分隔符，这使得整个Class文件中存储的内容几乎 全部是程序运行的必要数据，没有空隙存在。当遇到需要占用8位字节以上空间的数据项 时，则会按照高位在前 的方式分割成若干个8位字节进行存储。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;存储结构&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;无符号数
属于基本的数据类型，以u1、u2、u4、u8来分别代表1个字节、2个字节、4个 字节和8个字节的无符号数，无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8 编码构成字符串值。&lt;/li&gt;
&lt;li&gt;表
多个无符号数或者其他表作为数据项构成的复合数据类型，所有表都习惯性地 以“_info”结尾。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/assets/imported-images/24cc08fd0a159f51.jpg&quot; alt=&quot;img&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;魔数：每个Class文件的头4个字节称为魔数，它的唯一作用是确定这个文件 是否为一个能被虚拟机接受的Class文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;版本号：第5和第6个字节是次版本号（Minor Version），第7和第8个字节是主版本号（Major Version）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;常量池：常量池中常量的数量是不固定的，所以在常量池的入口需要放置一项u2类型的数 据，代表常量池容量计数值（constant_pool_count）。常量池中每一项常量都是一个表&lt;/p&gt;
&lt;p&gt;这个容 量计数是从1而不是0开始的&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字面量：字面量比较接近于Java语言层面的常量概念，如文本字符串、声明为final的常量值&lt;/li&gt;
&lt;li&gt;符号引用：
&lt;ul&gt;
&lt;li&gt;类和接口的全限定名&lt;/li&gt;
&lt;li&gt;字段的名称和描述符&lt;/li&gt;
&lt;li&gt;方法的名称和描述符&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;访问标志：在常量池结束之后，紧接着的两个字节代表访问标志（access_flags）。标志用于识 别一些类或者接口层次的访问信息，
包括：这个Class是类还是接口；是否定义为public类 型；是否定义为abstract类型；如果是类的话，是否被声明为final等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;类索引、父类索引与接口索引集合：类索引（this_class）和父类索引（super_class）都是一个u2类型的数据，而接口索引集 合（interfaces）是一组u2类型的数据的集合，Class文件中由这三项数据来确定这个类的继承 关系。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;字段表集合：用于描述接口或者类中声明的变量。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法表结合：访问标志（access_flags）、名称索引（name_index）、描述符索 引（descriptor_index）、属性表集合（attributes）几项，&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;属性表结合&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content></entry>

  <entry><title>虚拟机性能监控与故障处理工具</title><link href="https://ihehehe.com/posts/虚拟机性能监控与故障处理工具/"/><id>https://ihehehe.com/posts/虚拟机性能监控与故障处理工具/</id><updated>2017-09-20T19:01:00.000Z</updated><content type="html">&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;4.1 概述&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;知识、经验是关键基础&lt;/li&gt;
&lt;li&gt;数据是依据
&lt;ul&gt;
&lt;li&gt;运行日志&lt;/li&gt;
&lt;li&gt;异常堆栈&lt;/li&gt;
&lt;li&gt;GC日志&lt;/li&gt;
&lt;li&gt;线程快照（threaddump/javacore文件）&lt;/li&gt;
&lt;li&gt;堆转储快照（heapdump/hprof文件）&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;工具是运用知识处 理数据的手段&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4.2 JDK的命令行工具&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/imported-images/ed9eab2c11926183.jpg&quot; alt=&quot;img&quot;&gt;&lt;/p&gt;
&lt;p&gt;几乎所有工具的体积基本上 都稳定在27KB左右。是因 为这些命令行工具大多数是jdk/lib/tools.jar类库的一层薄包装而已，它们主要的功能代码是 在tools类库中实现的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;4.2.1 jps：虚拟机进程状况工具&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/assets/imported-images/87ad13f43ea6b86c.jpg&quot; alt=&quot;img&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4.2.2 jstat：虚拟机统计信息监视工具&lt;/p&gt;
&lt;p&gt;jstat[option vmid[interval[s|ms][count]]]
远程VMID格式：
[protocol：][//]lvmid[@hostname[：port]/servername]&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/assets/imported-images/26286b6ff5486e3b.jpg&quot; alt=&quot;img&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4.2.3 jinfo：Java配置信息工具&lt;/p&gt;
&lt;p&gt;jinfo（Configuration Info for Java）的作用是实时地查看和调整虚拟机各项参数。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C：&#92;＞jinfo-flag CMSInitiatingOccupancyFraction 1444
-XX：CMSInitiatingOccupancyFraction=85&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4.2.4 jmap：Java内存映像工具&lt;/p&gt;
&lt;p&gt;jmap的作用并不仅仅是为了获取dump文件，它还可以查询finalize执行队列、Java堆和永 久代的详细信息，如空间使用率、当前用的是哪种收集器等。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;-XX：+HeapDumpOnOutOfMemoryError参数，可以让虚拟机在OOM异常出 现之后自动生成dump文件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;jmap-dump：format=b,file=eclipse.bin 3500&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/imported-images/e6d4c57adc4426ab.jpg&quot; alt=&quot;img&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4.2.5 jhat：虚拟机堆转储快照分析工具
jhat（JVM Heap Analysis Tool）命令与jmap搭配使用，来分析jmap生成的堆 转储快照。jhat内置了一个微型的HTTP/HTML服务器，生成dump文件的分析结果后，可以在 浏览器中查看。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4.2.6 jstack：Java堆栈跟踪工具&lt;/p&gt;
&lt;p&gt;jstack（Stack Trace for Java）命令用于生成虚拟机当前时刻的线程快照（一般称为 threaddump或者javacore文件）。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈 的集合，生成线程快照的主要目的是定位线程出现长时间停顿的原因，如线程间死锁、死循 环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/assets/imported-images/115f4674db5c7c4e.jpg&quot; alt=&quot;img&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4.2.7 HSDIS：JIT生成代码反汇编&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4.3　JDK的可视化工具&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4.3.1　JConsole：Java监视与管理控制台
&lt;ul&gt;
&lt;li&gt;概览&lt;/li&gt;
&lt;li&gt;内存&lt;/li&gt;
&lt;li&gt;线程&lt;/li&gt;
&lt;li&gt;类&lt;/li&gt;
&lt;li&gt;VM概要&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;4.3.2 VisualVM：多合一故障处理工具
&lt;ul&gt;
&lt;li&gt;生成、分析堆转储文件&lt;/li&gt;
&lt;li&gt;性能分析&lt;/li&gt;
&lt;li&gt;插件……&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content></entry>

  <entry><title>Docker 推送镜像</title><link href="https://ihehehe.com/posts/Docker 推送镜像/"/><id>https://ihehehe.com/posts/Docker 推送镜像/</id><updated>2017-08-30T21:01:00.000Z</updated><content type="html">&lt;h4&gt;注册账号&lt;/h4&gt;
&lt;p&gt;首先你需要到&lt;a href=&quot;https://hub.docker.com/&quot;&gt;Docker Hub&lt;/a&gt;注册一个账号，具体步骤不在这里复述&lt;/p&gt;
&lt;h4&gt;登录Docker Hub&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-Shell&quot;&gt;[HesjdeMacBook-Pro]:~ hesj$ docker logout
Login with your Docker ID to push and pull images from Docker Hub. If you don&#39;t have a Docker ID, head over to https://hub.docker.com to create one.
Username: xxxxxx
Password: xxxxxx
Login Succeeded
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;推送镜像&lt;/h4&gt;
&lt;p&gt;以下命令中的 &lt;code&gt;username&lt;/code&gt; 请替换为你的 Docker 账号用户名。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# 创建引用 SOURCE_IMAGE 的标记 TARGET_IMAGE
[HesjdeMacBook-Pro]:~ hesj$ docker tag nginx:latest username/nginx:1.13.12
# 查看
[HesjdeMacBook-Pro]:~ hesj$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
username/nginx       1.13.12             ae513a47849c        4 weeks ago         109MB
# 推送
[HesjdeMacBook-Pro]:~ hesj$ docker push username/nginx
The push refers to repository [docker.io/username/nginx]
7ab428981537: Mounted from library/nginx
82b81d779f83: Mounted from library/nginx
d626a8ad97a1: Mounted from library/mysql
1.13.12: digest: sha256:e4f0474a75c510f40b37b6b7dc2516241ffa8bde5a442bde3d372c9519c84d90 size: 948

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;推送完成后，你就可以在网站中或者使用&lt;code&gt;docker search&lt;/code&gt; 看到你自己的镜像。&lt;/p&gt;
</content></entry>

  <entry><title>Docker 推送镜像</title><link href="https://ihehehe.com/posts/MAC批量转文件编码集/"/><id>https://ihehehe.com/posts/MAC批量转文件编码集/</id><updated>2017-08-30T21:01:00.000Z</updated><content type="html">&lt;h3&gt;MAC批量转文件编码集&lt;/h3&gt;
&lt;p&gt;使用的命令是iconv,iconv命令用法如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Shell&quot;&gt;用法： iconv [选项...] [文件...]
转换给定文件的编码。

 输入/输出格式规范：
 -f, --from-code=名称 原始文本编码
 -t, --to-code=名称 输出编码

 信息：
 -l, --list 列举所有已知的字符集

 输出控制：
 -c 从输出中忽略无效的字符
 -o, --output=檔案 输出文件
 -s, --silent 关闭警告
 --verbose 打印进度信息

 -?, --help 给出该系统求助列表
 --usage 给出简要的用法信息
 -V, --version 打印程序版本号
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;单个文件&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ iconv -f UTF8  -t GB2312  1.txt &amp;gt; 2.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;批量转换&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
</content></entry>

  <entry><title>Docker 的常用命令——容器</title><link href="https://ihehehe.com/posts/Docker 的常用命令——容器/"/><id>https://ihehehe.com/posts/Docker 的常用命令——容器/</id><updated>2017-08-29T19:05:00.000Z</updated><content type="html">&lt;h4&gt;启动容器&lt;/h4&gt;
&lt;p&gt;两种方式，一种是基于镜像新建一个容器并启动，另外一个是将在终止状态（&lt;code&gt;stopped&lt;/code&gt;）的容器重新启动。&lt;/p&gt;
&lt;h5&gt;新建并启动&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;常用option：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;-d, --detach  在后台和打印容器 ID 中运行容器&lt;/li&gt;
&lt;li&gt;-i, --interactive   即使未连接, 也保持 STDIN 打开，也就是则让容器的标准输入保持打开。&lt;/li&gt;
&lt;li&gt;-t, --tty  分配伪 TTY ，也就是Docker分配一个伪终端（pseudo-tty）并绑定到容器的标准输入上&lt;/li&gt;
&lt;li&gt;-v, --volume list   绑定装入卷&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当利用 &lt;code&gt;docker run&lt;/code&gt; 来创建容器时，Docker 在后台运行的标准操作包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;检查本地是否存在指定的镜像，不存在就从公有仓库下载&lt;/li&gt;
&lt;li&gt;利用镜像创建并启动一个容器&lt;/li&gt;
&lt;li&gt;分配一个文件系统，并在只读的镜像层外面挂载一层可读写层&lt;/li&gt;
&lt;li&gt;从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去&lt;/li&gt;
&lt;li&gt;从地址池配置一个 ip 地址给容器&lt;/li&gt;
&lt;li&gt;执行用户指定的应用程序&lt;/li&gt;
&lt;li&gt;执行完毕后容器被终止&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;启动已终止容器&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ docker start 容器id/容器名字
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;获取容器输出信息&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ docker logs 容器id/容器名字
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;终止容器&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ docker stop 容器id/容器名字
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此外，当 Docker 容器中指定的应用终结时，容器也自动终止。&lt;/p&gt;
&lt;p&gt;例如对于只启动了一个终端的容器，用户通过 &lt;code&gt;exit&lt;/code&gt; 命令或 &lt;code&gt;Ctrl+d&lt;/code&gt; 来退出终端时，所创建的容器立刻终止。&lt;/p&gt;
&lt;p&gt;终止状态的容器可以用 &lt;code&gt;docker ps -a&lt;/code&gt; 命令看到。&lt;/p&gt;
&lt;h4&gt;重启容器&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ docker restart 容器id/容器名字
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;进入容器&lt;/h4&gt;
&lt;p&gt;进入容器进行操作，包括使用 &lt;code&gt;docker attach&lt;/code&gt; 命令或 &lt;code&gt;docker exec&lt;/code&gt; 命令，推荐大家使用 &lt;code&gt;docker exec&lt;/code&gt; 命令，原因是从这个 stdin 中 exit，会导致容器的停止。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker exec&lt;/code&gt; 一般搭配 &lt;code&gt;-it&lt;/code&gt;一起使用&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;[HesjdeMacBook-Pro]:~ hesj$ docker exec -it fad3caa5cd60 bash
root@fad3caa5cd60:/# ls
bin   dev  home  lib64	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 media	opt  root  sbin  sys  usr
root@fad3caa5cd60:/# ^C
root@fad3caa5cd60:/# exit
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;导出导入容器&lt;/h4&gt;
&lt;h5&gt;导出容器&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;[HesjdeMacBook-Pro]:~ hesj$ docker export fad3caa5cd60 &amp;gt; nginx.tar
[HesjdeMacBook-Pro]:~ hesj$ ls | grep *.tar
nginx.tar
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;导入容器快照&lt;/h5&gt;
&lt;p&gt;可以使用 &lt;code&gt;docker import&lt;/code&gt; 从容器快照文件中再导入为镜像&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# 导入容器快照
[HesjdeMacBook-Pro]:~ hesj$ cat nginx.tar | docker import - xxxx/nginx:v3
sha256:980e5d59cfb73e123c072f933acc109ce2d9ec896aecd5b5f3b9ca1eea122a8a
# 查看镜像
[HesjdeMacBook-Pro]:~ hesj$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
xxxx/nginx          v3                  980e5d59cfb7        34 seconds ago      107MB
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;删除镜像&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ docker rm 容器id/容器名字
&lt;/code&gt;&lt;/pre&gt;
</content></entry>

  <entry><title>Docker 的常用命令——镜像</title><link href="https://ihehehe.com/posts/Docker 的常用命令——镜像/"/><id>https://ihehehe.com/posts/Docker 的常用命令——镜像/</id><updated>2017-08-27T11:01:00.000Z</updated><content type="html">&lt;h5&gt;获取镜像&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
# 示例
[HesjdeMacBook-Pro]:~ hesj$ docker pull redis
Using default tag: latest
latest: Pulling from library/redis
4d0d76e05f3c: Pull complete
cfbf30a55ec9: Pull complete
82648e31640d: Pull complete
fb7ace35d550: Pull complete
497bf119bebf: Pull complete
89340f6074da: Pull complete
Digest: sha256:4aed8ea5a5fc4cf05c8d5341b4ae4a4f7c0f9301082a74f6f9a5f321140e0cd3
Status: Downloaded newer image for redis:latest
[HesjdeMacBook-Pro]:~ hesj$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面的命令中没有给出 Docker 镜像仓库地址，因此将会从 Docker Hub 获取镜像。&lt;/p&gt;
&lt;p&gt;从下载过程中可以看到我们之前提及的分层存储的概念，镜像是由多层存储所构成。下载也是一层层的去下载，并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后，给出该镜像完整的 &lt;code&gt;sha256&lt;/code&gt; 的摘要，以确保下载一致性。&lt;/p&gt;
&lt;h5&gt;列出镜像&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;[HesjdeMacBook-Pro]:~ hesj$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mysql               latest              a8a59477268d        3 weeks ago         445MB
redis               latest              bfcb1f6df2db        4 weeks ago         107MB
nginx               latest              ae513a47849c        4 weeks ago         109MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;另外一个需要注意的问题是，&lt;code&gt;docker image ls&lt;/code&gt; 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构，并且可以继承、复用，因此不同镜像可能会因为使用相同的基础镜像，从而拥有共同的层。由于 Docker 使用 Union FS，相同的层只需要保存一份即可，因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。&lt;/p&gt;
&lt;p&gt;你可以通过以下命令来便捷的查看镜像、容器、数据卷所占用的空间。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;[HesjdeMacBook-Pro]:~ hesj$ docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              3                   0                   605.2MB             605.2MB (100%)
Containers          0                   0                   0B                  0B
Local Volumes       0                   0                   0B                  0B
Build Cache                                                 0B                  0B
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;中间层镜像&lt;/h5&gt;
&lt;p&gt;为了加速镜像构建、重复利用资源，Docker 会利用 &lt;strong&gt;中间层镜像&lt;/strong&gt;。所以在使用一段时间后，可能会看到一些依赖的中间层镜像。默认的 &lt;code&gt;docker image ls&lt;/code&gt; 列表中只会显示顶层镜像，如果希望显示包括中间层镜像在内的所有镜像的话，需要加 &lt;code&gt;-a&lt;/code&gt; 参数。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;docker image ls -a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样会看到很多无标签的镜像，与之前的虚悬镜像不同，这些无标签的镜像很多都是中间层镜像，是其它镜像所依赖的镜像。这些无标签镜像不应该删除，否则会导致上层镜像因为依赖丢失而出错。实际上，这些镜像也没必要删除，相同的层只会存一遍，而这些镜像是别的镜像的依赖，因此并不会因为它们被列出来而多存了一份，无论如何你也会需要它们。只要删除那些依赖它们的镜像后，这些依赖的中间层镜像也会被连带删除。&lt;/p&gt;
&lt;h5&gt;列出部分镜像&lt;/h5&gt;
&lt;p&gt;不加任何参数的情况下，&lt;code&gt;docker image ls&lt;/code&gt; 会列出所有顶级镜像，但是有时候我们只希望列出部分镜像。&lt;code&gt;docker image ls&lt;/code&gt; 有好几个参数可以帮助做到这个事情。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# 根据仓库名列出镜像 
docker image ls redis
# 列出特定的某个镜像，也就是说指定仓库名和标签
docker image ls redis:3.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;删除镜像&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ docker image rm [选项] &amp;lt;镜像1&amp;gt; [&amp;lt;镜像2&amp;gt; ...]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中，&lt;code&gt;&amp;lt;镜像&amp;gt;&lt;/code&gt; 可以是 &lt;code&gt;镜像短 ID&lt;/code&gt;、&lt;code&gt;镜像长 ID&lt;/code&gt;、&lt;code&gt;镜像名&lt;/code&gt; 或者 &lt;code&gt;镜像摘要&lt;/code&gt;。但是我们通常使用&lt;code&gt;镜像短 ID&lt;/code&gt; 、&lt;code&gt;镜像名&lt;/code&gt;、或者结合命令做多个镜像删除&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ docker image rm a8a59477268d
$ docker image rm $(docker image ls -q redis)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;利用 commit 理解镜像构成（不推荐）&lt;/h5&gt;
&lt;p&gt;注意： &lt;code&gt;docker commit&lt;/code&gt; 命令除了学习之外，还有一些特殊的应用场合，比如被入侵后保存现场等。但是，不要使用 &lt;code&gt;docker commit&lt;/code&gt; 定制镜像，定制镜像应该使用 &lt;code&gt;Dockerfile&lt;/code&gt; 来完成。&lt;/p&gt;
&lt;p&gt;以定制一个 Web 服务器为例子，来讲解镜像是如何构建的。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Shell&quot;&gt;[HesjdeMacBook-Pro]:~ hesj$ docker run --name webserver -d -p 80:80 nginx
fad3caa5cd60dc745cea56ea545b8ffbcad555bec9506b525e6aa8222041d5e0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;直接用浏览器访问&lt;a href=&quot;http://localhost/&quot;&gt;http://localhost&lt;/a&gt;的话，我们会看到默认的 Nginx 欢迎页面。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ws4.sinaimg.cn/large/006tNc79gy1frsjbqrp29j317w0k841v.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;做一个简单的修改，修改这个欢迎页。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# 交互式终端方式进入 webserver 容器，并执行了 bash 命令，也就是获得一个可操作的 Shell。
[HesjdeMacBook-Pro]:~ hesj$ docker exec -it webserver bash
# 用 &amp;lt;h1&amp;gt;Hello, Docker!&amp;lt;/h1&amp;gt; 覆盖了 /usr/share/nginx/html/index.html 的内容。
root@fad3caa5cd60:/# echo &#39;&amp;lt;h1&amp;gt;Hello, Docker!&amp;lt;/h1&amp;gt;&#39; &amp;gt; /usr/share/nginx/html/index.html
root@fad3caa5cd60:/# exit
exit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在我们再刷新浏览器的话，会发现内容被改变了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ws1.sinaimg.cn/large/006tKfTcgy1fruczjpj4wj316c07cwf6.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;我们修改了容器的文件，也就是改动了容器的存储层。我们可以通过 &lt;code&gt;docker diff&lt;/code&gt; 命令看到具体的改动。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;[HesjdeMacBook-Pro]:~ hesj$ docker diff webserver
C /root
A /root/.bash_history
C /run
A /run/nginx.pid
C /usr/share/nginx/html/index.html
C /var/cache/nginx
A /var/cache/nginx/client_temp
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定制好了变化，我们希望能将其保存下来形成镜像。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Shell&quot;&gt;# docker commit [选项] &amp;lt;容器ID或容器名&amp;gt; [&amp;lt;仓库名&amp;gt;[:&amp;lt;标签&amp;gt;]]
[HesjdeMacBook-Pro]:~ hesj$ docker commit --author &amp;quot;西瓜糖 &amp;lt;sssss.gmail.com&amp;gt;&amp;quot; --message &amp;quot;修改默认网页&amp;quot; webserver nginx:v2
sha256:55efe5ea0e8a34ccf204989f44749363a8b78d6d8892ca4f686046faa780917b
# 查看镜像
[HesjdeMacBook-Pro]:~ hesj$ docker image ls nginx
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               v2                  55efe5ea0e8a        48 seconds ago      109MB
nginx               latest              ae513a47849c        4 weeks ago         109MB
#查看修改历史
[HesjdeMacBook-Pro]:~ hesj$ docker history nginx:v2
IMAGE               CREATED              CREATED BY                                      SIZE                COMMENT
55efe5ea0e8a        About a minute ago   nginx -g daemon off;                            102B                修改默认网页
ae513a47849c        4 weeks ago          /bin/sh -c #(nop)  CMD [&amp;quot;nginx&amp;quot; &amp;quot;-g&amp;quot; &amp;quot;daemon…   0B
&amp;lt;missing&amp;gt;           4 weeks ago          /bin/sh -c #(nop)  STOPSIGNAL [SIGTERM]         0B
&amp;lt;missing&amp;gt;           4 weeks ago          /bin/sh -c #(nop)  EXPOSE 80/tcp                0B
&amp;lt;missing&amp;gt;           4 weeks ago          /bin/sh -c ln -sf /dev/stdout /var/log/nginx…   22B
&amp;lt;missing&amp;gt;           4 weeks ago          /bin/sh -c set -x  &amp;amp;&amp;amp; apt-get update  &amp;amp;&amp;amp; apt…   53.7MB
&amp;lt;missing&amp;gt;           4 weeks ago          /bin/sh -c #(nop)  ENV NJS_VERSION=1.13.12.0…   0B
&amp;lt;missing&amp;gt;           4 weeks ago          /bin/sh -c #(nop)  ENV NGINX_VERSION=1.13.12…   0B
&amp;lt;missing&amp;gt;           4 weeks ago          /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B
&amp;lt;missing&amp;gt;           4 weeks ago          /bin/sh -c #(nop)  CMD [&amp;quot;bash&amp;quot;]                 0B
&amp;lt;missing&amp;gt;           4 weeks ago          /bin/sh -c #(nop) ADD file:ec5be7eec56a74975…   55.3MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新的镜像定制好后，我们可以来运行这个镜像。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run --name web2 -d -p 81:80 nginx:v2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里我们命名为新的服务为 &lt;code&gt;web2&lt;/code&gt;，并且映射到 &lt;code&gt;81&lt;/code&gt; 端口。直接访问 &lt;a href=&quot;http://localhost:81/&quot;&gt;http://localhost:81&lt;/a&gt; 看到结果，其内容应该和之前修改后的 &lt;code&gt;webserver&lt;/code&gt; 一样。&lt;/p&gt;
&lt;h6&gt;为什么不推荐使用？&lt;/h6&gt;
&lt;p&gt;仔细观察之前的 &lt;code&gt;docker diff webserver&lt;/code&gt; 的结果，你会发现除了真正想要修改的 &lt;code&gt;/usr/share/nginx/html/index.html&lt;/code&gt; 文件外，由于命令的执行，还有很多文件被改动或添加了。这还仅仅是最简单的操作，如果是安装软件包、编译构建，那会有大量的无关内容被添加进来，如果不小心清理，将会导致镜像极为臃肿。&lt;/p&gt;
&lt;p&gt;此外，使用 &lt;code&gt;docker commit&lt;/code&gt; 意味着所有对镜像的操作都是黑箱操作，生成的镜像也被称为&lt;strong&gt;黑箱镜像&lt;/strong&gt;，换句话说，就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像，别人根本无从得知。而且，即使是这个制作镜像的人，过一段时间后也无法记清具体在操作的。&lt;/p&gt;
</content></entry>

  <entry><title>macOS 安装 Docker 以及修改镜像源</title><link href="https://ihehehe.com/posts/macOS 安装 Docker以及修改镜像源/"/><id>https://ihehehe.com/posts/macOS 安装 Docker以及修改镜像源/</id><updated>2017-08-26T10:22:00.000Z</updated><content type="html">&lt;h3&gt;macOS 安装 Docker&lt;/h3&gt;
&lt;h4&gt;系统要求&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/docker-for-mac/&quot;&gt;Docker for Mac&lt;/a&gt; 要求系统最低为 macOS 10.10.3 Yosemite。如果系统不满足需求，可以安装 &lt;a href=&quot;https://docs.docker.com/toolbox/overview/&quot;&gt;Docker Toolbox&lt;/a&gt;。&lt;/p&gt;
&lt;h4&gt;安装&lt;/h4&gt;
&lt;h5&gt;使用 Homebrew 安装&lt;/h5&gt;
&lt;p&gt;&lt;a href=&quot;http://brew.sh/&quot;&gt;Homebrew&lt;/a&gt; 的 &lt;a href=&quot;https://caskroom.github.io/&quot;&gt;Cask&lt;/a&gt; 已经支持 Docker for Mac，因此可以很方便的使用 Homebrew Cask 来进行安装：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ brew cask install docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;手动下载安装&lt;/h4&gt;
&lt;p&gt;如果需要手动下载，请点击以下链接下载 &lt;a href=&quot;https://download.docker.com/mac/stable/Docker.dmg&quot;&gt;Stable&lt;/a&gt; 或 &lt;a href=&quot;https://download.docker.com/mac/edge/Docker.dmg&quot;&gt;Edge&lt;/a&gt; 版本的 Docker for Mac。&lt;/p&gt;
&lt;p&gt;如同 macOS 其它软件一样，安装也非常简单，双击下载的 &lt;code&gt;.dmg&lt;/code&gt; 文件，然后将那只叫 &lt;a href=&quot;https://blog.docker.com/2013/10/call-me-moby-dock/&quot;&gt;Moby&lt;/a&gt; 的鲸鱼图标拖拽到 &lt;code&gt;Application&lt;/code&gt; 文件夹即可（其间需要输入用户密码）。&lt;/p&gt;
&lt;h4&gt;运行&lt;/h4&gt;
&lt;p&gt;先打开docker，然后启动终端后，通过命令可以检查安装后的 Docker 版本。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Shell&quot;&gt;[HesjdeMacBook-Pro]:~ hesj$ docker --version
Docker version 18.03.1-ce, build 9ee9f40
[HesjdeMacBook-Pro]:~ hesj$ docker-compose --version
docker-compose version 1.21.1, build 5a3f1a3
[HesjdeMacBook-Pro]:~ hesj$ docker-machine --version
docker-machine version 0.14.0, build 89b8332
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;docker version&lt;/code&gt;、&lt;code&gt;docker info&lt;/code&gt; 都正常的话，可以尝试运行一个 &lt;a href=&quot;https://store.docker.com/images/nginx/&quot;&gt;Nginx 服务器&lt;/a&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ docker run -d -p 80:80 --name webserver nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;服务运行后，可以访问 &lt;a href=&quot;http://localhost/&quot;&gt;http://localhost&lt;/a&gt;，如果看到了 &amp;quot;Welcome to nginx!&amp;quot;，就说明 Docker for Mac 安装成功了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ws4.sinaimg.cn/large/006tNc79gy1frsjbqrp29j317w0k841v.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;要停止 Nginx 服务器并删除执行下面的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;[HesjdeMacBook-Pro]:~ hesj$ docker stop webserver
webserver
[HesjdeMacBook-Pro]:~ hesj$ docker rm webserver
webserver
[HesjdeMacBook-Pro]:~ hesj$
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;镜像加速&lt;/h4&gt;
&lt;p&gt;鉴于国内网络问题，后续拉取 Docker 镜像十分缓慢，强烈建议安装 Docker 之后配置 &lt;a href=&quot;https://yeasy.gitbooks.io/docker_practice/content/install/mirror.html&quot;&gt;国内镜像加速&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;国内从 Docker Hub 拉取镜像有时会遇到困难，此时可以配置镜像加速器。Docker 官方和国内很多云服务商都提供了国内加速器服务，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.docker.com/registry/recipes/mirror/#use-case-the-china-registry-mirror&quot;&gt;Docker 官方提供的中国 registry mirror&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cr.console.aliyun.com/#/accelerator&quot;&gt;阿里云加速器&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.daocloud.io/mirror#accelerator-doc&quot;&gt;DaoCloud 加速器&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;macOS&lt;/h5&gt;
&lt;p&gt;对于使用 macOS 的用户，在任务栏点击 Docker for mac 应用图标 -&amp;gt; Perferences... -&amp;gt; Daemon -&amp;gt; Registry mirrors。在列表中填写加速器地址 &lt;code&gt;https://registry.docker-cn.com&lt;/code&gt;。修改完成之后，点击 &lt;code&gt;Apply &amp;amp; Restart&lt;/code&gt; 按钮，Docker 就会重启并应用配置的镜像地址了。&lt;/p&gt;
&lt;h5&gt;检查加速器是否生效&lt;/h5&gt;
&lt;p&gt;配置加速器之后，如果拉取镜像仍然十分缓慢，请手动检查加速器配置是否生效，在命令行执行 &lt;code&gt;docker info&lt;/code&gt;，如果从结果中看到了如下内容，说明配置成功。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Registry Mirrors:
 https://registry.docker-cn.com/
&lt;/code&gt;&lt;/pre&gt;
</content></entry>

  <entry><title>初识docker</title><link href="https://ihehehe.com/posts/初识docker/"/><id>https://ihehehe.com/posts/初识docker/</id><updated>2017-08-25T19:28:00.000Z</updated><content type="html">&lt;h3&gt;什么是docker&lt;/h3&gt;
&lt;p&gt;Docker 使用 Google 公司推出的 &lt;a href=&quot;https://golang.org/&quot;&gt;Go 语言&lt;/a&gt; 进行开发实现，基于 Linux 内核的 &lt;a href=&quot;https://zh.wikipedia.org/wiki/Cgroups&quot;&gt;cgroup&lt;/a&gt;，&lt;a href=&quot;https://en.wikipedia.org/wiki/Linux_namespaces&quot;&gt;namespace&lt;/a&gt;，以及&lt;a href=&quot;https://en.wikipedia.org/wiki/Aufs&quot;&gt;AUFS&lt;/a&gt; 类的 &lt;a href=&quot;https://en.wikipedia.org/wiki/Union_mount&quot;&gt;Union FS&lt;/a&gt; 等技术，对进程进行封装隔离，属于 &lt;a href=&quot;https://en.wikipedia.org/wiki/Operating-system-level_virtualization&quot;&gt;操作系统层面的虚拟化技术&lt;/a&gt;。由于隔离的进程独立于宿主和其它的隔离的进程，因此也称其为容器。&lt;/p&gt;
&lt;p&gt;Docker 在容器的基础上，进行了进一步的封装，从文件系统、网络互联到进程隔离等等，极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。&lt;/p&gt;
&lt;p&gt;下面的图片比较了 Docker 和传统虚拟化方式的不同之处。传统虚拟机技术是虚拟出一套硬件后，在其上运行一个完整操作系统，在该系统上再运行所需应用进程；而容器内的应用进程直接运行于宿主的内核，容器内没有自己的内核，而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://yeasy.gitbooks.io/docker_practice/content/introduction/_images/virtualization.png&quot; alt=&quot;传统虚拟化&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://yeasy.gitbooks.io/docker_practice/content/introduction/_images/docker.png&quot; alt=&quot;Docker&quot;&gt;&lt;/p&gt;
&lt;h3&gt;为什么要使用 Docker？&lt;/h3&gt;
&lt;h4&gt;1.更高效的利用系统资源&lt;/h4&gt;
&lt;p&gt;由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销，Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度，都要比传统虚拟机技术更高效。因此，相比虚拟机技术，一个相同配置的主机，往往可以运行更多数量的应用。&lt;/p&gt;
&lt;h4&gt;2.更快速的启动时间&lt;/h4&gt;
&lt;p&gt;传统的虚拟机技术启动应用服务往往需要数分钟，而 Docker 容器应用，由于直接运行于宿主内核，无需启动完整的操作系统，因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。&lt;/p&gt;
&lt;h4&gt;3.一致的运行环境&lt;/h4&gt;
&lt;p&gt;开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致，导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境，确保了应用运行环境一致性，从而不会再出现 &lt;em&gt;「这段代码在我机器上没问题啊」&lt;/em&gt; 这类问题。&lt;/p&gt;
&lt;h4&gt;4.持续交付和部署&lt;/h4&gt;
&lt;p&gt;对开发和运维（&lt;a href=&quot;https://zh.wikipedia.org/wiki/DevOps&quot;&gt;DevOps&lt;/a&gt;）人员来说，最希望的就是一次创建或配置，可以在任意地方正常运行。&lt;/p&gt;
&lt;p&gt;使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 &lt;a href=&quot;https://yeasy.gitbooks.io/docker_practice/content/image/dockerfile&quot;&gt;Dockerfile&lt;/a&gt; 来进行镜像构建，并结合 &lt;a href=&quot;https://en.wikipedia.org/wiki/Continuous_integration&quot;&gt;持续集成(Continuous Integration)&lt;/a&gt; 系统进行集成测试，而运维人员则可以直接在生产环境中快速部署该镜像，甚至结合 &lt;a href=&quot;https://en.wikipedia.org/wiki/Continuous_delivery&quot;&gt;持续部署(Continuous Delivery/Deployment)&lt;/a&gt; 系统进行自动部署。&lt;/p&gt;
&lt;p&gt;而且使用 &lt;code&gt;Dockerfile&lt;/code&gt; 使镜像构建透明化，不仅仅开发团队可以理解应用运行环境，也方便运维团队理解应用运行所需条件，帮助更好的生产环境中部署该镜像。&lt;/p&gt;
&lt;h4&gt;5.更轻松的迁移&lt;/h4&gt;
&lt;p&gt;由于 Docker 确保了执行环境的一致性，使得应用的迁移更加容易。Docker 可以在很多平台上运行，无论是物理机、虚拟机、公有云、私有云，甚至是笔记本，其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用，迁移到另一个平台上，而不用担心运行环境的变化导致应用无法正常运行的情况。&lt;/p&gt;
&lt;h4&gt;6.更轻松的维护和扩展&lt;/h4&gt;
&lt;p&gt;Docker 使用的&lt;strong&gt;分层存储&lt;/strong&gt;以及&lt;strong&gt;镜像的技术&lt;/strong&gt;，使得应用重复部分的复用更为容易，也使得应用的维护更新更加简单，基于基础镜像进一步扩展镜像也变得非常简单。此外，Docker 团队同各个开源项目团队一起维护了一大批高质量的 &lt;a href=&quot;https://store.docker.com/search?q=&amp;amp;source=verified&amp;amp;type=image&quot;&gt;官方镜像&lt;/a&gt;，既可以直接在生产环境使用，又可以作为基础进一步定制，大大的降低了应用服务的镜像制作成本。&lt;/p&gt;
&lt;h4&gt;对比传统虚拟机总结&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;容器&lt;/th&gt;
&lt;th&gt;虚拟机&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;启动&lt;/td&gt;
&lt;td&gt;秒级&lt;/td&gt;
&lt;td&gt;分钟级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;硬盘使用&lt;/td&gt;
&lt;td&gt;一般为 &lt;code&gt;MB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;一般为 &lt;code&gt;GB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;性能&lt;/td&gt;
&lt;td&gt;接近原生&lt;/td&gt;
&lt;td&gt;弱于&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;系统支持量&lt;/td&gt;
&lt;td&gt;单机支持上千个容器&lt;/td&gt;
&lt;td&gt;一般几十个&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Docker基本概念&lt;/h3&gt;
&lt;h4&gt;1.镜像（&lt;code&gt;Image&lt;/code&gt;）&lt;/h4&gt;
&lt;p&gt;我们都知道，操作系统分为内核和用户空间。对于 Linux 而言，内核启动后，会挂载 &lt;code&gt;root&lt;/code&gt; 文件系统为其提供用户空间支持。而 Docker 镜像（Image），就相当于是一个 &lt;code&gt;root&lt;/code&gt; 文件系统。比如官方镜像 &lt;code&gt;ubuntu:16.04&lt;/code&gt; 就包含了完整的一套 Ubuntu 16.04 最小系统的 &lt;code&gt;root&lt;/code&gt; 文件系统。&lt;/p&gt;
&lt;p&gt;Docker 镜像是一个特殊的文件系统，除了提供容器运行时所需的程序、库、资源、配置等文件外，还包含了一些为运行时准备的一些配置参数（如匿名卷、环境变量、用户等）。镜像不包含任何动态数据，其内容在构建之后也不会被改变。&lt;/p&gt;
&lt;h5&gt;分层存储&lt;/h5&gt;
&lt;p&gt;因为镜像包含操作系统完整的 &lt;code&gt;root&lt;/code&gt; 文件系统，其体积往往是庞大的，因此在 Docker 设计时，就充分利用 &lt;a href=&quot;https://en.wikipedia.org/wiki/Union_mount&quot;&gt;Union FS&lt;/a&gt; 的技术，将其设计为分层存储的架构。所以严格来说，镜像并非是像一个 ISO 那样的打包文件，镜像只是一个虚拟的概念，其实际体现并非由一个文件组成，而是由一组文件系统组成，或者说，由多层文件系统联合组成。&lt;/p&gt;
&lt;p&gt;**镜像构建时，会一层层构建，前一层是后一层的基础。每一层构建完就不会再发生改变，后一层上的任何改变只发生在自己这一层。**比如，删除前一层文件的操作，实际不是真的删除前一层的文件，而是仅在当前层标记为该文件已删除。在最终容器运行的时候，虽然不会看到这个文件，但是实际上该文件会一直跟随镜像。因此，在构建镜像的时候，需要额外小心，每一层尽量只包含该层需要添加的东西，任何额外的东西应该在该层构建结束前清理掉。&lt;/p&gt;
&lt;p&gt;分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层，然后进一步添加新的层，以定制自己所需的内容，构建新的镜像。&lt;/p&gt;
&lt;h4&gt;2.容器（&lt;code&gt;Container&lt;/code&gt;）&lt;/h4&gt;
&lt;p&gt;镜像（&lt;code&gt;Image&lt;/code&gt;）和容器（&lt;code&gt;Container&lt;/code&gt;）的关系，就像是面向对象程序设计中的 &lt;code&gt;类&lt;/code&gt; 和 &lt;code&gt;实例&lt;/code&gt; 一样，镜像是静态的定义，容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。&lt;/p&gt;
&lt;p&gt;容器的实质是进程，但与直接在宿主执行的进程不同，容器进程运行于属于自己的独立的 &lt;a href=&quot;https://en.wikipedia.org/wiki/Linux_namespaces&quot;&gt;命名空间&lt;/a&gt;。因此容器可以拥有自己的 &lt;code&gt;root&lt;/code&gt; 文件系统、自己的网络配置、自己的进程空间，甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里，使用起来，就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。&lt;/p&gt;
&lt;p&gt;前面讲过镜像使用的是分层存储，容器也是如此。每一个容器运行时，是以镜像为基础层，在其上创建一个当前容器的存储层，我们可以称这个为容器运行时读写而准备的存储层为&lt;strong&gt;容器存储层&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;容器存储层的生存周期和容器一样，容器消亡时，容器存储层也随之消亡。因此，任何保存于容器存储层的信息都会随容器删除而丢失。&lt;/p&gt;
&lt;p&gt;按照 Docker 最佳实践的要求，容器不应该向其存储层内写入任何数据，容器存储层要保持无状态化。所有的文件写入操作，都应该使用 &lt;a href=&quot;https://yeasy.gitbooks.io/docker_practice/content/data_management/volume.html&quot;&gt;数据卷（Volume）&lt;/a&gt;、或者绑定宿主目录，在这些位置的读写会跳过容器存储层，直接对宿主（或网络存储）发生读写，其性能和稳定性更高。&lt;/p&gt;
&lt;h4&gt;3.仓库（&lt;code&gt;Repository&lt;/code&gt;）&lt;/h4&gt;
&lt;p&gt;镜像构建完成后，可以很容易的在当前宿主机上运行，但是，如果需要在其它服务器上使用这个镜像，我们就需要一个集中的存储、分发镜像的服务，&lt;a href=&quot;https://yeasy.gitbooks.io/docker_practice/content/repository/registry.html&quot;&gt;Docker Registry&lt;/a&gt; 就是这样的服务。&lt;/p&gt;
&lt;p&gt;一个 &lt;strong&gt;Docker Registry&lt;/strong&gt; 中可以包含多个&lt;strong&gt;仓库&lt;/strong&gt;（&lt;code&gt;Repository&lt;/code&gt;）；每个仓库可以包含多个&lt;strong&gt;标签&lt;/strong&gt;（&lt;code&gt;Tag&lt;/code&gt;）；每个标签对应一个镜像。&lt;/p&gt;
&lt;p&gt;通常，一个仓库会包含同一个软件不同版本的镜像，而标签就常用于对应该软件的各个版本。我们可以通过 &lt;code&gt;&amp;lt;仓库名&amp;gt;:&amp;lt;标签&amp;gt;&lt;/code&gt; 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签，将以 &lt;code&gt;latest&lt;/code&gt; 作为默认标签。&lt;/p&gt;
&lt;h5&gt;Docker Registry 公开服务&lt;/h5&gt;
&lt;p&gt;Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像，并可能提供收费服务供用户管理私有镜像。&lt;/p&gt;
&lt;p&gt;最常使用的 Registry 公开服务是官方的 &lt;a href=&quot;https://hub.docker.com/&quot;&gt;Docker Hub&lt;/a&gt;，这也是默认的 Registry，并拥有大量的高质量的官方镜像。除此以外，还有 &lt;a href=&quot;https://coreos.com/&quot;&gt;CoreOS&lt;/a&gt; 的 &lt;a href=&quot;https://quay.io/repository/&quot;&gt;Quay.io&lt;/a&gt;，CoreOS 相关的镜像存储在这里；Google 的 &lt;a href=&quot;https://cloud.google.com/container-registry/&quot;&gt;Google Container Registry&lt;/a&gt;，&lt;a href=&quot;http://kubernetes.io/&quot;&gt;Kubernetes&lt;/a&gt; 的镜像使用的就是这个服务。&lt;/p&gt;
&lt;p&gt;由于某些原因，在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务（&lt;code&gt;Registry Mirror&lt;/code&gt;），这些镜像服务被称为&lt;strong&gt;加速器&lt;/strong&gt;。常见的有 &lt;a href=&quot;https://cr.console.aliyun.com/#/accelerator&quot;&gt;阿里云加速器&lt;/a&gt;、&lt;a href=&quot;https://www.daocloud.io/mirror#accelerator-doc&quot;&gt;DaoCloud 加速器&lt;/a&gt; 等。使用加速器会直接从国内的地址下载 Docker Hub 的镜像，比直接从 Docker Hub 下载速度会提高很多。&lt;/p&gt;
&lt;p&gt;国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 &lt;a href=&quot;https://hub.tenxcloud.com/&quot;&gt;时速云镜像仓库&lt;/a&gt;、&lt;a href=&quot;https://c.163.com/hub#/m/library/&quot;&gt;网易云镜像服务&lt;/a&gt;、&lt;a href=&quot;https://hub.daocloud.io/&quot;&gt;DaoCloud 镜像市场&lt;/a&gt;、&lt;a href=&quot;https://cr.console.aliyun.com/&quot;&gt;阿里云镜像库&lt;/a&gt; 等。&lt;/p&gt;
&lt;h5&gt;私有 Docker Registry&lt;/h5&gt;
&lt;p&gt;除了使用公开服务外，用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 &lt;a href=&quot;https://store.docker.com/images/registry/&quot;&gt;Docker Registry&lt;/a&gt;镜像，可以直接使用做为私有 Registry 服务。&lt;/p&gt;
</content></entry>

  <entry><title>SSH远程文件/目录传输命令scp</title><link href="https://ihehehe.com/posts/SSH远程文件-目录传输命令scp/"/><id>https://ihehehe.com/posts/SSH远程文件-目录传输命令scp/</id><updated>2017-08-23T11:17:28.000Z</updated><content type="html">&lt;p&gt;在使用VPS时会经常在不同VPS间互相备份数据或者转移数据，大部分情况下VPS上都已经安装了Nginx或者类似的web server，直接将要传输的文件放到web server的目录，然后在目标机器上执行：&lt;code&gt;wget https://www.vpser.net/testfile.zip&lt;/code&gt; 就行了。当VPS上没有安装web server和ftp server的时候或感觉上面的方法比较麻烦，那么用 scp命令 就会排上用场。&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2&gt;scp是什么？&lt;/h2&gt;
&lt;p&gt;scp是secure copy的简写，用于在Linux下进行远程拷贝文件的命令，和它类似的命令有cp，不过cp只是在本机进行拷贝不能跨服务器，而且scp传输是加密的。可能会稍微影响一下速度。&lt;/p&gt;
&lt;h2&gt;scp有什么用？&lt;/h2&gt;
&lt;p&gt;1、我们需要获得远程服务器上的某个文件，远程服务器既没有配置ftp服务器，没有开启web服务器，也没有做共享，无法通过常规途径获得文件时，只需要通过 scp命令 便可轻松的达到目的。&lt;/p&gt;
&lt;p&gt;2、我们需要将本机上的文件上传到远程服务器上，远程服务器没有开启ftp服务器或共享，无法通过常规途径上传是，只需要通过scp命令便可以轻松的达到目的。&lt;/p&gt;
&lt;h2&gt;scp使用方法&lt;/h2&gt;
&lt;h3&gt;获取远程服务器上的文件&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;scp -P 2222 root@www.vpser.net:/root/lnmp0.4.tar.gz /home/lnmp0.4.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上端口大写P 为参数，2222 表示更改SSH端口后的端口，如果没有更改SSH端口可以不用添加该参数。 root@www.vpser.net 表示使用root用户登录远程服务器www.vpser.net，:/root/lnmp0.4.tar.gz 表示远程服务器上的文件，最后面的/home/lnmp0.4.tar.gz表示保存在本地上的路径和文件名。还可能会用到p参数保持目录文件的权限访问时间等。&lt;/p&gt;
&lt;h3&gt;获取远程服务器上的目录&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;*scp -P 2222 -r root@www.vpser.net:/root/lnmp0.4/ /home/lnmp0.4/*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上端口大写P 为参数，2222 表示更改SSH端口后的端口，如果没有更改SSH端口可以不用添加该参数。-r 参数表示递归复制（即复制该目录下面的文件和目录）；root@www.vpser.net 表示使用root用户登录远程服务器www.vpser.net，:/root/lnmp0.4/ 表示远程服务器上的目录，最后面的/home/lnmp0.4/表示保存在本地上的路径。&lt;/p&gt;
&lt;h3&gt;将本地文件上传到服务器上&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;scp -P 2222 /home/lnmp0.4.tar.gz **root@www.vpser.net:/root/lnmp0.4.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上端口大写P 为参数，2222 表示更改SSH端口后的端口，如果没有更改SSH端口可以不用添加该参数。 /home/lnmp0.4.tar.gz表示本地上准备上传文件的路径和文件名。root@www.vpser.net 表示使用root用户登录远程服务器www.vpser.net，:/root/lnmp0.4.tar.gz 表示保存在远程服务器上目录和文件名。&lt;/p&gt;
&lt;h3&gt;将本地目录上传到服务器上&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-Shell&quot;&gt;scp -P 2222 -r /home/lnmp0.4/** root@www.vpser.net:/root/lnmp0.4/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上 端口大写P 为参数，2222 表示更改SSH端口后的端口，如果没有更改SSH端口可以不用添加该参数。-r 参数表示递归复制（即复制该目录下面的文件和目录）；/home/lnmp0.4/表示准备要上传的目录，root@www.vpser.net 表示使用root用户登录远程服务器www.vpser.net，:/root/lnmp0.4/ 表示保存在远程服务器上的目录位置。&lt;/p&gt;
&lt;h3&gt;可能有用的几个参数 :&lt;/h3&gt;
&lt;p&gt;-v 和大多数 linux 命令中的 -v 意思一样 , 用来显示进度 . 可以用来查看连接 , 认证 , 或是配置错误 .&lt;/p&gt;
&lt;p&gt;-C 使能压缩选项 .&lt;/p&gt;
&lt;p&gt;-4 强行使用 IPV4 地址 .&lt;/p&gt;
&lt;p&gt;-6 强行使用 IPV6 地址 .&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;转载&lt;a href=&quot;https://www.vpser.net/manage/scp.html&quot;&gt;Linux SSH远程文件/目录传输命令scp&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content></entry>

</feed>
