<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>阿叩·杂记</title>
  
  <subtitle>学着写博客</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://zyouwei.com/"/>
  <updated>2019-09-10T07:19:40.000Z</updated>
  <id>http://zyouwei.com/</id>
  
  <author>
    <name>Angus_Lu</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>ActiveMQ 高可用配置（LevelDB）</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/ActiveMQ-High-availability.html"/>
    <id>http://zyouwei.com/技术笔记/Java/ActiveMQ-High-availability.html</id>
    <published>2019-09-10T07:19:40.000Z</published>
    <updated>2019-09-10T07:19:40.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>本文写于2017年8月14日</p></blockquote><p>ActiveMQ的LevelDB方案是在<code>5.9.0</code>版本开始引入，使用zookeeper来决定当前Replicas中的Master与Slave，并不采用zookeeper来存储MQ数据；其工作原理是生产者（或消费者）向AMQ Broker发送（或消费）消息时，Master将消息内容同步到Slave之后再响应生产者（或消费者）结果，同步的方式有多种，在下面详细描述。</p><a id="more"></a><h2 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h2><blockquote><p>这种配置方案能够实现<code>n/2 + 1</code>的容错率，也就是三台服务器允许挂一台，五台能宕掉2台依次类推。</p></blockquote><table><thead><tr><th>服务</th><th>机器IP</th></tr></thead><tbody><tr><td>zookeeper服务器</td><td><code>192.168.1.1</code>、<code>192.168.1.2</code>、<code>192.168.1.3</code></td></tr><tr><td>activemq服务器</td><td><code>192.168.1.4</code>、<code>192.168.1.5</code>、<code>192.168.1.6</code></td></tr></tbody></table><h2 id="搭建ZooKeeper"><a href="#搭建ZooKeeper" class="headerlink" title="搭建ZooKeeper"></a>搭建ZooKeeper</h2><p>请参考：<a href="https://blog.51cto.com/nileader/1032157" target="_blank" rel="noopener">ZooKeeper管理员指南——部署与管理ZooKeeper</a></p><h2 id="下载activemq"><a href="#下载activemq" class="headerlink" title="下载activemq"></a>下载activemq</h2><pre class=" language-bash"><code class="language-bash"> curl -O http://www.apache.org/dyn/closer.cgi?path<span class="token operator">=</span>/activemq/apache-activemq/5.9.0/apache-activemq-5.9.0-bin.tar.gz</code></pre><h2 id="修改activemq配置"><a href="#修改activemq配置" class="headerlink" title="修改activemq配置"></a>修改activemq配置</h2><p>在<code>apache-activemq-5.9.0/conf/activemq.xml</code>找到这种字样的配置项目:</p><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>persistenceAdapter</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kahaDB</span> <span class="token attr-name">directory</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${activemq.data}/kahadb<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>persistenceAdapter</span><span class="token punctuation">></span></span></code></pre><p>默认情况下activemq是走kahadb存储的，这里替换为:</p><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>persistenceAdapter</span><span class="token punctuation">></span></span>    &lt;replicatedLevelDB  directory="${activemq.data}/leveldb"        replicas="3"        bind="tcp://0.0.0.0:0"        zkAddress="192.168.1.1:2181,192.168.1.2:2181,192.168.1.3:2181”          hostname="192.168.1.193"        sync=“quorum_mem"        zkPath="/activemq/leveldb-stores”        zkSessionTimeout="15s"    /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>persistenceAdapter</span><span class="token punctuation">></span></span></code></pre><p>配置项说明：</p><table><thead><tr><th>属性名</th><th>默认值</th><th>备注</th></tr></thead><tbody><tr><td>replicas</td><td>3</td><td>Cluster的节点数，只有大于等于(replicas/2)+1个节点存活时，Cluster才能提供服务</td></tr><tr><td>securityToken</td><td></td><td>各个副本节点复制数据的安全令牌，所有副本节点必须保持一致</td></tr><tr><td>zkAddress</td><td>127.0.0.1:28181</td><td>zookeeper服务列表，使用逗号分割</td></tr><tr><td>zkPassword</td><td></td><td>连接zookeeper服务器的密码</td></tr><tr><td>zkPath</td><td>/default</td><td>zookeeper上存放MQServer主从选举信息的节点位置</td></tr><tr><td>zkSesssionTmeout</td><td>2s</td><td>zookeeper探测MQ节点失效的周期</td></tr><tr><td>sync</td><td>quorum_mem</td><td>MQ节点间的数据同步策略。可选项如下：local_mem、local_disk、remote_mem、remote_disk、quorum_mem、quorum_disk；建议：quorum_mem</td></tr><tr><td>bind</td><td>tcp://0.0.0.0:61619</td><td>当此节点变成主节点时，在绑定的地址与端口上想其他副本发送消息。配置为：tcp://0.0.0.0:0时，会自动探测端口绑定</td></tr><tr><td>hostname</td><td></td><td>此节点的主机名，不设置时会自动检测；</td></tr><tr><td>weight</td><td>1</td><td>此节点成为主节点的选举权重，值越大越可能成为主节点</td></tr></tbody></table><blockquote><p>更多配置说明请参考：<a href="http://activemq.apache.org/replicated-leveldb-store.html" target="_blank" rel="noopener">http://activemq.apache.org/replicated-leveldb-store.html</a></p></blockquote><h2 id="剩下的就是启动服务了"><a href="#剩下的就是启动服务了" class="headerlink" title="剩下的就是启动服务了"></a>剩下的就是启动服务了</h2><p>首先开启三台服务器上的zookeeper服务：</p><pre class=" language-bash"><code class="language-bash">/opt/zookeeper-3.4.5/bin/zkServer.sh start</code></pre><p>然后分别开启三台服务器上的activemq服务：</p><pre class=" language-bash"><code class="language-bash">/opt/apache-activemq-5.9.0/bin/activemq start  <span class="token comment" spellcheck="true">#使用console可以在控制台启动，方便日志观察</span></code></pre><p>好了，基本没什么问题了。然后就开始访问：<a href="http://192.168.1.1:8161" target="_blank" rel="noopener">http://192.168.1.1:8161</a> 或者 <a href="http://192.168.1.2:8161" target="_blank" rel="noopener">http://192.168.1.2:8161</a> 或者 <a href="http://192.168.1.3:8161" target="_blank" rel="noopener">http://192.168.1.3:8161</a>, 这三个链接看哪个能访问，哪个消息服务器则表示存活提供服务。</p><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>关掉1、2、3其中任何一台，经过测试能够正常提供服务，客户端会自动切换连接，达到预期目的。</p><h2 id="客户端连接使用failover方案"><a href="#客户端连接使用failover方案" class="headerlink" title="客户端连接使用failover方案"></a>客户端连接使用failover方案</h2><pre class=" language-bash"><code class="language-bash">failover:<span class="token punctuation">(</span>tcp://192.168.1.1:61616,tcp://192.168.1.2:61616,tcp://192.168.1.3:61616<span class="token punctuation">)</span>?randomize<span class="token operator">=</span>false<span class="token operator">&amp;</span>timeout<span class="token operator">=</span>5000<span class="token operator">&amp;</span>initialReconnectDelay<span class="token operator">=</span>200<span class="token operator">&amp;</span>reconnectDelayExponent<span class="token operator">=</span>2<span class="token operator">&amp;</span>maxReconnectDelay<span class="token operator">=</span>3000<span class="token operator">&amp;</span>maxReconnectAttempts<span class="token operator">=</span>5<span class="token operator">&amp;</span>startupMaxReconnectAttempts<span class="token operator">=</span>5</code></pre><h2 id="配置jolokia"><a href="#配置jolokia" class="headerlink" title="配置jolokia"></a>配置jolokia</h2><p>在activemq的启动参数中，添加：</p><pre class=" language-bash"><code class="language-bash">ACTIVEMQ_OPTS<span class="token operator">=</span><span class="token string">"<span class="token variable">$ACTIVEMQ_OPTS</span> -javaagent:/home/amq/apache-activemq-5.9.1/lib/jolokia-jvm-1.2.0-agent.jar=port=8160,host=0.0.0.0"</span></code></pre><blockquote><p>更多参数选项请参考：<a href="http://www.jolokia.org/reference/html/agents.html#agents-jvm" target="_blank" rel="noopener">http://www.jolokia.org/reference/html/agents.html#agents-jvm</a></p></blockquote><h3 id="配置hawtio管理台"><a href="#配置hawtio管理台" class="headerlink" title="配置hawtio管理台"></a>配置hawtio管理台</h3><p><img src="/images/posts/2019-09/hawtio.png" alt=""></p><h2 id="QA"><a href="#QA" class="headerlink" title="QA"></a>QA</h2><ol><li><p>activemq启动出现异常，没有选举出主机。</p><p>很有可能<code>hostname</code>配置不对，建议·hostname·配置为本机内网IP，保障能与其他副本集的机器通信顺畅。 如果使用主机名的，最好在/etc/hosts中配置好其他副本集的主机路由关系，譬如：</p><pre class=" language-bash"><code class="language-bash"> 192.168.1.1   lin-1.1-mq-server 192.168.1.2   lin-1.2-mq-server 192.168.1.3   lin-1.3-mq-server</code></pre></li><li><p>开启JMX远程监控时，出现jmx.password文件权限太大的异常</p><p><code>chmod 400 apache-activemq-5.9.0/conf/jmx.password</code></p></li><li><p>配置jolokia后，启动出线Servlet异常？</p><p>在activemq 5.9.1版本中，注释掉<code>activemq.xml</code>中的<code>&lt;import resource=&quot;jetty.xml&quot;/&gt;</code>解决；</p></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;&lt;p&gt;本文写于2017年8月14日&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;ActiveMQ的LevelDB方案是在&lt;code&gt;5.9.0&lt;/code&gt;版本开始引入，使用zookeeper来决定当前Replicas中的Master与Slave，并不采用zookeeper来存储MQ数据；其工作原理是生产者（或消费者）向AMQ Broker发送（或消费）消息时，Master将消息内容同步到Slave之后再响应生产者（或消费者）结果，同步的方式有多种，在下面详细描述。&lt;/p&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
      <category term="MQ" scheme="http://zyouwei.com/tags/MQ/"/>
    
      <category term="ActiveMQ" scheme="http://zyouwei.com/tags/ActiveMQ/"/>
    
  </entry>
  
  <entry>
    <title>ActiveMQ 使用进阶</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/ActiveMQ-Useage.html"/>
    <id>http://zyouwei.com/技术笔记/Java/ActiveMQ-Useage.html</id>
    <published>2019-09-10T06:22:37.000Z</published>
    <updated>2019-09-10T06:22:37.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>本文写于2017年7月6日</p></blockquote><h2 id="Java消息服务回顾（JMS）"><a href="#Java消息服务回顾（JMS）" class="headerlink" title="Java消息服务回顾（JMS）"></a>Java消息服务回顾（JMS）</h2><h3 id="七大组件"><a href="#七大组件" class="headerlink" title="七大组件"></a>七大组件</h3><p><img src="/images/posts/2019-09/jms-cmp_1.png" alt=""></p><p><img src="/images/posts/2019-09/jms-cmp_2.png" alt=""></p><a id="more"></a><h3 id="两种模型"><a href="#两种模型" class="headerlink" title="两种模型"></a>两种模型</h3><p><img src="/images/posts/2019-09/jms-queue.png" alt=""></p><h4 id="点对点或队列模型"><a href="#点对点或队列模型" class="headerlink" title="点对点或队列模型"></a>点对点或队列模型</h4><ul><li>只有一个消费者将获得消息</li><li>生产者不需要在接收者消费该消息期间处于运行状态，接收者也同样不需要在消息发送时处于运行状态。</li><li>每一个成功处理的消息都由接收者签收</li></ul><p><img src="/images/posts/2019-09/jms-topic.png" alt=""></p><h4 id="发布者订阅者模型"><a href="#发布者订阅者模型" class="headerlink" title="发布者订阅者模型"></a>发布者订阅者模型</h4><ul><li>多个消费者可以获得消息</li><li>在发布者和订阅者之间存在时间依赖性。发布者需要建立一个订阅（subscription），以便客户能够购订阅。订阅者必须保持持续的活动状态以接收消息，除非订阅者建立了持久的订阅。在那种情况下，在订阅者未连接时发布的消息将在订阅者重新连接时重新发布。</li></ul><h2 id="ActiveMQ是一个开源的JMS服务提供者"><a href="#ActiveMQ是一个开源的JMS服务提供者" class="headerlink" title="ActiveMQ是一个开源的JMS服务提供者"></a>ActiveMQ是一个开源的JMS服务提供者</h2><p><img src="/images/posts/2019-09/activemq_1.png" alt=""></p><h3 id="名词解释"><a href="#名词解释" class="headerlink" title="名词解释"></a>名词解释</h3><ul><li><strong>Broker</strong>: JMS服务端，接收与保存生产者发送的消息内容，并推送给消费者使用；</li><li><strong>持久化消息</strong>：持久性属于消息的一个属性，生产者在发送消息时，可以指定消息属性为持久化消息，<code>broker</code>会将此消息持久化到文件或数据库之后，再推送给消费者；这样即使broker重启也不会丢失消息；<code>JMS</code>规范中，默认消息发送就是持久化的；</li><li><strong>非持久化消息</strong>：顾名思义消息不会在<code>broker</code>中持久化到文件，重启可能会产生消息丢失，非持久化消息在异步发送时，性能比持久化消息快20倍（有待考证）；</li><li><strong>同步发送</strong>：生产者发送消息到<code>broker</code>时，需要同步等待<code>broker</code>的响应；如果你不开启事务、并且发送持久化消息，<code>borker</code>将在消息持久化完成之后，才给生产者发送确认消息；这样做的好处是，<code>broker</code>一旦确认收到消息，消息将不会被丢失；可以在连接工厂上设置，也可以在单个连接上设置；</li><li><strong>异步发送</strong>：生产者发送消息到<code>broker</code>时，不用同步等待<code>broker</code>的响应；异步发送效率高，可能会出现消息丢失；可以在连接工厂上设置，也可以在单个连接上设置；</li><li><strong>非持久订阅</strong>：消息模型一定是<code>发布者/订阅者</code>模型，只有在消费者在线的情况下，才会收到生产者发送到某个<code>topic</code>的消息，消费者处于离线状态时，这个时间段的消息将不会被收到，即使消费者重新在线也将无法接受到；</li><li><strong>持久订阅</strong>：消息模型一定是发布者/订阅者模型，消费者向<code>Broker</code>注册一个自己身份的标识（<code>ClientID+订阅者名字</code>），当这个消费者离线时，<code>broker</code>会为这个标识保存发送到某个<code>topic</code>的消息，当消费者重新在线时，会根据自己标识获取到离线期间的消息；</li></ul><h3 id="Connector-Types"><a href="#Connector-Types" class="headerlink" title="Connector Types"></a>Connector Types</h3><p><img src="/images/posts/2019-09/activemq_2.png" alt=""></p><p><img src="/images/posts/2019-09/activemq_3.png" alt=""></p><p><img src="/images/posts/2019-09/activemq_4.png" alt=""></p><p><img src="/images/posts/2019-09/activemq_5.png" alt=""></p><h3 id="Client-Failover"><a href="#Client-Failover" class="headerlink" title="Client Failover"></a>Client Failover</h3><p><code>failover:(tcp://host1:61616,tcp://host2:61616)?randomize=false</code></p><p><img src="/images/posts/2019-09/activemq_6.png" alt=""></p><h2 id="与Spring整合"><a href="#与Spring整合" class="headerlink" title="与Spring整合"></a>与Spring整合</h2><h3 id="组件依赖"><a href="#组件依赖" class="headerlink" title="组件依赖"></a>组件依赖</h3><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.apache.activemq<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>activemq-all<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>5.9.0<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.springframework<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>spring-context<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>3.2.8.RELEASE<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.springframework<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>spring-jms<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>3.2.8.RELEASE<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependence</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependence</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.apache.xbean<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>xbean-spring<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>3.12<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span></code></pre><h3 id="配置生产者"><a href="#配置生产者" class="headerlink" title="配置生产者"></a>配置生产者</h3><pre class=" language-xml"><code class="language-xml"><span class="token prolog">&lt;?xml version="1.0" encoding="UTF-8"?></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>beans</span> <span class="token attr-name">default-lazy-init</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>       <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.springframework.org/schema/beans<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>amq</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://activemq.apache.org/schema/core<span class="token punctuation">"</span></span>       <span class="token attr-name"><span class="token namespace">xmlns:</span>xsi</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.w3.org/2001/XMLSchema-instance<span class="token punctuation">"</span></span>       <span class="token attr-name"><span class="token namespace">xmlns:</span>context</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.springframework.org/schema/context<span class="token punctuation">"</span></span>       <span class="token attr-name"><span class="token namespace">xsi:</span>schemaLocation</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd       http://www.springframework.org/schema/context       http://www.springframework.org/schema/context/spring-context-3.1.xsd       http://activemq.apache.org/schema/core       http://activemq.apache.org/schema/core/activemq-core.xsd<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 自动装载com.aqlu包下的所有Bean --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">context:</span>component-scan</span> <span class="token attr-name">base-package</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>com.aqlu<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 加载配置文件 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">context:</span>property-placeholder</span> <span class="token attr-name">system-properties-mode</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>OVERRIDE<span class="token punctuation">"</span></span> <span class="token attr-name">ignore-resource-not-found</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>                                  <span class="token attr-name">location</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>classpath:config.properties<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 定义消息的目的地址（queue）--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>testQueue<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.apache.activemq.command.ActiveMQQueue<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>constructor-arg</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>TestQueue<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- queue名字 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 定义消息的目的地址（topic） --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>testTopic<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.apache.activemq.command.ActiveMQTopic<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>constructor-arg</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>testTopic<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- topic名字 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 定义消息的目的地址（虚拟topic）--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>testVirtualTopic<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.apache.activemq.command.ActiveMQTopic<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>constructor-arg</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>VirtualTopic.testTopic<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- topic名字,注意这里必须要带上VirtualTopic. --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 生产者连接工厂 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">amq:</span>connectionFactory</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>producerConnectionFactory<span class="token punctuation">"</span></span> <span class="token attr-name">brokerURL</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${producer.brokerUrl}<span class="token punctuation">"</span></span>                           <span class="token attr-name">useAsyncSend</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 配置具有session缓存给你的连接工厂 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>producerCachingConnectionFactory<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.springframework.jms.connection.CachingConnectionFactory<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>targetConnectionFactory<span class="token punctuation">"</span></span> <span class="token attr-name">ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>producerConnectionFactory<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>sessionCacheSize<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${producer.sessionCacheSize}<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 简单消息转换器，能够对String、byte[]、Map、Serializable类型的消息自动转换 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>simpleMessageConverter<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.springframework.jms.support.converter.SimpleMessageConverter<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>    <span class="token comment" spellcheck="true">&lt;!--配置生产者消息发送模板--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>producerJmsTemplate<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.springframework.jms.core.JmsTemplate<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>connectionFactory<span class="token punctuation">"</span></span> <span class="token attr-name">ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>producerCachingConnectionFactory<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token comment" spellcheck="true">&lt;!-- 指定连接工厂 --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>explicitQosEnabled<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 是否启用Qos; 只有开启Qos时，设置的deliveryMode, priority, timeToLive才能生效 --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>deliveryPersistent<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 设置消息是否需要持久化, 默认为PERSISTENT --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>priority<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>4<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 设置优先级, 默认为4 越高优先级越高。kahaDB只支持3种优先级，&lt;4,=4,>4 --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>timeToLive<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${producer.timeToLive}<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 消息有效时间，单位毫秒，默认是不过期；超过消息有效期的消息将被broker“删除”；强烈建议明确设置此参数，目前我司的业务场景消息如果在一小时还未被处理的话，大部分情况都没有价值了 --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>messageConverter<span class="token punctuation">"</span></span> <span class="token attr-name">ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>simpleMessageConverter<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 指定消息转换器 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>beans</span><span class="token punctuation">></span></span></code></pre><h3 id="生产者（消息发送者）代码"><a href="#生产者（消息发送者）代码" class="headerlink" title="生产者（消息发送者）代码"></a>生产者（消息发送者）代码</h3><pre class=" language-java"><code class="language-java"><span class="token keyword">package</span> com<span class="token punctuation">.</span>aqlu<span class="token punctuation">.</span>demo<span class="token punctuation">.</span>amq<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Autowired<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Qualifier<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>jms<span class="token punctuation">.</span>core<span class="token punctuation">.</span>JmsTemplate<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span>Component<span class="token punctuation">;</span><span class="token keyword">import</span> javax<span class="token punctuation">.</span>jms<span class="token punctuation">.</span>Destination<span class="token punctuation">;</span><span class="token keyword">import</span> java<span class="token punctuation">.</span>io<span class="token punctuation">.</span>Serializable<span class="token punctuation">;</span><span class="token comment" spellcheck="true">/** * Created by aqlu on 14-6-25. */</span><span class="token annotation punctuation">@Component</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MessageSender</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token annotation punctuation">@Qualifier</span><span class="token punctuation">(</span><span class="token string">"producerJmsTemplate"</span><span class="token punctuation">)</span>    <span class="token keyword">private</span> JmsTemplate jmsTemplate<span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token annotation punctuation">@Qualifier</span><span class="token punctuation">(</span><span class="token string">"testTopic"</span><span class="token punctuation">)</span>    <span class="token keyword">private</span> Destination destination<span class="token punctuation">;</span>    <span class="token comment" spellcheck="true">/**     * 发送消息     * @param message 消息对象     */</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">send</span><span class="token punctuation">(</span><span class="token keyword">final</span> Serializable message<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">try</span><span class="token punctuation">{</span>            jmsTemplate<span class="token punctuation">.</span><span class="token function">convertAndSend</span><span class="token punctuation">(</span>destination<span class="token punctuation">,</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span><span class="token keyword">catch</span><span class="token punctuation">(</span>JmsException e<span class="token punctuation">)</span><span class="token punctuation">{</span>            <span class="token comment" spellcheck="true">// ....  TODO</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span>    <span class="token keyword">public</span> Destination <span class="token function">getDestination</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">return</span> destination<span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setDestination</span><span class="token punctuation">(</span>Destination destination<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">this</span><span class="token punctuation">.</span>destination <span class="token operator">=</span> destination<span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token keyword">public</span> JmsTemplate <span class="token function">getJmsTemplate</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">return</span> jmsTemplate<span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setJmsTemplate</span><span class="token punctuation">(</span>JmsTemplate jmsTemplate<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">this</span><span class="token punctuation">.</span>jmsTemplate <span class="token operator">=</span> jmsTemplate<span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>        ApplicationContext context <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassPathXmlApplicationContext</span><span class="token punctuation">(</span><span class="token string">"application_provider.xml"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        MessageSender sender <span class="token operator">=</span> context<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>MessageSender<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        sender<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">"Hello broker!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="配置消费者"><a href="#配置消费者" class="headerlink" title="配置消费者"></a>配置消费者</h3><pre class=" language-xml"><code class="language-xml"><span class="token prolog">&lt;?xml version="1.0" encoding="UTF-8"?></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>beans</span> <span class="token attr-name">default-lazy-init</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>       <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.springframework.org/schema/beans<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>amq</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://activemq.apache.org/schema/core<span class="token punctuation">"</span></span>       <span class="token attr-name"><span class="token namespace">xmlns:</span>xsi</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.w3.org/2001/XMLSchema-instance<span class="token punctuation">"</span></span>       <span class="token attr-name"><span class="token namespace">xmlns:</span>context</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.springframework.org/schema/context<span class="token punctuation">"</span></span>       <span class="token attr-name"><span class="token namespace">xsi:</span>schemaLocation</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd       http://www.springframework.org/schema/context       http://www.springframework.org/schema/context/spring-context-3.1.xsd       http://activemq.apache.org/schema/core       http://activemq.apache.org/schema/core/activemq-core.xsd<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 自动装载com.aqlu包下的所有Bean --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">context:</span>component-scan</span> <span class="token attr-name">base-package</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>com.aqlu<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 加载配置文件 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">context:</span>property-placeholder</span> <span class="token attr-name">system-properties-mode</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>OVERRIDE<span class="token punctuation">"</span></span> <span class="token attr-name">ignore-resource-not-found</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>                                  <span class="token attr-name">location</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>classpath:config.properties<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 定义消息的目的地址（queue）--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>testQueue<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.apache.activemq.command.ActiveMQQueue<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>constructor-arg</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>TestQueue<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- queue名字 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 定义消息的目的地址（topic） --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>testTopic<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.apache.activemq.command.ActiveMQTopic<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>constructor-arg</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>testTopic<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- topic名字 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 定义消息的目的地址（虚拟topic）--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>testVirtualQueue<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.apache.activemq.command.ActiveMQQueue<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>constructor-arg</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>VirtualTopicConsumers.${consumer.clientId}.VirtualTopic.testTopic<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 前缀根据broker配置--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 消费者连接工厂; borkerURL: broker地址； useAsyncSend：是否使用异步发送；clientID：客户端标识；--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">amq:</span>connectionFactory</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>consumerConnectionFactory<span class="token punctuation">"</span></span> <span class="token attr-name">brokerURL</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${consumer.brokerUrl}<span class="token punctuation">"</span></span>                           <span class="token attr-name">clientID</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${consumer.clientId}<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 配置具有session缓存给你的连接工厂 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>consumerCachingConnectionFactory<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.springframework.jms.connection.CachingConnectionFactory<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>targetConnectionFactory<span class="token punctuation">"</span></span> <span class="token attr-name">ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>consumerConnectionFactory<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>sessionCacheSize<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${consumer.sessionCacheSize}<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- 配置消费者监听容器 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>taskContainer<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>org.springframework.jms.listener.DefaultMessageListenerContainer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>connectionFactory<span class="token punctuation">"</span></span> <span class="token attr-name">ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>consumerConnectionFactory<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token comment" spellcheck="true">&lt;!-- 指定连接工厂--></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>destination<span class="token punctuation">"</span></span> <span class="token attr-name">ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>testTopic<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token comment" spellcheck="true">&lt;!-- 目的地址，queue或者topic --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>messageListener<span class="token punctuation">"</span></span> <span class="token attr-name">ref</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>messageConsumer<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token comment" spellcheck="true">&lt;!-- 指定消息消费监听对象，实现MessageListener 接口 --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>concurrentConsumers<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${consumer.concurrentConsumers}<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 配置监听对象的并发数 --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>maxConcurrentConsumers<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${consumer.maxConcurrentConsumers}<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 配置监听对象的最大并发数 --></span>        <span class="token comment" spellcheck="true">&lt;!--&lt;property name="clientId" value="sss"/>--></span> <span class="token comment" spellcheck="true">&lt;!-- 客户端标识，不配的话会使用连接工程配置的clientID，连接工厂也没陪的话会自动删除一个 --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>sessionAcknowledgeModeName<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>CLIENT_ACKNOWLEDGE<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 在使用Spring的情况下，AUTO确认模式会在消息进入业务方法前进行回应，Client则会在之后，DUL_OK可以延迟消息回应并批量处理，但这可能导致消息重复发送。 --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>sessionTransacted<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token comment" spellcheck="true">&lt;!-- 是否开启事务，默认false--></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>subscriptionDurable<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 是否持久化订阅，此属性仅在订阅topic时有效；持久化订阅时，监听对象的并发数只能为1；在使用queue消费时，请注释掉，否则会影响启动 --></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>durableSubscriptionName<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>node1<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span><span class="token comment" spellcheck="true">&lt;!-- 持久化订阅者名称；持久化订阅开启才生效，持久化订阅时必配；在使用queue消费时，请注释掉，否则会影响启动 --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>beans</span> <span class="token punctuation">></span></span></code></pre><h3 id="消费者（消息接收者）代码"><a href="#消费者（消息接收者）代码" class="headerlink" title="消费者（消息接收者）代码"></a>消费者（消息接收者）代码</h3><pre class=" language-java"><code class="language-java"><span class="token keyword">package</span> com<span class="token punctuation">.</span>aqlu<span class="token punctuation">.</span>demo<span class="token punctuation">.</span>amq<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span>Component<span class="token punctuation">;</span><span class="token keyword">import</span> javax<span class="token punctuation">.</span>jms<span class="token punctuation">.</span>*<span class="token punctuation">;</span><span class="token comment" spellcheck="true">/** * Created by aqlu on 14-6-25. */</span><span class="token annotation punctuation">@Component</span><span class="token punctuation">(</span><span class="token string">"messageConsumer"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MessageConsumer</span> <span class="token keyword">implements</span> <span class="token class-name">MessageListener</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Override</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onMessage</span><span class="token punctuation">(</span>Message message<span class="token punctuation">)</span>  <span class="token punctuation">{</span>        <span class="token keyword">try</span> <span class="token punctuation">{</span>            <span class="token keyword">if</span> <span class="token punctuation">(</span>message <span class="token keyword">instanceof</span> <span class="token class-name">TextMessage</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>                System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>Thread<span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span>  <span class="token string">"接受到消息: "</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>TextMessage<span class="token punctuation">)</span> message<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getText</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>message <span class="token keyword">instanceof</span> <span class="token class-name">ObjectMessage</span><span class="token punctuation">)</span><span class="token punctuation">{</span>                System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>Thread<span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span>  <span class="token string">"接受到消息: "</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>ObjectMessage<span class="token punctuation">)</span> message<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token punctuation">}</span><span class="token keyword">else</span><span class="token punctuation">{</span>                System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>Thread<span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"不能识别此消息:"</span> <span class="token operator">+</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token punctuation">}</span>        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">JMSException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>            e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>        ApplicationContext context <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassPathXmlApplicationContext</span><span class="token punctuation">(</span><span class="token string">"application_consumer.xml"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h2 id="业务使用建议"><a href="#业务使用建议" class="headerlink" title="业务使用建议"></a>业务使用建议</h2><h3 id="消息内容"><a href="#消息内容" class="headerlink" title="消息内容"></a>消息内容</h3><ol><li>消息内容不宜过大，建议尽量使用简单文本消息，每个消息体大小不超过1kb；如有大的消息需要传递，建议使用业务设计避免开，譬如：货品状态发生变化时，消息体里面仅保存货品id以及变化后的状态，如需其他货品信息可以由业务根据货品id调接口查询；</li><li>合理配置消息有效期；</li></ol><h3 id="发送方式"><a href="#发送方式" class="headerlink" title="发送方式"></a>发送方式</h3><ol><li>建议所有发送方式设置为持久化发送；非持久化消息在<code>broker</code>重启或故障时，会有消息丢失的可能；</li><li>对消息生产者来说，异步发送会会极大的提高系统的吞吐量；ActiveMQ缺省是采用异步发送发送，但按照JMS规范，如果不开启事务且发送持久化消息时，会强制采用同步方式发送，在这种情况下，每一次发送都是同步的，而且阻塞到收到<code>broker</code>的应答，这个应答保证了<code>broker</code>已经成功地将消息持久化，而且不会丢失，但是这样作也严重地影响了性能。如果你的系统可以容忍少量的小事丢失，也可以指定在不开启事务时采用<code>异步方式</code>发送<code>持久化消息</code>；配置方式如下：<pre class=" language-properties"><code class="language-properties"><span class="token attr-name"> 配置BorkerURL参数</span><span class="token punctuation">:</span> <span class="token attr-value">failover:(tcp://host1:61616,tcp://host2:51616)?randomize=false&amp;jms.useAsyncSend=true</span><span class="token attr-name"> 配置连接工厂useAsyncSend属性</span><span class="token punctuation">:</span> <span class="token attr-value">&lt;amq:connectionFactory id="producerConnectionFactory" brokerURL="${producer.brokerUrl}" useAsyncSend="true"/></span></code></pre></li><li>建议消息生产者对于重要消息根据自己业务建立补发机制，针对发送消息异常情况进行补发；</li></ol><h3 id="消费方式"><a href="#消费方式" class="headerlink" title="消费方式"></a>消费方式</h3><ol><li>建议消费者在<code>MessageListener</code>的实现类中，异步处理自己的业务逻辑，以达到快消费；处理流程示例：<img src="/images/posts/2019-09/activemq_7.png" alt=""></li><li>在生产者产能过剩且消费者处理能力很快的情况下，可以通过设置消费者预取值来提升性能；配置方式如下：<pre class=" language-properties"><code class="language-properties"><span class="token attr-name"> 配置所有消费者的预取值</span><span class="token punctuation">:</span> <span class="token attr-value">failover:(tcp://host1:61616,tcp://host2:51616)?randomize=false&amp;jms.prefetchPolicy.all=50</span><span class="token attr-name"> 配置所有queue消费者的预取值</span><span class="token punctuation">:</span> <span class="token attr-value">failover:(tcp://host1:61616,tcp://host2:51616)?randomize=false&amp;jms.prefetchPolicy.queuePrefetch=1000</span><span class="token attr-name"> 配置所有topic消费者的预取值</span><span class="token punctuation">:</span> <span class="token attr-value">failover:(tcp://host1:61616,tcp://host2:51616)?randomize=false&amp;jms.prefetchPolicy.topicPrefetch=1000</span></code></pre></li><li>建议消费端处理接口采用幂等设计；</li></ol><h3 id="Consumer数量配置"><a href="#Consumer数量配置" class="headerlink" title="Consumer数量配置"></a>Consumer数量配置</h3><p><code>MessageListenerContainer</code>中允许定义并发的<code>consumer</code>数量，在<code>Queue</code>模式下，通过增大并发<code>consumer</code>数量可以提高消费能力，而且<code>Queue</code>中的消息只会被消费一次；但在<code>Topic</code>模式下，增大并发<code>consumer</code>并不会提升消费能力，因为在<code>Topic</code>模式中，每个<code>consumer</code>都是独立的订阅者，即每个<code>consumer</code>都会收到<strong>全量</strong>的<code>topic</code>消息，通常情况下<code>concurrentConsumers</code>与<code>maxConcurrentConsumers</code>都配置为1；</p><h3 id="模型选择"><a href="#模型选择" class="headerlink" title="模型选择"></a>模型选择</h3><ol><li>如何实现消费者在集群环境下，不重复消费<code>Topic</code>消息？<ol><li>设置集群中的所有<code>Consumer</code>的<code>ClientID</code>相同；可以在<code>ConnectionFactory</code>中配置，也可以在<code>MessageListenerContainer</code>中配置; <code>ActiveMQ</code>会保证同一时刻、只有唯一的<code>ClientID</code>的<code>consumer</code>连接上<code>Broker</code>，直到这个<code>consumer</code>关闭，集群中的其他<code>consumer</code>才能连接；<strong>优点</strong>：生产、消费速度较均衡，<code>broker</code>压力小；<strong>弱点</strong>：消费者为冷备，压力集中在一个负载上；<strong>推荐使用</strong>；</li><li>使用<code>virtualtopic</code>；生产者发送特殊的<code>Topic</code>消息到<code>broker</code>，<code>broker</code>负责给<code>topic</code>的订阅者创建<code>Queue</code>，并将消息放入<code>queue</code>，然后消费者从<code>Queue</code>中消费；<strong>优点</strong>：可以配置多个消费者，消费速度快；<strong>弱点</strong>：生产速度慢，特别是同步发送持久化消息时，<code>broker</code>压力大；</li></ol></li><li>如何实现消费者下线后，恢复上线时能接受到离线期间的<code>Topic</code>消息？<ol><li>消费者配置持久化订阅；持久化订阅需要消费者同时满足几个条件：①指定<code>ClientID</code>；②<code>concurrentConsumers</code>与<code>maxConcurrentConsumers</code>都配置为1；③开启<code>subscriptionDurable</code>与设定<code>durableSubscriptionName</code>；<strong>推荐使用</strong>；</li><li>使用<code>virtualtopic;</code></li></ol></li></ol><h3 id="延时与定时投递"><a href="#延时与定时投递" class="headerlink" title="延时与定时投递"></a>延时与定时投递</h3><p>ActiveMQ提供了一种<code>broker</code>端消息定时调度机制，能够满足用户这样的场景：有时候不希望消息马上被broker投递出去，而是想要<code>60秒</code>之后发给消费者，或者我们想让消息每隔一定时间段投递一次，一共投递指定的次数；虽然ActiveMQ提供了满足这样场景的功能，但<strong>不推荐使用</strong>；原因如下：</p><ol><li>基于目前的AMQ设计，发送持久化延时消息时可能会导致某些持久化文件无法正常清除；</li><li>额外增大了broker的负担；</li><li>在使用新的leveldb高可靠方案时，由于这些信息依然还存在单独的kahaDB中，apache已明确声明暂不支持；</li></ol><p>如有延时与定时投递的需求场景，建议业务层使用JDK自带的<code>DelayQueue</code>来实现，生产者在到达投递时间后再将消息投递到<code>broker</code>中；</p>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;&lt;p&gt;本文写于2017年7月6日&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;Java消息服务回顾（JMS）&quot;&gt;&lt;a href=&quot;#Java消息服务回顾（JMS）&quot; class=&quot;headerlink&quot; title=&quot;Java消息服务回顾（JMS）&quot;&gt;&lt;/a&gt;Java消息服务回顾（JMS）&lt;/h2&gt;&lt;h3 id=&quot;七大组件&quot;&gt;&lt;a href=&quot;#七大组件&quot; class=&quot;headerlink&quot; title=&quot;七大组件&quot;&gt;&lt;/a&gt;七大组件&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;/images/posts/2019-09/jms-cmp_1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;/images/posts/2019-09/jms-cmp_2.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
      <category term="MQ" scheme="http://zyouwei.com/tags/MQ/"/>
    
      <category term="ActiveMQ" scheme="http://zyouwei.com/tags/ActiveMQ/"/>
    
  </entry>
  
  <entry>
    <title>基于SpringBoot快速发布SOAP接口</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/Spring-WebServive.html"/>
    <id>http://zyouwei.com/技术笔记/Java/Spring-WebServive.html</id>
    <published>2019-01-02T06:22:35.000Z</published>
    <updated>2019-01-02T06:22:35.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="WebService、SOAP、WSDL的简单回顾"><a href="#WebService、SOAP、WSDL的简单回顾" class="headerlink" title="WebService、SOAP、WSDL的简单回顾"></a>WebService、SOAP、WSDL的简单回顾</h2><h3 id="WebService是什么？"><a href="#WebService是什么？" class="headerlink" title="WebService是什么？"></a>WebService是什么？</h3><p>WebService在的概念很容易引起误解，很多初级开发人员的认知WebService接口等同于Soap接口，其实这是一种错误的认知。</p><p>WebService是相对于“本地服务”（使用同一台机器提供的服务，不需要网络）来说的一个概念。”网络服务”（Web Service）的本质，就是通过网络来调用其他服务器的资源。</p><p>常见的WebService接口类型有RPC、SOAP、Restful风格。</p><a id="more"></a><h3 id="SOAP是什么？"><a href="#SOAP是什么？" class="headerlink" title="SOAP是什么？"></a>SOAP是什么？</h3><p>SOAP全称 Simple Object Access Protocal，中文翻译为简单对象访问协议。它是一种用于交换XML编码信息的轻量级协议，用来描述传递信息的格式。一个完整的SOAP协议结构如下：</p><pre class=" language-xml"><code class="language-xml"><span class="token prolog">&lt;?xml version="1.0"?></span><span class="token comment" spellcheck="true">&lt;!--必需的 Envelope 元素，可把此 XML 文档标识为一条 SOAP 消息--></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soap:</span>Envelope</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>soap</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.w3.org/2001/12/soap-envelope<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">soap:</span>encoding</span><span class="token style-attr language-css"><span class="token attr-name"><span class="token attr-name">Style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">http</span><span class="token punctuation">:</span>//www<span class="token number">.</span>w<span class="token number">3.</span>org/<span class="token number">2001</span>/<span class="token number">12</span>/soap-encoding</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span>      <span class="token comment" spellcheck="true">&lt;!--可选的 Header 元素，包含头部信息--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soap:</span>Header</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">soap:</span>Header</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!--必需的 Body 元素，包含所有的调用和响应信息--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soap:</span>Body</span><span class="token punctuation">></span></span>        <span class="token comment" spellcheck="true">&lt;!--可选的 Fault 元素，提供有关在处理此消息所发生错误的信息--></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soap:</span>Fault</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">soap:</span>Fault</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">soap:</span>Body</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">soap:</span>Envelope</span><span class="token punctuation">></span></span></code></pre><h3 id="WSDL是什么？"><a href="#WSDL是什么？" class="headerlink" title="WSDL是什么？"></a>WSDL是什么？</h3><p>WSDL全称Web Services Description Language，中文翻译为Web服务描述语言。它将Web服务描述定义为一组服务访问点，客户端可以通过这些服务访问点对服务进行访问。WSDL的内容也是基于XML的格式，用于描述WebServiced及其函数、参数、返回值。</p><p>一个基本的WSDL文档包含7个重要的元素。下面是一个WSDL文档的示例：</p><pre class=" language-xml"><code class="language-xml"><span class="token prolog">&lt;?xml version="1.0" encoding="UTF-8" standalone="no"?></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>definitions</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>wsdl</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://schemas.xmlsoap.org/wsdl/<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>sch</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://myself.org<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>soap</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://schemas.xmlsoap.org/wsdl/soap/<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>tns</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://myself.org<span class="token punctuation">"</span></span> <span class="token attr-name">targetNamespace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://myself.org<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!-- Types: 数据类型定义的容器，它使用某种类型系统(一般地使用XML Schema中的类型系统) --></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>types</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>schema</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>xs</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.w3.org/2001/XMLSchema<span class="token punctuation">"</span></span> <span class="token attr-name">elementFormDefault</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>qualified<span class="token punctuation">"</span></span> <span class="token attr-name">targetNamespace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://myself.org<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLServiceRequest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>complexType</span><span class="token punctuation">></span></span>                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>sequence</span><span class="token punctuation">></span></span>                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>input<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>xs:string<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>sequence</span><span class="token punctuation">></span></span>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>complexType</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>element</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLServiceResponse<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>complexType</span><span class="token punctuation">></span></span>                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>sequence</span><span class="token punctuation">></span></span>                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>result<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>xs:string<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>sequence</span><span class="token punctuation">></span></span>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>complexType</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>element</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>schema</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>types</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!--Message: 通信消息的数据结构的抽象类型化定义。使用Types所定义的类型来定义整个消息的数据结构--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>message</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLServiceRequest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>part</span> <span class="token attr-name">element</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tns:XMLServiceRequest<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLServiceRequest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>part</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>message</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>message</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLServiceResponse<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>part</span> <span class="token attr-name">element</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tns:XMLServiceResponse<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLServiceResponse<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>part</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>message</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!--PortType: 对于某个访问入口点类型所支持的操作的抽象集合，这些操作可以由一个或多个服务访问点来支持--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>portType</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>DemoServiceSoap<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token comment" spellcheck="true">&lt;!--Operation: 对服务中所支持的操作的抽象描述，一般单个Operation描述了一个访问入口的请求/响应消息对--></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>operation</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLService<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>input</span> <span class="token attr-name">message</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tns:XMLServiceRequest<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLServiceRequest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>input</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>output</span> <span class="token attr-name">message</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tns:XMLServiceResponse<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLServiceResponse<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>output</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>operation</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>portType</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!--Binding: 特定端口类型的具体协议和数据格式规范的绑定--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>binding</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>DemoServiceSoapSoap11<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tns:DemoServiceSoap<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soap:</span>binding</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value">document</span><span class="token punctuation">"</span></span> <span class="token attr-name">transport</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://schemas.xmlsoap.org/soap/http<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>operation</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLService<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soap:</span>operation</span> <span class="token attr-name">soapAction</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://myself.org/DemoService.XMLService<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLServiceRequest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soap:</span>body</span> <span class="token attr-name">use</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>literal<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>input</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>output</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>XMLServiceResponse<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soap:</span>body</span> <span class="token attr-name">use</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>literal<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>output</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>operation</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>binding</span><span class="token punctuation">></span></span>    <span class="token comment" spellcheck="true">&lt;!--Service: 相关服务访问点的集合--></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>service</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>DemoServiceSoap<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token comment" spellcheck="true">&lt;!--Port: 定义为协议/数据格式绑定与具体Web访问地址组合的单个服务访问点--></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">wsdl:</span>port</span> <span class="token attr-name">binding</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tns:DemoServiceSoapSoapSoap11<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>DemoServiceSoapSoapSoap11<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soap:</span>address</span> <span class="token attr-name">location</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://127.0.0.1:8080/ws/DemoService.XMLService.CLS<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>port</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>service</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">wsdl:</span>definitions</span><span class="token punctuation">></span></span></code></pre><h2 id="基于SpringBoot快速发布SOAP接口"><a href="#基于SpringBoot快速发布SOAP接口" class="headerlink" title="基于SpringBoot快速发布SOAP接口"></a>基于SpringBoot快速发布SOAP接口</h2><blockquote><p>本文所写示例基于springboot 2.0.4.RELEASE版本。</p></blockquote><h3 id="添加依赖"><a href="#添加依赖" class="headerlink" title="添加依赖"></a>添加依赖</h3><pre class=" language-xml"><code class="language-xml"> <span class="token comment" spellcheck="true">&lt;!-- tag::springws[] --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-web-services<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>wsdl4j<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>wsdl4j<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span class="token comment" spellcheck="true">&lt;!-- end::springws[] --></span></code></pre><h3 id="定义数据结构，创建src-main-resources-countries-xsd"><a href="#定义数据结构，创建src-main-resources-countries-xsd" class="headerlink" title="定义数据结构，创建src/main/resources/countries.xsd"></a>定义数据结构，创建<code>src/main/resources/countries.xsd</code></h3><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>schema</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>xs</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.w3.org/2001/XMLSchema<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>tns</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://spring.io/guides/gs-producing-web-service<span class="token punctuation">"</span></span>           <span class="token attr-name">targetNamespace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://spring.io/guides/gs-producing-web-service<span class="token punctuation">"</span></span> <span class="token attr-name">elementFormDefault</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>qualified<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>getCountryRequest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>complexType</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>sequence</span><span class="token punctuation">></span></span>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>name<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>xs:string<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>sequence</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>complexType</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>element</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>getCountryResponse<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>complexType</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>sequence</span><span class="token punctuation">></span></span>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>country<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tns:country<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>sequence</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>complexType</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>element</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>complexType</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>country<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>sequence</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>name<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>xs:string<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>population<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>xs:int<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>capital<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>xs:string<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>element</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>currency<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tns:currency<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>sequence</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>complexType</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>simpleType</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>currency<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>restriction</span> <span class="token attr-name">base</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>xs:string<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>enumeration</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>GBP<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>enumeration</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>EUR<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">xs:</span>enumeration</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>PLN<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>restriction</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>simpleType</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">xs:</span>schema</span><span class="token punctuation">></span></span></code></pre><h3 id="根据Schma生成实体对象"><a href="#根据Schma生成实体对象" class="headerlink" title="根据Schma生成实体对象"></a>根据Schma生成实体对象</h3><p>在<code>pom.xml</code>中添加如下插件，执行<code>mvn compile</code>即可在<code>src/main/java</code>目录下发现生成的实体对象类。</p><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugin</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.codehaus.mojo<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>jaxb2-maven-plugin<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>1.6<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>executions</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>execution</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>id</span><span class="token punctuation">></span></span>xjc<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>id</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>goals</span><span class="token punctuation">></span></span>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>goal</span><span class="token punctuation">></span></span>xjc<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>goal</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>goals</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>execution</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>executions</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>schemaDirectory</span><span class="token punctuation">></span></span>${project.basedir}/src/main/resources/<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>schemaDirectory</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>outputDirectory</span><span class="token punctuation">></span></span>${project.basedir}/src/main/java<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>outputDirectory</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>clearOutputDir</span><span class="token punctuation">></span></span>false<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>clearOutputDir</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugin</span><span class="token punctuation">></span></span></code></pre><h3 id="创建数据仓库访问类"><a href="#创建数据仓库访问类" class="headerlink" title="创建数据仓库访问类"></a>创建数据仓库访问类</h3><pre class=" language-java"><code class="language-java"><span class="token keyword">import</span> javax<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>PostConstruct<span class="token punctuation">;</span><span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>HashMap<span class="token punctuation">;</span><span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>Map<span class="token punctuation">;</span><span class="token keyword">import</span> io<span class="token punctuation">.</span>spring<span class="token punctuation">.</span>guides<span class="token punctuation">.</span>gs_producing_web_service<span class="token punctuation">.</span>Country<span class="token punctuation">;</span><span class="token keyword">import</span> io<span class="token punctuation">.</span>spring<span class="token punctuation">.</span>guides<span class="token punctuation">.</span>gs_producing_web_service<span class="token punctuation">.</span>Currency<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span>Component<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>util<span class="token punctuation">.</span>Assert<span class="token punctuation">;</span><span class="token annotation punctuation">@Component</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CountryRepository</span> <span class="token punctuation">{</span>    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Country<span class="token operator">></span> countries <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token annotation punctuation">@PostConstruct</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">initData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        Country spain <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Country</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        spain<span class="token punctuation">.</span><span class="token function">setName</span><span class="token punctuation">(</span><span class="token string">"Spain"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        spain<span class="token punctuation">.</span><span class="token function">setCapital</span><span class="token punctuation">(</span><span class="token string">"Madrid"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        spain<span class="token punctuation">.</span><span class="token function">setCurrency</span><span class="token punctuation">(</span>Currency<span class="token punctuation">.</span>EUR<span class="token punctuation">)</span><span class="token punctuation">;</span>        spain<span class="token punctuation">.</span><span class="token function">setPopulation</span><span class="token punctuation">(</span><span class="token number">46704314</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        countries<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>spain<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> spain<span class="token punctuation">)</span><span class="token punctuation">;</span>        Country poland <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Country</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        poland<span class="token punctuation">.</span><span class="token function">setName</span><span class="token punctuation">(</span><span class="token string">"Poland"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        poland<span class="token punctuation">.</span><span class="token function">setCapital</span><span class="token punctuation">(</span><span class="token string">"Warsaw"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        poland<span class="token punctuation">.</span><span class="token function">setCurrency</span><span class="token punctuation">(</span>Currency<span class="token punctuation">.</span>PLN<span class="token punctuation">)</span><span class="token punctuation">;</span>        poland<span class="token punctuation">.</span><span class="token function">setPopulation</span><span class="token punctuation">(</span><span class="token number">38186860</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        countries<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>poland<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> poland<span class="token punctuation">)</span><span class="token punctuation">;</span>        Country uk <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Country</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        uk<span class="token punctuation">.</span><span class="token function">setName</span><span class="token punctuation">(</span><span class="token string">"United Kingdom"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        uk<span class="token punctuation">.</span><span class="token function">setCapital</span><span class="token punctuation">(</span><span class="token string">"London"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        uk<span class="token punctuation">.</span><span class="token function">setCurrency</span><span class="token punctuation">(</span>Currency<span class="token punctuation">.</span>GBP<span class="token punctuation">)</span><span class="token punctuation">;</span>        uk<span class="token punctuation">.</span><span class="token function">setPopulation</span><span class="token punctuation">(</span><span class="token number">63705000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        countries<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>uk<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> uk<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token keyword">public</span> Country <span class="token function">findCountry</span><span class="token punctuation">(</span>String name<span class="token punctuation">)</span> <span class="token punctuation">{</span>        Assert<span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> <span class="token string">"The country's name must not be null"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> countries<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="创建服务类"><a href="#创建服务类" class="headerlink" title="创建服务类"></a>创建服务类</h3><pre class=" language-java"><code class="language-java"><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Autowired<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>ws<span class="token punctuation">.</span>server<span class="token punctuation">.</span>endpoint<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Endpoint<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>ws<span class="token punctuation">.</span>server<span class="token punctuation">.</span>endpoint<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>PayloadRoot<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>ws<span class="token punctuation">.</span>server<span class="token punctuation">.</span>endpoint<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>RequestPayload<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>ws<span class="token punctuation">.</span>server<span class="token punctuation">.</span>endpoint<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>ResponsePayload<span class="token punctuation">;</span><span class="token keyword">import</span> io<span class="token punctuation">.</span>spring<span class="token punctuation">.</span>guides<span class="token punctuation">.</span>gs_producing_web_service<span class="token punctuation">.</span>GetCountryRequest<span class="token punctuation">;</span><span class="token keyword">import</span> io<span class="token punctuation">.</span>spring<span class="token punctuation">.</span>guides<span class="token punctuation">.</span>gs_producing_web_service<span class="token punctuation">.</span>GetCountryResponse<span class="token punctuation">;</span><span class="token annotation punctuation">@Endpoint</span> <span class="token comment" spellcheck="true">//使用Spring WS注册该类作为处理传入SOAP消息的潜在候选者</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CountryEndpoint</span> <span class="token punctuation">{</span>    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> String NAMESPACE_URI <span class="token operator">=</span> <span class="token string">"http://spring.io/guides/gs-producing-web-service"</span><span class="token punctuation">;</span>    <span class="token keyword">private</span> CountryRepository countryRepository<span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">public</span> <span class="token function">CountryEndpoint</span><span class="token punctuation">(</span>CountryRepository countryRepository<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">this</span><span class="token punctuation">.</span>countryRepository <span class="token operator">=</span> countryRepository<span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token annotation punctuation">@PayloadRoot</span><span class="token punctuation">(</span>namespace <span class="token operator">=</span> NAMESPACE_URI<span class="token punctuation">,</span> localPart <span class="token operator">=</span> <span class="token string">"getCountryRequest"</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">//Spring WS使用它来根据消息的名称空间和localPart选择处理程序方法。</span>    <span class="token annotation punctuation">@ResponsePayload</span> <span class="token comment" spellcheck="true">//将返回值映射到响应</span>    <span class="token keyword">public</span> GetCountryResponse <span class="token function">getCountry</span><span class="token punctuation">(</span>        <span class="token annotation punctuation">@RequestPayload</span> GetCountryRequest request <span class="token comment" spellcheck="true">// 表示传入消息将映射到方法的请求参数</span>        <span class="token punctuation">)</span> <span class="token punctuation">{</span>        GetCountryResponse response <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GetCountryResponse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        response<span class="token punctuation">.</span><span class="token function">setCountry</span><span class="token punctuation">(</span>countryRepository<span class="token punctuation">.</span><span class="token function">findCountry</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> response<span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="配置WebService服务"><a href="#配置WebService服务" class="headerlink" title="配置WebService服务"></a>配置WebService服务</h3><pre class=" language-java"><code class="language-java"><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token punctuation">.</span>web<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span>ServletRegistrationBean<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>ApplicationContext<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Bean<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Configuration<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>core<span class="token punctuation">.</span>io<span class="token punctuation">.</span>ClassPathResource<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>ws<span class="token punctuation">.</span>config<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>EnableWs<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>ws<span class="token punctuation">.</span>config<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>WsConfigurerAdapter<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>ws<span class="token punctuation">.</span>transport<span class="token punctuation">.</span>http<span class="token punctuation">.</span>MessageDispatcherServlet<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>ws<span class="token punctuation">.</span>wsdl<span class="token punctuation">.</span>wsdl11<span class="token punctuation">.</span>DefaultWsdl11Definition<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>xml<span class="token punctuation">.</span>xsd<span class="token punctuation">.</span>SimpleXsdSchema<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>xml<span class="token punctuation">.</span>xsd<span class="token punctuation">.</span>XsdSchema<span class="token punctuation">;</span><span class="token annotation punctuation">@EnableWs</span> <span class="token comment" spellcheck="true">// 开启WebService</span><span class="token annotation punctuation">@Configuration</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">WebServiceConfig</span> <span class="token keyword">extends</span> <span class="token class-name">WsConfigurerAdapter</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Bean</span>    <span class="token keyword">public</span> ServletRegistrationBean <span class="token function">messageDispatcherServlet</span><span class="token punctuation">(</span>ApplicationContext applicationContext<span class="token punctuation">)</span> <span class="token punctuation">{</span>        MessageDispatcherServlet servlet <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MessageDispatcherServlet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        servlet<span class="token punctuation">.</span><span class="token function">setApplicationContext</span><span class="token punctuation">(</span>applicationContext<span class="token punctuation">)</span><span class="token punctuation">;</span>        servlet<span class="token punctuation">.</span><span class="token function">setTransformWsdlLocations</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ServletRegistrationBean</span><span class="token punctuation">(</span>servlet<span class="token punctuation">,</span> <span class="token string">"/ws/*"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 通过"/ws/*"路径来发布WSDL文件</span>    <span class="token punctuation">}</span>    <span class="token annotation punctuation">@Bean</span><span class="token punctuation">(</span>name <span class="token operator">=</span> <span class="token string">"countries"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> DefaultWsdl11Definition <span class="token function">defaultWsdl11Definition</span><span class="token punctuation">(</span>XsdSchema countriesSchema<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token comment" spellcheck="true">// 使用XsdSchema公开标准WSDL 1.1 发布描述文件，访问路径：http://&lt;host>:&lt;port>/ws/countries.wsdl</span>        DefaultWsdl11Definition wsdl11Definition <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DefaultWsdl11Definition</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        wsdl11Definition<span class="token punctuation">.</span><span class="token function">setPortTypeName</span><span class="token punctuation">(</span><span class="token string">"CountriesPort"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        wsdl11Definition<span class="token punctuation">.</span><span class="token function">setLocationUri</span><span class="token punctuation">(</span><span class="token string">"/ws"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        wsdl11Definition<span class="token punctuation">.</span><span class="token function">setTargetNamespace</span><span class="token punctuation">(</span><span class="token string">"http://spring.io/guides/gs-producing-web-service"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        wsdl11Definition<span class="token punctuation">.</span><span class="token function">setSchema</span><span class="token punctuation">(</span>countriesSchema<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> wsdl11Definition<span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token annotation punctuation">@Bean</span>    <span class="token keyword">public</span> XsdSchema <span class="token function">countriesSchema</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">SimpleXsdSchema</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ClassPathResource</span><span class="token punctuation">(</span><span class="token string">"countries.xsd"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="创建启动类"><a href="#创建启动类" class="headerlink" title="创建启动类"></a>创建启动类</h3><pre class=" language-java"><code class="language-java"><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token punctuation">.</span>SpringApplication<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token punctuation">.</span>autoconfigure<span class="token punctuation">.</span>SpringBootApplication<span class="token punctuation">;</span><span class="token annotation punctuation">@SpringBootApplication</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Application</span> <span class="token punctuation">{</span>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>        SpringApplication<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span>Application<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><h4 id="查看WSDL文件"><a href="#查看WSDL文件" class="headerlink" title="查看WSDL文件"></a>查看WSDL文件</h4><p>在浏览器中访问：<code>http://&lt;host&gt;:&lt;port&gt;/ws/countries.wsdl</code>，将会看到WSDL文件。</p><h4 id="发送SOAP测试请求"><a href="#发送SOAP测试请求" class="headerlink" title="发送SOAP测试请求"></a>发送SOAP测试请求</h4><p>本地创建<code>request.xml</code>，内容如下：</p><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soapenv:</span>Envelope</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>soapenv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://schemas.xmlsoap.org/soap/envelope/<span class="token punctuation">"</span></span>                  <span class="token attr-name"><span class="token namespace">xmlns:</span>gs</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://spring.io/guides/gs-producing-web-service<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>   <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soapenv:</span>Header</span><span class="token punctuation">/></span></span>   <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">soapenv:</span>Body</span><span class="token punctuation">></span></span>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">gs:</span>getCountryRequest</span><span class="token punctuation">></span></span>         <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">gs:</span>name</span><span class="token punctuation">></span></span>Spain<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">gs:</span>name</span><span class="token punctuation">></span></span>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">gs:</span>getCountryRequest</span><span class="token punctuation">></span></span>   <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">soapenv:</span>Body</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">soapenv:</span>Envelope</span><span class="token punctuation">></span></span></code></pre><p>使用<code>curl</code>在、在终端中执行如下指令：</p><pre class=" language-bash"><code class="language-bash">$ curl --header <span class="token string">"content-type: text/xml"</span> -d @request.xml http://localhost:8080/ws</code></pre><p>测试成功的结果如下：</p><pre class=" language-xml"><code class="language-xml"><span class="token prolog">&lt;?xml version="1.0"?></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">SOAP-ENV:</span>Envelope</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>SOAP-ENV</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://schemas.xmlsoap.org/soap/envelope/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">SOAP-ENV:</span>Header</span><span class="token punctuation">/></span></span>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">SOAP-ENV:</span>Body</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">ns2:</span>getCountryResponse</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>ns2</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://spring.io/guides/gs-producing-web-service<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">ns2:</span>country</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">ns2:</span>name</span><span class="token punctuation">></span></span>Spain<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">ns2:</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">ns2:</span>population</span><span class="token punctuation">></span></span>46704314<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">ns2:</span>population</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">ns2:</span>capital</span><span class="token punctuation">></span></span>Madrid<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">ns2:</span>capital</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">ns2:</span>currency</span><span class="token punctuation">></span></span>EUR<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">ns2:</span>currency</span><span class="token punctuation">></span></span>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">ns2:</span>country</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">ns2:</span>getCountryResponse</span><span class="token punctuation">></span></span>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">SOAP-ENV:</span>Body</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">SOAP-ENV:</span>Envelope</span><span class="token punctuation">></span></span></code></pre>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;WebService、SOAP、WSDL的简单回顾&quot;&gt;&lt;a href=&quot;#WebService、SOAP、WSDL的简单回顾&quot; class=&quot;headerlink&quot; title=&quot;WebService、SOAP、WSDL的简单回顾&quot;&gt;&lt;/a&gt;WebService、SOAP、WSDL的简单回顾&lt;/h2&gt;&lt;h3 id=&quot;WebService是什么？&quot;&gt;&lt;a href=&quot;#WebService是什么？&quot; class=&quot;headerlink&quot; title=&quot;WebService是什么？&quot;&gt;&lt;/a&gt;WebService是什么？&lt;/h3&gt;&lt;p&gt;WebService在的概念很容易引起误解，很多初级开发人员的认知WebService接口等同于Soap接口，其实这是一种错误的认知。&lt;/p&gt;&lt;p&gt;WebService是相对于“本地服务”（使用同一台机器提供的服务，不需要网络）来说的一个概念。”网络服务”（Web Service）的本质，就是通过网络来调用其他服务器的资源。&lt;/p&gt;&lt;p&gt;常见的WebService接口类型有RPC、SOAP、Restful风格。&lt;/p&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
      <category term="soap" scheme="http://zyouwei.com/tags/soap/"/>
    
      <category term="webservice" scheme="http://zyouwei.com/tags/webservice/"/>
    
  </entry>
  
  <entry>
    <title>Springboot启动异常日志被吞掉</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/Springboot-startup-error-log-be-eat-off.html"/>
    <id>http://zyouwei.com/技术笔记/Java/Springboot-startup-error-log-be-eat-off.html</id>
    <published>2018-12-26T03:53:07.000Z</published>
    <updated>2018-12-26T03:53:07.000Z</updated>
    
    <content type="html"><![CDATA[<p>项目中使用了springboot2.0.4，并集成了elasticsearch、dubbo等组件。在更新了创库代码后，发现突然程序启动不了，总是启动后tomcat自动关闭，日志信息如下： <img src="/images/posts/2018-12/image_1.png" alt="image_1.png"><a id="more"></a></p><p>很纳闷 :-(，任何异常日志都没有，出什么情况了？</p><p>第一感觉是程序有问题，更新了代码导致。可是本次更新的代码量有比较大，从源代码比对分析不靠谱，需要大量时间。另外源码有问题启动日志也应该要有报错信息。</p><h2 id="是日志级别不对吗？"><a href="#是日志级别不对吗？" class="headerlink" title="是日志级别不对吗？"></a>是日志级别不对吗？</h2><p>检查配置<code>logging.level.root</code>已经是<code>INFO</code>级别了，且没有其它级别控制。尝试将<code>logging.level.root=debug</code>，重启发现依然没有任何异常信息展现。</p><h2 id="日志输出组件冲突了吗？"><a href="#日志输出组件冲突了吗？" class="headerlink" title="日志输出组件冲突了吗？"></a>日志输出组件冲突了吗？</h2><p>由于笔者之前有过<code>log4j</code>框架切换<code>logback</code>的经验，因此怀疑<code>pom</code>中引入的组件传递依赖了一些不需要的<code>jar</code>包，导致日志组件冲突。</p><p>检查<code>pom</code>文件发现：</p><ol><li><p><code>elasticsearch-rest-client-sniffer</code>与<code>dubbo</code>组件传递依赖引入了<code>common-logging</code>组件。导致<code>spring-jcl</code>组件的<code>org.apache.commons.logging.LogFactory</code>没有被加载，而使用了<code>common-logging</code>中的<code>org.apache.commons.logging.LogFactory</code>类； 这是因为在JVM环境中存在同名的的class，类加载器根据加载顺序选择的。</p></li><li><p>在<code>spring-jcl</code>的<code>LogFactory</code>中，实现逻辑是如果<code>log4j2</code>的<code>jar</code>包存在，这优先使用<code>log4j2</code>来记录日志、然后才是<code>slf4j</code>。而在<code>common-logging</code>的<code>LogFactory</code>中，实现逻辑是如果<code>log4j</code>的<code>jar</code>包存在，优先采用<code>log4j</code>、然后采用jdk的util包中的logger记录。</p></li><li><p>在我们的启动环境中，我们采用通过<code>spring-boot-starter-logging</code>组件来输出日志，其默认采用的是<code>slf4j-api</code>来通过<code>logback</code>记录日志，<code>log4j2</code>组件是不存在的，于是<code>springboot</code>在启动时，因为类加载器的原因使用了<code>common-logging</code>的<code>LogFactory</code>，又因为<code>dubbo</code>、<code>zookeeper</code>、<code>zkclient</code>引入了<code>log4j</code>组件，导致<code>springboot</code>启动时的部分日志输出到了<code>log4j</code>，因此异常被吃掉了。</p></li></ol><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><p>在依赖的“elasticsearch-rest-client-sniffer”、“dubbo”组件中排除掉对<code>common-logging</code>的依赖。</p><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.elasticsearch.client<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>elasticsearch-rest-client-sniffer<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>${elasticsearch.version}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>exclusions</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>exclusion</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>commons-logging<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>commons-logging<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>exclusion</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>exclusions</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>com.alibaba<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>dubbo<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>${dubbo.version}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>exclusions</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>exclusion</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>commons-logging<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>commons-logging<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>exclusion</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>exclusions</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span></code></pre><p>解决后重启程序，终于看到了异常原因： <img src="/images/posts/2018-12/image_2.jpg" alt="image_2.jpg"></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;项目中使用了springboot2.0.4，并集成了elasticsearch、dubbo等组件。在更新了创库代码后，发现突然程序启动不了，总是启动后tomcat自动关闭，日志信息如下： &lt;img src=&quot;/images/posts/2018-12/image_1.png&quot; alt=&quot;image_1.png&quot;&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
      <category term="springboot" scheme="http://zyouwei.com/tags/springboot/"/>
    
  </entry>
  
  <entry>
    <title>HornetQ引发的一次生产环境故障</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/A-falut-caused-by-HornetQ.html"/>
    <id>http://zyouwei.com/技术笔记/Java/A-falut-caused-by-HornetQ.html</id>
    <published>2018-09-03T12:46:34.000Z</published>
    <updated>2018-09-03T12:46:34.000Z</updated>
    
    <content type="html"><![CDATA[<p>前段时间生产环境遇到一次故障，最终分析原因是HornetQ队列空间满造成。<a href="http://hornetq.jboss.org/" target="_blank" rel="noopener">HornetQ</a>是JBoss可能旗下的一款MQ产品，现已捐献给了Apache的ActiveMQ。下面将本次故障分析分享给大家，因涉及产品的信息安全问题，本文隐去了某些敏感信息。</p><a id="more"></a><h2 id="一、事故现场"><a href="#一、事故现场" class="headerlink" title="一、事故现场"></a>一、事故现场</h2><p>2018-06-01 运维接收到数据库锁等待的预警以及前台工作站JVM断连的告警，与此同时大量系统用户反馈数据保存失败。</p><pre><code>运维通过关停服务，kill数据库阻塞会话后重启启动服务，系统得到恢复没有反馈异常。案发现场留存线索有限，事故分析受阻。</code></pre><p>2018-07-04 15:30左右，类似“6.1大案”的问题再现。</p><pre><code>现场项目经理组织运维、开发、迅速到到位，着手恢复业务与问题修复。</code></pre><h2 id="二、分析过程"><a href="#二、分析过程" class="headerlink" title="二、分析过程"></a>二、分析过程</h2><p>从事故现场的分析，得到线索如下：</p><ol><li>在测试<code>0.36.8</code>(含PDF上传功能版本)时，发现在页面单击保存按钮时，数据库能看到三条锁等待记录；</li><li>回退<code>x-server</code>版本到<code>0.36.1</code>(PDF上传功能的前一个版本)问题解决；回退到<code>0.36.8</code>(PDF上传功能版本)问题依然存在；</li><li>故障期间对其中一台<code>x-server</code>的负载做了一个JVM线程堆的dump文件“threaddump-1530705117569.tdump”，同时做了两个JVM CPU的抽样快照。（注：从后续分析看，这条线索非常有价值——VIP线索）</li></ol><p><strong>分析线索1：</strong></p><p>第一反应是代码存在bug，在某些诱因下存在并发异常，导致数据库出现死锁。（因为问题是以数据库锁来暴漏出来的，因此把目光盯在了死锁上面，最后从结果来看此处方向走偏了，浪费了一些分析时间）。</p><p>可是故障期间，为了避免医生在故障之前已打开某功能书写页面且一直没有关闭，可能存在编辑器自动保存的并发问题，运维兄弟关闭了生产站点，重新搭了一台独立站点供排查问题。基于独立的站点保存此数据，<code>线索1</code>的现象会100%的复现。在此之前与运维确认已重启过<code>HornetQ</code>，初步排除了<code>HornetQ</code>故障；运维也重启过数据库，也初步排除数据库故障的可能。</p><p>是什么样的诱因会一直存在呢？</p><p>如果是代码存在100%复现的bug，那也不可能从6与1号稳定运行到7月4号。</p><p>无法得出结论，于是进行其它线索分析。</p><p><strong>分析线索2：</strong></p><p>不用思考就能得出一条结论，<code>0.36.8</code>(含PDF上传功能版本)存在bug。</p><p>可是反过来思考，为什么能稳定运行一个多月不出问题呢？而且生产测试环境也一直没有问题。一定还是存在某种诱因导致的。从<code>0.36.8</code>(PDF上传功能版本)的代码变更内容来看，在数据保存时，新增了JMS消息的发送，数据库<code>mapper</code>层与<code>mybatis</code>的<code>sqlmap</code>文件变化都不大，看不出造成<strong>死锁</strong>的可能。</p><p>结合自身之前项目中的一些经验，我还是把第一怀疑放在了JMS消息的发送上面，怀疑网络抖动或<code>HornetQ</code>假死。（从最后结果看，此处是转折点。猜对了第一“嫌疑人”，方向又调整了回来）</p><p>于是协调运维兄弟在现场测试环境重现问题，尝试关闭测试环境的<code>HornetQ</code>，发现虽然后台保存出现jms异常，但保存正常。尝试通过网络防火墙屏蔽<code>HornetQ</code>端口，数据也依然保存正常。</p><p>问题复现失败。</p><p>此时想起之前故障期间有做过JVM线程的dump与CPU的采样，那里面会不会有jms相关的线索呢？</p><p><strong>分析线索3:</strong></p><p>通过VisualVM打开线程dump文件，搜索“jms”关键字，发现有个<code>dubbo</code>服务线程在<code>WAITING</code>状态，里面包含了jms相关信息，如下图： <img src="/images/posts/2018-09/HornetQ_FALUT_1.png" alt="HornetQ_FALUT_1.png"></p><p>而且从线程的堆栈信息来看，确实是由数据保存时触发。于是马上打开之前的两次CPU采样，查找线程<code>&quot;DubboServerHandler-192.168.125.30:20880-thread-44&quot;</code>的相关信息。 <img src="/images/posts/2018-09/HornetQ_FALUT_3.png" alt="HornetQ_FALUT_3.png"></p><p>展开其耗时堆栈，堆栈很深，截图关键部位： <img src="/images/posts/2018-09/HornetQ_FALUT_2.png" alt="HornetQ_FALUT_2.png"></p><p>发现<code>Semaphore.acquire()</code>的耗时达到了<code>17430ms</code>，已超过了前台数据保存时，dubbo接口调用设置的10秒超时时间，这能解释现场保存数据报错时提示的dubbo超时异常信息。</p><p><em>新的疑问又来了：</em></p><blockquote><ol><li><p>HornetQ客户端在发送消息时，到底在等什么呢？</p></li><li><p>为什么JMS没有结束而数据库里面却有三条锁对象呢？难道是JMS消息发送在数据库事务中？</p></li></ol></blockquote><p>问题一步一步往下分析，</p><p><strong>分析疑问1：</strong></p><p>虽然对JMS规范是了然于心，但之前没有使用HornetQ的经验，于是请求Google，搜索“hornetq accquireCredits”: <img src="/images/posts/2018-09/HornetQ_FALUT_4.png" alt="HornetQ_FALUT_4.png"></p><p>看到第一条结果的标题立马怦然心动，看来有人也“hang”住过啊。赶紧点开阅读（在此附上文章链接，方便后续读者阅读。<a href="https://developer.jboss.org/thread/217626）：" target="_blank" rel="noopener">https://developer.jboss.org/thread/217626）：</a> <img src="/images/posts/2018-09/HornetQ_FALUT_5.png" alt="HornetQ_FALUT_5.png"></p><p>从这篇讨论信息中，得到几个重点信息，HornetQ应该有针对<strong>队列有大小控制</strong>，超过大小后通过配置可能会<strong>阻塞</strong>消息生产者。于是马上查阅HornetQ官方文档：(<a href="http://docs.jboss.org/hornetq/2.4.0.Final/docs/user-manual/html_single/index.html" target="_blank" rel="noopener">http://docs.jboss.org/hornetq/2.4.0.Final/docs/user-manual/html_single/index.html</a>) <img src="/images/posts/2018-09/HornetQ_FALUT_6.png" alt="HornetQ_FALUT_6.png"> <img src="/images/posts/2018-09/HornetQ_FALUT_7.png" alt="HornetQ_FALUT_7.png"></p><p>通过官方文档，明确知道了对应的配置项，于是马上快速验证生产环境的配置： <img src="/images/posts/2018-09/HornetQ_FALUT_8.png" alt="HornetQ_FALUT_8.png"></p><p>线上配置队列容量为<code>10485760 byte</code>，即<code>10MB</code>。且开启了<code>BLOCK(阻塞)</code>策略。于是马上进一步验证PDF消息队列当前大小： <img src="/images/posts/2018-09/HornetQ_FALUT_9.png" alt="HornetQ_FALUT_9.png"></p><p>果然队列容量已超过<code>10MB</code>的限制。为什么会挤压这么多消息没处理呢？与现场同事确认，原来是之前现场部署PDF打印服务因机器中病毒，机器已关闭。</p><p>到此，我们可以得出一个结论，现场因为机器中毒，关闭了我们的一台服务，导致消息不能消费，全部积压在了HornetQ上，从而导致了<code>x-server</code>发送消息时受到阻塞。</p><p><em>此时有同事提问：为什么HornetQ重启了也没用呢？</em></p><p>通过查阅源码，发现HornetQ客户端工具类中，默认都是采用<code>“PERSISTENT”</code>方式发送消息，即持久化方式发送。HornetQ服务端接收到持久化消息后，都会保存到磁盘来避免意外丢失。重启HornetQ服务时，消息从磁盘重新加载到内存，因此队列空间依然是满的。</p><p>根据上面的结论，在本次测试环境中，通过程序将HornetQ中pdf任务队列塞满，果然此时保存数据失败，并得到与事故发生时同样的调用超时异常。观测数据库锁对象，信息与故障期间完全一致，同样是三条锁数据。至此，问题我们也已经复现。</p><p><em>如何基于这种情况恢复业务呢？</em></p><blockquote><p>删除HornetQ的保存数据的文件夹data，并重启HornetQ。然后在前端页面保存数据，提示保存成功。</p></blockquote><p><strong>分析疑问2：</strong></p><p>为什么JMS没有结束而数据库里面却有三条锁对象呢？难道是JMS消息发送在数据库事务中？</p><p>翻阅源码，发现<code>x-server</code>项目中，事务是通过aop配置，拦截了所有实现“AutoAopService”接口的类，默认在方法开始前打开jdbc事务，并在方法正常结束提交事务、异常结束回滚事务。X数据文档保存的实现类“XServicesImpl”正是派生自“AutoAopService”，因此默认“XServicesImpl”下面的所有方法都是在完整的jdbc事务中执行，因此当JMS消息发送未完成时，数据库事务一直没有被关闭。</p><p>三、事故总结 从导火索看，现场关闭了PDF服务的机器，导致了消息没有消费者，从而导致了HornetQ的积压，进一步影响到<code>x-server</code>的保存，并通过jdbc事务触发了数据库锁等待的报警。</p><p>整个链路是雪崩式的垮塌，通过这个事件，后续的改善措施如下：</p><ol><li>现场迅速恢复PDF服务的机器。（不需要改任何程序，现场业务就能恢复）</li><li>HornetQ的队列达到容量上限后，策略的配置方式请运维进行优化，从官方文档描述来看，建议设置为<code>PAGE</code>或<code>FAIL</code>，不建议设置为<code>BLOCK</code>或<code>DROP</code>。</li><li>对HornetQ队列容量纳入监控，超过阈值后进行告警。</li><li><code>x-server</code>在保存时，将JMS消息的发送提到jdbc事务外面，并采用异步方式发送。（即使后续HornetQ发生意外，也不影响数据库和用户正常的保存流程）</li></ol>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;前段时间生产环境遇到一次故障，最终分析原因是HornetQ队列空间满造成。&lt;a href=&quot;http://hornetq.jboss.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HornetQ&lt;/a&gt;是JBoss可能旗下的一款MQ产品，现已捐献给了Apache的ActiveMQ。下面将本次故障分析分享给大家，因涉及产品的信息安全问题，本文隐去了某些敏感信息。&lt;/p&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
      <category term="MQ" scheme="http://zyouwei.com/tags/MQ/"/>
    
  </entry>
  
  <entry>
    <title>Spring MVC中@RequestBody、@ResponseBody如何接收Abstract或Interface类型的参数？</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/Abstract-Class-With-Responsebody.html"/>
    <id>http://zyouwei.com/技术笔记/Java/Abstract-Class-With-Responsebody.html</id>
    <published>2018-06-01T02:53:53.000Z</published>
    <updated>2018-06-01T02:53:53.000Z</updated>
    
    <content type="html"><![CDATA[<p>在使用Spring-MVC对外发布Rest接口时，在某些场景中，入参可能会希望是一个接口类型或者抽象类型。SpingMVC的序列化默认采用的是<code>Jackson</code>来实现入参与出参的序列化，在调用方传递一个json字符串时，如何将json字符串转换为具体的实现class呢？</p><a id="more"></a><p>如果不做任何处理？你可能会得到一个类似如下的异常：</p><pre class=" language-java"><code class="language-java">Caused by<span class="token operator">:</span> com<span class="token punctuation">.</span>fasterxml<span class="token punctuation">.</span>jackson<span class="token punctuation">.</span>databind<span class="token punctuation">.</span>JsonMappingException<span class="token operator">:</span> Can not construct instance of com<span class="token punctuation">.</span>xxx<span class="token punctuation">.</span>Task<span class="token punctuation">,</span> problem<span class="token operator">:</span> <span class="token keyword">abstract</span> types either need to be mapped to concrete types<span class="token punctuation">,</span> have custom deserializer<span class="token punctuation">,</span> or be instantiated with additional type information at <span class="token punctuation">[</span>Source<span class="token operator">:</span> java<span class="token punctuation">.</span>io<span class="token punctuation">.</span>PushbackInputStream<span class="token annotation punctuation">@4e40388</span><span class="token punctuation">;</span> line<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> column<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">]</span> <span class="token punctuation">(</span>through reference chain<span class="token operator">:</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>ArrayList<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span>    at com<span class="token punctuation">.</span>fasterxml<span class="token punctuation">.</span>jackson<span class="token punctuation">.</span>databind<span class="token punctuation">.</span>JsonMappingException<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>JsonMappingException<span class="token punctuation">.</span>java<span class="token operator">:</span><span class="token number">148</span><span class="token punctuation">)</span>    at com<span class="token punctuation">.</span>fasterxml<span class="token punctuation">.</span>jackson<span class="token punctuation">.</span>databind<span class="token punctuation">.</span>DeserializationContext<span class="token punctuation">.</span><span class="token function">instantiationException</span><span class="token punctuation">(</span>DeserializationContext<span class="token punctuation">.</span>java<span class="token operator">:</span><span class="token number">892</span><span class="token punctuation">)</span>    at com<span class="token punctuation">.</span>fasterxml<span class="token punctuation">.</span>jackson<span class="token punctuation">.</span>databind<span class="token punctuation">.</span>deser<span class="token punctuation">.</span>AbstractDeserializer<span class="token punctuation">.</span><span class="token function">deserialize</span><span class="token punctuation">(</span>AbstractDeserializer<span class="token punctuation">.</span>java<span class="token operator">:</span><span class="token number">139</span><span class="token punctuation">)</span>    at com<span class="token punctuation">.</span>fasterxml<span class="token punctuation">.</span>jackson<span class="token punctuation">.</span>databind<span class="token punctuation">.</span>deser<span class="token punctuation">.</span>std<span class="token punctuation">.</span>CollectionDeserializer<span class="token punctuation">.</span><span class="token function">deserialize</span><span class="token punctuation">(</span>CollectionDeserializer<span class="token punctuation">.</span>java<span class="token operator">:</span><span class="token number">245</span><span class="token punctuation">)</span>    at com<span class="token punctuation">.</span>fasterxml<span class="token punctuation">.</span>jackson<span class="token punctuation">.</span>databind<span class="token punctuation">.</span>deser<span class="token punctuation">.</span>std<span class="token punctuation">.</span>CollectionDeserializer<span class="token punctuation">.</span><span class="token function">deserialize</span><span class="token punctuation">(</span>CollectionDeserializer<span class="token punctuation">.</span>java<span class="token operator">:</span><span class="token number">217</span><span class="token punctuation">)</span>    at com<span class="token punctuation">.</span>fasterxml<span class="token punctuation">.</span>jackson<span class="token punctuation">.</span>databind<span class="token punctuation">.</span>deser<span class="token punctuation">.</span>std<span class="token punctuation">.</span>CollectionDeserializer<span class="token punctuation">.</span><span class="token function">deserialize</span><span class="token punctuation">(</span>CollectionDeserializer<span class="token punctuation">.</span>java<span class="token operator">:</span><span class="token number">25</span><span class="token punctuation">)</span>    at com<span class="token punctuation">.</span>fasterxml<span class="token punctuation">.</span>jackson<span class="token punctuation">.</span>databind<span class="token punctuation">.</span>ObjectMapper<span class="token punctuation">.</span><span class="token function">_readMapAndClose</span><span class="token punctuation">(</span>ObjectMapper<span class="token punctuation">.</span>java<span class="token operator">:</span><span class="token number">3736</span><span class="token punctuation">)</span>    at com<span class="token punctuation">.</span>fasterxml<span class="token punctuation">.</span>jackson<span class="token punctuation">.</span>databind<span class="token punctuation">.</span>ObjectMapper<span class="token punctuation">.</span><span class="token function">readValue</span><span class="token punctuation">(</span>ObjectMapper<span class="token punctuation">.</span>java<span class="token operator">:</span><span class="token number">2810</span><span class="token punctuation">)</span></code></pre><p>那么应该如何处理呢？接下来我们以具体的示例来演示。</p><p>假设我们需要实现一个向后台添加打印任务的接口，任务的类型分为课程打印、成绩单打印。（在此我们不讨论接口设计的合理性，只讨论存技术的实现）。</p><p>首先我们定义一个任务接口：</p><pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">PrintTask</span> <span class="token keyword">extends</span> <span class="token class-name">Serializable</span><span class="token punctuation">{</span>    Type <span class="token function">getType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">enum</span> Type<span class="token punctuation">{</span>        COURSE<span class="token punctuation">,</span>        SCORE    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p>接着我们定义两个具体的任务实现类：</p><pre class=" language-java"><code class="language-java"><span class="token annotation punctuation">@Data</span> <span class="token comment" spellcheck="true">// 此注解来自`lombok`，具体作用请参考：http://projectlombok.org</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CoursePrintTask</span> <span class="token keyword">implements</span> <span class="token class-name">PrintTask</span><span class="token punctuation">{</span>    <span class="token keyword">private</span> String course<span class="token punctuation">;</span>    <span class="token keyword">public</span> Type <span class="token function">getType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>        <span class="token keyword">return</span> PrintTask<span class="token punctuation">.</span>Type<span class="token punctuation">.</span>COURSE<span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token annotation punctuation">@Data</span> <span class="token comment" spellcheck="true">// 此注解来自`lombok`，具体作用请参考：http://projectlombok.org</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ScorePrintTask</span> <span class="token keyword">implements</span> <span class="token class-name">PrintTask</span><span class="token punctuation">{</span>    <span class="token keyword">private</span> Double score<span class="token punctuation">;</span>    <span class="token keyword">public</span> Type <span class="token function">getType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>        <span class="token keyword">return</span> PrintTask<span class="token punctuation">.</span>Type<span class="token punctuation">.</span>SCORE<span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p>定义Rest接口：</p><pre class=" language-java"><code class="language-java"><span class="token annotation punctuation">@RestController</span><span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"/task"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Slf4j</span> <span class="token comment" spellcheck="true">// 此注解来自`lombok`，具体作用请参考：http://projectlombok.org</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">TaskController</span><span class="token punctuation">{</span>    <span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token string">"/print"</span><span class="token punctuation">,</span> method <span class="token operator">=</span> RequestMethod<span class="token punctuation">.</span>POST<span class="token punctuation">)</span>    <span class="token keyword">public</span> Task <span class="token function">print</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> Task task<span class="token punctuation">)</span><span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"received task: {}"</span><span class="token punctuation">,</span> task<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment" spellcheck="true">// TODO print ...</span>        <span class="token keyword">return</span> task<span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 直接返回任务对象</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p>构造请求：</p><pre class=" language-bash"><code class="language-bash">curl -XPOST http://localhost:8080/task/print -d <span class="token string">'{"course": "english", "type": "SCORE"}'</span></code></pre><p>不出意外，如上请求将会触发后端<strong>异常</strong>，且异常堆栈与文章开头所列类似。这是因为<code>Jackson</code>不知道该采用哪个子类来对<code>{&quot;course&quot;: &quot;english&quot;, &quot;type&quot;: &quot;SCORE&quot;}</code>进行反序列化。</p><p>如何告诉<code>Jackson</code>采用何种类型进行序列化呢？要做的事情也特别简单，代码如下：</p><pre class=" language-java"><code class="language-java"><span class="token annotation punctuation">@JsonTypeInfo</span><span class="token punctuation">(</span>use <span class="token operator">=</span> JsonTypeInfo<span class="token punctuation">.</span>Id<span class="token punctuation">.</span>NAME<span class="token punctuation">,</span> include <span class="token operator">=</span> JsonTypeInfo<span class="token punctuation">.</span>As<span class="token punctuation">.</span>PROPERTY<span class="token punctuation">,</span> property <span class="token operator">=</span> <span class="token string">"type"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@JsonSubTypes</span><span class="token punctuation">(</span><span class="token punctuation">{</span>        <span class="token annotation punctuation">@JsonSubTypes</span><span class="token punctuation">.</span><span class="token function">Type</span><span class="token punctuation">(</span>value <span class="token operator">=</span> CoursePrintTask<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> name <span class="token operator">=</span> <span class="token string">"COURSE"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>        <span class="token annotation punctuation">@JsonSubTypes</span><span class="token punctuation">.</span><span class="token function">Type</span><span class="token punctuation">(</span>value <span class="token operator">=</span> ScorePrintTask<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> name <span class="token operator">=</span> <span class="token string">"SCORE"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">PrintTask</span> <span class="token keyword">extends</span> <span class="token class-name">Serializable</span><span class="token punctuation">{</span>    Type <span class="token function">getType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">enum</span> Type<span class="token punctuation">{</span>        COURSE<span class="token punctuation">,</span>        SCORE    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><blockquote><p>更多关于<code>jackson</code>的异常与解决方案，请参考：<a href="http://www.baeldung.com/jackson-exception" target="_blank" rel="noopener">http://www.baeldung.com/jackson-exception</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在使用Spring-MVC对外发布Rest接口时，在某些场景中，入参可能会希望是一个接口类型或者抽象类型。SpingMVC的序列化默认采用的是&lt;code&gt;Jackson&lt;/code&gt;来实现入参与出参的序列化，在调用方传递一个json字符串时，如何将json字符串转换为具体的实现class呢？&lt;/p&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
      <category term="spring-mvc" scheme="http://zyouwei.com/tags/spring-mvc/"/>
    
  </entry>
  
  <entry>
    <title>Redis有序集合（SortedSet）的POP实现方法</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/Redis-ZSET-POP.html"/>
    <id>http://zyouwei.com/技术笔记/Java/Redis-ZSET-POP.html</id>
    <published>2018-05-09T11:50:28.000Z</published>
    <updated>2018-05-09T11:50:28.000Z</updated>
    
    <content type="html"><![CDATA[<p>最近在项目中遇到一个场景需要使用分布式的优先级队列，第一反应就是通过<code>redis</code>的<code>sortedset</code>数据结构来实现。但是阅读其API发现其没有类似<code>List</code>的<code>LPOP</code>与<code>RPOP</code>指令，但是可以根据其提供的<code>ZRANG</code>、<code>ZREVRANGE</code>、<code>ZREM</code>组合命令来实现<code>POP</code>的操作。</p><a id="more"></a><p><code>POP</code>的动作可以拆解成两步：</p><ul><li>获取队首元素</li><li>删除队首元素</li></ul><p>为保证执行的原子性，我们可以通过定义<code>LUA</code>脚本来执行组合指令：</p><pre class=" language-lua"><code class="language-lua"><span class="token keyword">local</span> result <span class="token operator">=</span> redis<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token string">'ZRANGE'</span><span class="token punctuation">,</span> KEYS<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token keyword">local</span> element <span class="token operator">=</span> result<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token keyword">if</span> element <span class="token keyword">then</span>    redis<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token string">'ZREM'</span><span class="token punctuation">,</span> KEYS<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> element<span class="token punctuation">)</span>    <span class="token keyword">return</span> element<span class="token keyword">else</span>    <span class="token keyword">return</span> <span class="token keyword">nil</span><span class="token keyword">end</span>"</code></pre><blockquote><p>将上面脚本中<code>ZRANG</code>替换为<code>ZREVRANGE</code>就可以实现从对尾<code>POP</code>了</p></blockquote><p>相关Java代码的实现，使用了<code>spring-data-redis</code>的<code>RedisTemplate</code>：</p><pre class=" language-java"><code class="language-java"><span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> String ZSET_LPOP_SCRIPT <span class="token operator">=</span> <span class="token string">"local result = redis.call('ZRANGE', KEYS[1], 0, 0)\n"</span> <span class="token operator">+</span>            <span class="token string">"local element = result[1]\n"</span> <span class="token operator">+</span>            <span class="token string">"if element then\n"</span> <span class="token operator">+</span>            <span class="token string">"    redis.call('ZREM', KEYS[1], element)\n"</span> <span class="token operator">+</span>            <span class="token string">"    return element\n"</span> <span class="token operator">+</span>            <span class="token string">"else\n"</span> <span class="token operator">+</span>            <span class="token string">"    return nil\n"</span> <span class="token operator">+</span>            <span class="token string">"end"</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">/**  * 有序集合按得分的升序提取元素，提取完成后并该删除  * @param key zset的key名字  * @param resultClass 返回值类型  * @return 提取的元素，集合为空时返回null  */</span><span class="token keyword">public</span> <span class="token operator">&lt;</span>T<span class="token operator">></span> T <span class="token function">zsetLPop</span><span class="token punctuation">(</span>String key<span class="token punctuation">,</span> Class<span class="token operator">&lt;</span>T<span class="token operator">></span> resultClass<span class="token punctuation">)</span><span class="token punctuation">{</span>    <span class="token keyword">return</span> redisTemplate<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">DefaultRedisScript</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span>ZSET_LPOP_SCRIPT<span class="token punctuation">,</span> resultClass<span class="token punctuation">)</span><span class="token punctuation">,</span> Collections<span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span></code></pre><p>测试代码:</p><pre class=" language-java"><code class="language-java"><span class="token annotation punctuation">@Test</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testZsetLpop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>    String zsetKey <span class="token operator">=</span> <span class="token string">"test_sorted"</span><span class="token punctuation">;</span>    Set<span class="token operator">&lt;</span>ZSetOperations<span class="token punctuation">.</span>TypedTuple<span class="token operator">&lt;</span>String<span class="token operator">>></span> initValues <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    initValues<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">DefaultTypedTuple</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token string">"zhangsan"</span><span class="token punctuation">,</span> <span class="token number">1d</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    initValues<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">DefaultTypedTuple</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token string">"lisi"</span><span class="token punctuation">,</span> <span class="token number">2d</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    initValues<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">DefaultTypedTuple</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token string">"zhaoliu"</span><span class="token punctuation">,</span> <span class="token number">4d</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    initValues<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">DefaultTypedTuple</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token string">"wangwu"</span><span class="token punctuation">,</span> <span class="token number">3d</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    Long count <span class="token operator">=</span> stringRedisTemplate<span class="token punctuation">.</span><span class="token function">boundZSetOps</span><span class="token punctuation">(</span>zsetKey<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>initValues<span class="token punctuation">)</span><span class="token punctuation">;</span>    System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"成功添加"</span> <span class="token operator">+</span> count <span class="token operator">+</span> <span class="token string">"条初始化数据"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    String element<span class="token punctuation">;</span>    <span class="token keyword">do</span> <span class="token punctuation">{</span>        element <span class="token operator">=</span> redisUtil<span class="token punctuation">.</span><span class="token function">zsetLPop</span><span class="token punctuation">(</span>zsetKey<span class="token punctuation">,</span> String<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span>Objects<span class="token punctuation">.</span><span class="token function">nonNull</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Get element: "</span> <span class="token operator">+</span> element<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span>    <span class="token keyword">while</span> <span class="token punctuation">(</span>element <span class="token operator">!=</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">// 控制台输出</span><span class="token comment" spellcheck="true">// 成功添加4条初始化数据</span><span class="token comment" spellcheck="true">// Get element: zhangsan</span><span class="token comment" spellcheck="true">// Get element: lisi</span><span class="token comment" spellcheck="true">// Get element: wangwu</span><span class="token comment" spellcheck="true">// Get element: zhaoliu</span></code></pre>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;最近在项目中遇到一个场景需要使用分布式的优先级队列，第一反应就是通过&lt;code&gt;redis&lt;/code&gt;的&lt;code&gt;sortedset&lt;/code&gt;数据结构来实现。但是阅读其API发现其没有类似&lt;code&gt;List&lt;/code&gt;的&lt;code&gt;LPOP&lt;/code&gt;与&lt;code&gt;RPOP&lt;/code&gt;指令，但是可以根据其提供的&lt;code&gt;ZRANG&lt;/code&gt;、&lt;code&gt;ZREVRANGE&lt;/code&gt;、&lt;code&gt;ZREM&lt;/code&gt;组合命令来实现&lt;code&gt;POP&lt;/code&gt;的操作。&lt;/p&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
      <category term="Redis" scheme="http://zyouwei.com/tags/Redis/"/>
    
  </entry>
  
  <entry>
    <title>GraphQL入门介绍（一）</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/GraphQL-Introduction-1.html"/>
    <id>http://zyouwei.com/技术笔记/Java/GraphQL-Introduction-1.html</id>
    <published>2018-01-31T11:39:21.000Z</published>
    <updated>2018-01-31T11:39:21.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="什么是GraphQL"><a href="#什么是GraphQL" class="headerlink" title="什么是GraphQL"></a>什么是GraphQL</h2><p>GraphQL是由FaceBook提出的一种基于API的查询语言（尽管它也支持修改数据）。它能够根据描述按需获取字段数据，不会有任何冗余信息。也能够通过一个请求一次获取多个资源。</p><p>GraphQL最早的实现是由FaceBook基于javascript实现，随后几乎被所有主流编程语言支持，已知服务端的实现包含了：<code>C#/.NET</code>、<code>Clojure</code>、<code>Elixir</code>、<code>Erlang</code>、<code>GO</code>、<code>Groovy</code>、<code>Java</code>、<code>JavaScript</code>、<code>PHP</code>、<code>Python</code>、<code>Scala</code>、<code>Ruby</code>，已知客户端的实现包含了：<code>C#/.NET</code>、<code>GO</code>、<code>Java/Android</code>、<code>JavaScript</code>、<code>Swift/Objective-C iOS</code>、<code>Python</code>。更多信息请参考：<a href="http://graphql.cn/code/" target="_blank" rel="noopener">http://graphql.cn/code/</a></p><a id="more"></a><h2 id="GraphQL的优势与劣势"><a href="#GraphQL的优势与劣势" class="headerlink" title="GraphQL的优势与劣势"></a>GraphQL的优势与劣势</h2><h3 id="优点："><a href="#优点：" class="headerlink" title="优点："></a>优点：</h3><ol><li>可以通过一个请求获取多个或多种资源，避免过多碎片化的请求；Restful强调的是通过资源定位URL，每种类型的资源通过POST、DELETE、PUT、GET来实现增删改查，这样对于一个稍微复杂一点的页面来说，就容易造成碎片化的请求过多。</li><li>字段按需获取，节省冗余数据的加载与传输。</li></ol><h3 id="缺点："><a href="#缺点：" class="headerlink" title="缺点："></a>缺点：</h3><ol><li>对排序、分页等请求的支持语义上支持的还是比较别扭。</li><li>一个新的语法，看起来类json却又不是json，有一定学习 成本。</li></ol><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><p>本样例采用GraphQL的<code>java</code>实现来演示。</p><h3 id="定义POM"><a href="#定义POM" class="headerlink" title="定义POM"></a>定义POM</h3><p><code>`</code>xml pom.xml</p><p><groupid>com.aqlu</groupid></p><p><artifactid>graphql-demo</artifactid></p><version>1.0.0-SNAPSHOT</version><parent><groupid>org.springframework.boot</groupid><artifactid>spring-boot-starter-parent</artifactid><version>1.5.9.RELEASE</version></parent><properties>&lt;disable.checks&gt;true&lt;/disable.checks&gt; &lt;java.version&gt;1.8&lt;/java.version&gt; &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt; &lt;project.reporting.outputEncoding&gt;UTF-8&lt;/project.reporting.outputEncoding&gt;</properties><p><dependencies><dependency><groupid>org.springframework.boot</groupid><artifactid>spring-boot-starter-web</artifactid></dependency><dependency><groupid>com.graphql-java</groupid><artifactid>graphql-java</artifactid><version>7.0</version></dependency><dependency><groupid>com.h2database</groupid><artifactid>h2</artifactid></dependency><dependency><groupid>org.projectlombok</groupid><artifactid>lombok</artifactid><optional>true</optional></dependency></dependencies></p><pre><code>&gt; `graphql-java`是`GrahpQL`的`Java`实现， 使用`spring-boot`快速构建项目，`lombok`用来精简bean的编写。### 定义GraphQL的Schema下面以商品SKU为例来定义我们本次演示的schema，在项目的`resources`目录下新建一个`sku.graphqls`文件：```graphql resources/sku.graphqlstype Sku {    id: Int    name: String    specs: [Spec]    stocks: [Stock]    price: Float}type Spec {    id: Int    skuId: Int    name: String    value: String}type Stock {    id: Int    skuId: Int    area: String    stocks: Int}schema {    query: Query}type Query {    allSkus: [Sku]    sku(id: Int): Sku}</code></pre><p>如上所示，我们定义了一个<code>Sku</code>类型，其具有<code>id</code>、名称、规格属性、库存信息、价格字段，其中规格属性与库存信息两个字段的类型又分别引用了下面定义的<code>Spec</code>与<code>Stock</code>两个类型。</p><p>接着定义了一个<code>schema</code>，并指明其<code>query</code>使用<code>Query</code>类型来进行组织，<code>Query</code>类型中定义了两个查询方式，<code>allSkus</code>用来查询所有的Sku信息，返回值是<code>Sku</code>的列表；<code>sku</code>用来获取某一个Sku信息，入参需要一个<code>id</code>。</p><h3 id="编写Java服务端"><a href="#编写Java服务端" class="headerlink" title="编写Java服务端"></a>编写Java服务端</h3><p>在设计的场景中，后端的<code>Sku</code>结构与上面定义的<code>Schema</code>结构存在差异。模拟后端采用微服务架构，商品基础信息在一个微服务中、库存在另一个微服务中。</p><p><code>`</code>java com/aqlu/graphql/demo/sku/domain/Sku.java /**</p><ul><li><p>SKU实体 */ @Data @Entity public class Sku { @Id private int id;</p><p>private String name;</p><p>@OneToMany(targetEntity = Spec.class, mappedBy = “sku”, cascade = CascadeType.ALL) private List<spec>specs = new ArrayList&lt;&gt;(); // 规格属性，一对多</spec></p><p>private BigDecimal price;</p><p>public void addSpec(String name, String value) {</p><pre><code> specs.add(new Spec(this, name, value));</code></pre><p>} } <code>`</code></p></li></ul><p><code>`</code>java com/aqlu/graphql/demo/sku/domain/Spec.java /**</p><ul><li><p>规格属性 */ @Data @Entity @NoArgsConstructor public class Spec { @Id @GeneratedValue private int id;</p><p>private String name;</p><p>private String value;</p><p>@ManyToOne @JoinColumn(name = “sku_id”, nullable = false) private Sku sku;</p><p>public Spec(Sku sku, String name, String value) {</p><pre><code> this.sku = sku; this.name = name; this.value = value;</code></pre><p>} } <code>`</code></p></li></ul><p><code>`</code>java com/aqlu/graphql/demo/sku/domain/Stock.java /**</p><ul><li><p>库存实体 */ @Data @NoArgsConstructor @Entity public class Stock { @Id @GeneratedValue private Integer id; private Integer skuId; private String area; private Integer stocks;</p><p>public Stock(Integer skuId, String area, Integer stocks) {</p><pre><code> this.skuId = skuId; this.area = area; this.stocks = stocks;</code></pre><p>} } <code>`</code></p></li></ul><p><code>`</code>java com/aqlu/graphql/demo/sku/repository/SkuRepository.java /**</p><ul><li>SKU信息仓库, JPA实现 */ public interface SkuRepository extends JpaRepository&lt;Sku, Integer&gt; { } <code>`</code></li></ul><p><code>`</code>java com/aqlu/graphql/demo/sku/repository/StockRepository.java /**</p><ul><li>库存信息仓库, JPA实现 */ public interface StockRepository extends JpaRepository&lt;Sku, Integer&gt; { /**<ul><li>根据sku id获取对应的库存信息 */ List<stock>queryStocksBySkuId(Integer skuId); } <code>`</code></stock></li></ul></li></ul><p><code>`</code>java com/aqlu/graphql/demo/sku/GraphQLService.java /**</p><ul><li><p>GrahpQL服务 */ @Service public class GraphQLService { @Value(“classpath:sku.graphqls”) private Resource schemaResource;</p><p>@Autowired private SkuDataFetcher skuFetcher;</p><p>@Autowired private StocksDataFetcher stocksFetcher;</p><p>@Autowired private AllSkusDataFetcher allSkusFetcher;</p><p>private GraphQL graphQL;</p><p>/**</p><ul><li>GrahpQL dsl 执行入口</li><li><p>@param dsl GraphQL dsl查询语言 */ public ExecutionResult query(String dsl) { return graphQL.execute(dsl); }</p><p>@PostConstruct private void loadSchema() throws IOException { // 类型定义注册，加载“sku.graphqls”文件定义的schema TypeDefinitionRegistry registry = new SchemaParser().parse(schemaResource.getFile());</p><p>// 运行时接线 RuntimeWiring runtimeWiring = buildRuntimeWiring();</p><p>// 生成Schema GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(registry, runtimeWiring);</p><p>// 生成graphQL对象 this.graphQL = GraphQL.newGraphQL(graphQLSchema).build(); }</p><p>private RuntimeWiring buildRuntimeWiring() { return RuntimeWiring.newRuntimeWiring()</p><pre><code>     .type(&quot;Query&quot;,             runtimeWiring -&gt; runtimeWiring.dataFetcher(&quot;allSkus&quot;, allSkusFetcher)                     .dataFetcher(&quot;sku&quot;, skuFetcher)     ) // 分别指定Query类型中allSkus字段与sku字段对应的Fetcher     .type(&quot;Sku&quot;,             runtimeWiring -&gt; runtimeWiring.dataFetcher(&quot;stocks&quot;, stocksFetcher)     ) // 指定schemaSku类型的stocks字段对应的Fetcher     .build();</code></pre><p>} } <code>`</code></p></li></ul></li></ul><p><code>`</code>java com/aqlu/graphqls/demo/sku/fetcher/AllSkusDataFetcher.java /**</p><ul><li><p><code>allSkus</code>的数据提取器 */ @Component public class AllSkusDataFetcher implements DataFetcher&lt;List<sku>&gt;{</sku></p><p>@Autowired private SkuRepository skuRepository;</p><p>@Override public List<sku>get(DataFetchingEnvironment environment) {</sku></p><pre><code> return skuRepository.findAll();</code></pre><p>}</p><p>@PostConstruct public void init(){</p><pre><code> // 添加一些初始化数据 Sku sku_1 = new Sku(); sku_1.setId(1); sku_1.setName(&quot;iPhone X 银色 64G&quot;); sku_1.setPrice(new BigDecimal(&quot;8388.00&quot;)); sku_1.addSpec(&quot;容量&quot;, &quot;64G&quot;); sku_1.addSpec(&quot;颜色&quot;, &quot;银色&quot;); Sku sku_2 = new Sku(); sku_2.setId(2); sku_2.setName(&quot;iPhone X 银色 256G&quot;); sku_2.setPrice(new BigDecimal(&quot;9688.00&quot;)); sku_2.addSpec(&quot;容量&quot;, &quot;256G&quot;); sku_2.addSpec(&quot;颜色&quot;, &quot;银色&quot;); Sku sku_3 = new Sku(); sku_3.setId(3); sku_3.setName(&quot;iPhone X 深空灰色 64G&quot;); sku_3.setPrice(new BigDecimal(&quot;8388.00&quot;)); sku_3.addSpec(&quot;容量&quot;, &quot;64G&quot;); sku_3.addSpec(&quot;颜色&quot;, &quot;深空灰色&quot;); Sku sku_4 = new Sku(); sku_4.setId(4); sku_4.setName(&quot;iPhone X 深空灰色 256G&quot;); sku_4.setPrice(new BigDecimal(&quot;9688.00&quot;)); sku_4.addSpec(&quot;容量&quot;, &quot;256G&quot;); sku_4.addSpec(&quot;颜色&quot;, &quot;深空灰色&quot;);</code></pre></li></ul><pre><code>    Sku sku_5 = new Sku();    sku_5.setId(5);    sku_5.setName(&quot;iPhone 8 深空灰色 256G&quot;);    sku_5.setPrice(new BigDecimal(&quot;6888.00&quot;));    sku_5.addSpec(&quot;容量&quot;, &quot;256G&quot;);    sku_5.addSpec(&quot;颜色&quot;, &quot;深空灰色&quot;);    skuRepository.save(Arrays.asList(sku_1, sku_2, sku_3, sku_4, sku_5));}</code></pre><p>}</p><pre><code>```java com/aqlu/graphqls/demo/sku/fetcher/SkuDataFetcher.java/** * `sku`的数据提取器 */@Component@Slf4jpublic class SkuDataFetcher implements DataFetcher&lt;Sku&gt;{    @Autowired    private SkuRepository skuRepository;    @Override    public Sku get(DataFetchingEnvironment env) {        Integer id = env.getArgument(&quot;id&quot;); // 获取参数        try {            return skuRepository.findOne(id);        } catch (Exception e) {            log.error(&quot;load sku failed.errMsg:{}&quot;, e.getMessage());            return null;        }    }}</code></pre><p><code>`</code>java com/aqlu/graphqls/demo/sku/fetcher/StocksDataFetcher.java /**</p><ul><li><p><code>stocks</code>的数据提取器 */ @Component public class StocksDataFetcher implements DataFetcher&lt;List<stock>&gt; {</stock></p><p>@Autowired private StockRepository stockRepository;</p><p>@Override public List<stock>get(DataFetchingEnvironment env) {</stock></p><pre><code> Sku sku = env.getSource();  // 从上下文环境中获取源对象 return stockRepository.queryStocksBySkuId(sku.getId());</code></pre><p>}</p><p>@PostConstruct private void init() {</p><pre><code> // 添加初始化数据 Stock stock_1 = new Stock(1, &quot;华东&quot;, 10); Stock stock_2 = new Stock(1, &quot;华南&quot;, 20); Stock stock_3 = new Stock(1, &quot;华北&quot;, 30); Stock stock_4 = new Stock(2, &quot;华东&quot;, 40); Stock stock_5 = new Stock(2, &quot;华南&quot;, 50); Stock stock_6 = new Stock(3, &quot;华东&quot;, 60); Stock stock_7 = new Stock(3, &quot;华北&quot;, 70); Stock stock_8 = new Stock(4, &quot;华东&quot;, 80); stockRepository.save(Arrays.asList(stock_1, stock_2, stock_3, stock_4, stock_5, stock_6, stock_7, stock_8));</code></pre><p>} } <code>`</code></p></li></ul><p><code>`</code>java com/aqlu/graphql/demo/sku/SkuController.java /**</p><ul><li><p>sku控制器 */ @RestController @RequestMapping(“/sku”) public class SkuController {</p><p>@Autowired private GraphQLService graphQLService;</p><p>@PostMapping(“/query”) public ResponseEntity query(@RequestBody String dsl) {</p><pre><code> ExecutionResult result = graphQLService.query(dsl); if (result.getErrors().isEmpty()) {     return ResponseEntity.ok(result); } else {     return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result); }</code></pre><p>} } <code>`</code></p></li></ul><p><code>`</code>java com/aqlu/graphql/demo/GraphqlDemoApplication.java @SpringBootApplication public class GraphqlDemoApplication {</p><pre><code>public static void main(String[] args) {    SpringApplication.run(GraphqlDemoApplication.class, args);}</code></pre><p>}</p><pre><code>```properties resources/application.properties# 开启h2控制台spring.h2.console.enabled=true# 打印sqlspring.jpa.show-sql=true</code></pre><h3 id="执行查询"><a href="#执行查询" class="headerlink" title="执行查询"></a>执行查询</h3><p>启动<code>GraphqlDemoApplication</code>。</p><p><strong>1. 查询所有sku的id与name信息</strong></p><pre class=" language-bash"><code class="language-bash">$ curl -XPOST -H <span class="token string">'Content-Type:application/json;charset=UTF-8'</span> <span class="token string">'http://localhost:8080/sku/query'</span> -d <span class="token string">'{    allSkus {        id        name    }}'</span></code></pre><p>返回：</p><pre class=" language-json"><code class="language-json"><span class="token punctuation">{</span>    <span class="token property">"data"</span><span class="token operator">:</span> <span class="token punctuation">{</span>        <span class="token property">"allSkus"</span><span class="token operator">:</span> <span class="token punctuation">[</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 银色 64G"</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 银色 256G"</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 深空灰色 64G"</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">4</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 深空灰色 256G"</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone 8 深空灰色 256G"</span>            <span class="token punctuation">}</span>        <span class="token punctuation">]</span>    <span class="token punctuation">}</span><span class="token punctuation">,</span>    <span class="token property">"errors"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>    <span class="token property">"extensions"</span><span class="token operator">:</span> <span class="token null">null</span><span class="token punctuation">}</span></code></pre><p><strong>2. 查询所有sku的id、name、price信息</strong></p><pre class=" language-bash"><code class="language-bash">$ curl -XPOST -H <span class="token string">'Content-Type:application/json;charset=UTF-8'</span> <span class="token string">'http://localhost:8080/sku/query'</span> -d <span class="token string">'{    allSkus {        id        name        price    }}'</span></code></pre><p>返回：</p><pre class=" language-json"><code class="language-json"><span class="token punctuation">{</span>    <span class="token property">"data"</span><span class="token operator">:</span> <span class="token punctuation">{</span>        <span class="token property">"allSkus"</span><span class="token operator">:</span> <span class="token punctuation">[</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 银色 64G"</span><span class="token punctuation">,</span>                <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">8388</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 银色 256G"</span><span class="token punctuation">,</span>                <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">9688</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 深空灰色 64G"</span><span class="token punctuation">,</span>                <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">8388</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">4</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 深空灰色 256G"</span><span class="token punctuation">,</span>                <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">9688</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone 8 深空灰色 256G"</span><span class="token punctuation">,</span>                <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">6888</span>            <span class="token punctuation">}</span>        <span class="token punctuation">]</span>    <span class="token punctuation">}</span><span class="token punctuation">,</span>    <span class="token property">"errors"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>    <span class="token property">"extensions"</span><span class="token operator">:</span> <span class="token null">null</span><span class="token punctuation">}</span></code></pre><blockquote><p>执行上面两个查询时，注意观察后台日志，你会发现每次请求执行的sql为： <code>Hibernate: select sku0_.id as id1_0_, sku0_.name as name2_0_, sku0_.price as price3_0_ from sku sku0_</code>，说明只查询了<code>sku</code>表。</p></blockquote><p><strong>3. 查询所有sku的id、name、price、specs信息</strong></p><pre class=" language-bash"><code class="language-bash">$ curl -XPOST -H <span class="token string">'Content-Type:application/json;charset=UTF-8'</span> <span class="token string">'http://localhost:8080/sku/query'</span> -d <span class="token string">'{    allSkus {        id        name        price        specs {            name            value        }    }}'</span></code></pre><blockquote><p>查询语句添加了<code>specs</code>字段，同时为<code>spec</code>指定了<code>name</code>与<code>value</code>字段。 注意：因为<code>specs</code>返回的是<code>Spec</code>类型列表，因此必须要进一步指定需要提取的<code>Spec</code>的字段名。否则会返回语法验证错误：<code>Validation error of type SubSelectionRequired: Sub selection required for type null of field specs</code></p></blockquote><p>返回：</p><pre class=" language-json"><code class="language-json"><span class="token punctuation">{</span>    <span class="token property">"data"</span><span class="token operator">:</span> <span class="token punctuation">{</span>        <span class="token property">"allSkus"</span><span class="token operator">:</span> <span class="token punctuation">[</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 银色 64G"</span><span class="token punctuation">,</span>                <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">8388</span><span class="token punctuation">,</span>                <span class="token property">"specs"</span><span class="token operator">:</span> <span class="token punctuation">[</span>                    <span class="token punctuation">{</span>                        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"容量"</span><span class="token punctuation">,</span>                        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"64G"</span>                    <span class="token punctuation">}</span><span class="token punctuation">,</span>                    <span class="token punctuation">{</span>                        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"颜色"</span><span class="token punctuation">,</span>                        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"银色"</span>                    <span class="token punctuation">}</span>                <span class="token punctuation">]</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 银色 256G"</span><span class="token punctuation">,</span>                <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">9688</span><span class="token punctuation">,</span>                <span class="token property">"specs"</span><span class="token operator">:</span> <span class="token punctuation">[</span>                    <span class="token punctuation">{</span>                        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"容量"</span><span class="token punctuation">,</span>                        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"256G"</span>                    <span class="token punctuation">}</span><span class="token punctuation">,</span>                    <span class="token punctuation">{</span>                        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"颜色"</span><span class="token punctuation">,</span>                        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"银色"</span>                    <span class="token punctuation">}</span>                <span class="token punctuation">]</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 深空灰色 64G"</span><span class="token punctuation">,</span>                <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">8388</span><span class="token punctuation">,</span>                <span class="token property">"specs"</span><span class="token operator">:</span> <span class="token punctuation">[</span>                    <span class="token punctuation">{</span>                        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"容量"</span><span class="token punctuation">,</span>                        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"64G"</span>                    <span class="token punctuation">}</span><span class="token punctuation">,</span>                    <span class="token punctuation">{</span>                        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"颜色"</span><span class="token punctuation">,</span>                        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"深空灰色"</span>                    <span class="token punctuation">}</span>                <span class="token punctuation">]</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">4</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 深空灰色 256G"</span><span class="token punctuation">,</span>                <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">9688</span><span class="token punctuation">,</span>                <span class="token property">"specs"</span><span class="token operator">:</span> <span class="token punctuation">[</span>                    <span class="token punctuation">{</span>                        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"容量"</span><span class="token punctuation">,</span>                        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"256G"</span>                    <span class="token punctuation">}</span><span class="token punctuation">,</span>                    <span class="token punctuation">{</span>                        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"颜色"</span><span class="token punctuation">,</span>                        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"深空灰色"</span>                    <span class="token punctuation">}</span>                <span class="token punctuation">]</span>            <span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token punctuation">{</span>                <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">,</span>                <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone 8 深空灰色 256G"</span><span class="token punctuation">,</span>                <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">6888</span><span class="token punctuation">,</span>                <span class="token property">"specs"</span><span class="token operator">:</span> <span class="token punctuation">[</span>                    <span class="token punctuation">{</span>                        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"容量"</span><span class="token punctuation">,</span>                        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"256G"</span>                    <span class="token punctuation">}</span><span class="token punctuation">,</span>                    <span class="token punctuation">{</span>                        <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"颜色"</span><span class="token punctuation">,</span>                        <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"深空灰色"</span>                    <span class="token punctuation">}</span>                <span class="token punctuation">]</span>            <span class="token punctuation">}</span>        <span class="token punctuation">]</span>    <span class="token punctuation">}</span><span class="token punctuation">,</span>    <span class="token property">"errors"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>    <span class="token property">"extensions"</span><span class="token operator">:</span> <span class="token null">null</span><span class="token punctuation">}</span></code></pre><blockquote><p>执行查询时，注意观察后台日志，你会发现此次请求执行的sql为：</p><pre><code>Hibernate: select sku0_.id as id1_0_, sku0_.name as name2_0_, sku0_.price as price3_0_ from sku sku0_Hibernate: select specs0_.sku_id as sku_id4_1_0_, specs0_.id as id1_1_0_, specs0_.id as id1_1_1_, specs0_.name as name2_1_1_, specs0_.sku_id as sku_id4_1_1_, specs0_.value as value3_1_1_ from spec specs0_ where specs0_.sku_id=?Hibernate: select specs0_.sku_id as sku_id4_1_0_, specs0_.id as id1_1_0_, specs0_.id as id1_1_1_, specs0_.name as name2_1_1_, specs0_.sku_id as sku_id4_1_1_, specs0_.value as value3_1_1_ from spec specs0_ where specs0_.sku_id=?Hibernate: select specs0_.sku_id as sku_id4_1_0_, specs0_.id as id1_1_0_, specs0_.id as id1_1_1_, specs0_.name as name2_1_1_, specs0_.sku_id as sku_id4_1_1_, specs0_.value as value3_1_1_ from spec specs0_ where specs0_.sku_id=?Hibernate: select specs0_.sku_id as sku_id4_1_0_, specs0_.id as id1_1_0_, specs0_.id as id1_1_1_, specs0_.name as name2_1_1_, specs0_.sku_id as sku_id4_1_1_, specs0_.value as value3_1_1_ from spec specs0_ where specs0_.sku_id=?Hibernate: select specs0_.sku_id as sku_id4_1_0_, specs0_.id as id1_1_0_, specs0_.id as id1_1_1_, specs0_.name as name2_1_1_, specs0_.sku_id as sku_id4_1_1_, specs0_.value as value3_1_1_ from spec specs0_ where specs0_.sku_id=?</code></pre><p>说明同时查询了<code>sku</code>、<code>spec</code>两张表。</p></blockquote><p><strong>4. 查询指定id的sku信息，包含id、name、price、specs、stocks字段</strong></p><pre class=" language-bash"><code class="language-bash">$ curl -XPOST -H <span class="token string">'Content-Type:application/json;charset=UTF-8'</span> <span class="token string">'http://localhost:8080/sku/query'</span> -d <span class="token string">'{    sku(id: 1) {        id        name        price        specs {            name            value        }        stocks {            area            stocks        }    }}'</span></code></pre><blockquote><p>注意查询语句<code>sku</code>指令后面跟了参数<code>id:1</code></p></blockquote><p>返回：</p><pre class=" language-json"><code class="language-json"><span class="token punctuation">{</span>    <span class="token property">"data"</span><span class="token operator">:</span> <span class="token punctuation">{</span>        <span class="token property">"sku"</span><span class="token operator">:</span> <span class="token punctuation">{</span>            <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>            <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"iPhone X 银色 64G"</span><span class="token punctuation">,</span>            <span class="token property">"price"</span><span class="token operator">:</span> <span class="token number">8388</span><span class="token punctuation">,</span>            <span class="token property">"specs"</span><span class="token operator">:</span> <span class="token punctuation">[</span>                <span class="token punctuation">{</span>                    <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"容量"</span><span class="token punctuation">,</span>                    <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"64G"</span>                <span class="token punctuation">}</span><span class="token punctuation">,</span>                <span class="token punctuation">{</span>                    <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"颜色"</span><span class="token punctuation">,</span>                    <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"银色"</span>                <span class="token punctuation">}</span>            <span class="token punctuation">]</span><span class="token punctuation">,</span>            <span class="token property">"stocks"</span><span class="token operator">:</span> <span class="token punctuation">[</span>                <span class="token punctuation">{</span>                    <span class="token property">"area"</span><span class="token operator">:</span> <span class="token string">"华东"</span><span class="token punctuation">,</span>                    <span class="token property">"stocks"</span><span class="token operator">:</span> <span class="token number">10</span>                <span class="token punctuation">}</span><span class="token punctuation">,</span>                <span class="token punctuation">{</span>                    <span class="token property">"area"</span><span class="token operator">:</span> <span class="token string">"华南"</span><span class="token punctuation">,</span>                    <span class="token property">"stocks"</span><span class="token operator">:</span> <span class="token number">20</span>                <span class="token punctuation">}</span><span class="token punctuation">,</span>                <span class="token punctuation">{</span>                    <span class="token property">"area"</span><span class="token operator">:</span> <span class="token string">"华北"</span><span class="token punctuation">,</span>                    <span class="token property">"stocks"</span><span class="token operator">:</span> <span class="token number">30</span>                <span class="token punctuation">}</span>            <span class="token punctuation">]</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span><span class="token punctuation">,</span>    <span class="token property">"errors"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>    <span class="token property">"extensions"</span><span class="token operator">:</span> <span class="token null">null</span><span class="token punctuation">}</span></code></pre><blockquote><p>执行查询时，注意观察后台日志，你会发现此次请求执行的sql为：</p><pre><code>Hibernate: select sku0_.id as id1_0_, sku0_.name as name2_0_, sku0_.price as price3_0_ from sku sku0_Hibernate: select specs0_.sku_id as sku_id4_1_0_, specs0_.id as id1_1_0_, specs0_.id as id1_1_1_, specs0_.name as name2_1_1_, specs0_.sku_id as sku_id4_1_1_, specs0_.value as value3_1_1_ from spec specs0_ where specs0_.sku_id=?Hibernate: select stock0_.id as id1_2_, stock0_.area as area2_2_, stock0_.sku_id as sku_id3_2_, stock0_.stocks as stocks4_2_ from stock stock0_ where stock0_.sku_id=?</code></pre><p>说明同时查询了<code>sku</code>、<code>spec</code>、<code>stock</code>三张表。</p></blockquote><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>通过上面的Demo，可以发现如果是一个相对简单的需求场景，使用GraphQL反而会增加编写<code>DataFetcher</code>的工作量，因此简单的场景使用Rest风格更合适。</p><p>但Graphql带来的灵活度非常高，统一的一个http查询接口，根据不同的dsl即可以得到想要的数据，甚至能为每个字段定义单独的<code>DataFetcher</code>。</p><p>在大家都在追逐微服务架构的今天，<code>GraphQL</code>的诞生恰逢其实，它能很好的承担起<code>中台</code>的角色，根据不同类型<code>前台</code>页面的展示逻辑，编制各个<code>后台</code>的微服务业务，真正做到按需加载、减少交互次数。</p><p><strong>完整示例代码：</strong> <a href="https://github.com/aqlu/graphql-demo" target="_blank" rel="noopener">https://github.com/aqlu/graphql-demo</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;什么是GraphQL&quot;&gt;&lt;a href=&quot;#什么是GraphQL&quot; class=&quot;headerlink&quot; title=&quot;什么是GraphQL&quot;&gt;&lt;/a&gt;什么是GraphQL&lt;/h2&gt;&lt;p&gt;GraphQL是由FaceBook提出的一种基于API的查询语言（尽管它也支持修改数据）。它能够根据描述按需获取字段数据，不会有任何冗余信息。也能够通过一个请求一次获取多个资源。&lt;/p&gt;&lt;p&gt;GraphQL最早的实现是由FaceBook基于javascript实现，随后几乎被所有主流编程语言支持，已知服务端的实现包含了：&lt;code&gt;C#/.NET&lt;/code&gt;、&lt;code&gt;Clojure&lt;/code&gt;、&lt;code&gt;Elixir&lt;/code&gt;、&lt;code&gt;Erlang&lt;/code&gt;、&lt;code&gt;GO&lt;/code&gt;、&lt;code&gt;Groovy&lt;/code&gt;、&lt;code&gt;Java&lt;/code&gt;、&lt;code&gt;JavaScript&lt;/code&gt;、&lt;code&gt;PHP&lt;/code&gt;、&lt;code&gt;Python&lt;/code&gt;、&lt;code&gt;Scala&lt;/code&gt;、&lt;code&gt;Ruby&lt;/code&gt;，已知客户端的实现包含了：&lt;code&gt;C#/.NET&lt;/code&gt;、&lt;code&gt;GO&lt;/code&gt;、&lt;code&gt;Java/Android&lt;/code&gt;、&lt;code&gt;JavaScript&lt;/code&gt;、&lt;code&gt;Swift/Objective-C iOS&lt;/code&gt;、&lt;code&gt;Python&lt;/code&gt;。更多信息请参考：&lt;a href=&quot;http://graphql.cn/code/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://graphql.cn/code/&lt;/a&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="graphql" scheme="http://zyouwei.com/tags/graphql/"/>
    
      <category term="微服务" scheme="http://zyouwei.com/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
  </entry>
  
  <entry>
    <title>ES日志收集定期清理与备份</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%B7%A5%E5%85%B7%E7%B1%BB/ES-Clean-And-Backup.html"/>
    <id>http://zyouwei.com/技术笔记/工具类/ES-Clean-And-Backup.html</id>
    <published>2018-01-30T11:20:41.000Z</published>
    <updated>2018-01-30T02:42:55.750Z</updated>
    
    <content type="html"><![CDATA[<h2 id="按天清理索引"><a href="#按天清理索引" class="headerlink" title="按天清理索引"></a>按天清理索引</h2><pre class=" language-bash"><code class="language-bash">$ <span class="token function">crontab</span> -e<span class="token comment" spellcheck="true">## 每日凌晨1点定时删除30天之前的`logstash-YYYY.MM.DD`索引</span>0 1 * * * /home/kibana/indexClean.sh es.zyouwei.com logstash -30</code></pre><a id="more"></a><p><code>`</code>bash indexClean.sh</p><p>#!/bin/sh</p><h1 id="before-1-day"><a href="#before-1-day" class="headerlink" title="before $1 day"></a>before $1 day</h1><p>index=”$2-“<code>date -d &quot;$3 day &quot; +%Y.%m.%d</code></p><p>beginDate=<code>date &quot;+%Y-%m-%d %H:%M:%S&quot;</code> echo “$beginDate: begin to deleting index: $index” &gt;&gt; /tmp/indexClean.log</p><p>result=<code>curl --connect-timeout 10 -m 20 -XDELETE $1/$index</code></p><p>endDate=<code>date &quot;+%Y-%m-%d %H:%M:%S&quot;</code> echo “$endDate: exec result is : $result” &gt;&gt; /tmp/indexClean.log</p><pre><code>## 按天关闭索引```bash$ crontab -e## 每日凌晨1点05分定时关闭7天之前的`logstash-YYYY.MM.DD`索引5 1 * * * /home/kibana/indexClose.sh es.zyouwei.com logstash -7</code></pre><p><code>`</code>bash indexClose.sh</p><p>#!/bin/sh</p><h1 id="before-1-week"><a href="#before-1-week" class="headerlink" title="before 1 week"></a>before 1 week</h1><p>index=”$2-“<code>date -d &quot;$3 day &quot; +%Y.%m.%d</code></p><p>beginDate=<code>date &quot;+%Y-%m-%d %H:%M:%S&quot;</code> echo “$beginDate: begin to closing index: $index” &gt;&gt; /tmp/indexClose.log</p><p>result=<code>curl --connect-timeout 10 -m 20 -XPOST $1/$index/_close</code></p><p>endDate=<code>date &quot;+%Y-%m-%d %H:%M:%S&quot;</code> echo “$endDate: exec result is : $result” &gt;&gt; /tmp/indexClose.log</p><pre><code>## 按月清理索引```bash$ crontab -e## 每月1号凌晨2点定时删除3月之前的`logstash-srv-YYYY.MM`索引0 2 1 * * /home/kibana/indexCleanByMonth.sh es.zyouwei.com logstash-srv -3</code></pre><p><code>`</code>bash indexCleanByMonth.sh</p><p>#!/bin/sh</p><h1 id="before-3-month"><a href="#before-3-month" class="headerlink" title="before $3 month"></a>before $3 month</h1><p>index=”$2-“<code>date -d &quot;$3 month &quot; +%Y.%m</code></p><p>beginDate=<code>date &quot;+%Y-%m-%d %H:%M:%S&quot;</code> echo “$beginDate: begin to deleting index: $index” &gt;&gt; /tmp/indexCleanByMonth.log</p><p>result=<code>curl --connect-timeout 10 -m 20 -XDELETE $1/$index</code></p><p>endDate=<code>date &quot;+%Y-%m-%d %H:%M:%S&quot;</code> echo “$endDate: exec result is : $result” &gt;&gt; /tmp/indexCleanByMonth.log</p><pre><code>## 备份索引到其它ES集群```bash$ crontab -e0 3 * * * /home/kibana/indexBackup.sh</code></pre><p><code>`</code>bash indexBackup.sh</p><p>#!/usr/bin/env python</p><h1 id="coding-utf-8"><a href="#coding-utf-8" class="headerlink" title="-- coding:utf-8 --"></a>-<em>- coding:utf-8 -</em>-</h1><p><strong>author</strong> = ‘aqlu’</p><p>import urllib2 import json</p><h1 id="import-traceback"><a href="#import-traceback" class="headerlink" title="import traceback"></a>import traceback</h1><p>import logging import os from datetime import timedelta, datetime</p><p>logging.basicConfig(filename=os.path.join(os.getcwd(), ‘indexBackup.log’), level=logging.DEBUG, filemode=’a’, format=’%(asctime)s - %(levelname)s: %(message)s’) log = logging.getLogger(‘root’)</p><h1 id="noinspection-PyBroadException"><a href="#noinspection-PyBroadException" class="headerlink" title="noinspection PyBroadException"></a>noinspection PyBroadException</h1><p>def backup_index(index, src_es_host, dest_es_host, close_index_name): snapshot_name = ‘snapshot-‘ + index + ‘_’ + datetime.now().strftime(‘%Y%m%d-%H%M%S’)</p><pre><code># step.0 validate indextry:    validate_url = &#39;http://&#39; + src_es_host + &#39;/&#39; + index    validate_request = urllib2.Request(validate_url)    validate_request.get_method = lambda: &#39;HEAD&#39;    validate_response = urllib2.urlopen(validate_request)except urllib2.HTTPError, e:    if e.code == 404:        log.info(&quot;index [%s] is not exists&quot;, index)else:    if validate_response.getcode() == 200:        # step.1 backup index        # print datetime.now().strftime(&#39;%Y-%m-%d %H:%M:%S&#39;), &#39; begin to backup index [&#39;, indexName, &#39;]&#39;        log.debug(&#39;begin to backup index [&#39; + index + &#39;]&#39;)        backup_url = &#39;http://&#39; + src_es_host + &quot;/_snapshot/log_backup/&quot; \                     + snapshot_name + &#39;?wait_for_completion=true&#39;        backup_param = {&quot;indices&quot;: index}        try:            backup_request = urllib2.Request(backup_url, json.dumps(backup_param))            backup_request.get_method = lambda: &#39;PUT&#39;  # 设置HTTP的访问方式            backup_response = urllib2.urlopen(backup_request)            backup_result = json.loads(backup_response.read(), &#39;utf-8&#39;)        except:            log.exception(&#39;exception&#39;)            send_alarm(&quot;告警：备份索引&quot; + index + &quot;异常【xxx】&quot;)        else:            log.debug(&quot;backup index end. result: %s&quot;, backup_result)            if backup_result[&#39;snapshot&#39;][&#39;state&#39;] == &#39;SUCCESS&#39;:                # if snapshot success then                # step.2 delete index                log.debug(&#39;begin to delete index [&#39; + index + &#39;]&#39;)                try:                    delete_url = &#39;http://&#39; + src_es_host + &quot;/&quot; + index                    delete_request = urllib2.Request(delete_url)                    delete_request.get_method = lambda: &#39;DELETE&#39;                    delete_response = urllib2.urlopen(delete_request)                    delete_result = json.loads(delete_response.read())                    log.debug(&quot;delete index end. result: %s&quot;, delete_result)                except:                    log.exception(&#39;exception&#39;)                    send_alarm(&quot;告警：删除索引&quot; + index + &quot;异常【xxx】&quot;)                # step.3 restore index                log.debug(&#39;begin to restore snapshot [&#39; + snapshot_name + &#39;]&#39;)                try:                    restore_url = &#39;http://&#39; + dest_es_host + &#39;/_snapshot/log_backup/&#39; + snapshot_name \                                  + &#39;/_restore?wait_for_completion=true&#39;                    restore_request = urllib2.Request(restore_url)                    restore_request.get_method = lambda: &#39;POST&#39;                    restore_response = urllib2.urlopen(restore_request)                    restore_result = json.loads(restore_response.read())                except:                    log.exception(&#39;exception&#39;)                    send_alarm(&quot;告警：还原快照&quot; + snapshot_name + &quot;异常【xxx】&quot;)                else:                    log.debug(&quot;restore snapshot end. result: %s&quot;, restore_result)                    if restore_result[&#39;snapshot&#39;][&#39;shards&#39;][&#39;failed&#39;] == 0:                        # if no failed shards then                        # step.4 delete snapshot                        log.debug(&#39;begin to delete snapshot [&#39; + snapshot_name + &#39;]&#39;)                        try:                            del_snapshot_url = &#39;http://&#39; + dest_es_host + &#39;/_snapshot/log_backup/&#39; + snapshot_name                            del_snapshot_request = urllib2.Request(del_snapshot_url)                            del_snapshot_request.get_method = lambda: &#39;DELETE&#39;                            del_snapshot_response = urllib2.urlopen(del_snapshot_request)                            del_snapshot_result = json.loads(del_snapshot_response.read())                            log.debug(&quot;delete snapshot end. result: %s&quot;, del_snapshot_result)                            if close_index_name                                # close index for before 1 month on backup cluster                                close_index(close_index_name, dest_es_host)                        except:                            log.exception(&#39;exception&#39;)                            send_alarm(&quot;告警：删除快照&quot; + snapshot_name + &quot;异常【xxx】&quot;)</code></pre><h1 id="noinspection-PyBroadException-1"><a href="#noinspection-PyBroadException-1" class="headerlink" title="noinspection PyBroadException"></a>noinspection PyBroadException</h1><p>def close_index(index, esHost): log.debug(“begin to close index [%s]”, index) try: url = ‘http://‘ + esHost + ‘/‘ + index + ‘/_close’ request = urllib2.Request(url) request.get_method = lambda: ‘POST’ response = urllib2.urlopen(request) result = json.loads(response.read()) log.debug(“close index [%s] end. result: %s”, index, result) except: log.exception(‘exception’) send_alarm(“告警：关闭索引” + index + “异常【xxx】”)</p><h1 id="noinspection-PyBroadException-2"><a href="#noinspection-PyBroadException-2" class="headerlink" title="noinspection PyBroadException"></a>noinspection PyBroadException</h1><p>def send_alarm(content): log.debug(“send sms, content: [%s]”, content) try:</p><pre><code>    ## TODO implements alarm as sms、call、dingTalk ......    log.debug(&quot;send sms end. result: %s&quot;, result)except:    log.exception(&#39;exception&#39;)</code></pre><p>before1Week = datetime.now() + timedelta(days=-7) before1month = datetime.now() + timedelta(days=-30) indexName = ‘logstash-‘ + before1Week.strftime(‘%Y.%m.%d’) closeIndexName = ‘logstash-‘ + before1month.strftime(‘%Y.%m.%d’) srcEsHost = ‘es.zyouwei.com’ destEsHost = ‘es-bak.zyouwei.com’</p><p>backup_index(indexName, srcEsHost, destEsHost, closeIndexName) <code>`</code></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;按天清理索引&quot;&gt;&lt;a href=&quot;#按天清理索引&quot; class=&quot;headerlink&quot; title=&quot;按天清理索引&quot;&gt;&lt;/a&gt;按天清理索引&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;$ crontab -e

## 每日凌晨1点定时删除30天之前的`logstash-YYYY.MM.DD`索引
0 1 * * * /home/kibana/indexClean.sh es.zyouwei.com logstash -30
&lt;/code&gt;&lt;/pre&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="工具类" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%B7%A5%E5%85%B7%E7%B1%BB/"/>
    
    
      <category term="elasticsearch" scheme="http://zyouwei.com/tags/elasticsearch/"/>
    
  </entry>
  
  <entry>
    <title>高可用HBASE搭建</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%A4%A7%E6%95%B0%E6%8D%AE/2017-5-Hbase-Deployed.html"/>
    <id>http://zyouwei.com/技术笔记/大数据/2017-5-Hbase-Deployed.html</id>
    <published>2018-01-30T11:14:12.000Z</published>
    <updated>2017-05-09T04:15:14.000Z</updated>
    
    <content type="html"><![CDATA[<p>HDFS的高可用搭建不在本篇中描述，请参考<a href="/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%A4%A7%E6%95%B0%E6%8D%AE/HDFS-Deployed.html">《Hadoop HDFS与YARN高可用安装》</a>。</p><h2 id="节点规划"><a href="#节点规划" class="headerlink" title="节点规划"></a>节点规划</h2><table><thead><tr><th>hostname</th><th>ip</th><th>安装服务</th></tr></thead><tbody><tr><td>zk1</td><td>192.168.1.1</td><td>zookeeper</td></tr><tr><td>zk2</td><td>192.168.1.2</td><td>zookeeper</td></tr><tr><td>zk3</td><td>192.168.1.3</td><td>zookeeper</td></tr></tbody></table><a id="more"></a><blockquote><p>提示：</p><p>zookeeper集群安装不在本文描述范围， 请自行参考其他文档。（zookeeper集群也可以跟datanode安装在一起）</p></blockquote><table><thead><tr><th>hostname</th><th>ip</th><th>安装服务</th></tr></thead><tbody><tr><td>master1</td><td>192.168.1.10</td><td>HMaster</td></tr><tr><td>master2</td><td>192.168.1.11</td><td>HMaster</td></tr><tr><td>region1</td><td>192.168.1.12</td><td>HRegionServer</td></tr><tr><td>region2</td><td>192.168.1.13</td><td>HRegionServer</td></tr></tbody></table><ul><li>HBase Master 用于协调多个 Region Server，侦测各个 Region Server 之间的状态，并平衡 Region Server 之间的负载。HBase Master 还有一个职责就是负责分配 Region 给 Region Server。HBase 允许多个 Master 节点共存，但是这需要 Zookeeper 的帮助。不过当多个 Master 节点共存时，只有一个 Master 是提供服务的，其他的 Master 节点处于待命的状态。当正在工作的 Master 节点宕机时，其他的 Master 则会接管 HBase 的集群。</li><li>对于一个 Region Server 而言，其包括了多个 Region。Region Server 的作用只是管理表格，以及实现读写操作。Client 直接连接 Region Server，并通信获取 HBase 中的数据。对于 Region 而言，则是真实存放 HBase 数据的地方，也就说 Region 是 HBase 可用性和分布式的基本单位。如果当一个表格很大，并由多个 CF 组成时，那么表的数据将存放在多个 Region 之间，并且在每个 Region 中会关联多个存储的单元（Store）。</li><li>对于 HBase 而言，Zookeeper 的作用是至关重要的。首先 Zookeeper 是作为 HBase Master 的 HA 解决方案。也就是说，是 Zookeeper 保证了至少有一个 HBase Master 处于运行状态。并且 Zookeeper 负责 Region 和 Region Server 的注册。其实 Zookeeper 发展到目前为止，已经成为了分布式大数据框架中容错性的标准框架。不光是 HBase，几乎所有的分布式大数据相关的开源框架，都依赖于 Zookeeper 实现 HA。</li></ul><h2 id="安装前准备"><a href="#安装前准备" class="headerlink" title="安装前准备"></a>安装前准备</h2><p>操作系统：CentOS 6.5 x86_64 JDK：1.8.0_74-b02 (JDK的安装本文不进行描述) Hbase: Hbase-1.2.5-bin.tar.gz Zookeeper: 3.4.6</p><h2 id="安装过程"><a href="#安装过程" class="headerlink" title="安装过程"></a>安装过程</h2><h3 id="1-主机名修改"><a href="#1-主机名修改" class="headerlink" title="1. 主机名修改"></a>1. 主机名修改</h3><p>登录nna节点，修改/etc/hosts文件，在后面追加如下内容：</p><pre class=" language-bash"><code class="language-bash">192.168.1.1   zk1192.168.1.2   zk2192.168.1.3   zk3192.168.1.10   master1192.168.1.11   master2192.168.1.12   region1192.168.1.13   region2<span class="token comment" spellcheck="true"># hdfs服务器地址，hdfs搭建请参考《Hadoop HDFS与YARN高可用安装》</span>192.168.1.4  nna192.168.1.5  nns192.168.1.6  dn1192.168.1.7  dn2192.168.1.8  dn3</code></pre><p>分发到其它主机，下面以zk1为例：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">scp</span> /etc/hosts root@zk1:/etc</code></pre><h3 id="2-创建hbase用户"><a href="#2-创建hbase用户" class="headerlink" title="2. 创建hbase用户"></a>2. 创建hbase用户</h3><p>登录除zk外的主机，分别创建hbase用户：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">useradd</span> hbase</code></pre><h3 id="3-添加ssh信任"><a href="#3-添加ssh信任" class="headerlink" title="3. 添加ssh信任"></a>3. 添加ssh信任</h3><pre class=" language-bash"><code class="language-bash"><span class="token function">su</span> - hbasessh-keygen -t rsa  <span class="token comment" spellcheck="true">#一直按回车键，直到交互结束。会在 ~/.ssh/ 目录下生成 id_rsa.pub 文件</span><span class="token function">cat</span> ~/.ssh/id_rsa.pub  <span class="token comment" spellcheck="true"># 拷贝里面的内容</span><span class="token comment" spellcheck="true"># 在所有主机中重复以上步骤</span><span class="token function">touch</span> ~/.ssh/authorized_keys <span class="token operator">&amp;&amp;</span> <span class="token function">chmod</span> 644 ~/.ssh/authorized_keys<span class="token function">vi</span> ~/.ssh/authorized_keys  <span class="token comment" spellcheck="true"># 将所有主机的 id_rsa.pub 文件中的内容都拷贝到此文件中，并将此文件分发到所有主机。注意是hbase用户下的 ~/.ssh/authorized_keys 文件</span></code></pre><p>做完上述动作后，可以使用如下命令验证免密登录是否设置成功，若登录过程不需要输入密码则已设置成功：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">su</span> - hbase<span class="token function">ssh</span> master1</code></pre><p>有必要在所有机器上都相互验证下。</p><h3 id="4-线程数与打开文件句柄数修改"><a href="#4-线程数与打开文件句柄数修改" class="headerlink" title="4. 线程数与打开文件句柄数修改"></a>4. 线程数与打开文件句柄数修改</h3><p>Hbase会在同一时间使用很多的文件句柄.大多数linux系统使用的默认值1024是不能满足的。</p><p>编辑/etc/security/limits.conf：</p><pre class=" language-bash"><code class="language-bash">* soft nofile 65535* hard nofile 65535* soft nproc 16384* hard nproc 16384</code></pre><p>若操作系统为centos6.5，还需编辑/etc/security/limits.d/90-nproc.conf：</p><pre class=" language-bash"><code class="language-bash">*          soft    nproc     1024root       soft    nproc     unlimitedhbase soft nproc 16384hbase hard nproc 16384</code></pre><p>除zk外的主机，请都完成上述设置。</p><h3 id="5-关闭防火墙"><a href="#5-关闭防火墙" class="headerlink" title="5. 关闭防火墙"></a>5. 关闭防火墙</h3><p>由于hbase的节点之间需要通信（RPC机制），这样一来就需要监听对应的端口，这里我就直接将防火墙关闭了，命令如下：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">chkconfig</span>  iptables off</code></pre><h3 id="6-开启时钟同步"><a href="#6-开启时钟同步" class="headerlink" title="6. 开启时钟同步"></a>6. 开启时钟同步</h3><p>各个节点的时间如果不同步，会出现启动异常，或其他原因。</p><pre class=" language-bash"><code class="language-bash"><span class="token function">service</span> ntpd start</code></pre><h3 id="7-环境变量配置"><a href="#7-环境变量配置" class="headerlink" title="7. 环境变量配置"></a>7. 环境变量配置</h3><pre class=" language-bash"><code class="language-bash"><span class="token function">export</span> JAVA_HOME<span class="token operator">=</span>/usr/lib/java<span class="token function">export</span> HBASE_HOME<span class="token operator">=</span>/home/hbase/hbase<span class="token function">export</span> HADOOP_HOME<span class="token operator">=</span>/home/hbase/hadoop  <span class="token comment" spellcheck="true">#配置hadoop变量，否则hbase不识别hdfs集群名</span><span class="token function">export</span> HBASE_MANAGES_ZK<span class="token operator">=</span>false   <span class="token comment" spellcheck="true">#不使用hbase自带的zookeeper，使用搭建的zk集群。</span><span class="token function">export</span> PATH<span class="token operator">=</span><span class="token variable">$PATH</span><span class="token keyword">:</span><span class="token variable">$JAVA_HOME</span>/bin:<span class="token variable">$HADOOP_HOME</span>/bin:<span class="token variable">$HADOOP_HOME</span>/sbin</code></pre><ol start="8"><li>核心文件配置</li></ol><p>将hbase-1.2.5-bin.tar.gz上传到/home/hbase下，并解压。拷贝hdfs节点上的hadoop安装目录到/home/hbase。</p><pre class=" language-bash"><code class="language-bash"><span class="token function">su</span> - hbase <span class="token operator">&amp;&amp;</span> <span class="token function">cd</span> /home/hbase<span class="token function">tar</span> -xzvf hbase-1.2.5-bin.tar.gz <span class="token operator">&amp;&amp;</span> <span class="token function">ln</span> -s hbase-1.2.5 hbase</code></pre><p><code>`</code>bash $HBASE_HOME/conf/hbase-site.xml</p><p><configuration><property><name>hbase.rootdir</name><value>hdfs://cluster1/hbase</value></property><property><name>hbase.cluster.distributed</name><value>true</value></property><property><name>hbase.zookeeper.quorum</name><value>zk1:2181,zk2:2181,zk3:2181</value></property></configuration></p><pre><code>```bash $HBASE_HOME/conf/regionserversregion1region2</code></pre><p>将配置好的hbase与hadoop复制到其它节点；</p><pre class=" language-bash"><code class="language-bash"><span class="token function">scp</span> -r hbase master2:~/<span class="token function">scp</span> -r hbase region1:~/<span class="token function">scp</span> -r hbase region2:~/<span class="token function">scp</span> -r hadoop master2:~/<span class="token function">scp</span> -r hadoop region1:~/<span class="token function">scp</span> -r hadoop region2:~/</code></pre><ol start="9"><li>启动</li></ol><p>登录到master1：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">su</span> - hbase<span class="token variable">$HBASE_HOME</span>/bin/start-hbase.sh <span class="token comment" spellcheck="true"># 此脚本会在master1上启动HMaster，region1与region2上启动HRegionServer</span></code></pre><p>登录到master2:</p><pre class=" language-bash"><code class="language-bash"><span class="token function">su</span> - hbase<span class="token variable">$HBASE_HOME</span>/bin/hbase-daemon.sh start master <span class="token comment" spellcheck="true"># 此脚本会在master2上启动HMaster</span></code></pre><p>访问：<a href="http://192.168.1.10:16010" target="_blank" rel="noopener">http://192.168.1.10:16010</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;HDFS的高可用搭建不在本篇中描述，请参考&lt;a href=&quot;/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%A4%A7%E6%95%B0%E6%8D%AE/HDFS-Deployed.html&quot;&gt;《Hadoop HDFS与YARN高可用安装》&lt;/a&gt;。&lt;/p&gt;&lt;h2 id=&quot;节点规划&quot;&gt;&lt;a href=&quot;#节点规划&quot; class=&quot;headerlink&quot; title=&quot;节点规划&quot;&gt;&lt;/a&gt;节点规划&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;hostname&lt;/th&gt;&lt;th&gt;ip&lt;/th&gt;&lt;th&gt;安装服务&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;zk1&lt;/td&gt;&lt;td&gt;192.168.1.1&lt;/td&gt;&lt;td&gt;zookeeper&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;zk2&lt;/td&gt;&lt;td&gt;192.168.1.2&lt;/td&gt;&lt;td&gt;zookeeper&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;zk3&lt;/td&gt;&lt;td&gt;192.168.1.3&lt;/td&gt;&lt;td&gt;zookeeper&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="大数据" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="hadoop" scheme="http://zyouwei.com/tags/hadoop/"/>
    
      <category term="hbase" scheme="http://zyouwei.com/tags/hbase/"/>
    
  </entry>
  
  <entry>
    <title>高可用HDFS搭建</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%A4%A7%E6%95%B0%E6%8D%AE/HDFS-Deployed.html"/>
    <id>http://zyouwei.com/技术笔记/大数据/HDFS-Deployed.html</id>
    <published>2018-01-30T11:12:21.000Z</published>
    <updated>2017-05-08T03:25:41.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="HDFS-HA架构图"><a href="#HDFS-HA架构图" class="headerlink" title="HDFS+HA架构图"></a>HDFS+HA架构图</h2><p><img src="/images/posts/2017-05/hdfs-arch.png" alt="hdfs-arch"></p><a id="more"></a><p>上图大致架构包括：</p><ol><li>利用共享存储来在两个NN间同步edits信息。以前的HDFS是share nothing but NN，现在NN又share storage，这样其实是转移了单点故障的位置，但中高端的存储设备内部都有各种RAID以及冗余硬件，包括电源以及网卡等，比服务器的可靠性还是略有提高。通过NN内部每次元数据变动后的flush操作，加上NFS的close-to-open，数据的一致性得到了保证。</li><li>DN同时向两个NN汇报块信息。这是让Standby NN保持集群的最新状态的必须步骤。</li><li>用于监视和控制NN进程的FailoverController进程。显然，我们不能在NN进程内部进行心跳等信息同步，最简单的原因，一次FullGC就可以让NN挂起十几分钟，所以，必须要有一个独立的短小精悍的watchdog来专门负责监控。这也是一个松耦合的设计，便于扩展或更改，目前版本里是用ZooKeeper（简称ZK）来做同步锁，但用户可以方便的把这个Zookeeper FailoverController（简称ZKFC）替换为其他的HA方案或leader选举方案。</li><li>隔离（Fencing），防止脑裂，就是保证在任何时候只有一个主NN，包括三个方面：<ul><li>共享存储fencing，确保只有一个NN可以写入edits。</li><li>客户端fencing，确保只有一个NN可以响应客户端的请求。</li><li>DN fencing，确保只有一个NN向DN下发命令，譬如删除块，复制块等等。</li></ul></li></ol><h2 id="节点规划"><a href="#节点规划" class="headerlink" title="节点规划"></a>节点规划</h2><table><thead><tr><th>hostname</th><th>ip</th><th>安装服务</th></tr></thead><tbody><tr><td>zk1</td><td>192.168.1.1</td><td>zookeeper</td></tr><tr><td>zk2</td><td>192.168.1.2</td><td>zookeeper</td></tr><tr><td>zk3</td><td>192.168.1.3</td><td>zookeeper</td></tr></tbody></table><blockquote><p>提示：</p><p>zookeeper集群安装不在本文描述范围， 请自行参考其他文档。（zookeeper集群也可以跟datanode安装在一起）</p></blockquote><table><thead><tr><th>hostname</th><th>ip</th><th>安装服务</th></tr></thead><tbody><tr><td>nna</td><td>192.168.1.4</td><td>NameNode、DFSZKFailoverController</td></tr><tr><td>nns</td><td>192.168.1.5</td><td>NameNode、DFSZKFailoverController</td></tr><tr><td>dn1</td><td>192.168.1.6</td><td>JournalNode、NodeManager、DataNode</td></tr><tr><td>dn2</td><td>192.168.1.7</td><td>JournalNode、NodeManager、DataNode</td></tr><tr><td>dn3</td><td>192.168.1.8</td><td>JournalNode、NodeManager、DataNode</td></tr></tbody></table><ul><li>namenode服务器: 运行namenode的服务器应该有相同的硬件配置。在HA集群中,standby状态的namenode可以完成checkpoint操作，因此没必要配置Secondary namenode、CheckpointNode、BackupNode。如果真的配置了还会报错。</li><li>journalnode服务器: 运行的journalnode进程非常轻量，可以部署在其他的服务器上。注意：必须允许至少3个节点。当然可以运行更多，但是必须是奇数个,如3,5,7,9个等等。当运行N个节点时，系统可以容忍至少(N-1)/2个节点失败而不影响正常运行。</li></ul><h2 id="安装前准备"><a href="#安装前准备" class="headerlink" title="安装前准备"></a>安装前准备</h2><p>操作系统：CentOS 6.5 x86_64 JDK：1.8.0_74-b02 (JDK的安装本文不进行描述) Hadoop：hadoop-2.8.0.tar.gz Zookeeper: 3.4.6</p><h2 id="安装过程"><a href="#安装过程" class="headerlink" title="安装过程"></a>安装过程</h2><h3 id="1-主机名修改"><a href="#1-主机名修改" class="headerlink" title="1. 主机名修改"></a>1. 主机名修改</h3><p>登录<code>nna</code>节点，修改<code>/etc/hosts</code>文件，在后面追加如下内容：</p><pre class=" language-js"><code class="language-js"><span class="token number">192.168</span><span class="token punctuation">.</span><span class="token number">1.1</span>   zk1<span class="token number">192.168</span><span class="token punctuation">.</span><span class="token number">1.2</span>   zk2<span class="token number">192.168</span><span class="token punctuation">.</span><span class="token number">1.3</span>   zk3<span class="token number">192.168</span><span class="token punctuation">.</span><span class="token number">1.4</span>   nna<span class="token number">192.168</span><span class="token punctuation">.</span><span class="token number">1.5</span>   nns<span class="token number">192.168</span><span class="token punctuation">.</span><span class="token number">1.6</span>   dn1<span class="token number">192.168</span><span class="token punctuation">.</span><span class="token number">1.7</span>   dn2<span class="token number">192.168</span><span class="token punctuation">.</span><span class="token number">1.8</span>   dn3</code></pre><p>分发到其它主机，下面以<code>zk1</code>为例：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">scp</span> /etc/hosts root@zk1:/etc</code></pre><h3 id="2-创建hadoop用户"><a href="#2-创建hadoop用户" class="headerlink" title="2. 创建hadoop用户"></a>2. 创建hadoop用户</h3><p>登录除zk外的主机，分别创建<code>hadoop</code>用户：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">useradd</span> hadoop</code></pre><h3 id="3-添加ssh信任"><a href="#3-添加ssh信任" class="headerlink" title="3. 添加ssh信任"></a>3. 添加ssh信任</h3><pre class=" language-bash"><code class="language-bash"><span class="token function">su</span> - hadoopssh-keygen -t rsa  <span class="token comment" spellcheck="true">#一直按回车键，直到交互结束。会在 ~/.ssh/ 目录下生成 id_rsa.pub 文件</span><span class="token function">cat</span> ~/.ssh/id_rsa.pub  <span class="token comment" spellcheck="true"># 拷贝里面的内容</span><span class="token comment" spellcheck="true"># 在所有主机中重复以上步骤</span><span class="token function">touch</span> ~/.ssh/authorized_keys <span class="token operator">&amp;&amp;</span> <span class="token function">chmod</span> 644 ~/.ssh/authorized_keys<span class="token function">vi</span> ~/.ssh/authorized_keys  <span class="token comment" spellcheck="true"># 将所有主机的 id_rsa.pub 文件中的内容都拷贝到此文件中，并将此文件分发到所有主机。注意是hadoop用户下的 ~/.ssh/authorized_keys 文件</span></code></pre><p>做完上述动作后，可以使用如下命令验证免密登录是否设置成功，若登录过程不需要输入密码则已设置成功：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">su</span> - hadoop<span class="token function">ssh</span> nns</code></pre><p>有必要在所有机器上都相互验证下。</p><h3 id="4-线程数与打开文件句柄数修改"><a href="#4-线程数与打开文件句柄数修改" class="headerlink" title="4. 线程数与打开文件句柄数修改"></a>4. 线程数与打开文件句柄数修改</h3><p>Hdaoop会在同一时间使用很多的文件句柄.大多数linux系统使用的默认值1024是不能满足的。</p><p>编辑<code>/etc/security/limits.conf</code>：</p><pre class=" language-js"><code class="language-js"><span class="token operator">*</span> soft nofile <span class="token number">65535</span><span class="token operator">*</span> hard nofile <span class="token number">65535</span><span class="token operator">*</span> soft nproc <span class="token number">16384</span><span class="token operator">*</span> hard nproc <span class="token number">16384</span></code></pre><p>若操作系统为centos6.5，还需编辑<code>/etc/security/limits.d/90-nproc.conf</code>：</p><pre class=" language-js"><code class="language-js"><span class="token operator">*</span>          soft    nproc     <span class="token number">1024</span>root       soft    nproc     unlimitedhadoop soft nproc <span class="token number">16384</span>hadoop hard nproc <span class="token number">16384</span></code></pre><p>除zk外的主机，请都完成上述设置。</p><h3 id="5-关闭防火墙"><a href="#5-关闭防火墙" class="headerlink" title="5. 关闭防火墙"></a>5. 关闭防火墙</h3><p>由于hadoop的节点之间需要通信（RPC机制），这样一来就需要监听对应的端口，这里我就直接将防火墙关闭了，命令如下：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">chkconfig</span>  iptables off</code></pre><h3 id="6-开启时钟同步"><a href="#6-开启时钟同步" class="headerlink" title="6. 开启时钟同步"></a>6. 开启时钟同步</h3><p>各个节点的时间如果不同步，会出现启动异常，或其他原因。</p><pre class=" language-bash"><code class="language-bash"><span class="token function">service</span> ntpd start</code></pre><h3 id="7-环境变量配置"><a href="#7-环境变量配置" class="headerlink" title="7. 环境变量配置"></a>7. 环境变量配置</h3><pre class=" language-bash"><code class="language-bash"><span class="token function">export</span> JAVA_HOME<span class="token operator">=</span>/usr/lib/java<span class="token function">export</span> HADOOP_HOME<span class="token operator">=</span>/home/hadoop/hadoop<span class="token function">export</span> PATH<span class="token operator">=</span><span class="token variable">$PATH</span><span class="token keyword">:</span><span class="token variable">$JAVA_HOME</span>/bin:<span class="token variable">$HADOOP_HOME</span>/bin:<span class="token variable">$HADOOP_HOME</span>/sbin</code></pre><h3 id="8-核心文件配置"><a href="#8-核心文件配置" class="headerlink" title="8. 核心文件配置"></a>8. 核心文件配置</h3><p>创建文件夹：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">mkdir</span> -p /home/hadoop/tmp<span class="token function">mkdir</span> -p /home/hadoop/data/tmp/journal<span class="token function">mkdir</span> -p /home/hadoop/data/dfs/name<span class="token function">mkdir</span> -p /home/hadoop/data/dfs/data<span class="token function">mkdir</span> -p /home/hadoop/data/yarn/local<span class="token function">mkdir</span> -p /home/hadoop/log/yarn</code></pre><p>解压安装包：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">tar</span> -xzvf hadoop-2.8.0.tar.gz<span class="token function">ln</span> -s hadoop-2.8.0 hadoop</code></pre><ul><li>$HADOOP_HOME/etc/hadoop/core-site.xml</li></ul><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>fs.defaultFS<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>hdfs://cluster1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>io.file.buffer.size<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>131072<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>hadoop.tmp.dir<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>/home/hadoop/tmp<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>hadoop.proxyuser.hadoop.hosts<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>*<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>hadoop.proxyuser.hadoop.groups<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>*<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>ha.zookeeper.quorum<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>zk1:2181,zk2:2181,zk3:2181<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span></code></pre><ul><li>$HADOOP_HOME/etc/hadoop/hdfs-site.xml</li></ul><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.nameservices<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>cluster1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.ha.namenodes.cluster1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>nna,nns<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.namenode.rpc-address.cluster1.nna<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>nna:9000<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.namenode.rpc-address.cluster1.nns<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>nns:9000<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.namenode.http-address.cluster1.nna<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>nna:50070<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.namenode.http-address.cluster1.nns<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>nns:50070<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.namenode.shared.edits.dir<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>qjournal://dn1:8485;dn2:8485;dn3:8485/cluster1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.client.failover.proxy.provider.cluster1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.ha.fencing.methods<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>sshfence<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.ha.fencing.ssh.private-key-files<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>/home/hadoop/.ssh/id_rsa<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.journalnode.edits.dir<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>/home/hadoop/data/tmp/journal<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.ha.automatic-failover.enabled<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>true<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.namenode.name.dir<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>/home/hadoop/data/dfs/name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.datanode.data.dir<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>/home/hadoop/data/dfs/data<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.replication<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.webhdfs.enabled<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>true<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.journalnode.http-address<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>0.0.0.0:8480<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>dfs.journalnode.rpc-address<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>0.0.0.0:8485<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>ha.zookeeper.quorum<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>zk1:2181,zk2:2181,zk3:2181<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span></code></pre><ul><li>$HADOOP_HOME/etc/hadoop/mapred-site.xml</li></ul><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>mapreduce.framework.name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>yarn<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>mapreduce.jobhistory.address<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>nna:10020<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>mapreduce.jobhistory.webapp.address<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>nna:19888<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span></code></pre><pre class=" language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>hbase.rootdir<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>hdfs://cluster1/hbase<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>hbase.cluster.distributed<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>true<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>name</span><span class="token punctuation">></span></span>hbase.zookeeper.quorum<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>name</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>value</span><span class="token punctuation">></span></span>172.17.18.9:2181,172.17.18.112:2181,172.17.17.19:2181<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>value</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span></code></pre><ul><li>$HADOOP_HOME/etc/hadoop/slaves</li></ul><pre class=" language-txt"><code class="language-txt">dn1dn2dn3</code></pre><p>将配置好的hadoop复制到其它节点：</p><pre class=" language-bash"><code class="language-bash"><span class="token function">scp</span> -r <span class="token variable">$HADOOP_HOME</span>/ nns:~/<span class="token function">scp</span> -r <span class="token variable">$HADOOP_HOME</span>/ dn1:~/<span class="token function">scp</span> -r <span class="token variable">$HADOOP_HOME</span>/ dn2:~/<span class="token function">scp</span> -r <span class="token variable">$HADOOP_HOME</span>/ dn3:~/</code></pre><h3 id="9-启动"><a href="#9-启动" class="headerlink" title="9. 启动"></a>9. 启动</h3><p>由于我们配置了QJM，所以我们需要先启动QJM的服务，启动顺序如下所示：</p><ol><li>启动zookeeper集群。启动完成之后可以输入<code>zkServer.sh status</code>查看启动状态，会出现一个leader和两个follower。输入<code>jps</code>，会显示启动进程：QuorumPeerMain</li><li>在NN节点上（选一台即可，这里我选择的是一台预NNA节点），然后启动journalnode服务，命令如下：<code>hadoop-daemons.sh start journalnode</code>。或者单独进入到每个DN输入启动命令：<code>hadoop-daemon.sh start journalnode</code>。输入<code>jps</code>显示启动进程：JournalNode</li><li>接着若是配置后首次启动，需要格式化HDFS，命令如下：<code>hadoop namenode –format</code></li><li>之后我们需要格式化ZK，命令如下：<code>hdfs zkfc –formatZK</code></li><li>接着我们启动hdfs，命令如下：<code>start-dfs.sh</code>，我们在nna输入<code>jps</code>查看进程，显示如下：DFSZKFailoverController，NameNode，ResourceManager。DN节点也会自动启动DataNode、NodeManager</li><li>接着我们在NNS输入jps查看，发现只有DFSZKFailoverController进程，这里我们需要手动启动NNS上的namenode。命令如下：<code>hadoop-daemon.sh start namenode</code></li><li>最后我们需要同步NNA节点的元数据，命令如下：<code>hdfs namenode –bootstrapStandby</code></li></ol><p>启动yarn:</p><ol><li>接着上面的步骤，在NNA节点上：start-yarn.sh。输入jps查看进程，会发现多了：ResourceManager</li><li>登录NNS节点，使用：yarn-daemon.sh start resourcemanager。需要注意的是，在NNS上的yarn-site.xml中，需要配置指向NNS，属性配置为rm2，在NNA中配置的是rm1。</li></ol><p>启动完成之后，可以访问：</p><p>hdfs: <a href="http://192.168.1.4:50770/dfshealth.html" target="_blank" rel="noopener">http://192.168.1.4:50770/dfshealth.html</a> 或 <a href="http://192.168.1.5:50770/dfshealth.html" target="_blank" rel="noopener">http://192.168.1.5:50770/dfshealth.html</a> （其中一个为“active”,一个为“standby”） resourcemanger: <a href="http://192.168.1.4:8188/cluster/cluster" target="_blank" rel="noopener">http://192.168.1.4:8188/cluster/cluster</a> 或 <a href="http://192.168.1.5:8188/cluster/cluster" target="_blank" rel="noopener">http://192.168.1.5:8188/cluster/cluster</a></p><h4 id="测试："><a href="#测试：" class="headerlink" title="测试："></a>测试：</h4><pre class=" language-bash"><code class="language-bash">hadoop fs -mkdir hdfs:/test/ hadoop fs -copyFromLocal /home/hadoop/data/webcount hdfs:/test/ hadoop fs -ls hdfs:/test/ hadoop fs -cat hdfs:/test/webcount</code></pre><h4 id="HA的切换"><a href="#HA的切换" class="headerlink" title="HA的切换"></a>HA的切换</h4><p>由于我配置的是自动切换，若NNA节点宕掉，NNS节点会立即由standby状态切换为active状态。若是配置的手动状态，可以输入如下命令进行人工切换：</p><pre class=" language-bash"><code class="language-bash">hdfs haadmin -failover --forcefence --forceactive nna  nns</code></pre><p>这条命令的意思是，将nna变成standby，nns变成active。而且手动状态下需要重启服务。</p><h3 id="工具脚本"><a href="#工具脚本" class="headerlink" title="工具脚本"></a>工具脚本</h3><p><code>`</code>bash copy-config.sh</p><p>#!/bin/sh</p><p>scp /home/hadoop/hadoop/etc/hadoop/<em> nns:/home/hadoop/hadoop/etc/hadoop scp /home/hadoop/hadoop/etc/hadoop/</em> dn1:/home/hadoop/hadoop/etc/hadoop scp /home/hadoop/hadoop/etc/hadoop/<em> dn2:/home/hadoop/hadoop/etc/hadoop scp /home/hadoop/hadoop/etc/hadoop/</em> dn3:/home/hadoop/hadoop/etc/hadoop</p><p>scp /home/hadoop/hadoop/libexec/<em> nns:/home/hadoop/hadoop/libexec scp /home/hadoop/hadoop/libexec/</em> dn1:/home/hadoop/hadoop/libexec scp /home/hadoop/hadoop/libexec/<em> dn2:/home/hadoop/hadoop/libexec scp /home/hadoop/hadoop/libexec/</em> dn3:/home/hadoop/hadoop/libexec <code>`</code></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;HDFS-HA架构图&quot;&gt;&lt;a href=&quot;#HDFS-HA架构图&quot; class=&quot;headerlink&quot; title=&quot;HDFS+HA架构图&quot;&gt;&lt;/a&gt;HDFS+HA架构图&lt;/h2&gt;&lt;p&gt;&lt;img src=&quot;/images/posts/2017-05/hdfs-arch.png&quot; alt=&quot;hdfs-arch&quot;&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="大数据" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="hadoop" scheme="http://zyouwei.com/tags/hadoop/"/>
    
      <category term="hdfs" scheme="http://zyouwei.com/tags/hdfs/"/>
    
  </entry>
  
  <entry>
    <title>RocketMQ Performance Test</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/RocketMQ-Performance-Test.html"/>
    <id>http://zyouwei.com/技术笔记/Java/RocketMQ-Performance-Test.html</id>
    <published>2018-01-15T07:45:23.000Z</published>
    <updated>2018-01-15T09:58:12.916Z</updated>
    
    <content type="html"><![CDATA[<h2 id="测试环境"><a href="#测试环境" class="headerlink" title="测试环境"></a>测试环境</h2><table><thead><tr><th>硬件配置</th><th>4C 4G SSD</th></tr></thead><tbody><tr><td>操作系统</td><td>centeOS 6.5</td></tr><tr><td>MQ版本</td><td>rocketmq-broker-4.2.0-incubating-SNAPSHOT （2017-08-23）</td></tr></tbody></table><p>测试程序运行机器：Macbook Pro i7 2.3GHz, 16G</p><a id="more"></a><h2 id="单Broker场景"><a href="#单Broker场景" class="headerlink" title="单Broker场景"></a>单Broker场景</h2><h3 id="单broker同步发送（单发送进程），每条message大小：30bytes"><a href="#单broker同步发送（单发送进程），每条message大小：30bytes" class="headerlink" title="单broker同步发送（单发送进程），每条message大小：30bytes"></a>单broker同步发送（单发送进程），每条message大小：30bytes</h3><p>NameServer与Broker分别在不同机器部署，broker采用异步持久化</p><pre><code>topic设置：    perm: 6JVM设置：    NameServer: -Xms1g -Xmx1g -Xmn512m    Broker: -Xms2g -Xmx2g -Xmn1g</code></pre><table><thead><tr><th>发送条数</th><th>成功率</th><th>耗时（s)</th><th>发送速率（条/秒）</th><th>备注</th></tr></thead><tbody><tr><td>100000</td><td>100%</td><td>74.265</td><td>1346.52</td><td>写queue数量：4</td></tr><tr><td>100000</td><td>100%</td><td>75.347</td><td>1327.22</td><td>写queue数量：8</td></tr></tbody></table><blockquote><p><strong>结论</strong></p><p>成功率非常有保证，调整topic的queue数量并不能提升发送速率。broker的能力并没有完全被发挥出来，CPU非常闲。</p></blockquote><h3 id="单broker异步发送（单发送进程），每条message大小：30bytes"><a href="#单broker异步发送（单发送进程），每条message大小：30bytes" class="headerlink" title="单broker异步发送（单发送进程），每条message大小：30bytes"></a>单broker异步发送（单发送进程），每条message大小：30bytes</h3><p>NameServer与Broker分别在不同机器部署，broker采用异步持久化</p><pre><code>topic设置：    perm: 6JVM设置：    NameServer： -Xms1g -Xmx1g -Xmn512m    Broker:    -Xms2g -Xmx2g -Xmn1g</code></pre><table><thead><tr><th>发送条数</th><th>成功率</th><th>耗时（s)</th><th>发送速率（条/秒）</th><th>备注</th></tr></thead><tbody><tr><td>1000000</td><td>100%</td><td>33.420</td><td>29922.20</td><td>写queue数量：4，每发送37条休眠一毫秒，限制生产者速度不超过37000条/秒</td></tr><tr><td>1000000</td><td>96.6024%</td><td>20.871</td><td>46285.46</td><td>写queue数量：4，发送端不限速</td></tr><tr><td>1000000</td><td>100%</td><td>33.477</td><td>29871.25</td><td>写queue数量：8，每发送37条休眠一毫秒，限制生产者速度不超过37000条/秒</td></tr><tr><td>1000000</td><td>97.9327%</td><td>22.961</td><td>48844.23</td><td>写queue数量：8，发送端不限速</td></tr></tbody></table><blockquote><p><strong>结论</strong></p><ol><li>吞吐量较大时，会触发broker端的流控限制，会导致消息发送失败，出现异常：<code>Rocketmq:MQBrokerException: CODE: 2 DESC: [TIMEOUT_CLEAN_QUEUE]</code>。</li><li>通过多次反复测试，发现在保证发送成功率<code>100%</code>的情况下，发送速率最大为<code>29922.20条/秒</code>，测试过程中，测试程序最大上传网速达到<code>15.9MB/s</code>。</li><li>在成功率<code>100%</code>的情况下，吞吐量是同步发送的超<code>20</code>倍。不限速（允许少量失败）的情况下，吞吐量是同步发送的超<code>34</code>倍。</li><li>在单发送进程场景下，增加topic的queue数量并不能提升生产速率。</li></ol></blockquote><blockquote><p>发现的问题（或BUG）</p><p>测试发现<code>producer.shutdown()</code>时，并没有完全等待<code>SendCallBack</code>执行完就退出，可能会导致有部分callback无法接收到。因此在在测试程序异步发送完成之后，还需要添加一个线程休眠，确保全部消息全部发送到broker。</p></blockquote><h3 id="单broker消费（单消费进程），每条message大小：30bytes"><a href="#单broker消费（单消费进程），每条message大小：30bytes" class="headerlink" title="单broker消费（单消费进程），每条message大小：30bytes"></a>单broker消费（单消费进程），每条message大小：30bytes</h3><p>NameServer与Broker分别在不同机器部署</p><pre><code>topic设置：    perm: 6JVM设置：    NameServer： -Xms1g -Xmx1g -Xmn512m    Broker:    -Xms2g -Xmx2g -Xmn1gconsumer设置：    consumeThreadMax: 64    pullBatchSize: 32</code></pre><table><thead><tr><th>消费条数</th><th>成功率</th><th>耗时（s)</th><th>消费速率（条/秒）</th><th>备注</th></tr></thead><tbody><tr><td>3000000</td><td>100%</td><td>21.353</td><td>140495.48</td><td>读queue数量：8</td></tr><tr><td>3000000</td><td>100%</td><td>34.371</td><td>87282.88</td><td>读queue数量：4</td></tr></tbody></table><blockquote><p><strong>结论</strong></p><ol><li>RocketMQ的消息消费能力非常高，只要消费端的业务处理足够快。单broker的消费超过<code>7w条每秒</code>，基本上可以说满足大部分的实际业务场景。</li><li>增加topic的queue数量，能明显提高消费速率。（前提是生产的消息要平均分布在各个queue）</li><li>从测试环境看，消费高峰每秒下载流量超过<code>31MB</code>，Broker的CPU会占用维持在<code>50%</code>左右，消费过程中CPU的IOWait在某一瞬间会超过<code>20%</code>。</li></ol></blockquote><h3 id="单broker消费（双消费进程），每条message大小：30bytes"><a href="#单broker消费（双消费进程），每条message大小：30bytes" class="headerlink" title="单broker消费（双消费进程），每条message大小：30bytes"></a>单broker消费（双消费进程），每条message大小：30bytes</h3><p>NameServer与Broker分别在不同机器部署</p><pre><code>topic设置：    读队列数量：4    写队列数量：4    perm: 6JVM设置：    NameServer： -Xms1g -Xmx1g -Xmn512m    Broker:    -Xms2g -Xmx2g -Xmn1gconsumer设置：    consumeThreadMax: 64    pullBatchSize: 32</code></pre><table><thead><tr><th></th><th>消费条数</th><th>成功率</th><th>耗时（s)</th><th>消费速率（条/秒）</th></tr></thead><tbody><tr><td>消费进程 1</td><td>1000000</td><td>100%</td><td>22.549</td><td>44347.86</td></tr><tr><td>消费进程2</td><td>1000000</td><td>100%</td><td>21.360</td><td>46796.76</td></tr></tbody></table><blockquote><p><strong>结论</strong></p><p>增加消费者进程并不能提高整体的消费速度，因为消费者本身就是多线程的模式消费。但可以通过增加消费者进程来提升消费端高可用能力，避免消费端的单点故障。</p></blockquote><h2 id="多Master-Broker场景"><a href="#多Master-Broker场景" class="headerlink" title="多Master Broker场景"></a>多Master Broker场景</h2><h3 id="双Master-Broker-同步发送，每条message大小：30bytes"><a href="#双Master-Broker-同步发送，每条message大小：30bytes" class="headerlink" title="双Master Broker 同步发送，每条message大小：30bytes"></a>双Master Broker 同步发送，每条message大小：30bytes</h3><p>三台主机，一台部署NameServer，另外两台各部署Broker master</p><pre><code>topic设置：    perm: 6JVM设置：    NameServer: -Xms1g -Xmx1g -Xmn512m    Broker: -Xms2g -Xmx2g -Xmn1g</code></pre><table><thead><tr><th>发送条数</th><th>成功率</th><th>耗时（s)</th><th>发送速率（条/秒）</th><th>备注</th></tr></thead><tbody><tr><td>100000</td><td>100%</td><td>66.845</td><td>1495.99</td><td>每个broker4个queue</td></tr></tbody></table><blockquote><p><strong>结论</strong></p><p>同步发送速率双Master Borker与单broker基本在一个数量级 双Master Broker 异步发送（单发送进程），每条message大小：30bytes</p></blockquote><h3 id="三台主机，一台部署NameServer，另外两台各部署Broker-master"><a href="#三台主机，一台部署NameServer，另外两台各部署Broker-master" class="headerlink" title="三台主机，一台部署NameServer，另外两台各部署Broker master"></a>三台主机，一台部署NameServer，另外两台各部署Broker master</h3><pre><code>topic设置：    perm: 6JVM设置：    NameServer: -Xms1g -Xmx1g -Xmn512m    Broker: -Xms2g -Xmx2g -Xmn1gBroker设置：    为提高成功率，将waitTimeMillsInSendQueue的默认值从200调整到1000</code></pre><table><thead><tr><th>发送条数</th><th>成功率</th><th>耗时（s)</th><th>发送速率（条/秒）</th><th>备注</th></tr></thead><tbody><tr><td>1000000</td><td>100%</td><td>20.862</td><td>47934.04</td><td>每个broker各4个queue， 每发送70条休眠1毫秒，确保每秒发送数量不超过7w条</td></tr><tr><td>1000000</td><td>100%</td><td>20.616</td><td>48506.01</td><td>每个broker各8个queue， 每发送70条休眠1毫秒，确保每秒发送数量不超过7w条</td></tr><tr><td>1000000</td><td>99.7571%</td><td>16.026</td><td>62247.03</td><td>每个broker各4个queue，生产者不限速</td></tr><tr><td>1000000</td><td>99.8807%</td><td>17.108</td><td>58,382.45</td><td>每个broker各8个queue，生产者不限速</td></tr></tbody></table><blockquote><p><strong>结论</strong></p><ol><li>在保证成功率100%的情况下，双master broker比单broker的发送速率明显提高，提升超<code>60%</code>。</li><li>增加topic的queue数量，但发送进程并不能提高发送速率</li></ol></blockquote><p>双broker消费（单消费进程），每条message大小：30bytes</p><h3 id="三台主机，一台部署NameServer，另外两台各部署Broker-master-1"><a href="#三台主机，一台部署NameServer，另外两台各部署Broker-master-1" class="headerlink" title="三台主机，一台部署NameServer，另外两台各部署Broker master"></a>三台主机，一台部署NameServer，另外两台各部署Broker master</h3><pre><code>topic设置：    perm: 6JVM设置：    NameServer： -Xms1g -Xmx1g -Xmn512m    Broker:    -Xms2g -Xmx2g -Xmn1gconsumer设置：    consumeThreadMax: 64    pullBatchSize: 32</code></pre><table><thead><tr><th>消费条数</th><th>成功率</th><th>耗时（s)</th><th>消费速率（条/秒）</th><th>备注</th></tr></thead><tbody><tr><td>2000000</td><td>100%</td><td>11.170</td><td>179051.02</td><td>读queue数量：4</td></tr><tr><td>3000000</td><td>100%</td><td>11.979</td><td>250438.26</td><td>读queue数量：8</td></tr></tbody></table><blockquote><p><strong>结论</strong></p><ol><li>由于topic在每个broker上都有4个queue，发送的消息基本是均匀分配在两个broker的，因此在消费时能从两个broker分别进行消费，消费速率较单broker的消费提升了近1倍。</li><li>增大topic的queue数量，明显能提高消费速率。</li></ol></blockquote><h3 id="双Master双Slave异步复制，异步发送，每条message大小：30bytes"><a href="#双Master双Slave异步复制，异步发送，每条message大小：30bytes" class="headerlink" title="双Master双Slave异步复制，异步发送，每条message大小：30bytes"></a>双Master双Slave异步复制，异步发送，每条message大小：30bytes</h3><p>五台主机，一台部署NameServer，两台分别部署broker-a-master、broker-a-slave，两台分别部署broker-b-master、broker-b-slave。</p><pre><code>topic设置：    perm: 6JVM设置：    NameServer: -Xms1g -Xmx1g -Xmn512m    Broker: -Xms2g -Xmx2g -Xmn1gBroker设置：    master broker设置: brokerRole=ASYNC_MASTER</code></pre><table><thead><tr><th>发送条数</th><th>成功率</th><th>耗时（s)</th><th>发送速率（条/秒）</th><th>备注</th></tr></thead><tbody><tr><td>100000</td><td>100%</td><td>20.755</td><td>48181.16</td><td>每个broker各8个queue，每发送70条休眠1毫秒，确保每秒发送数量不超过7w条</td></tr></tbody></table><blockquote><p><strong>结论</strong></p><ol><li>Master与Slave采用异步复制时，基本不影响消息发送的吞吐量。</li><li>发送100w条消息到broker后（平均分配到broker-a与broker-b），关掉broker-b-master，启动消费者时，如果订阅组之前不存在，则不能从broker-b-slave上进行消费。如果订阅组存在，则能从broker-b-slave上进行消费，但当broker-b-master恢复之后，会重复消费broker-b-master上的消息。</li></ol></blockquote><blockquote><p>发现的问题（或BUG）</p><p>在slave上进行消费时，消费的offset近保存到了slave上，当master恢复时，slave上的消费offset并没有被同步到master上，master上的offset还是最初在master上的消费位置，因此会导致master故障期间从slave上消费的消息会被重复消费。这应该不是RocketMQ的设计初衷，感觉像是个bug。</p></blockquote><h2 id="RocketMQ-vs-Kafka"><a href="#RocketMQ-vs-Kafka" class="headerlink" title="RocketMQ vs Kafka"></a>RocketMQ vs Kafka</h2><ol><li>RocketMQ与Kafka一样，都没有JMS中定义的Queue的存在，只有Topic的存在。但他们都可以基于Topic来实现Queue的功能与特性。</li><li>RocketMQ可以创建只有1个queue的topic，再结合Cluster的消费方式来实现JMS queue的功能与特性。</li><li>Kafka则可以创建只有一个partition的topic来实现JMS Queue的功能与特性。</li><li>RocketMQ中queue的概念可类比为Kafka中的partition，若要实现消息的强顺序消费，RocketMQ需要设置topic只有一个queue，Kafka则需要设置topic只有一个partition。</li><li>若业务规定只需保证同一订单编号的状态变更的顺序性，RocketMQ可以在发送时通过订单编号与MessageQueueSelector来实现同一编号的订单消息放置在topic的同一个queue中，Kafka则可以将订单编号作为消息的key来确保同一编号的订单消息放置在了同一个partition。</li></ol><style>table th:nth-of-type(5){width:200px}</style>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;测试环境&quot;&gt;&lt;a href=&quot;#测试环境&quot; class=&quot;headerlink&quot; title=&quot;测试环境&quot;&gt;&lt;/a&gt;测试环境&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;硬件配置&lt;/th&gt;&lt;th&gt;4C 4G SSD&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;操作系统&lt;/td&gt;&lt;td&gt;centeOS 6.5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;MQ版本&lt;/td&gt;&lt;td&gt;rocketmq-broker-4.2.0-incubating-SNAPSHOT （2017-08-23）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;测试程序运行机器：Macbook Pro i7 2.3GHz, 16G&lt;/p&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
      <category term="MQ" scheme="http://zyouwei.com/tags/MQ/"/>
    
  </entry>
  
  <entry>
    <title>JVM内存结构简介</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/Java-Memory-Model.html"/>
    <id>http://zyouwei.com/技术笔记/Java/Java-Memory-Model.html</id>
    <published>2018-01-10T03:34:55.000Z</published>
    <updated>2018-01-12T08:00:46.919Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Java运行时内存结构"><a href="#Java运行时内存结构" class="headerlink" title="Java运行时内存结构"></a>Java运行时内存结构</h2><pre class=" language-txt"><code class="language-txt">+-------------------------------------------------+|          Runtime Memory Structure Chart         ||  +--------------+ | +----------+  +----------+  ||  |              | | |          |  |  Native  |  ||  |  MethodArea  | | | VM Stack |  |  Method  |  ||  |   (PermGen)  | | |          |  |   Stack  |  ||  +--------------+ | +----------+  +----------+  ||  +--------------+ | +------------------------+  ||  |              | | |                        |  ||  |     Heap     | | |    Program Counter     |  ||  |              | | |        Register        |  ||  +--------------+   +------------------------+  |+-------------------------------------------------+</code></pre><a id="more"></a><ul><li>线程共享数据区域<ul><li>Heap: 堆，是JVM最大的内存区。</li><li>Method Area(PermGen): 方法区，存储类、常量、静态变量等数据。在某些JVM的实现中也称持久代、永久代。</li></ul></li></ul><blockquote><p>JDK8之后，PermGen被元空间（MetaSpace）替代</p></blockquote><ul><li>线程之间相互独立区域<ul><li>VM Stack: 虚拟机栈</li><li>Native Method Stack: 本地方法栈</li><li>Program Counter Register: 程序计数器</li></ul></li></ul><p>通过一张图来了解如何通过参数来控制各区域的内存大小: <img src="/images/posts/2018-01/JVM_Memory.jpg" alt="JVM Memory"></p><h2 id="堆（Heap）"><a href="#堆（Heap）" class="headerlink" title="堆（Heap）"></a>堆（Heap）</h2><p>对于大多数应用来说，java堆（Heap）是JVM管理的最大一块内存。Java堆是被所有线程共享的一块内存，在虚拟机启动时创建，主要用来存放对象实例。</p><p>堆也是垃圾收集器管理的主要区域，从内存回收的角度看，大部分收集器采用分代回收，所以Java的堆可以细分为：新生代、老生代。新生代可以分为Eden空间、From空间、To空间。</p><p>如果堆中没有内存完成实例分配，并且也无法扩展时将会抛出<code>OutOfMemoryError</code>异常。</p><pre class=" language-txt"><code class="language-txt">+----------------------------------------+|           Heap Structure               || +--------------------+ +-------------+ || |  Young Generation  | |             | || | +----------------+ | |             | || | |       Eden     | | |             | || | |      Space     | | |             | || | +----------------+ | |             | || | +----------------+ | | Old/Tenured | || | |    FromSpace   | | |  Generation | || | |   (Survivor1)  | | |             | || | +----------------+ | |             | || | +----------------+ | |             | || | |      ToSpace   | | |             | || | |   (Survivor2)  | | |             | || | +----------------+ | |             | || +--------------------+ +-------------+ |+----------------------------------------+</code></pre><ul><li>新生代(Young Generation)<ul><li><a href="#eden">Eden Space</a></li><li><a href="#survivor">From Space(Survivor1)</a></li><li><a href="#survivor">To Space(Survivor2)</a></li></ul></li><li>老生代(Old Generation)</li></ul><p>相关参数设置：</p><table><thead><tr><th>参数名</th><th>描述</th><th>默认值</th><th>备注</th></tr></thead><tbody><tr><td><code>-Xms</code></td><td>堆的初始值，设置示例：<code>-Xms10m</code></td><td></td><td>如果没有设置此值，默认值=分配的新生代值+分配的老生代的值。设置的值必须是<code>1KB</code>的倍数，且最小为<code>1MB</code>。等同于<code>-XX:InitialHeapSize</code>。</td></tr><tr><td><code>-Xmx</code></td><td>堆的最大值，设置示例：<code>-Xmx1g</code></td><td>根据运行时系统配置选择。</td><td>等同于<code>-XX:MaxHeapSize</code>。设置的值必须是<code>1KB</code>的倍数，且最小为<code>2MB</code>。在最为服务器模式运行时，一般都设置<code>-Xms</code>等于<code>-Xmx</code>。</td></tr><tr><td><code>-Xmn</code></td><td>新生代的最大值，设置示例：<code>-Xmn10m</code></td><td></td><td>建议设置新生代的大小为整个堆大小的：<code>1/4</code>至<code>1/2</code>之间。等同于<code>-XX:MaxNewSize</code>。</td></tr><tr><td><code>-XX:NewSize</code></td><td>新生代的初始值</td><td></td><td></td></tr><tr><td><code>-XX:SurvivorRation</code></td><td>用于设置<a href="#Eden">Eden</a>和其中一个<a href="#Survivor">Survivor</a>的比值</td><td>默认值为<code>8</code>，表示80%的为<code>Eden</code>，两个<code>Survivor</code>各占10%</td><td></td></tr><tr><td><code>-XX:MaxTenuringThreshold</code></td><td>对象在新生代存活周期的阈值</td><td>在并行收集器中默认为<code>15</code>，CMS收集器中默认为<code>6</code></td><td>最大值<code>15</code></td></tr><tr><td><code>-XX:+PrintTenuringDistribution</code></td><td>用于在<code>Minor GC</code>时打印<code>Survivor</code>中各个年龄段对象的占用空间大小</td><td></td><td></td></tr><tr><td><code>-XX:NewRatio</code></td><td>用于设置老生代和新生代的比例</td><td>默认为<code>2</code>，即<code>1/3</code>为新生代，<code>2/3</code>为老生代</td><td>参数命名有些奇怪，实际计算公式: <code>比例值=老生代大小/新生代大小</code></td></tr></tbody></table><h3 id="新生代（Young-Generation）"><a href="#新生代（Young-Generation）" class="headerlink" title="新生代（Young Generation）"></a>新生代（Young Generation）</h3><p>用来存放新的对象实例，垃圾收集器会频繁的在此区域工作，当新生代的<code>Eden区</code>满了之后，会触发<code>Minor GC</code>或<code>Young GC</code>。因此新生代设置过小会导致频繁的<code>Minor GC</code>或<code>Young GC</code>。如果设置过大，则只会在<code>Full GC</code>时才被执行，这会消耗较长的时间。</p><p>为了优化GC的性能，把新生代又细分成了<code>Eden</code>、<code>Survivor1(from)</code>、<code>Survivor2(to)</code>三个区域。</p><h4 id="Eden"><a href="#Eden" class="headerlink" title="Eden"></a>Eden</h4><p>存储新生的对象。一般新创建的对象都会被分配到Eden区中，某些对象会特殊处理。默认Eden占新生代80%的大小。</p><p>Eden区满了之后会触发<code>Minor GC</code>。</p><h4 id="Survivor"><a href="#Survivor" class="headerlink" title="Survivor"></a>Survivor</h4><p>新生代中有两个<code>Survivor</code>区，一个标记为<code>From</code>，一个标记为<code>TO</code>，在<code>GC</code>开始时，被标记为<code>TO</code>的空间一定是空的。</p><p>在<code>Minor GC</code>或<code>Young GC</code>发生时，<code>Eden</code>区中没有被引用（<code>ref</code>）的对象将被清除，需要存活的对象都会被复制到一个标记为<code>TO</code>的<code>Survivor</code>区中，<code>From</code>区中需要继续存活的对象会根据存活周期来决定去向，如果超过存活的周期来会被移动到<code>老生代</code>中，反之也会被复制到标记为<code>TO</code>的<code>Survivor</code>区中，如果<code>TO</code>被填满，则<code>TO</code>中所有的对象都会被移动到<code>老生代</code>中。GC完成之后，每个对象的生命周期年龄都会被加<code>1</code>，<code>Eden</code>和<code>From</code>都被清空，<code>FROM</code>和<code>TO</code>也会互换角色，上一次的<code>TO</code>变成新的<code>FROM</code>，新的<code>TO</code>又将是一个空的区域。</p><h3 id="老生代（Old-Generation）"><a href="#老生代（Old-Generation）" class="headerlink" title="老生代（Old Generation）"></a>老生代（Old Generation）</h3><p>存放生命周期长的对象。也称为“老年代”。对老生代的垃圾回收称为<code>Old GC</code>，当<code>老生代</code>满了之后会触发此<code>GC</code>。</p><blockquote><p>注意</p><p><code>Old GC</code>并不等同于<code>Major GC</code>或<code>Full GC</code>，根据不同的<code>GC</code>的实现，它们所指的范围都不一样。</p></blockquote><h2 id="方法区（MethodArea-Perm-Genration）"><a href="#方法区（MethodArea-Perm-Genration）" class="headerlink" title="方法区（MethodArea/Perm Genration）"></a>方法区（MethodArea/Perm Genration）</h2><p>方法区称作“非堆（Non-Heap）”，用来存放类对象、常量、静态变量、即时编译后的代码数据。与<code>Heap</code>一样都属于线程共享。</p><p>在习惯在HotSpot虚拟机上开发和部署的程序员来说，很多人把它称为“永久代（Permanent Generation）”，平常所说的永久代也是指这个区域。</p><p>尽管这个区域被称为永久代，但有些垃圾收集器也会在此区域执行回收，这个区域的回收主要是常量池的回收、以及类型的卸载。JVM规范没对此区域的限制非常宽松，允许不对此区域实现垃圾收集。</p><p>当方法区无法满足内存分配需求时，将抛出<code>OutOfMemoryError: PermGen</code>异常。</p><p>此空间的调整参数：</p><table><thead><tr><th>参数名</th><th>描述</th><th>默认值</th><th>备注</th></tr></thead><tbody><tr><td><code>-XX:PermSize</code></td><td>永久代内存初始值</td><td>物理内存的1/64，例如：2G内存的机器初始值为32M</td><td></td></tr><tr><td><code>-XX:MaxPermSize</code></td><td>永久代内存最大值</td><td>物理内存的1/4，例如：2G内存的机器初始值为512M</td><td></td></tr></tbody></table><h2 id="元空间（MetaSpace）"><a href="#元空间（MetaSpace）" class="headerlink" title="元空间（MetaSpace）"></a>元空间（MetaSpace）</h2><p>JDK8开始，PermGen被元空间（MetaSpace）替代, PermGen被移除。</p><p>其实移除PermGen的工作从JDK7就开始了，但并没有完全移除，譬如类的静态变量、字面量（interned strings）都转移到了java heap中，符号引用转移到了native heap。</p><p>元空间的本质与PermGen类似，都是对JVM规范中方法区的实现。最大区别是元空间并不在虚拟机中，而是使用本地内存，因此元空间的大小受本地内存限制。</p><p>当方法区无法满足内存分配需求时，将抛出<code>OutOfMemoryError: PermGen</code>异常。</p><p>元空间的大小是JVM根据垃圾收集的结果来自动调整的。也可以通过如下参数来调整：</p><table><thead><tr><th>参数名</th><th>描述</th><th>默认值</th><th>备注</th></tr></thead><tbody><tr><td><code>-XX:MetaspaceSize</code></td><td>元空间初始值</td><td>在默认情况下，这个值大小根据不同的平台在12M到20M浮动</td><td>该值越大触发Metaspace GC的时机就越晚，达到该值就会触发垃圾收集进行类型卸载。同时垃圾收集器会对该值进行调整：如果释放了大量空间，就会适当降低该值。如果释放了很少的空间，在不超过<code>MaxMetaspaceSize</code>的时，会适当提高该值。受本机最大可用内存限制，受32位与64位的JVM、操作系统限制</td></tr><tr><td><code>-XX:MaxMetaspaceSize</code></td><td>元空间最大值</td><td>无限制</td><td>超过最大值时，将抛出<code>OutOfMemoryError: PermGen</code>异常。</td></tr><tr><td><code>-XX:MinMetaspaceFreeRatio</code></td><td>元空间最小空闲占比</td><td>NA</td><td>当进行过元空间GC之后，如果当前元空间的空闲占比小于此值，则增长元空间的大小。此参数可以控制元空间的增长速度，如果该值过小会导致元空间的增长缓慢，可能会影响之后的类加载，如果该值过大会导致元空间增长过快，浪费内存。本机测试效果来看默认值在40左右，也就是40%</td></tr><tr><td><code>-XX:MaxMetaspaceFreeRatio</code></td><td>元空间最大空闲占比</td><td>NA</td><td>当进行过元空间GC之后，如果当前元空间的空闲占比超过此值，则会释放部分元空间。本机测试效果来看默认值在70左右，也就是70%</td></tr><tr><td><code>-XX:MinMetaspaceExpansion</code></td><td>元空间增长时的最小幅度</td><td>NA</td><td>在本机上该参数的默认值为340784B（大约330KB为）</td></tr><tr><td><code>-XX:MaxMetaspaceExpansion</code></td><td>元空间增长时的最大幅度</td><td>NA</td><td>在本机上该参数的默认值为5452592B（大约为5MB）</td></tr></tbody></table><blockquote><p>为什么要将<code>PermGen</code>切换为<code>Metaspace</code>?</p><ol><li>字符串存在永久代中，容易出现性能问题和内存溢出。</li><li>类及方法的信息等比较难确定其大小，因此对于永久代的大小指定比较困难，太小容易出现永久代溢出，太大则容易导致老生代溢出。</li><li>永久代会为 GC 带来不必要的复杂度，并且回收效率偏低。</li><li>Oracle 可能会将HotSpot 与 JRockit 合二为一。</li></ol></blockquote><h3 id="程序计数器（Program-Counter）"><a href="#程序计数器（Program-Counter）" class="headerlink" title="程序计数器（Program Counter）"></a>程序计数器（Program Counter）</h3><p>程序计数器也被称为“PC寄存器”。JVM支持多线程同时运行，每个线程都有自己独立且私有的程序计数器，占用空间极少，在线程创建时创建。</p><p>解释器通过它来获取下一条的字节码执行指令。如果执行的是java的方法，该程序计数器中保存的是当前执行指令的地址，如果是native方法，则该程序计数器中的值为undefined。</p><p>不会有<code>OutOfMemoryError</code>抛出。</p><h2 id="栈（Stack）"><a href="#栈（Stack）" class="headerlink" title="栈（Stack）"></a>栈（Stack）</h2><h3 id="虚拟机栈（VM-Stack）"><a href="#虚拟机栈（VM-Stack）" class="headerlink" title="虚拟机栈（VM Stack）"></a>虚拟机栈（VM Stack）</h3><p>虚拟机栈所使用的空间也是线程私有的，以栈帧为单位进行压栈和出栈。</p><pre class=" language-txt"><code class="language-txt">            当前线程                 线程2           线程n+-----------------------------+   +------+        +------+|           当前栈帧           |   |      |        |      ||     Current Stack Frame     |   |      |        |      |+-----------------------------+   |      |        |      || +-------------------------+ |   |      |        |      || |         局部变量         | |   |      |        |      || |   Local Variable Table  | |   |      |        |      || +-------------------------+ |   |      |        |      || +-------------------------+ |   |      |        |      || |         操作数栈         | |   |      |        |      || |      Operand Stack      | |   |      |        |      || +-------------------------+ |   |      |        |      || +-------------------------+ |   |      |        |      || |         动态连接         | |   |      |        |      || |     Dynamic Linking     | |   |      |        |      || +-------------------------+ |   |      |        |      || +-------------------------+ |   |      |        |      || |         返回地址         | |   |      |        |      || |      Return Address     | |   |      | ...... |      || +-------------------------+ |   |      |        |      || +-------------------------+ |   |      |        |      || |         附加信息         | |   |      |        |      || |      Additional Info    | |   |      |        |      || +-------------------------+ |   |      |        |      ||           ......            |   |      |        |      |+-----------------------------+   |      |        |      ||                             |   |      |        |      ||         Stack Frame n       |   |      |        |      |+-----------------------------+   |      |        |      ||                             |   |      |        |      ||         Stack Frame 2       |   |      |        |      |+-----------------------------+   |      |        |      ||                             |   |      |        |      ||         Stack Frame 1       |   |      |        |      |+-----------------------------+   +------+        +------+</code></pre><ul><li><p>局部变量表</p><p>每一个方法都拥有一块属于自己的内存区域来保存方法内部定义的局部变量，这块区域就是局部变量表，当这个方法运行结束后，这个局部变量的生命周期也就宣告结束。我们平常工作中所指的栈，实际上指的是虚拟机栈中的栈帧中的局部变量表。</p></li><li><p>操作数栈</p><p>每个方法的内部都可以计算数据，而计算数据势必需要拥有一块内存区域，为虚拟机用来进行数值计算。因此在栈帧中，就需要有一块区域专门为当前方法计算数据使用，它就是操作数栈。</p><p>在每进行一次完整的计算之后，栈中的数据都已经出栈，所以操作数栈的空间在一个方法内部是可以反复使用的。所以虚拟机在分配内存大小时，只分配当前方法，单次完整计算所需要的最大内存空间给当前栈帧，以减少内存的消耗。</p><p>同时为了增加运行效率，减少数据的不断复制，在大部分虚拟机的实现中，将当前方法的局部变量表和上层方法的操作数栈的内存形成部分重叠，从而减少参数的不断复制而引起的性能消费。</p></li><li><p>动态连接</p><p>虚拟机在执行方法时有两种形式被用来确定执行指令所对应的方法，第一种是类加载时，可以直接确定要执行的方法，譬如静态方法，私有方法，final方法等。这种形式叫做静态解析。第二种是在真正运行时，根据对象的真实引用来判断当前真正要执行的方法，这种形式称之为动态连接。</p><p>在字节码文件中，都存在一个常量池，在这个常量池中保存有大量的符号引用，这个符号引用是每一个方法的间接引用。在字节码指令的中，使用的是这个符号引用。但是在运行时阶段，肯定需要调用到要执行方法在内存中真实的地址。这就需要将间接引用转化成直接引用。而这里的“动态连接”就是为了保证在运行时阶段，方法可以正确的找到要调用的方法，每个栈帧将自己在运行时常量池中所对应的真实地址记录的位置。</p><p>这里需要注意的是，在栈帧中的动态连接和查找符号引用为真实引用中的动态连接，是两个概念。前者表示的是一个区域，后者表示的是一种查找方式。</p></li><li><p>返回地址</p><p>退出当前方法的方式有两种，第一种是遇到返回指令时，正常的退出当前方法。另一种形式是遇到没有捕获而被抛出的异常。无论何种返回形式，在方法退出后，栈帧的顶端都应是当前退出方法的上层方法。同时上层方法的执行状态也需要根据当前的返回结果重新调整。所以每个栈帧可以利用“返回地址”这块区域帮助上层方法恢复状态。</p></li><li><p>附加信息</p><p>对于虚拟机规范中没有申明的，拥有指定存放位置的信息可以由各个虚拟机自己决定，放置到这个区域中。</p></li></ul><p>有两种可能的异常抛出：<code>StackOverflowError</code>、<code>OutOfMemoryError</code>。<code>StackOverflowError</code>指的是内存中的栈结构在不断的入栈，最终导致栈的深度超过了虚拟机所允许的栈深度时，所抛出的错误</p><p>相关参数设置：</p><table><thead><tr><th>参数名</th><th>描述</th><th>默认值</th><th>备注</th></tr></thead><tbody><tr><td><code>-Xss</code></td><td>线程栈大小，设置示例：-Xss320k</td><td>不同的平台默认值不同。32位环境一般为320kb，64位环境一般为1024kb。</td><td>此参数等同于<code>XX:ThreadStackSize</code></td></tr></tbody></table><h3 id="本地方法栈（Native-Method-Stack）"><a href="#本地方法栈（Native-Method-Stack）" class="headerlink" title="本地方法栈（Native Method Stack）"></a>本地方法栈（Native Method Stack）</h3><p>在虚拟机中，不但运行java方法，还会运行本地方法，也就是常见的<code>native</code>关键字修饰的方法。本地方法运行所使用的空间就是本地方法栈，其也是线程私有的。</p><p>它的作用跟虚拟机栈基本相似，其区别就是一个为java方法服务，一个为Native发光法服务。在虚拟机规范中，对于本地方法栈中的结构、方法的语言、方式，都没有强制规定，各个虚拟机可以自由的实现它。</p><h2 id="直接内存（Direct-Memory）"><a href="#直接内存（Direct-Memory）" class="headerlink" title="直接内存（Direct Memory）"></a>直接内存（Direct Memory）</h2><p>这块内存不属于运行时数据区，所以不受JVM堆大小的限制。</p><p>从Jdk1.4开始，NIO（new I/O）变可以直接使用Native函数直接分配这块内存。使用Java堆中的<code>DirectByteBuffer</code>对象作为这块内存的引用。</p><p>在使用NIO的应用中，配置虚拟机参数需要考虑到这块内存的大小分配，申请不到内存时也会抛出<code>OutOfMemoryError</code>。</p><p>相关参数设置：</p><table><thead><tr><th>参数名</th><th>描述</th><th>默认值</th><th>备注</th></tr></thead><tbody><tr><td><code>-XX:MaxDirectMemorySize</code></td><td>最大直接内存值，设置示例：-XX:MaxDirectMemorySize=10m</td><td>默认情况下，大小设置为0，这意味着JVM将自动分配和扩展。</td><td></td></tr></tbody></table><h2 id="关于GC"><a href="#关于GC" class="headerlink" title="关于GC"></a>关于GC</h2><p>针对<code>HotSpot VM</code>的实现，GC的分类只有两大种：</p><ul><li>Partial GC: 局部GC<ul><li>Young GC: 只收集<code>新生代</code></li><li>Old GC: 只收集<code>老生代</code>，只有<code>CMS</code>的<code>concurrent collection</code>是这个模式</li><li>Mixed GC: 收集所有<code>新生代</code>以及部分<code>老生代</code>。只有<code>G1</code>才有此模式。</li></ul></li><li>Full GC: 全量GC，收集整个堆(包括<code>新生代</code>和<code>老生代</code>)、以及方法区（java8之前的<code>PermGen</code>, java8开始的<code>metaspace</code>）。</li></ul><p>通常所说的<code>Major GC</code>与<code>Full GC</code>是等价的。但由于<code>HotSpot VM</code>发展了这么多年，很对名词解读已经混乱，当有人说<code>Major GC</code>时，一点要问清楚他说的是<code>Full GC</code>还是<code>Old GC</code>。</p><p>对于<code>HotSpot VM</code>的串行收集器（Serial GC）的实现来看，各<code>GC</code>场景的触发条件是：</p><ul><li>Young GC: Eden区没有足够空间进行分配时触发；</li><li>Old GC: 老生代没有足够空间进行分配时触发；只有<code>CMS</code>的<code>concurrent collection</code>是这个模式</li><li>Full GC:<ul><li>方法区<code>PermGen</code>或<code>Metaspace</code>没有足够空间进行分配时触发</li><li>在准备触发<code>Young GC</code>时，如果发现之前<code>Young GC</code>移动到<code>老生代</code>的平均大小大于当前<code>老生代</code>剩余空间时，会取消<code>Young GC</code>转而触发<code>Full GC</code> (除<code>CMS</code>的<code>concurrent collection</code>之外，其它的针对<code>老生代</code>的回收一般都会包含对<code>新生代</code>的处理)</li><li>程序调用<code>System.gc()</code></li><li>堆<code>Heap</code>Dump时带GC，默认也会触发</li></ul></li></ul><p>对于<code>HotSpot VM</code>的并行收集器（Parallel GC）的实现则不一样，以<code>CMS</code>为例，它会定时去检查<code>老生代</code>的是用量，超过一定的比例就会触发。</p><h2 id="相关测试代码"><a href="#相关测试代码" class="headerlink" title="相关测试代码"></a>相关测试代码</h2><h3 id="堆（Heap）内存溢出测试代码"><a href="#堆（Heap）内存溢出测试代码" class="headerlink" title="堆（Heap）内存溢出测试代码"></a>堆（Heap）内存溢出测试代码</h3><pre class=" language-java"><code class="language-java"><span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>ArrayList<span class="token punctuation">;</span><span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>List<span class="token punctuation">;</span><span class="token comment" spellcheck="true">/** * 堆（Heap）内存溢出测试代码 * 启动时添加如下参数可以观察GC日志： *  -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:-UseCompressedClassPointers * 添加如下参数设置JVM堆大小： *  -Xms16m -Xmn8m -Xmx16m */</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">HeapTest</span> <span class="token punctuation">{</span>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>        List<span class="token operator">&lt;</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span> list <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token operator">&lt;</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>        <span class="token keyword">boolean</span> flag <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>        <span class="token keyword">while</span> <span class="token punctuation">(</span>flag<span class="token punctuation">)</span><span class="token punctuation">{</span>            <span class="token keyword">try</span> <span class="token punctuation">{</span>                i<span class="token operator">++</span><span class="token punctuation">;</span>                list<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">byte</span><span class="token punctuation">[</span><span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">//每次增加一个1M大小的数组对象</span>            <span class="token punctuation">}</span><span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> e<span class="token punctuation">)</span><span class="token punctuation">{</span>                e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                flag <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>                System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"count="</span> <span class="token operator">+</span> i<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">//记录运行的次数</span>            <span class="token punctuation">}</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span></code></pre><h3 id="方法区（PermGen-Metaspace）内存溢出测试代码"><a href="#方法区（PermGen-Metaspace）内存溢出测试代码" class="headerlink" title="方法区（PermGen/Metaspace）内存溢出测试代码"></a>方法区（PermGen/Metaspace）内存溢出测试代码</h3><pre class=" language-java"><code class="language-java"><span class="token keyword">import</span> jdk<span class="token punctuation">.</span>internal<span class="token punctuation">.</span>org<span class="token punctuation">.</span>objectweb<span class="token punctuation">.</span>asm<span class="token punctuation">.</span>ClassWriter<span class="token punctuation">;</span><span class="token keyword">import</span> jdk<span class="token punctuation">.</span>internal<span class="token punctuation">.</span>org<span class="token punctuation">.</span>objectweb<span class="token punctuation">.</span>asm<span class="token punctuation">.</span>MethodVisitor<span class="token punctuation">;</span><span class="token keyword">import</span> jdk<span class="token punctuation">.</span>internal<span class="token punctuation">.</span>org<span class="token punctuation">.</span>objectweb<span class="token punctuation">.</span>asm<span class="token punctuation">.</span>Opcodes<span class="token punctuation">;</span><span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>ArrayList<span class="token punctuation">;</span><span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>List<span class="token punctuation">;</span><span class="token comment" spellcheck="true">/** * 方法区（PermGen/Metaspace）内存溢出测试代码 * 启动时添加如下参数可以观察GC日志： *  -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:-UseCompressedClassPointers */</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MetaspaceTest</span> <span class="token keyword">extends</span> <span class="token class-name">ClassLoader</span> <span class="token punctuation">{</span>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token comment" spellcheck="true">// 类持有</span>        List<span class="token operator">&lt;</span>Class<span class="token operator">&lt;</span><span class="token operator">?</span><span class="token operator">>></span> classes <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment" spellcheck="true">// 死循环不断的生成不同的类。</span>        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            ClassWriter cw <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassWriter</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token comment" spellcheck="true">// 定义一个类名称为Class{i}，它的访问域为public，父类为java.lang.Object，不实现任何接口</span>            cw<span class="token punctuation">.</span><span class="token function">visit</span><span class="token punctuation">(</span>Opcodes<span class="token punctuation">.</span>V1_1<span class="token punctuation">,</span> Opcodes<span class="token punctuation">.</span>ACC_PUBLIC<span class="token punctuation">,</span> <span class="token string">"Class"</span> <span class="token operator">+</span> i<span class="token punctuation">,</span> null<span class="token punctuation">,</span>                    <span class="token string">"java/lang/Object"</span><span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token comment" spellcheck="true">// 定义构造函数&lt;init>方法</span>            MethodVisitor mw <span class="token operator">=</span> cw<span class="token punctuation">.</span><span class="token function">visitMethod</span><span class="token punctuation">(</span>Opcodes<span class="token punctuation">.</span>ACC_PUBLIC<span class="token punctuation">,</span> <span class="token string">"&lt;init>"</span><span class="token punctuation">,</span>                    <span class="token string">"()V"</span><span class="token punctuation">,</span> null<span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token comment" spellcheck="true">// 第一个指令为加载this</span>            mw<span class="token punctuation">.</span><span class="token function">visitVarInsn</span><span class="token punctuation">(</span>Opcodes<span class="token punctuation">.</span>ALOAD<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token comment" spellcheck="true">// 第二个指令为调用父类Object的构造函数</span>            mw<span class="token punctuation">.</span><span class="token function">visitMethodInsn</span><span class="token punctuation">(</span>Opcodes<span class="token punctuation">.</span>INVOKESPECIAL<span class="token punctuation">,</span> <span class="token string">"java/lang/Object"</span><span class="token punctuation">,</span>                    <span class="token string">"&lt;init>"</span><span class="token punctuation">,</span> <span class="token string">"()V"</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token comment" spellcheck="true">// 第三条指令为return</span>            mw<span class="token punctuation">.</span><span class="token function">visitInsn</span><span class="token punctuation">(</span>Opcodes<span class="token punctuation">.</span>RETURN<span class="token punctuation">)</span><span class="token punctuation">;</span>            mw<span class="token punctuation">.</span><span class="token function">visitMaxs</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            mw<span class="token punctuation">.</span><span class="token function">visitEnd</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            MetaspaceTest test <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MetaspaceTest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> code <span class="token operator">=</span> cw<span class="token punctuation">.</span><span class="token function">toByteArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token comment" spellcheck="true">// 定义类</span>            Class<span class="token operator">&lt;</span><span class="token operator">?</span><span class="token operator">></span> exampleClass <span class="token operator">=</span> test<span class="token punctuation">.</span><span class="token function">defineClass</span><span class="token punctuation">(</span><span class="token string">"Class"</span> <span class="token operator">+</span> i<span class="token punctuation">,</span> code<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> code<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span>            classes<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>exampleClass<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="虚拟机栈（VM-Stack）溢出测试源码"><a href="#虚拟机栈（VM-Stack）溢出测试源码" class="headerlink" title="虚拟机栈（VM Stack）溢出测试源码"></a>虚拟机栈（VM Stack）溢出测试源码</h3><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/** * 虚拟机栈（VM Stack）溢出测试源码 * 本机测试大概在栈深度达到22217时会出现溢出，每次运行值存在一定偏差 */</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">StackTest</span> <span class="token punctuation">{</span>    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">int</span> index <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>        index<span class="token operator">++</span><span class="token punctuation">;</span>        <span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>        StackTest mock <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StackTest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">try</span> <span class="token punctuation">{</span>            mock<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span><span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> e<span class="token punctuation">)</span><span class="token punctuation">{</span>            System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Stack deep : "</span> <span class="token operator">+</span> index<span class="token punctuation">)</span><span class="token punctuation">;</span>            e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="字符串常量溢出测试源码"><a href="#字符串常量溢出测试源码" class="headerlink" title="字符串常量溢出测试源码"></a>字符串常量溢出测试源码</h3><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/** * 字符串常量溢出测试源码. * jvm 6中运行会抛出`OutOfMemoryError: PermGen space` * jvm 7和jvm 8中运行会抛出`OutOfMemoryError: Java heap space` */</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">StringTest</span> <span class="token punctuation">{</span>    <span class="token keyword">static</span> String static_str <span class="token operator">=</span> <span class="token string">"xxxxxx"</span><span class="token punctuation">;</span>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>        List<span class="token operator">&lt;</span>String<span class="token operator">></span> list <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token operator">&lt;</span>String<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment" spellcheck="true">// 以2的指数级不断的生成新的字符串</span>        <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">{</span>            String str <span class="token operator">=</span> static_str <span class="token operator">+</span> static_str<span class="token punctuation">;</span>            base <span class="token operator">=</span> static_str<span class="token punctuation">;</span>            list<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>str<span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html" target="_blank" rel="noopener">https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html</a></li><li><a href="http://www.importnew.com/23746.html" target="_blank" rel="noopener">http://www.importnew.com/23746.html</a></li><li><a href="http://blog.csdn.net/xlnjulp/article/details/46763045" target="_blank" rel="noopener">http://blog.csdn.net/xlnjulp/article/details/46763045</a></li><li><a href="https://dzone.com/articles/java-8-permgen-metaspace" target="_blank" rel="noopener">https://dzone.com/articles/java-8-permgen-metaspace</a></li><li><a href="http://caoyaojun1988-163-com.iteye.com/blog/1969853" target="_blank" rel="noopener">http://caoyaojun1988-163-com.iteye.com/blog/1969853</a></li><li><a href="http://www.cnblogs.com/smyhvae/p/4748392.html" target="_blank" rel="noopener">http://www.cnblogs.com/smyhvae/p/4748392.html</a></li></ul><style></style>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;Java运行时内存结构&quot;&gt;&lt;a href=&quot;#Java运行时内存结构&quot; class=&quot;headerlink&quot; title=&quot;Java运行时内存结构&quot;&gt;&lt;/a&gt;Java运行时内存结构&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;txt&quot;&gt;+-------------------------------------------------+
|          Runtime Memory Structure Chart         |
|  +--------------+ | +----------+  +----------+  |
|  |              | | |          |  |  Native  |  |
|  |  MethodArea  | | | VM Stack |  |  Method  |  |
|  |   (PermGen)  | | |          |  |   Stack  |  |
|  +--------------+ | +----------+  +----------+  |
|  +--------------+ | +------------------------+  |
|  |              | | |                        |  |
|  |     Heap     | | |    Program Counter     |  |
|  |              | | |        Register        |  |
|  +--------------+   +------------------------+  |
+-------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
      <category term="jvm" scheme="http://zyouwei.com/tags/jvm/"/>
    
  </entry>
  
  <entry>
    <title>Hystrix 配置属性参考</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/Hystrix-configuration.html"/>
    <id>http://zyouwei.com/技术笔记/Java/Hystrix-configuration.html</id>
    <published>2018-01-04T02:39:38.000Z</published>
    <updated>2018-01-05T04:49:05.107Z</updated>
    
    <content type="html"><![CDATA[<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>Hystrix使用<a href="https://github.com/Netflix/archaius" target="_blank" rel="noopener">Archaius</a>作为配置属性的默认实现。</p><p>下面的文档描述了默认使用的<a href="http://netflix.github.io/Hystrix/javadoc/index.html?com/netflix/hystrix/strategy/properties/HystrixPropertiesStrategy.html" target="_blank" rel="noopener">HystrixPropertiesStrategy</a>实现，你也可以使用<a href="https://github.com/Netflix/Hystrix/wiki/Plugins" target="_blank" rel="noopener">插件</a>的方式来覆盖它。</p><p>每个属性有四个优先级：</p><ol><li><p><strong>代码的全局默认值</strong></p><p>如果没有设置以下3个，则这是默认值。 全局默认值在下表中显示为 <strong>“默认值”</strong>。</p></li><li><p><strong>动态全局默认属性</strong></p><p>你可以使用属性更改全局默认值。 全局默认属性名称在下表中显示为 <strong>“默认属性”</strong>。</p><a id="more"></a></li><li><p><strong>用代码定义默认实例属性</strong></p><p>你可以定义特定于实例的默认值。例：</p><pre class=" language-java"><code class="language-java"> HystrixCommandProperties<span class="token punctuation">.</span><span class="token function">Setter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">withExecutionTimeoutInMilliseconds</span><span class="token punctuation">(</span><span class="token keyword">int</span> value<span class="token punctuation">)</span></code></pre><p>你需要类似于以下的方式将这种命令插入到<code>HystrixCommand</code>构造函数中：</p><pre class=" language-java"><code class="language-java"> <span class="token keyword">public</span> <span class="token function">HystrixCommandInstance</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>     <span class="token keyword">super</span><span class="token punctuation">(</span>Setter<span class="token punctuation">.</span><span class="token function">withGroupKey</span><span class="token punctuation">(</span>HystrixCommandGroupKey<span class="token punctuation">.</span>Factory<span class="token punctuation">.</span><span class="token function">asKey</span><span class="token punctuation">(</span><span class="token string">"ExampleGroup"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>                 <span class="token punctuation">.</span><span class="token function">andCommandPropertiesDefaults</span><span class="token punctuation">(</span>HystrixCommandProperties<span class="token punctuation">.</span><span class="token function">Setter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">withExecutionTimeoutInMilliseconds</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>     <span class="token keyword">this</span><span class="token punctuation">.</span>id <span class="token operator">=</span> id<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre><p>一般的设置初始值可以采用便利的构造函数方式。这是一个例子：</p><pre class=" language-java"><code class="language-java"> <span class="token keyword">public</span> <span class="token function">HystrixCommandInstance</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>     <span class="token keyword">super</span><span class="token punctuation">(</span>HystrixCommandGroupKey<span class="token punctuation">.</span>Factory<span class="token punctuation">.</span><span class="token function">asKey</span><span class="token punctuation">(</span><span class="token string">"ExampleGroup"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span>     <span class="token keyword">this</span><span class="token punctuation">.</span>id <span class="token operator">=</span> id<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></li><li><p><strong>动态设置实例属性</strong></p><p>你可以动态设置实例特定的值，从而覆盖前面三个默认级别。</p><p>动态实例属性名称在下表中显示为 <strong>“实例属性”</strong>。</p><p>例：</p><table><thead><tr><th>实例属性</th><th><code>hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutInMilliseconds</code></th></tr></thead></table><p>将属性的<a href="http://netflix.github.io/Hystrix/javadoc/index.html?com/netflix/hystrix/HystrixCommandKey.html" target="_blank" rel="noopener">HystrixCommandKey</a>部分替换为您所定位的<code>HystrixCommand</code>的<a href="http://netflix.github.io/Hystrix/javadoc/com/netflix/hystrix/HystrixCommandKey.html#name(" target="_blank" rel="noopener">HystrixCommandKey.name()</a>)值。</p><p>例如，如果被命名为“<code>SubscriberGetAccount</code>”，则属性名称将是：</p><blockquote><p><code>hystrix.command.SubscriberGetAccount.execution.isolation.thread.timeoutInMilliseconds</code></p></blockquote></li></ol><h2 id="命令属性（Command-Properties）"><a href="#命令属性（Command-Properties）" class="headerlink" title="命令属性（Command Properties）"></a>命令属性（Command Properties）</h2><p>以下<a href="http://netflix.github.io/Hystrix/javadoc/index.html?com/netflix/hystrix/HystrixCommandProperties.html" target="_blank" rel="noopener">属性</a>将控制<code>HystrixCommand</code>的行为：</p><h3 id="执行（Execution）"><a href="#执行（Execution）" class="headerlink" title="执行（Execution）"></a>执行（Execution）</h3><p>以下属性控制<a href="http://netflix.github.io/Hystrix/javadoc/com/netflix/hystrix/HystrixCommand.html#run(" target="_blank" rel="noopener">HystrixCommand.run()</a>)执行。</p><h4 id="execution-isolation-strategy"><a href="#execution-isolation-strategy" class="headerlink" title="execution.isolation.strategy"></a>execution.isolation.strategy</h4><p>这个属性指示<code>HystrixCommand.run()</code>执行的隔离策略，有以下两种选择之一：</p><ul><li><code>THREAD</code> —— 它在单独的线程上执行，并发请求受线程池中线程数量的限制</li><li><code>SEMAPHORE</code> —— 它在调用线程上执行，并发请求受信号计数的限制</li></ul><p><strong>线程或信号量</strong></p><p>缺省值和建议的设置是运行<code>HystrixCommand</code>时使用线程隔离（<code>THREAD</code>），和运行<code>HystrixObservableCommand</code>时使用信号隔离（<code>SEMAPHORE</code>）。</p><p>在线程中执行命令能对网络超时提供另一层保护。</p><p>通常情况下，对于<code>HystrixCommand</code>来说，唯一使用信号量隔离的场景是，当调用量非常大（每秒数百次）时单独线程的开销太高；这通常只适用于非网络调用。</p><blockquote><p>Netflix的API在40+线程池中运行100+命令，并且只有少数命令不在线程中运行 —— 那些从内存中的缓存提取元数据或或是门面到线程隔离命令（更多信息请参见<a href="https://github.com/Netflix/Hystrix/wiki/How-To-Use#common-patterns" target="_blank" rel="noopener">Primary + Secondary with Fallback” pattern</a>）。</p></blockquote><p><img src="https://raw.githubusercontent.com/wiki/Netflix/Hystrix/images/isolation-options-1280.png" alt="isolation-options"></p><p>更多信息请参见<a href="https://github.com/Netflix/Hystrix/wiki/How-it-Works#isolation" target="_blank" rel="noopener">隔离是如何工作的</a>。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>THREAD</code>（参见：<code>ExecutionIsolationStrategy.THREAD</code>）</th></tr></thead><tbody><tr><td><strong>取值范围</strong></td><td style="text-align:left"><code>THREAD</code>, <code>SEMAPHORE</code></td></tr><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.execution.isolation.strategy</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.execution.isolation.strategy</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left">// to use thread isolation<br><code>HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD)</code><br>// to use semaphore isolation<br><code>HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)</code></td></tr></tbody></table><h4 id="execution-isolation-thread-timeoutInMilliseconds"><a href="#execution-isolation-thread-timeoutInMilliseconds" class="headerlink" title="execution.isolation.thread.timeoutInMilliseconds"></a>execution.isolation.thread.timeoutInMilliseconds</h4><p>该属性设置以毫秒为单位，在该时间之后，调用者将观察到超时并离开命令执行。 Hystrix将<code>HystrixCommand</code>标记为<code>TIMEOUT</code>，并执行回退逻辑。请注意，如果需要可以为每个命令配置关闭超时的设置（请参阅<code>command.timeout.enabled</code>）。</p><p><strong>注意：</strong> 超时将在<code>HystrixCommand.queue()</code>上触发，即使调用者从未在生成的<code>Future</code>上调用<code>get()</code>。在Hystrix <code>1.4.0</code>之前，只有调用<code>get()</code>才能使超时机制在这种情况下生效。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>1000</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutInMilliseconds</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(int value)</code></td></tr></tbody></table><h4 id="execution-timeout-enabled"><a href="#execution-timeout-enabled" class="headerlink" title="execution.timeout.enabled"></a>execution.timeout.enabled</h4><p>该属性指示<code>HystrixCommand.run()</code>执行是否应该有一个超时。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>true</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.execution.timeout.enabled</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.execution.timeout.enabled</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withExecutionTimeoutEnabled(boolean value)</code></td></tr></tbody></table><h4 id="execution-isolation-thread-interruptOnTimeout"><a href="#execution-isolation-thread-interruptOnTimeout" class="headerlink" title="execution.isolation.thread.interruptOnTimeout"></a>execution.isolation.thread.interruptOnTimeout</h4><p>这个属性指示<code>HystrixCommand.run()</code>执行是否应该在发生超时时被中断。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>true</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.execution.isolation.thread.interruptOnTimeout</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.execution.isolation.thread.interruptOnTimeout</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withExecutionIsolationThreadInterruptOnTimeout(boolean value)</code></td></tr></tbody></table><h4 id="execution-isolation-thread-interruptOnCancel"><a href="#execution-isolation-thread-interruptOnCancel" class="headerlink" title="execution.isolation.thread.interruptOnCancel"></a>execution.isolation.thread.interruptOnCancel</h4><p>这个属性指示<code>HystrixCommand.run()</code>执行是否应该在发生取消时被中断。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>false</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.execution.isolation.thread.interruptOnCancel</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.execution.isolation.thread.interruptOnCancel</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withExecutionIsolationThreadInterruptOnCancel(boolean value)</code></td></tr></tbody></table><h4 id="execution-isolation-semaphore-maxConcurrentRequests"><a href="#execution-isolation-semaphore-maxConcurrentRequests" class="headerlink" title="execution.isolation.semaphore.maxConcurrentRequests"></a>execution.isolation.semaphore.maxConcurrentRequests</h4><p>当您使用<code>ExecutionIsolationStrategy.SEMAPHORE</code>时，此属性设置允许<code>HystrixCommand.run()</code>方法的最大请求数。</p><p>如果达到这个最大并发限制，则后续请求将被拒绝。</p><p>当你选择一个信号量时，你使用的逻辑基本上和你选择线程池中添加多少个线程相同，但是信号量的开销要小得多，通常执行速度要快得多（亚毫秒） ，否则你会使用线程。</p><blockquote><p>例如，5000rps的单个实例在内存中查找指标聚集仅需要2个信号量就能工作。</p></blockquote><p>隔离原理仍然是相同的，所以信号量应该仍然是整个容器（如：Tomcat）线程池的一小部分，而不是全部或大部分，否则它不提供保护。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>10</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.execution.isolation.semaphore.maxConcurrentRequests</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withExecutionIsolationSemaphoreMaxConcurrentRequests(int value)</code></td></tr></tbody></table><h3 id="回退（Fallback）"><a href="#回退（Fallback）" class="headerlink" title="回退（Fallback）"></a>回退（Fallback）</h3><p>以下属性控制<code>HystrixCommand.getFallback()</code>如何执行。这些属性适用于<code>ExecutionIsolationStrategy.THREAD</code>和<code>ExecutionIsolationStrategy.SEMAPHORE</code>。</p><h4 id="fallback-isolation-semaphore-maxConcurrentRequests"><a href="#fallback-isolation-semaphore-maxConcurrentRequests" class="headerlink" title="fallback.isolation.semaphore.maxConcurrentRequests"></a>fallback.isolation.semaphore.maxConcurrentRequests</h4><p>该属性设置<code>HystrixCommand.getFallback()</code>方法允许从调用线程中创建的最大请求数。</p><p>如果达到最大并发限制，则随后的请求将被拒绝并抛出异常，因为没有回退被提取到。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>10</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.fallback.isolation.semaphore.maxConcurrentRequests</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withFallbackIsolationSemaphoreMaxConcurrentRequests(int value)</code></td></tr></tbody></table><h4 id="fallback-enabled"><a href="#fallback-enabled" class="headerlink" title="fallback.enabled"></a>fallback.enabled</h4><p>Since: 1.2</p><p>该属性设置是否在发生故障或拒绝时尝试调用<code>HystrixCommand.getFallback()</code>。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>true</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.fallback.enabled</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.fallback.enabled</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withFallbackEnabled(boolean value)</code></td></tr></tbody></table><h3 id="断路器（Circuit-Breaker）"><a href="#断路器（Circuit-Breaker）" class="headerlink" title="断路器（Circuit Breaker）"></a>断路器（Circuit Breaker）</h3><p>断路器属性控制<code>HystrixCircuitBreaker</code>的行为。</p><h4 id="circuitBreaker-enabled"><a href="#circuitBreaker-enabled" class="headerlink" title="circuitBreaker.enabled"></a>circuitBreaker.enabled</h4><p>该属性设置是否将使用断路器来跟踪健康状况并且如果断路器跳闸则将其短路。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>true</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.circuitBreaker.enabled</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.circuitBreaker.enabled</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withCircuitBreakerEnabled(boolean value)</code></td></tr></tbody></table><h4 id="circuitBreaker-requestVolumeThreshold"><a href="#circuitBreaker-requestVolumeThreshold" class="headerlink" title="circuitBreaker.requestVolumeThreshold"></a>circuitBreaker.requestVolumeThreshold</h4><p>该属性设置滚动窗口中将使电路跳闸的最小请求数量。</p><p>例如，如果值是20，那么如果在滚动窗口中接收到19个请求（例如10秒的窗口），则即使所有19个请求都失败，电路也不会跳闸。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>20</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.circuitBreaker.requestVolumeThreshold</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.circuitBreaker.requestVolumeThreshold</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withCircuitBreakerRequestVolumeThreshold(int value)</code></td></tr></tbody></table><h4 id="circuitBreaker-sleepWindowInMilliseconds"><a href="#circuitBreaker-sleepWindowInMilliseconds" class="headerlink" title="circuitBreaker.sleepWindowInMilliseconds"></a>circuitBreaker.sleepWindowInMilliseconds</h4><p>该属性设置跳闸后的时间量，拒绝请求，然后再次尝试确定电路是否应再次闭合。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>5000</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.circuitBreaker.sleepWindowInMilliseconds</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withCircuitBreakerSleepWindowInMilliseconds(int value)</code></td></tr></tbody></table><h4 id="circuitBreaker-errorThresholdPercentage"><a href="#circuitBreaker-errorThresholdPercentage" class="headerlink" title="circuitBreaker.errorThresholdPercentage"></a>circuitBreaker.errorThresholdPercentage</h4><p>该属性设置错误百分比，在该值以上，电路应断开并开始将请求短路到回退逻辑。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>50</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.circuitBreaker.errorThresholdPercentage</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.circuitBreaker.errorThresholdPercentage</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withCircuitBreakerErrorThresholdPercentage(int value)</code></td></tr></tbody></table><h4 id="circuitBreaker-forceOpen"><a href="#circuitBreaker-forceOpen" class="headerlink" title="circuitBreaker.forceOpen"></a>circuitBreaker.forceOpen</h4><p>如果该属性为真，则强制断路器进入打开（跳闸）状态，将拒绝所有的请求。</p><p>此属性优先于<code>circuitBreaker.forceClosed</code>。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>false</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.circuitBreaker.forceOpen</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.circuitBreaker.forceOpen</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withCircuitBreakerForceOpen(boolean value)</code></td></tr></tbody></table><h4 id="circuitBreaker-forceClosed"><a href="#circuitBreaker-forceClosed" class="headerlink" title="circuitBreaker.forceClosed"></a>circuitBreaker.forceClosed</h4><p>如果该属性为真，则强制断路器进入关闭状态，在该状态下将允许请求，而不管错误百分比如何。</p><p><code>circuitBreaker.forceOpen</code>属性优先，所以如果它被设置为<code>true</code>，这个属性什么都不做。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>false</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.circuitBreaker.forceClosed</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.circuitBreaker.forceClosed</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withCircuitBreakerForceClosed(boolean value)</code></td></tr></tbody></table><h3 id="度量（Mitrics）"><a href="#度量（Mitrics）" class="headerlink" title="度量（Mitrics）"></a>度量（Mitrics）</h3><p>以下属性与从<code>HystrixCommand</code>和<code>HystrixObservableCommand</code>执行捕获指标有关。</p><h4 id="metrics-rollingStats-timeInMilliseconds"><a href="#metrics-rollingStats-timeInMilliseconds" class="headerlink" title="metrics.rollingStats.timeInMilliseconds"></a>metrics.rollingStats.timeInMilliseconds</h4><p>该属性设置统计滚动窗口的持续时间，以毫秒为单位。这是Hystrix保持断路器使用和发布指标的时间。</p><p>从1.4.12开始，此属性仅影响初始度量标准的创建，启动后对此属性所做的调整将不会生效。这样可以避免指标数据丢失，也可以优化指标收集。</p><p>窗口根据这些增量被分成桶和“卷”。</p><p>例如，如果将此属性设置为10秒（<code>10000</code>），具有十个1秒桶的，则下图显示了如何将新桶和旧桶关闭：</p><p><img src="https://github.com/Netflix/Hystrix/wiki/images/rolling-stats-640.png" alt="rolling-stats"></p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>10000</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.metrics.rollingStats.timeInMilliseconds</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.metrics.rollingStats.timeInMilliseconds</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withMetricsRollingStatisticalWindowInMilliseconds(int value)</code></td></tr></tbody></table><h4 id="metrics-rollingStats-numBuckets"><a href="#metrics-rollingStats-numBuckets" class="headerlink" title="metrics.rollingStats.numBuckets"></a>metrics.rollingStats.numBuckets</h4><p>该属性设置滚动统计窗口分成的桶的数量。</p><p><strong>注意：</strong> 必须要确保如下条件： “<code>metrics.rollingStats.timeInMilliseconds ％ metrics.rollingStats.numBuckets == 0</code>” ， 否则会引发异常。</p><p>换句话说，10000/10是可以的，10000/20也行，但是10000/7不行。</p><p>从1.4.12开始，此属性仅影响初始度量标准的创建，启动后对此属性所做的调整将不会生效。这样可以避免指标数据丢失，也可以优化指标收集。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>10</code></th></tr></thead><tbody><tr><td><strong>取值范围</strong></td><td style="text-align:left">可以被<code>metric.rollingStats.timeInMilliseconds</code>整除的任何值。结果应该是数百或数千毫秒的桶。大容量的性能还没有测试过小于100ms的桶。</td></tr><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.metrics.rollingStats.numBuckets</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.metrics.rollingStats.numBuckets</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withMetricsRollingStatisticalWindowBuckets(int value)</code></td></tr></tbody></table><h4 id="metrics-rollingPercentile-enabled"><a href="#metrics-rollingPercentile-enabled" class="headerlink" title="metrics.rollingPercentile.enabled"></a>metrics.rollingPercentile.enabled</h4><p>这个属性表示执行延迟是否应该跟踪和计算为百分比。如果他们被禁用，则所有汇总统计（平均值，百分位数）返回为-1。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>true</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.metrics.rollingPercentile.enabled</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.metrics.rollingPercentile.enabled</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withMetricsRollingPercentileEnabled(boolean value)</code></td></tr></tbody></table><h4 id="metrics-rollingPercentile-timeInMilliseconds"><a href="#metrics-rollingPercentile-timeInMilliseconds" class="headerlink" title="metrics.rollingPercentile.timeInMilliseconds"></a>metrics.rollingPercentile.timeInMilliseconds</h4><p>此属性设置滚动窗口的持续时间，在该窗口中保留执行时间以允许百分数计算（以毫秒为单位）。</p><p>窗口根据这些增量被分成桶和“卷”。</p><p>从1.4.12开始，此属性仅影响初始度量标准的创建，启动后对此属性所做的调整将不会生效。这样可以避免指标数据丢失，也可以优化指标收集。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>6000</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.metrics.rollingPercentile.timeInMilliseconds</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withMetricsRollingPercentileWindowInMilliseconds(int value)</code></td></tr></tbody></table><h4 id="metrics-rollingPercentile-numBuckets"><a href="#metrics-rollingPercentile-numBuckets" class="headerlink" title="metrics.rollingPercentile.numBuckets"></a>metrics.rollingPercentile.numBuckets</h4><p>这个属性设置了<code>rollingPercentile</code>窗口将被分成的桶的数量。</p><p><strong>注意：</strong> 必须要确保如下条件：“<code>metrics.rollingPercentile.timeInMilliseconds ％ metrics.rollingPercentile.numBuckets == 0</code>” ，否则会引发异常。</p><p>换句话说，60000/6是可以的，60000/60也是可以的，但10000/7不行。</p><p>从1.4.12开始，此属性仅影响初始度量标准的创建，启动后对此属性所做的调整将不会生效。这样可以避免指标数据丢失，也可以优化指标收集。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>6</code></th></tr></thead><tbody><tr><td><strong>取值范围</strong></td><td style="text-align:left">可以被<code>metric.rollingPercentile.timeInMilliseconds</code>整除的任何值。结果应该是数千毫秒的桶。大容量的性能还没有测试过小于1000ms的桶。</td></tr><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.metrics.rollingPercentile.numBuckets</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.metrics.rollingPercentile.numBuckets</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withMetricsRollingPercentileWindowBuckets(int value)</code></td></tr></tbody></table><h4 id="metrics-rollingPercentile-bucketSize"><a href="#metrics-rollingPercentile-bucketSize" class="headerlink" title="metrics.rollingPercentile.bucketSize"></a>metrics.rollingPercentile.bucketSize</h4><p>该属性设置每个桶的最大执行次数。如果更多的执行在此期间发生，他们将环绕并开始在桶的开头重写。</p><p>例如，如果桶大小设置为100，并桶的窗口为10秒，但是在此期间发生500次执行，则只有最后100次执行将保留在该10秒的桶中。</p><p>如果增加这个大小，这也增加了存储值所需的内存量，并增加了对列表进行排序以进行百分比计算所需的时间。</p><p>从1.4.12开始，此属性仅影响初始度量标准的创建，启动后对此属性所做的调整将不会生效。这样可以避免指标数据丢失，也可以优化指标收集。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>100</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.metrics.rollingPercentile.bucketSize</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.metrics.rollingPercentile.bucketSize</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withMetricsRollingPercentileBucketSize(int value)</code></td></tr></tbody></table><h4 id="metrics-healthSnapshot-intervalInMilliseconds"><a href="#metrics-healthSnapshot-intervalInMilliseconds" class="headerlink" title="metrics.healthSnapshot.intervalInMilliseconds"></a>metrics.healthSnapshot.intervalInMilliseconds</h4><p>此属性设置允许执行运行成功和错误百分比并影响断路器状态的健康快照之间等待的时间（以毫秒为单位）。</p><p>在大容量的电路上，连续计算误差百分比可能会成为CPU密集型的，因此这个属性允许你控制计算的频率。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>500</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.metrics.healthSnapshot.intervalInMilliseconds</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withMetricsHealthSnapshotIntervalInMilliseconds(int value)</code></td></tr></tbody></table><h3 id="请求上下文（Request-Context）"><a href="#请求上下文（Request-Context）" class="headerlink" title="请求上下文（Request Context）"></a>请求上下文（Request Context）</h3><p>这些属性涉及<code>HystrixCommand</code>使用<a href="http://netflix.github.com/Hystrix/javadoc/index.html?com/netflix/hystrix/strategy/concurrency/HystrixRequestContext.html" target="_blank" rel="noopener">HystrixRequestContext</a>功能。</p><h4 id="requestCache-enabled"><a href="#requestCache-enabled" class="headerlink" title="requestCache.enabled"></a>requestCache.enabled</h4><p>此属性指示<a href="http://netflix.github.io/Hystrix/javadoc/com/netflix/hystrix/HystrixCommand.html#getCacheKey(" target="_blank" rel="noopener">HystrixCommand.getCacheKey()</a>)是否应与<a href="http://netflix.github.io/Hystrix/javadoc/index.html?com/netflix/hystrix/HystrixRequestCache.html" target="_blank" rel="noopener">HystrixRequestCache</a>一起使用，以通过请求范围缓存提供重复数据删除功能。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>true</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.requestCache.enabled</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.requestCache.enabled</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withRequestCacheEnabled(boolean value)</code></td></tr></tbody></table><h4 id="requestLog-enabled"><a href="#requestLog-enabled" class="headerlink" title="requestLog.enabled"></a>requestLog.enabled</h4><p>此属性指示是否应将<code>HystrixCommand</code>执行和事件记录到<a href="http://netflix.github.io/Hystrix/javadoc/index.html?com/netflix/hystrix/HystrixRequestLog.html" target="_blank" rel="noopener">HystrixRequestLog</a></p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>true</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.command.default.requestLog.enabled</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.command.HystrixCommandKey.requestLog.enabled</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCommandProperties.Setter().withRequestLogEnabled(boolean value)</code></td></tr></tbody></table><h2 id="破裂器属性（Collapser-Properties）"><a href="#破裂器属性（Collapser-Properties）" class="headerlink" title="破裂器属性（Collapser Properties）"></a>破裂器属性（Collapser Properties）</h2><p>以下属性控制<a href="http://netflix.github.io/Hystrix/javadoc/index.html?com/netflix/hystrix/HystrixCollapser.html" target="_blank" rel="noopener">HystrixCollapser</a>行为。</p><h3 id="maxRequestsInBatch"><a href="#maxRequestsInBatch" class="headerlink" title="maxRequestsInBatch"></a>maxRequestsInBatch</h3><p>此属性设置批处理允许的最大请求数量，然后触发批处理执行。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>Integer.MAX_VALUE</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.collapser.default.maxRequestsInBatch</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.collapser.HystrixCollapserKey.maxRequestsInBatch</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCollapserProperties.Setter().withMaxRequestsInBatch(int value)</code></td></tr></tbody></table><h3 id="timerDelayInMilliseconds"><a href="#timerDelayInMilliseconds" class="headerlink" title="timerDelayInMilliseconds"></a>timerDelayInMilliseconds</h3><p>此属性设置触发执行之后创建批处理的毫秒数。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>10</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.collapser.default.timerDelayInMilliseconds</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.collapser.HystrixCollapserKey.timerDelayInMilliseconds</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(int value)</code></td></tr></tbody></table><h3 id="requestCache-enabled-1"><a href="#requestCache-enabled-1" class="headerlink" title="requestCache.enabled"></a>requestCache.enabled</h3><p>此属性指示是否为<a href="http://netflix.github.io/Hystrix/javadoc/com/netflix/hystrix/HystrixCollapser.html#execute--" target="_blank" rel="noopener">HystrixCollapser.execute()</a>和<a href="http://netflix.github.io/Hystrix/javadoc/com/netflix/hystrix/HystrixCollapser.html#queue--" target="_blank" rel="noopener">HystrixCollapser.queue()</a>调用启用了请求缓存。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>true</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.collapser.default.requestCache.enabled</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.collapser.HystrixCommandKey.requestCache.enabled</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixCollapserProperties.Setter().withRequestCacheEnabled(boolean value)</code></td></tr></tbody></table><h2 id="线程池属性（Thread-Pool-Properties）"><a href="#线程池属性（Thread-Pool-Properties）" class="headerlink" title="线程池属性（Thread Pool Properties）"></a>线程池属性（Thread Pool Properties）</h2><p>以下属性控制Hystrix命令执行的线程池的行为。请注意，这些名称与<a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html" target="_blank" rel="noopener">ThreadPoolExecutor Javadoc</a>中的名称相匹配</p><p>大多数情况下，10个线程的默认值都可以（通常可以做得更小）。</p><p>要确定是否需要更大，计算大小的基本公式是：</p><p><em>在健康时每秒的请求高峰数 × 99%的延迟秒数+一些喘息的空间时。</em></p><p>看下面的例子，看看这个公式是如何实施的。 总体原则是尽可能保持池的小，因为它是减轻负载并防止资源在延迟发生时被阻塞的主要工具。</p><blockquote><p>Netflix API有30+的线程池被设置为10，两个在20，一个在25。</p></blockquote><p><img src="https://github.com/Netflix/Hystrix/wiki/images/thread-configuration-1280.png" alt="thread-configuration"></p><p>上图显示了一个配置示例，其中依赖关系没有理由达到第99.5百分位，因此它在网络超时层将其缩短，并立即重试，并期望大部分时间会得到中位延迟能够在300ms线程超时内完成这一切。</p><p>如果依赖有合法的理由有时达到99.5％（比如缓存未命中），那么网络超时将被设置得比它高，比如325ms，重试0或1次，线程超时设置更高（350ms + ）。</p><p>线程池的大小为10，以处理第99个百分点请求的突发，但是当一切正常时，此线程池通常在任何给定时间通常只有1或2个线程处于活动状态，以服务大多数40ms的中间调用。</p><p>当你正确地配置它时，<code>HystrixCommand</code>层的超时应该是很少的，但是如果网络等待时间以外的事情影响了时间，或者在最坏的情况下连接+读+重试+连接+读超过配置的整体超时。</p><p>每个方向的配置和折衷的攻击性对于每个依赖性是不同的。</p><p>当性能特征发生变化或发现问题时，您可以根据需要实时更改配置，而且如果出现问题或配置错误，则无需关闭整个应用程序。</p><h3 id="coreSize"><a href="#coreSize" class="headerlink" title="coreSize"></a>coreSize</h3><p>此属性设置核心线程数。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>10</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.default.coreSize</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.HystrixThreadPoolKey.coreSize</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixThreadPoolProperties.Setter().withCoreSize(int value)</code></td></tr></tbody></table><h3 id="maximumSize"><a href="#maximumSize" class="headerlink" title="maximumSize"></a>maximumSize</h3><p>在<code>1.5.9</code>中添加。该属性设置最大的线程池大小。这是开始无需拒绝<code>HystrixCommand</code>即可支持的最大并发数量。请注意，如果您还设置了<code>allowMaximumSizeToDivergeFromCoreSize</code>，则此设置才会生效。在<code>1.5.9</code>之前，核心和最大大小总是相等的。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>10</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.default.maximumSize</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.HystrixThreadPoolKey.maximumSize</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixThreadPoolProperties.Setter().withMaximumSize(int value)</code></td></tr></tbody></table><h3 id="maxQueueSize"><a href="#maxQueueSize" class="headerlink" title="maxQueueSize"></a>maxQueueSize</h3><p>该属性设置<code>BlockingQueue</code>实现的最大队列大小。</p><p>如果将其设置为<code>-1</code>，则将使用<a href="http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/SynchronousQueue.html" target="_blank" rel="noopener">SynchronousQueue</a>，其它正值将使用<a href="http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/LinkedBlockingQueue.html" target="_blank" rel="noopener">LinkedBlockingQueue</a>。</p><p><strong>注意：</strong> 这个属性只适用于初始化时间，因为如果不重新初始化线程执行器，不支持的队列被调整或改变。</p><p>如果您需要越过此限制并允许在队列中进行动态更改，请参阅<code>queueSizeRejectionThreshold</code>属性。</p><p>要在<code>SynchronousQueue</code>和<code>LinkedBlockingQueue</code>之间切换，需要重新启动。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>-1</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.default.maxQueueSize</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.HystrixThreadPoolKey.maxQueueSize</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixThreadPoolProperties.Setter().withMaxQueueSize(int value)</code></td></tr></tbody></table><h3 id="queueSizeRejectionThreshold"><a href="#queueSizeRejectionThreshold" class="headerlink" title="queueSizeRejectionThreshold"></a>queueSizeRejectionThreshold</h3><p>此属性设置队列大小拒绝阈值 —— 即使<code>maxQueueSize</code>尚未达到，拒绝将发生的人为最大队列大小。此属性的存在是因为<a href="http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/BlockingQueue.html" target="_blank" rel="noopener">BlockingQueue</a>的<code>maxQueueSize</code>不能动态更改，我们希望允许您动态更改影响拒绝的队列大小。</p><p>当排队一个线程执行时，<code>HystrixCommand</code>使用它。</p><p><strong>注意：</strong> 如果<code>maxQueueSize == -1</code>，则此属性不适用。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>5</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.default.queueSizeRejectionThreshold</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.HystrixThreadPoolKey.queueSizeRejectionThreshold</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixThreadPoolProperties.Setter().withQueueSizeRejectionThreshold(int value)</code></td></tr></tbody></table><h3 id="keepAliveTimeMinutes"><a href="#keepAliveTimeMinutes" class="headerlink" title="keepAliveTimeMinutes"></a>keepAliveTimeMinutes</h3><p>该属性设置保持活动时间，以分钟为单位。 在<code>1.5.9</code>之前，所有线程池都是固定大小的，如<code>coreSize == maximumSize</code>。在<code>1.5.9</code>之后，将<code>allowMaximumSizeToDivergeFromCoreSize</code>设置为<code>true</code>允许这两个值发散，以便线程池可以获取/释放线程。如果<code>coreSize &lt;maximumSize</code>，那么这个属性控制一个线程在被释放之前将不被使用的时间。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>1</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.default.keepAliveTimeMinutes</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.HystrixThreadPoolKey.keepAliveTimeMinutes</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixThreadPoolProperties.Setter().withKeepAliveTimeMinutes(int value)</code></td></tr></tbody></table><h3 id="allowMaximumSizeToDivergeFromCoreSize"><a href="#allowMaximumSizeToDivergeFromCoreSize" class="headerlink" title="allowMaximumSizeToDivergeFromCoreSize"></a>allowMaximumSizeToDivergeFromCoreSize</h3><p>在<code>1.5.9</code>中添加。此属性允许<code>maximumSize</code>的配置生效。该值可以等于或高于<code>coreSize</code>。设置<code>coreSize &lt;maximumSize</code>会创建一个线程池，它可以支持<code>maximumSize</code>并发性，但是会在相对不活动的时间内将线程返回给系统。 （以<code>keepAliveTimeInMinutes</code>为准）</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>false</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.HystrixThreadPoolKey.allowMaximumSizeToDivergeFromCoreSize</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixThreadPoolProperties.Setter().withAllowMaximumSizeToDivergeFromCoreSize(boolean value)</code></td></tr></tbody></table><h3 id="metrics-rollingStats-timeInMilliseconds-1"><a href="#metrics-rollingStats-timeInMilliseconds-1" class="headerlink" title="metrics.rollingStats.timeInMilliseconds"></a>metrics.rollingStats.timeInMilliseconds</h3><p>该属性设置统计滚动窗口的持续时间，以毫秒为单位。这是为线程池保留多长时间的指标。</p><p>窗口根据这些增量被分成桶和“卷”。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>10000</code></th></tr></thead><tbody><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.default..metrics.rollingStats.timeInMilliseconds</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.HystrixThreadPoolKey.metrics.rollingStats.timeInMilliseconds</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixThreadPoolProperties.Setter().withMetricsRollingStatisticalWindowInMilliseconds(int value)</code></td></tr></tbody></table><h3 id="metrics-rollingStats-numBuckets-1"><a href="#metrics-rollingStats-numBuckets-1" class="headerlink" title="metrics.rollingStats.numBuckets"></a>metrics.rollingStats.numBuckets</h3><p>该属性设置滚动统计窗口分成的桶的数量。</p><p><strong>注意：</strong> 必须要确保如下条件：“<code>metrics.rollingStats.timeInMilliseconds ％ metrics.rollingStats.numBuckets == 0</code>” ，否则会引发异常。</p><p>换句话说，10000/10是可以的，10000/20也可以，但是10000/7不行。</p><table><thead><tr><th>默认值</th><th style="text-align:left"><code>10</code></th></tr></thead><tbody><tr><td><strong>取值范围</strong></td><td style="text-align:left">能被<code>metrics.rollingStats.timeInMilliseconds</code>整除的任何值，结果应该是数百或数千毫秒的桶。大容量的性能还没有测试过小于100ms的桶。</td></tr><tr><td><strong>默认属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.default.metrics.rollingStats.numBuckets</code></td></tr><tr><td><strong>实例属性</strong></td><td style="text-align:left"><code>hystrix.threadpool.HystrixThreadPoolKey.metrics.rollingStats.numBuckets</code></td></tr><tr><td><strong>如何设置<br>默认实例</strong></td><td style="text-align:left"><code>HystrixThreadPoolProperties.Setter().withMetricsRollingStatisticalWindowBuckets(int value)</code></td></tr></tbody></table><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://github.com/Netflix/Hystrix/wiki/Configuration" target="_blank" rel="noopener">https://github.com/Netflix/Hystrix/wiki/Configuration</a></p><style>table th:first-of-type{width:80px}</style>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;介绍&quot;&gt;&lt;a href=&quot;#介绍&quot; class=&quot;headerlink&quot; title=&quot;介绍&quot;&gt;&lt;/a&gt;介绍&lt;/h2&gt;&lt;p&gt;Hystrix使用&lt;a href=&quot;https://github.com/Netflix/archaius&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Archaius&lt;/a&gt;作为配置属性的默认实现。&lt;/p&gt;&lt;p&gt;下面的文档描述了默认使用的&lt;a href=&quot;http://netflix.github.io/Hystrix/javadoc/index.html?com/netflix/hystrix/strategy/properties/HystrixPropertiesStrategy.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HystrixPropertiesStrategy&lt;/a&gt;实现，你也可以使用&lt;a href=&quot;https://github.com/Netflix/Hystrix/wiki/Plugins&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;插件&lt;/a&gt;的方式来覆盖它。&lt;/p&gt;&lt;p&gt;每个属性有四个优先级：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;代码的全局默认值&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;如果没有设置以下3个，则这是默认值。 全局默认值在下表中显示为 &lt;strong&gt;“默认值”&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;动态全局默认属性&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;你可以使用属性更改全局默认值。 全局默认属性名称在下表中显示为 &lt;strong&gt;“默认属性”&lt;/strong&gt;。&lt;/p&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/Java/"/>
    
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
      <category term="微服务" scheme="http://zyouwei.com/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="hystrix" scheme="http://zyouwei.com/tags/hystrix/"/>
    
  </entry>
  
  <entry>
    <title>MacOS下基于Python2.7安装OpenCV3</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%B7%A5%E5%85%B7%E7%B1%BB/MacOs-install-opencv.html"/>
    <id>http://zyouwei.com/技术笔记/工具类/MacOs-install-opencv.html</id>
    <published>2017-12-13T01:28:37.000Z</published>
    <updated>2017-12-13T04:04:23.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="步骤概览"><a href="#步骤概览" class="headerlink" title="步骤概览"></a>步骤概览</h2><ol><li>安装<code>Xcode</code>以及<code>Apple Command Line Tools</code></li><li>安装<code>Homebrew</code></li><li>创建<code>Python</code>虚拟环境</li><li>安装<code>NumPy</code></li><li>安装<code>OpenCV</code></li></ol><a id="more"></a><h2 id="安装Xcode以及Apple-Command-Line-Tools"><a href="#安装Xcode以及Apple-Command-Line-Tools" class="headerlink" title="安装Xcode以及Apple Command Line Tools"></a>安装<code>Xcode</code>以及<code>Apple Command Line Tools</code></h2><ol><li>从<a href="https://itunes.apple.com/app/xcode/id497799835" target="_blank" rel="noopener">App Store</a>下载并安装Xcode。</li><li>接受Apple开发者协议。<pre class=" language-bash"><code class="language-bash"> <span class="token function">sudo</span> xcodebuild -license</code></pre></li><li>安装<code>Apple Command Line Tools</code><pre class=" language-bash"><code class="language-bash"> <span class="token function">sudo</span> xcode-select --install</code></pre>输入上面的命令后，会弹出一个窗口，确认你想要安装命令行工具，点击<code>install</code>或<code>安装</code>按钮进行安装。</li></ol><h2 id="安装Homebrew"><a href="#安装Homebrew" class="headerlink" title="安装Homebrew"></a>安装<code>Homebrew</code></h2><p><a href="http://brew.sh/" target="_blank" rel="noopener">Homebrew</a>是MacOS的包管理器，类似于<code>Ubuntu</code>的<code>apt-get</code>。</p><ol><li><p>安装命令：</p><pre class=" language-bash"><code class="language-bash"> /usr/bin/ruby -e <span class="token string">"<span class="token variable"><span class="token variable">$(</span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install<span class="token variable">)</span></span>"</span></code></pre></li><li><p>一旦安装了Homebrew，您应该对其进行更新，以确保下载最新的软件包定义：</p><pre class=" language-bash"><code class="language-bash"> brew update</code></pre></li><li><p>修改环境变量文件，如果你用的是<code>bash</code>请球盖<code>vi ~/.bash_profile</code>，如果你用的是<code>zsh</code>请修改<code>vi ~/.zshrc</code>，添加如下内容到对应的文件：</p><pre class=" language-bash"><code class="language-bash"> <span class="token comment" spellcheck="true"># Homebrew</span> <span class="token function">export</span> PATH<span class="token operator">=</span>/usr/local/bin:<span class="token variable">$PATH</span></code></pre></li><li><p>使用<code>source</code>命名来让配置生效：<code>source ~/.bash_profile</code> 或 <code>source ~/.zshrc</code>。</p></li></ol><h2 id="创建Python虚拟环境"><a href="#创建Python虚拟环境" class="headerlink" title="创建Python虚拟环境"></a>创建<code>Python</code>虚拟环境</h2><ol><li><p><code>MacOS</code>默认已安装<code>Python</code>，不同的<code>MacOS</code>版本可能对应的<code>Python</code>版本不同。可以使用<code>python --version</code>来获取当前系统中的<code>Python</code>版本。如果当前版本不是<code>2.7.x</code>，可以使用<code>Homebrew</code>来安装：</p><pre class=" language-bash"><code class="language-bash"> brew <span class="token function">install</span> python</code></pre><p>安装完成之后，我们需要把操作系统的<code>python</code>链接到<code>Homebrew</code>的安装目录：</p><pre class=" language-bash"><code class="language-bash"> brew linkapps python</code></pre><p>检查安装是否正确：</p><pre class=" language-bash"><code class="language-bash"> <span class="token function">which</span> python</code></pre><p>如果输出的路径是<code>/usr/local/bin/python</code>则说明安装<code>Python</code>成功。</p></li><li><p>安装虚拟环境</p><p>由于在OS X El Capitan中，在内核下引入了Rootless机制，即使root用户也无法对<code>/System</code>、<code>/bin</code>、<code>/sbin</code>、<code>/usr(except /usr/local)</code>目录有写和执行权限，只有Apple以及Apple授权签名的软件（包括命令行工具）可以修改此目录。</p><p>因此在使用操作系统自带的<code>Python</code>时，使用<code>pip</code>安装某些包时会存在失败的情况。所以我们在这里使用<code>python</code>虚拟环境来规避这个问题。</p><pre class=" language-bash"><code class="language-bash"> pip <span class="token function">install</span> virtualenv virtualenvwrapper</code></pre><p>安装完成之后，我们需要再次修改环境变量文件，如果你用的是<code>bash</code>请球盖<code>vi ~/.bash_profile</code>，如果你用的是<code>zsh</code>请修改<code>vi ~/.zshrc</code>，添加如下内容到对应的文件：</p><pre class=" language-bash"><code class="language-bash"> <span class="token comment" spellcheck="true"># Virtualenv/VirtualenvWrapper</span> <span class="token function">source</span> /usr/local/bin/virtualenvwrapper.sh</code></pre><p>使用<code>source</code>命名来让配置生效：<code>source ~/.bash_profile</code> 或 <code>source ~/.zshrc</code>。</p></li><li><p>创建虚拟环境</p><pre class=" language-bash"><code class="language-bash"> mkvirtualenv opencv</code></pre><p>上面命名创建了一个名叫<code>opencv</code>的虚拟环境，你可以使用<code>workon opencv</code>进入虚拟环境，也可以使用<code>deactivate</code>指令从虚拟环境中退出。</p></li></ol><h2 id="安装NumPy"><a href="#安装NumPy" class="headerlink" title="安装NumPy"></a>安装<code>NumPy</code></h2><ol><li>进入虚拟环境: <code>workon opencv</code></li><li><code>pip install numpy</code></li></ol><h2 id="安装OpenCV"><a href="#安装OpenCV" class="headerlink" title="安装OpenCV"></a>安装<code>OpenCV</code></h2><ol><li><p>环境准备：</p><pre class=" language-bash"><code class="language-bash"> brew <span class="token function">install</span> cmake pkg-config brew <span class="token function">install</span> jpeg libpng libtiff openexr brew <span class="token function">install</span> eigen tbb</code></pre></li><li><p>从github下载OpenCV3</p><pre class=" language-bash"><code class="language-bash"> <span class="token function">cd</span> ~ <span class="token function">git</span> clone https://github.com/opencv/opencv <span class="token function">git</span> clone https://github.com/opencv/opencv_contrib</code></pre><p>下载完成之后，进入源码目录切换到指定的版本，譬如安装OpenCV的3.3.1版本：</p><pre class=" language-bash"><code class="language-bash"> <span class="token function">cd</span> ~/opencv <span class="token function">git</span> checkout -b 3.3.1 3.3.1 <span class="token function">cd</span> ~/opencv_contrib <span class="token function">git</span> checkout -b 3.3.1 3.3.1</code></pre></li><li><p>配置<code>CMake</code></p><pre class=" language-bash"><code class="language-bash"> <span class="token function">cd</span> ~/opencv <span class="token function">mkdir</span> build <span class="token function">cd</span> build</code></pre><p>接着我们就可以执行<code>cmake</code>命令了，但在执行命令之前有两个参数需要调整：</p><pre class=" language-bash"><code class="language-bash"> cmake -D CMAKE_BUILD_TYPE<span class="token operator">=</span>RELEASE \ -D CMAKE_INSTALL_PREFIX<span class="token operator">=</span>/usr/local \ -D OPENCV_EXTRA_MODULES_PATH<span class="token operator">=</span>~/opencv_contrib/modules \ -D PYTHON2_LIBRARY<span class="token operator">=</span>YYY \ -D PYTHON2_INCLUDE_DIR<span class="token operator">=</span>ZZZ \ -D PYTHON2_EXECUTABLE<span class="token operator">=</span>/usr/local/bin/python \ -D BUILD_opencv_python2<span class="token operator">=</span>ON \ -D BUILD_opencv_python3<span class="token operator">=</span>OFF \ -D INSTALL_PYTHON_EXAMPLES<span class="token operator">=</span>ON \ -D INSTALL_C_EXAMPLES<span class="token operator">=</span>OFF \ -D BUILD_EXAMPLES<span class="token operator">=</span>ON <span class="token punctuation">..</span></code></pre><p><strong>cmake重要参数解释：</strong></p><ol><li><code>BUILD_opencv_python2=ON</code>：这表明我们要基于Python2.7来安装OpenCV 3；</li><li><code>BUILD_opencv_python3=OFF</code>：这表明我们关闭Python3；</li><li><code>PYTHON2_LIBRARY=YYY</code>：这是你自己填写的第一个值。您将需要用您的<code>libpython2.7.dylib</code>文件的路径替换<code>YYY</code>（我将帮助您在下一节中找到它）；</li><li><p><code>PYTHON2_INCLUDE_DIR=ZZZ</code>：这是您需要填写的第二个值。您需要将<code>ZZZ</code>替换为<code>Python.h</code>头文件所在目录的路径（同样，我将帮助您确定此路径）</p><p><strong>获取<code>PYTHON2_LIBRARY</code>的路径</strong></p><pre class=" language-bash"><code class="language-bash"><span class="token function">ls</span> /usr/local/Cellar/python/2.7.*/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config/libpython2.7.dylib</code></pre><p>将上面的输出拷贝出来替换掉<code>PYTHON2_LIBRARY=YYY</code>中的<code>YYY</code>。</p><p><strong>获取<code>PYTHON2_INCLUDE_DIR</code>的路径</strong></p><pre class=" language-bash"><code class="language-bash"><span class="token function">ls</span> -d /usr/local/Cellar/python/2.7.*/Frameworks/Python.framework/Versions/2.7/include/python2.7/</code></pre><p>将上面的输出拷贝出来替换掉<code>PYTHON2_INCLUDE_DIR=ZZZ</code>中的<code>ZZZ</code>。</p><p>替换完成之后即可执行。</p></li></ol></li><li><p>编译与安装</p><pre class=" language-bash"><code class="language-bash"> <span class="token function">make</span> -j4</code></pre><p><code>-j</code>开关控制并发编译OpenCV的处理器数量，因为我在四核系统上，所以我用<code>-j4</code>。编译可能需要几十分钟左右。</p><p>如果编译没有出错就可以进行安装了</p><pre class=" language-bash"><code class="language-bash"> <span class="token function">sudo</span> <span class="token function">make</span> <span class="token function">install</span></code></pre><p>安装成功后，可以在<code>/usr/local/lib/python2.7/site-packges/</code>下找到<code>cv2.so</code>文件。</p></li><li><p>在虚拟环境中添加<code>cv2.so</code>的软连接</p><pre class=" language-bash"><code class="language-bash">  <span class="token function">cd</span> ~/.virtualenvs/cv/lib/python2.7/site-packages/  <span class="token function">ln</span> -s /usr/local/lib/python2.7/site-packages/cv2.so cv2.so cv2.so</code></pre></li><li><p>测试与验证OpenCV</p><p>打开一个新的终端：</p><pre class=" language-bash"><code class="language-bash"> $ workon opencv $ python Python 2.7.12 <span class="token punctuation">(</span>default, Oct 11 2016, 05:20:59<span class="token punctuation">)</span>  <span class="token punctuation">[</span>GCC 4.2.1 Compatible Apple LLVM 8.0.0 <span class="token punctuation">(</span>clang-800.0.38<span class="token punctuation">)</span><span class="token punctuation">]</span> on darwin Type <span class="token string">"help"</span>, <span class="token string">"copyright"</span>, <span class="token string">"credits"</span> or <span class="token string">"license"</span> <span class="token keyword">for</span> <span class="token function">more</span> information. <span class="token operator">>></span><span class="token operator">></span> <span class="token function">import</span> cv2 <span class="token operator">>></span><span class="token operator">></span> cv2.__version__ '3.3.1 <span class="token operator">>></span><span class="token operator">></span></code></pre></li></ol><h2 id="如何完整的卸载OpenCV"><a href="#如何完整的卸载OpenCV" class="headerlink" title="如何完整的卸载OpenCV?"></a>如何完整的卸载OpenCV?</h2><ul><li>可以通过进入之前的build目录，执行<code>make uninstall</code>来完整卸载OpenCV.</li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://www.pyimagesearch.com/2016/11/28/macos-install-opencv-3-and-python-2-7/" target="_blank" rel="noopener">MacOS基于Python2.7安装OpenCV3</a></li><li><a href="https://www.pyimagesearch.com/2016/12/05/macos-install-opencv-3-and-python-3-5/" target="_blank" rel="noopener">MacOS基于Python3.5安装OpenCV3</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;步骤概览&quot;&gt;&lt;a href=&quot;#步骤概览&quot; class=&quot;headerlink&quot; title=&quot;步骤概览&quot;&gt;&lt;/a&gt;步骤概览&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;安装&lt;code&gt;Xcode&lt;/code&gt;以及&lt;code&gt;Apple Command Line Tools&lt;/code&gt;&lt;/li&gt;&lt;li&gt;安装&lt;code&gt;Homebrew&lt;/code&gt;&lt;/li&gt;&lt;li&gt;创建&lt;code&gt;Python&lt;/code&gt;虚拟环境&lt;/li&gt;&lt;li&gt;安装&lt;code&gt;NumPy&lt;/code&gt;&lt;/li&gt;&lt;li&gt;安装&lt;code&gt;OpenCV&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="工具类" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%B7%A5%E5%85%B7%E7%B1%BB/"/>
    
    
      <category term="python" scheme="http://zyouwei.com/tags/python/"/>
    
      <category term="opencv" scheme="http://zyouwei.com/tags/opencv/"/>
    
  </entry>
  
  <entry>
    <title>Geetest拖拽验证码破解思路（java）</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%B7%A5%E5%85%B7%E7%B1%BB/Geetest-Crack.html"/>
    <id>http://zyouwei.com/技术笔记/工具类/Geetest-Crack.html</id>
    <published>2017-11-24T08:29:56.000Z</published>
    <updated>2018-01-05T02:17:48.175Z</updated>
    
    <content type="html"><![CDATA[<p>项目中有个需求希望能获取公司的工商注册信息，刚开始是想与第三方数据公司合作，因种 种原因合作没有达成。于是想做个爬虫直接从工商局的《企业信用信息公示系统》中获取。 要想从《企业信用信息公示系统》中爬取数据，首先必须解决掉<code>Geetest</code>验证码的问题。 经过一系列的摸索，发现要想破解<code>geetest</code>的验证码，主要需要解决如下几个问题：</p><ol><li>背景图的还原</li><li>找到背景图中缺口的位置</li><li>将滑块拖拽到缺口</li></ol><a id="more"></a><h2 id="背景图的还原"><a href="#背景图的还原" class="headerlink" title="背景图的还原"></a>背景图的还原</h2><p><code>Geetest</code>背景图分为两张，一张是完整背景图，一张是带缺口的背景图。每张图片被分成 52 份， 上下两部分各 26 份，然后乱序排列。在网页上显示时，是通过<code>css</code>来将乱序的 碎片重新组列成完整的图片。</p><p>乱序图片：<img src="/images/posts/2017-11/9f9cff207.jpg" alt="origin_img"></p><p>正常图片：<img src="/images/posts/2017-11/re_9f9cff207.jpg" alt="show_img"></p><p>因此，我们必须将原始的背景乱序图片切根据页面上的<code>css</code>样式将图片切割成 52 份，并 按照先后顺序拼接还原成正确的图片。需要使用到<code>java.awt.image.BufferedImage</code>类：</p><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/**  * 将Geetest打乱的图片还原，Geetest的原始背景图是分成52份碎片乱序组合的。目前是上下各26份，每份碎片图片宽10px， 高58px。  *  * @param image     原始背景图  * @param locations 展示位置列表，数据结构：[{x=-25, y=-58}, ...]  * @return 顺序排列好的图片  */</span><span class="token keyword">public</span> <span class="token keyword">static</span> BufferedImage <span class="token function">recover</span><span class="token punctuation">(</span>BufferedImage image<span class="token punctuation">,</span> List<span class="token operator">&lt;</span>Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">>></span> locations<span class="token punctuation">)</span> <span class="token keyword">throws</span> IOException <span class="token punctuation">{</span>    <span class="token keyword">long</span> begin <span class="token operator">=</span> System<span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">int</span> per_image_with <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span>  <span class="token comment" spellcheck="true">// 每张碎片图片的宽度</span>    <span class="token keyword">int</span> per_image_height <span class="token operator">=</span> <span class="token number">58</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 每张碎片图片的高度</span>    List<span class="token operator">&lt;</span>BufferedImage<span class="token operator">></span> upperList <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    List<span class="token operator">&lt;</span>BufferedImage<span class="token operator">></span> downList <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment" spellcheck="true">// 将原始图片裁剪成碎片</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span>Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">></span> location <span class="token operator">:</span> locations<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">int</span> x <span class="token operator">=</span> location<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"x"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">int</span> y <span class="token operator">=</span> location<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"y"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span>y <span class="token operator">==</span> <span class="token operator">-</span><span class="token number">58</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            upperList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>image<span class="token punctuation">.</span><span class="token function">getSubimage</span><span class="token punctuation">(</span><span class="token function">abs</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">58</span><span class="token punctuation">,</span> per_image_with<span class="token punctuation">,</span> per_image_height<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>y <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            downList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>image<span class="token punctuation">.</span><span class="token function">getSubimage</span><span class="token punctuation">(</span><span class="token function">abs</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> per_image_with<span class="token punctuation">,</span> per_image_height<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span>    BufferedImage newImage <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedImage</span><span class="token punctuation">(</span>upperList<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> per_image_with<span class="token punctuation">,</span> image<span class="token punctuation">.</span><span class="token function">getHeight</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> image<span class="token punctuation">.</span><span class="token function">getType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment" spellcheck="true">// 重绘图片的上半部分</span>    <span class="token keyword">int</span> x_offset <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span>BufferedImage bufferedImage <span class="token operator">:</span> upperList<span class="token punctuation">)</span> <span class="token punctuation">{</span>        Graphics graphics <span class="token operator">=</span> newImage<span class="token punctuation">.</span><span class="token function">getGraphics</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        graphics<span class="token punctuation">.</span><span class="token function">drawImage</span><span class="token punctuation">(</span>bufferedImage<span class="token punctuation">,</span> x_offset<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>        x_offset <span class="token operator">+=</span> bufferedImage<span class="token punctuation">.</span><span class="token function">getWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment" spellcheck="true">// 重绘图片的下半部分</span>    x_offset <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span>BufferedImage bufferedImage <span class="token operator">:</span> downList<span class="token punctuation">)</span> <span class="token punctuation">{</span>        Graphics graphics <span class="token operator">=</span> newImage<span class="token punctuation">.</span><span class="token function">getGraphics</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        graphics<span class="token punctuation">.</span><span class="token function">drawImage</span><span class="token punctuation">(</span>bufferedImage<span class="token punctuation">,</span> x_offset<span class="token punctuation">,</span> <span class="token number">58</span><span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>        x_offset <span class="token operator">+=</span> bufferedImage<span class="token punctuation">.</span><span class="token function">getWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"还原图片耗时：{}ms"</span><span class="token punctuation">,</span> System<span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> begin<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> newImage<span class="token punctuation">;</span><span class="token punctuation">}</span></code></pre><h2 id="找到背景图中缺口的位置"><a href="#找到背景图中缺口的位置" class="headerlink" title="找到背景图中缺口的位置"></a>找到背景图中缺口的位置</h2><p>正常图片： <img src="/images/posts/2017-11/re_9f9cff207.jpg" alt="正常图片"></p><p>缺口图片： <img src="/images/posts/2017-11/qk_9f9cff207.jpg" alt=" 缺口图片"></p><p><code>Geetest</code>验证时，只需要将拼图块水平移动到正确的位置即可。因此通过上面两张图片比 对，发现我们只要找到缺口的<code>x</code>坐标即可。</p><p>这里我借用的参考内容的方法：两张原始图的大小都是相同的 260*116，那就通过两个 for 循环依次对比每个像素点的 RGB 值，如果相差超过 50 则就认为找到了缺口的位置。</p><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/**  * 计算验证图的缺口位置（x轴） 两张原始图的大小都是相同的260*116，那就通过两个for循环依次对比每个像素点的RGB值， 如果RGB三元素中有一个相差超过50则就认为找到了缺口的位置  *  * @param image1 图像1  * @param image2 图像2  * @return 缺口的x坐标  */</span><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">int</span> <span class="token function">getDiffX</span><span class="token punctuation">(</span>BufferedImage image1<span class="token punctuation">,</span> BufferedImage image2<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token keyword">long</span> begin <span class="token operator">=</span> System<span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> x <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> x <span class="token operator">&lt;</span> image1<span class="token punctuation">.</span><span class="token function">getWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> x<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> y <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> y <span class="token operator">&lt;</span> image1<span class="token punctuation">.</span><span class="token function">getHeight</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> y<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">isSimilar</span><span class="token punctuation">(</span>image1<span class="token punctuation">,</span> image2<span class="token punctuation">,</span> x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>                <span class="token keyword">return</span> x<span class="token punctuation">;</span>            <span class="token punctuation">}</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span>    log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"图片对比耗时：{}ms"</span><span class="token punctuation">,</span> System<span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> begin<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">/** * 判断image1, image2的[x, y]这一像素是否相似，如果该像素的RGB值相差都在50以内，则认为相似。 * * @param image1   图像1 * @param image2   图像2 * @param x_offset x坐标 * @param y_offset y坐标 * @return 是否相似 */</span><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">boolean</span> <span class="token function">isSimilar</span><span class="token punctuation">(</span>BufferedImage image1<span class="token punctuation">,</span> BufferedImage image2<span class="token punctuation">,</span> <span class="token keyword">int</span> x_offset<span class="token punctuation">,</span> <span class="token keyword">int</span> y_offset<span class="token punctuation">)</span> <span class="token punctuation">{</span>    Color pixel1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Color</span><span class="token punctuation">(</span>image1<span class="token punctuation">.</span><span class="token function">getRGB</span><span class="token punctuation">(</span>x_offset<span class="token punctuation">,</span> y_offset<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    Color pixel2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Color</span><span class="token punctuation">(</span>image2<span class="token punctuation">.</span><span class="token function">getRGB</span><span class="token punctuation">(</span>x_offset<span class="token punctuation">,</span> y_offset<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token function">abs</span><span class="token punctuation">(</span>pixel1<span class="token punctuation">.</span><span class="token function">getBlue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> pixel2<span class="token punctuation">.</span><span class="token function">getBlue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">50</span> <span class="token operator">&amp;&amp;</span> <span class="token function">abs</span><span class="token punctuation">(</span>pixel1<span class="token punctuation">.</span><span class="token function">getGreen</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> pixel2<span class="token punctuation">.</span><span class="token function">getGreen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">50</span> <span class="token operator">&amp;&amp;</span> <span class="token function">abs</span><span class="token punctuation">(</span>pixel1<span class="token punctuation">.</span><span class="token function">getRed</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> pixel2<span class="token punctuation">.</span><span class="token function">getRed</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">50</span><span class="token punctuation">;</span><span class="token punctuation">}</span></code></pre><h2 id="将滑块拖拽到缺口"><a href="#将滑块拖拽到缺口" class="headerlink" title="将滑块拖拽到缺口"></a>将滑块拖拽到缺口</h2><p><code>Geetest</code>在拖拽拼图块时，对拼图块的移动轨迹做了一些 “ 人工智能 ” 的算法识别，使 用程序直接将拼图块拖入缺口时，发现有很大的概率会被识别为机器操作，页面显示<code>怪物 吃掉了饼图</code>。因此我们需要采用一些算法来模拟人拖拽的行为。</p><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/**  * 根据缺口位置x_offset，仿照手动拖动滑块时的移动轨迹。  * 手动拖动滑块有几个特点：  * 开始时拖动速度快，最后接近目标时会慢下来；  * 总时间大概1~3秒；  * 有可能会拖超过后再拖回头；  *  * @return 返回一个轨迹数组，数组中的每个轨迹都是[x,y,z]三元素：x代表横向位移，y代表竖向位移，z代表时间间隔，单位毫秒  */</span><span class="token keyword">private</span> <span class="token keyword">static</span> List<span class="token operator">&lt;</span>Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">>></span> <span class="token function">getTrack</span><span class="token punctuation">(</span><span class="token keyword">int</span> x_offset<span class="token punctuation">)</span> <span class="token punctuation">{</span>    List<span class="token operator">&lt;</span>Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">>></span> tracks<span class="token punctuation">;</span>    <span class="token keyword">long</span> begin <span class="token operator">=</span> System<span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment" spellcheck="true">// 实际上滑块的起始位置并不是在图像的最左边，而是大概有6个像素的距离，所以滑动距离要减掉这个长度</span>    x_offset <span class="token operator">=</span> x_offset <span class="token operator">-</span> <span class="token number">6</span><span class="token punctuation">;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">2</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        tracks <span class="token operator">=</span> <span class="token function">strategics_1</span><span class="token punctuation">(</span>x_offset<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>        tracks <span class="token operator">=</span> <span class="token function">strategics_2</span><span class="token punctuation">(</span>x_offset<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"生成轨迹耗时: {}ms"</span><span class="token punctuation">,</span> System<span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> begin<span class="token punctuation">)</span><span class="token punctuation">;</span>    log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"计算出移动轨迹: {}"</span><span class="token punctuation">,</span> tracks<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> tracks<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">/**  * 轨迹策略1  */</span><span class="token keyword">private</span> <span class="token keyword">static</span> List<span class="token operator">&lt;</span>Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">>></span> <span class="token function">strategics_1</span><span class="token punctuation">(</span><span class="token keyword">int</span> x_offset<span class="token punctuation">)</span> <span class="token punctuation">{</span>    List<span class="token operator">&lt;</span>Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">>></span> tracks <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">float</span> totalTime <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>    <span class="token keyword">int</span> x <span class="token operator">=</span> <span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment" spellcheck="true">// 随机按1~3的步长生成各个点</span>    <span class="token keyword">while</span> <span class="token punctuation">(</span>x_offset <span class="token operator">-</span> x <span class="token operator">>=</span> <span class="token number">5</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">></span> point <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"x"</span><span class="token punctuation">,</span> x<span class="token punctuation">)</span><span class="token punctuation">;</span>        point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"y"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        tracks<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>point<span class="token punctuation">)</span><span class="token punctuation">;</span>        x_offset <span class="token operator">=</span> x_offset <span class="token operator">-</span> x<span class="token punctuation">;</span>        x <span class="token operator">=</span> <span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        totalTime <span class="token operator">+=</span> point<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">floatValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment" spellcheck="true">// 后面几个点放慢时间</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> x_offset<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">></span> point <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"x"</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"y"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">,</span> <span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        tracks<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>point<span class="token punctuation">)</span><span class="token punctuation">;</span>        totalTime <span class="token operator">+=</span> point<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">floatValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"预计拖拽耗时: {}ms"</span><span class="token punctuation">,</span> totalTime<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> tracks<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">/**  * 轨迹策略2  */</span><span class="token keyword">private</span> <span class="token keyword">static</span> List<span class="token operator">&lt;</span>Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">>></span> <span class="token function">strategics_2</span><span class="token punctuation">(</span><span class="token keyword">int</span> x_offset<span class="token punctuation">)</span> <span class="token punctuation">{</span>    List<span class="token operator">&lt;</span>Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">>></span> tracks <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">float</span> totalTime <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>    <span class="token keyword">int</span> dragX <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 已拖拽的横向偏移量</span>    <span class="token keyword">int</span> nearRange <span class="token operator">=</span> <span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 靠近缺口的范围</span>    <span class="token keyword">while</span> <span class="token punctuation">(</span>dragX <span class="token operator">&lt;</span> x_offset <span class="token operator">-</span> nearRange<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 生成快速拖拽点，拖拽距离非常靠近切口</span>        <span class="token keyword">int</span> stepLength <span class="token operator">=</span> <span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 随机按1~5的步长生成各个点</span>        Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">></span> point <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"x"</span><span class="token punctuation">,</span> stepLength<span class="token punctuation">)</span><span class="token punctuation">;</span>        point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"y"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">,</span> <span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        tracks<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>point<span class="token punctuation">)</span><span class="token punctuation">;</span>        totalTime <span class="token operator">+=</span> point<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">floatValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        dragX <span class="token operator">+=</span> stepLength<span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment" spellcheck="true">// 随机一定的比例将滑块拖拽过头</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">99</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">2</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">int</span> stepLength <span class="token operator">=</span> <span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">15</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 随机按1~5的步长生成各个点</span>        Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">></span> attachPoint <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        attachPoint<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"x"</span><span class="token punctuation">,</span> stepLength<span class="token punctuation">)</span><span class="token punctuation">;</span>        attachPoint<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"y"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        attachPoint<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">,</span> <span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        tracks<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>attachPoint<span class="token punctuation">)</span><span class="token punctuation">;</span>        dragX <span class="token operator">+=</span> stepLength<span class="token punctuation">;</span>        totalTime <span class="token operator">+=</span> attachPoint<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">floatValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment" spellcheck="true">// 精确点</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> Math<span class="token punctuation">.</span><span class="token function">abs</span><span class="token punctuation">(</span>dragX <span class="token operator">-</span> x_offset<span class="token punctuation">)</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span>dragX <span class="token operator">></span> x_offset<span class="token punctuation">)</span> <span class="token punctuation">{</span>            Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">></span> point <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"x"</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"y"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">,</span> <span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            tracks<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>point<span class="token punctuation">)</span><span class="token punctuation">;</span>            totalTime <span class="token operator">+=</span> point<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">floatValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>            Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">></span> point <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator">&lt;</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"x"</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"y"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            point<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">,</span> <span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            tracks<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>point<span class="token punctuation">)</span><span class="token punctuation">;</span>            totalTime <span class="token operator">+=</span> point<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">floatValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span>    log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"预计拖拽耗时: {}ms"</span><span class="token punctuation">,</span> totalTime<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> tracks<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">/**  * 根据移动轨迹，模拟拖动极验的验证滑块  */</span><span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">boolean</span> <span class="token function">simulateDrag</span><span class="token punctuation">(</span>WebDriver webDriver<span class="token punctuation">,</span> Site site<span class="token punctuation">,</span> List<span class="token operator">&lt;</span>Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">>></span> tracks<span class="token punctuation">)</span> <span class="token keyword">throws</span> InterruptedException <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"开始模拟拖动滑块"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    WebElement slider <span class="token operator">=</span> webDriver<span class="token punctuation">.</span><span class="token function">findElement</span><span class="token punctuation">(</span>By<span class="token punctuation">.</span><span class="token function">cssSelector</span><span class="token punctuation">(</span>site<span class="token punctuation">.</span><span class="token function">getGeetest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getSliderKnob</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"滑块初始位置: {}"</span><span class="token punctuation">,</span> slider<span class="token punctuation">.</span><span class="token function">getLocation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    Actions actions <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Actions</span><span class="token punctuation">(</span>webDriver<span class="token punctuation">)</span><span class="token punctuation">;</span>    actions<span class="token punctuation">.</span><span class="token function">clickAndHold</span><span class="token punctuation">(</span>slider<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span>Map<span class="token operator">&lt;</span>String<span class="token punctuation">,</span> Integer<span class="token operator">></span> point <span class="token operator">:</span> tracks<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">int</span> x <span class="token operator">=</span> point<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"x"</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">22</span><span class="token punctuation">;</span>        <span class="token keyword">int</span> y <span class="token operator">=</span> point<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"y"</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">22</span><span class="token punctuation">;</span>        actions<span class="token punctuation">.</span><span class="token function">moveToElement</span><span class="token punctuation">(</span>slider<span class="token punctuation">,</span> x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">int</span> z <span class="token operator">=</span> point<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"z"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        TimeUnit<span class="token punctuation">.</span>MILLISECONDS<span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span>z<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    TimeUnit<span class="token punctuation">.</span>MILLISECONDS<span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token function">getRandom</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 随机停顿100~200毫秒</span>    actions<span class="token punctuation">.</span><span class="token function">release</span><span class="token punctuation">(</span>slider<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    TimeUnit<span class="token punctuation">.</span>MILLISECONDS<span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 等待0.1秒后检查结果</span>    <span class="token keyword">try</span> <span class="token punctuation">{</span>        <span class="token comment" spellcheck="true">// 在5秒之内检查弹出框是否消失，如果消失则说明校验通过；如果没有消失说明校验失败。</span>        <span class="token keyword">new</span> <span class="token class-name">WebDriverWait</span><span class="token punctuation">(</span>webDriver<span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">until</span><span class="token punctuation">(</span><span class="token punctuation">(</span>ExpectedCondition<span class="token operator">&lt;</span>Boolean<span class="token operator">></span><span class="token punctuation">)</span> driver <span class="token operator">-</span><span class="token operator">></span> <span class="token punctuation">{</span>            <span class="token keyword">try</span> <span class="token punctuation">{</span>                WebElement popupElement <span class="token operator">=</span> driver<span class="token punctuation">.</span><span class="token function">findElement</span><span class="token punctuation">(</span>By<span class="token punctuation">.</span><span class="token function">cssSelector</span><span class="token punctuation">(</span>site<span class="token punctuation">.</span><span class="token function">getGeetest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getPopupWrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token keyword">return</span> <span class="token operator">!</span>popupElement<span class="token punctuation">.</span><span class="token function">isDisplayed</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">NoSuchElementException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>                <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 元素不存在也返回true</span>            <span class="token punctuation">}</span>        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>解决好上面的三点之后，<code>Geetest</code>验证基本就破解掉了，但是要爬取<code>企业信用信息公示系统</code>中的数据还有一些事情要做。目前工商局有多个地区系统，每个系统的页面解析处理也不一样。某些地区系统甚至还针对访问频次做了控制，你可以通过 IP 代理来解决。</p><p>完整的代码示例请参考: <a href="https://github.com/aqlu/geetest-crack" target="_blank" rel="noopener">https://github.com/aqlu/geetest-crack</a></p><p>运行效果：<img src="http://obbrvjuch.bkt.clouddn.com/blog/posts/2017-11/Kapture.gif" alt="效果图"></p><p>本文的参考资料：<a href="http://blog.csdn.net/paololiu/article/details/52514504" target="_blank" rel="noopener">http://blog.csdn.net/paololiu/article/details/52514504</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;项目中有个需求希望能获取公司的工商注册信息，刚开始是想与第三方数据公司合作，因种 种原因合作没有达成。于是想做个爬虫直接从工商局的《企业信用信息公示系统》中获取。 要想从《企业信用信息公示系统》中爬取数据，首先必须解决掉&lt;code&gt;Geetest&lt;/code&gt;验证码的问题。 经过一系列的摸索，发现要想破解&lt;code&gt;geetest&lt;/code&gt;的验证码，主要需要解决如下几个问题：&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;
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="工具类" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%B7%A5%E5%85%B7%E7%B1%BB/"/>
    
    
      <category term="geetest" scheme="http://zyouwei.com/tags/geetest/"/>
    
      <category term="验证码破解" scheme="http://zyouwei.com/tags/%E9%AA%8C%E8%AF%81%E7%A0%81%E7%A0%B4%E8%A7%A3/"/>
    
      <category term="java" scheme="http://zyouwei.com/tags/java/"/>
    
  </entry>
  
  <entry>
    <title>MacOS科学上网</title>
    <link href="http://zyouwei.com/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%B7%A5%E5%85%B7%E7%B1%BB/MacOs-scientific-Surf-the-Internet.html"/>
    <id>http://zyouwei.com/技术笔记/工具类/MacOs-scientific-Surf-the-Internet.html</id>
    <published>2017-11-21T07:07:27.000Z</published>
    <updated>2018-03-22T10:25:53.067Z</updated>
    
    <content type="html"><![CDATA[<p>本文将介绍如何在MacOS上采用<a href="https://www.v2ray.com/chapter_04/03_vmess.html" target="_blank" rel="noopener">VMess</a>协议与<code>墙外VPS</code>通信，其中主要用到了<a href="https://www.v2ray.com/" target="_blank" rel="noopener">v2ray</a>这款开源工具。</p><h2 id="服务端安装-Centos"><a href="#服务端安装-Centos" class="headerlink" title="服务端安装(Centos)"></a>服务端安装(Centos)</h2><p>因为我选择的VPS的Centos的操作系统，所以此章节主要基于Centos操作系统来描述。</p><ol><li>安装包下载：<code>wget https://github.com/v2ray/v2ray-core/releases/download/v2.40/v2ray-linux-64.zip</code></li><li>解压安装包到<code>/opt</code>目录：<code>unzip v2ray-linux-64.zip -d /opt</code></li><li>编写配置文件:<a id="more"></a><pre class=" language-bash"><code class="language-bash"> <span class="token function">cd</span> /opt/v2ray-v2.40-linux-64 <span class="token function">vi</span> config.json</code></pre>config.json内容：<pre class=" language-js"><code class="language-js"> <span class="token punctuation">{</span>     <span class="token string">"log"</span> <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 日志输出配置</span>         <span class="token string">"access"</span><span class="token punctuation">:</span> <span class="token string">"/var/log/v2ray/access.log"</span><span class="token punctuation">,</span>         <span class="token string">"error"</span><span class="token punctuation">:</span> <span class="token string">"/var/log/v2ray/error.log"</span><span class="token punctuation">,</span>         <span class="token string">"loglevel"</span><span class="token punctuation">:</span> <span class="token string">"warning"</span>     <span class="token punctuation">}</span><span class="token punctuation">,</span>     <span class="token comment" spellcheck="true">// 对外提供 vmess 协议</span>     <span class="token string">"inbound"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>         <span class="token string">"port"</span><span class="token punctuation">:</span> <span class="token number">28399</span><span class="token punctuation">,</span>  <span class="token comment" spellcheck="true">// 监听端口</span>         <span class="token string">"protocol"</span><span class="token punctuation">:</span> <span class="token string">"vmess"</span><span class="token punctuation">,</span>         <span class="token string">"settings"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>             <span class="token string">"clients"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>                 <span class="token string">"id"</span><span class="token punctuation">:</span> <span class="token string">"3b129ddd-72a3-4d28-aeee-028a0fe86e33"</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 可以随机生成一个，但必须保证服务端ID与客户端ID必须保持一致</span>                 <span class="token string">"level"</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>                 <span class="token string">"alterId"</span><span class="token punctuation">:</span> <span class="token number">64</span><span class="token punctuation">,</span>                 <span class="token string">"security"</span><span class="token punctuation">:</span> <span class="token string">"aes-128-gcm"</span>             <span class="token punctuation">}</span><span class="token punctuation">]</span>         <span class="token punctuation">}</span>     <span class="token punctuation">}</span><span class="token punctuation">,</span>     <span class="token string">"outbound"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>         <span class="token string">"protocol"</span><span class="token punctuation">:</span> <span class="token string">"freedom"</span><span class="token punctuation">,</span>         <span class="token string">"settings"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>     <span class="token punctuation">}</span><span class="token punctuation">,</span>     <span class="token string">"routing"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>         <span class="token string">"strategy"</span><span class="token punctuation">:</span> <span class="token string">"rules"</span><span class="token punctuation">,</span>         <span class="token string">"settings"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>             <span class="token string">"rules"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>                 <span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"field"</span><span class="token punctuation">,</span>                 <span class="token string">"ip"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>                     <span class="token string">"0.0.0.0/8"</span><span class="token punctuation">,</span>                     <span class="token string">"10.0.0.0/8"</span><span class="token punctuation">,</span>                     <span class="token string">"100.64.0.0/10"</span><span class="token punctuation">,</span>                     <span class="token string">"127.0.0.0/8"</span><span class="token punctuation">,</span>                     <span class="token string">"169.254.0.0/16"</span><span class="token punctuation">,</span>                     <span class="token string">"172.16.0.0/12"</span><span class="token punctuation">,</span>                     <span class="token string">"192.0.0.0/24"</span><span class="token punctuation">,</span>                     <span class="token string">"192.0.2.0/24"</span><span class="token punctuation">,</span>                     <span class="token string">"192.168.0.0/16"</span><span class="token punctuation">,</span>                     <span class="token string">"198.18.0.0/15"</span><span class="token punctuation">,</span>                     <span class="token string">"198.51.100.0/24"</span><span class="token punctuation">,</span>                     <span class="token string">"203.0.113.0/24"</span><span class="token punctuation">,</span>                     <span class="token string">"::1/128"</span><span class="token punctuation">,</span>                     <span class="token string">"fc00::/7"</span><span class="token punctuation">,</span>                     <span class="token string">"fe80::/10"</span>                 <span class="token punctuation">]</span><span class="token punctuation">,</span>                 <span class="token string">"outboundTag"</span><span class="token punctuation">:</span> <span class="token string">"blocked"</span>             <span class="token punctuation">}</span><span class="token punctuation">]</span>         <span class="token punctuation">}</span>     <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></li><li>启动<code>v2ray</code>服务：<code>/opt/v2ray-v2.40-linux-64/v2ray -config=/opt/v2ray-v2.40-linux-64/config.json &gt; v2ray.log &amp;</code></li></ol><h2 id="客户端安装-MacOs"><a href="#客户端安装-MacOs" class="headerlink" title="客户端安装(MacOs)"></a>客户端安装(MacOs)</h2><ol><li>安装包下载：<code>wget https://github.com/v2ray/v2ray-core/releases/download/v2.40/v2ray-macos.zip</code></li><li>解压安装包到<code>/usr/local/opt</code>目录：<code>unzip v2ray-linux-64.zip -d /usr/local/opt</code></li><li>编写配置文件:<pre class=" language-bash"><code class="language-bash"> <span class="token function">cd</span> /usr/local/opt/v2ray-v2.40-linux-64 <span class="token function">vi</span> config.json</code></pre>config.json内容：<pre class=" language-js"><code class="language-js"> <span class="token punctuation">{</span>     <span class="token string">"log"</span> <span class="token punctuation">:</span> <span class="token punctuation">{</span>         <span class="token string">"access"</span><span class="token punctuation">:</span> <span class="token string">"/var/log/v2ray/access.log"</span><span class="token punctuation">,</span>         <span class="token string">"error"</span><span class="token punctuation">:</span> <span class="token string">"/var/log/v2ray/error.log"</span><span class="token punctuation">,</span>         <span class="token string">"loglevel"</span><span class="token punctuation">:</span> <span class="token string">"warning"</span>     <span class="token punctuation">}</span><span class="token punctuation">,</span>     <span class="token comment" spellcheck="true">// 对外提供 socks5 协议</span>     <span class="token string">"inbound"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>         <span class="token string">"port"</span><span class="token punctuation">:</span> <span class="token number">1080</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 监听端口</span>         <span class="token string">"protocol"</span><span class="token punctuation">:</span> <span class="token string">"socks"</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 入口协议为 SOCKS 5</span>         <span class="token string">"settings"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>             <span class="token string">"auth"</span><span class="token punctuation">:</span> <span class="token string">"noauth"</span>  <span class="token comment" spellcheck="true">//socks的认证设置，noauth 代表不认证，由于 socks 通常在客户端使用，所以这里不认证</span>         <span class="token punctuation">}</span>     <span class="token punctuation">}</span><span class="token punctuation">,</span>     <span class="token string">"outbound"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>         <span class="token string">"protocol"</span><span class="token punctuation">:</span> <span class="token string">"vmess"</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 出口协议，对应服务端的入口协议</span>         <span class="token string">"settings"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>             <span class="token string">"vnext"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>                 <span class="token string">"address"</span><span class="token punctuation">:</span> <span class="token string">"serveraddr.com"</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 服务器地址，请修改为你自己的服务器 ip 或域名</span>                 <span class="token string">"port"</span><span class="token punctuation">:</span> <span class="token number">28399</span><span class="token punctuation">,</span>  <span class="token comment" spellcheck="true">// 服务器端口，对应服务器端入口的端口</span>                 <span class="token string">"users"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>                     <span class="token string">"id"</span><span class="token punctuation">:</span> <span class="token string">"3b129ddd-72a3-4d28-aeee-028a0fe86e33"</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 用户ID, 服务端ID与客户端ID必须保持一致</span>                     <span class="token string">"alterId"</span><span class="token punctuation">:</span> <span class="token number">64</span> <span class="token comment" spellcheck="true">// 此处的值也应当与服务器相同</span>                 <span class="token punctuation">}</span><span class="token punctuation">]</span>             <span class="token punctuation">}</span><span class="token punctuation">]</span>         <span class="token punctuation">}</span>     <span class="token punctuation">}</span><span class="token punctuation">,</span>     <span class="token string">"routing"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>         <span class="token string">"strategy"</span><span class="token punctuation">:</span> <span class="token string">"rules"</span><span class="token punctuation">,</span>         <span class="token string">"settings"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>             <span class="token string">"rules"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>                 <span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"field"</span><span class="token punctuation">,</span>                 <span class="token string">"ip"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>                     <span class="token string">"0.0.0.0/8"</span><span class="token punctuation">,</span>                     <span class="token string">"10.0.0.0/8"</span><span class="token punctuation">,</span>                     <span class="token string">"100.64.0.0/10"</span><span class="token punctuation">,</span>                     <span class="token string">"127.0.0.0/8"</span><span class="token punctuation">,</span>                     <span class="token string">"169.254.0.0/16"</span><span class="token punctuation">,</span>                     <span class="token string">"172.16.0.0/12"</span><span class="token punctuation">,</span>                     <span class="token string">"192.0.0.0/24"</span><span class="token punctuation">,</span>                     <span class="token string">"192.0.2.0/24"</span><span class="token punctuation">,</span>                     <span class="token string">"192.168.0.0/16"</span><span class="token punctuation">,</span>                     <span class="token string">"198.18.0.0/15"</span><span class="token punctuation">,</span>                     <span class="token string">"198.51.100.0/24"</span><span class="token punctuation">,</span>                     <span class="token string">"203.0.113.0/24"</span><span class="token punctuation">,</span>                     <span class="token string">"::1/128"</span><span class="token punctuation">,</span>                     <span class="token string">"fc00::/7"</span><span class="token punctuation">,</span>                     <span class="token string">"fe80::/10"</span>                 <span class="token punctuation">]</span><span class="token punctuation">,</span>                 <span class="token string">"outboundTag"</span><span class="token punctuation">:</span> <span class="token string">"blocked"</span>             <span class="token punctuation">}</span><span class="token punctuation">]</span>         <span class="token punctuation">}</span>     <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></li><li>启动<code>v2ray</code>服务：<code>/usr/local/opt/v2ray-v2.40-linux-64/v2ray -config=/usr/local/opt/v2ray-v2.40-linux-64/config.json &gt; v2ray.log &amp;</code></li></ol><h2 id="客户端安装的另一种方式（基于Docker安装）"><a href="#客户端安装的另一种方式（基于Docker安装）" class="headerlink" title="客户端安装的另一种方式（基于Docker安装）"></a>客户端安装的另一种方式（基于Docker安装）</h2><ol><li>下载镜像： <code>docker pull v2ray/official</code></li><li>编写配置文件：<pre class=" language-bash"><code class="language-bash"> <span class="token function">mkdir</span> -p ~/v2ray <span class="token function">cd</span> ~/v2ray <span class="token function">vi</span> config.json <span class="token comment" spellcheck="true"># 内容请参考客户端安装(MacOs)</span></code></pre></li><li>创建容器：<pre class=" language-bash"><code class="language-bash"> docker run -d --restart<span class="token operator">=</span>always --name v2ray -v ~/v2ray:/etc/v2ray -p 1080:1080 v2ray/official v2ray -config<span class="token operator">=</span>/etc/v2ray/config.json</code></pre><blockquote><p><code>-p</code> 后面跟的端口为<code>config.json</code>中<code>inbound</code>配置的监听端口</p></blockquote></li><li>校验容器是否创建成功：<code>docker container ls</code></li><li>启动容器：<code>docker start</code></li><li>停止v2ray：<code>docker stop v2ray</code></li><li>重启v2ray：<code>docker restart v2ray</code></li><li>查看日志：<code>docker container log v2ray</code></li></ol><h2 id="更多参考资料"><a href="#更多参考资料" class="headerlink" title="更多参考资料"></a>更多参考资料</h2><ul><li>项目：<a href="https://github.com/v2ray/v2ray-core" target="_blank" rel="noopener">https://github.com/v2ray/v2ray-core</a></li><li>用户手册：<a href="https://www.v2ray.com/" target="_blank" rel="noopener">https://www.v2ray.com/</a></li><li>其它文档：<a href="https://toutyrater.github.io/" target="_blank" rel="noopener">https://toutyrater.github.io/</a></li></ul><h2 id="可能遇到的问题"><a href="#可能遇到的问题" class="headerlink" title="可能遇到的问题"></a>可能遇到的问题</h2><ol><li>客户端版本与服务端版本不匹配是，可能会出现服务端报错：<code>rejected Proxy|VMess|Encoding: invalid auth</code>。</li></ol>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本文将介绍如何在MacOS上采用&lt;a href=&quot;https://www.v2ray.com/chapter_04/03_vmess.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;VMess&lt;/a&gt;协议与&lt;code&gt;墙外VPS&lt;/code&gt;通信，其中主要用到了&lt;a href=&quot;https://www.v2ray.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;v2ray&lt;/a&gt;这款开源工具。&lt;/p&gt;&lt;h2 id=&quot;服务端安装-Centos&quot;&gt;&lt;a href=&quot;#服务端安装-Centos&quot; class=&quot;headerlink&quot; title=&quot;服务端安装(Centos)&quot;&gt;&lt;/a&gt;服务端安装(Centos)&lt;/h2&gt;&lt;p&gt;因为我选择的VPS的Centos的操作系统，所以此章节主要基于Centos操作系统来描述。&lt;/p&gt;&lt;ol&gt;&lt;li&gt;安装包下载：&lt;code&gt;wget https://github.com/v2ray/v2ray-core/releases/download/v2.40/v2ray-linux-64.zip&lt;/code&gt;&lt;/li&gt;&lt;li&gt;解压安装包到&lt;code&gt;/opt&lt;/code&gt;目录：&lt;code&gt;unzip v2ray-linux-64.zip -d /opt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;编写配置文件:
    
    </summary>
    
      <category term="技术笔记" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/"/>
    
      <category term="工具类" scheme="http://zyouwei.com/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/%E5%B7%A5%E5%85%B7%E7%B1%BB/"/>
    
    
      <category term="vmess" scheme="http://zyouwei.com/tags/vmess/"/>
    
      <category term="翻墙" scheme="http://zyouwei.com/tags/%E7%BF%BB%E5%A2%99/"/>
    
  </entry>
  
  <entry>
    <title>Hello World!</title>
    <link href="http://zyouwei.com//hello-world.html"/>
    <id>http://zyouwei.com//hello-world.html</id>
    <published>2017-11-21T04:35:00.000Z</published>
    <updated>2017-11-21T09:11:42.021Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Hello-World"><a href="#Hello-World" class="headerlink" title="Hello World!"></a>Hello World!</h2><p>这是我的第一篇博客，感谢<a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>提供了一个简单好用的博客框架，感谢<a href="https://github.com/iissnan/hexo-theme-next" target="_blank" rel="noopener">NexT</a>优雅的的主题。</p><p>此博客是<a href="./about/index.html">本人</a>的一个杂记。为什么叫杂记？其实就是一些乱七八糟的东西，可能是曾经的一些技术笔记，也可能是一些技术专题的连载，也可能是一些生活观点与琐事。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;Hello-World&quot;&gt;&lt;a href=&quot;#Hello-World&quot; class=&quot;headerlink&quot; title=&quot;Hello World!&quot;&gt;&lt;/a&gt;Hello World!&lt;/h2&gt;&lt;p&gt;这是我的第一篇博客，感谢&lt;a href=&quot;https://hex
      
    
    </summary>
    
    
  </entry>
  
</feed>
