<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>WebFuse</title>
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://webfuse.cn/"/>
  <updated>2020-04-30T03:15:21.152Z</updated>
  <id>https://webfuse.cn/</id>
  
  <author>
    <name>Jesen Kwan</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>从0开始用SpringCloud搭建微服务系统【五】</title>
    <link href="https://webfuse.cn/2020/04/30/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%94%E3%80%91/"/>
    <id>https://webfuse.cn/2020/04/30/从0开始用SpringCloud搭建微服务系统【五】/</id>
    <published>2020-04-30T02:20:21.000Z</published>
    <updated>2020-04-30T03:15:21.152Z</updated>
    
    <content type="html"><![CDATA[<h2 id="调用链监控"><a href="#调用链监控" class="headerlink" title="调用链监控"></a>调用链监控</h2><p>如果能跟踪每个请求，中间请求经过哪些微服务，请求耗时，网络延迟，业务逻辑耗时等。我们就能更好地分析系统瓶颈、解决系统问题。因此链路跟踪很重要。</p>
<p>链路追踪目的：解决错综复杂的服务调用中链路的查看。排查慢服务。</p>
<p>市面上链路追踪产品，大部分基于<a href="http://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/36356.pdf" target="_blank" rel="external">google的Dapper论文</a>。</p>
<h3 id="链路追踪要考虑的几个问题"><a href="#链路追踪要考虑的几个问题" class="headerlink" title="链路追踪要考虑的几个问题"></a>链路追踪要考虑的几个问题</h3><ol>
<li>探针的性能消耗。尽量不影响 服务本尊。</li>
<li>易用。开发可以很快接入，别浪费太多精力。</li>
<li>数据分析。要实时分析。维度足够。</li>
</ol>
<h3 id="Sleuth"><a href="#Sleuth" class="headerlink" title="Sleuth"></a>Sleuth</h3><p>Sleuth是Spring cloud的分布式跟踪解决方案。</p>
<ol>
<li><p>span(跨度)，基本工作单元。一次链路调用，创建一个span，span用一个64位id唯一标识。包括：id，描述，时间戳事件，spanId,span父id。</p>
<p>span被启动和停止时，记录了时间信息，初始化span叫：root span，它的span id和trace id相等。</p>
</li>
<li><p>trace(跟踪)，一组共享“root span”的span组成的树状结构 称为 trace，trace也有一个64位ID，trace中所有span共享一个trace id。类似于一颗 span 树。</p>
</li>
<li><p>annotation（标签），annotation用来记录事件的存在，其中，核心annotation用来定义请求的开始和结束。</p>
<ul>
<li>CS(Client Send客户端发起请求)。客户端发起请求描述了span开始。</li>
<li>SR(Server Received服务端接到请求)。服务端获得请求并准备处理它。SR-CS=网络延迟。</li>
<li>SS（Server Send服务器端处理完成，并将结果发送给客户端）。表示服务器完成请求处理，响应客户端时。SS-SR=服务器处理请求的时间。</li>
<li>CR（Client Received 客户端接受服务端信息）。span结束的标识。客户端接收到服务器的响应。CR-CS=客户端发出请求到服务器响应的总时间。</li>
</ul>
</li>
</ol>
<p>其实数据结构是一颗树，从root span 开始。</p>
<img src="/2020/04/30/从0开始用SpringCloud搭建微服务系统【五】/sleuth.png" alt="sleuth.png" title="">
<h4 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h4><p>在每个需要被监控的系统的pom中引入：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- 引入sleuth依赖 --&gt;</span></div><div class="line">		<span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">			<span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></div><div class="line">			<span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-sleuth<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></div><div class="line">		<span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div></pre></td></tr></table></figure>
<p>启动后，日志中可以看到类似于：<code>[account-service,,,]</code>的数据，说明如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[服务名称，traceId（一条请求调用链中 唯一ID），spanID（基本的工作单元，获取数据等），是否让zipkin收集和展示此信息]</div></pre></td></tr></table></figure>
<h3 id="zipkin"><a href="#zipkin" class="headerlink" title="zipkin"></a>zipkin</h3><p>zipkin是twitter开源的分布式跟踪系统。</p>
<p>原理收集系统的时序数据，从而追踪微服务架构中系统延时等问题。还有一个友好的界面。</p>
<p>由4个部分组成：Collector（采集器）、Storage（存储器）、Restful API（接口）、Web UI</p>
<h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h4><p>sleuth收集跟踪信息通过http请求发送给zipkin server，zipkin将跟踪信息存储，以及提供RESTful API接口，zipkin ui通过调用api进行数据展示。</p>
<p>默认内存存储，可以用mysql，ES等存储。</p>
<h4 id="使用-1"><a href="#使用-1" class="headerlink" title="使用"></a>使用</h4><p>在每个需要被监控的系统的pom中引入：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- zipkin --&gt;</span></div><div class="line">		<span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">			<span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></div><div class="line">			<span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-zipkin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></div><div class="line">		<span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div></pre></td></tr></table></figure>
<p>在每个需要监听的服务的配置文件中加入：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="attr">spring:</span></div><div class="line">  <span class="comment">#zipkin</span></div><div class="line"><span class="attr">  zipkin:</span></div><div class="line"><span class="attr">    base-url:</span> <span class="attr">http://localhost:9411/</span></div><div class="line">    <span class="comment">#采样比例1</span></div><div class="line"><span class="attr">  sleuth:</span></div><div class="line"><span class="attr">    sampler:</span></div><div class="line"><span class="attr">      rate:</span> <span class="number">1</span></div></pre></td></tr></table></figure>
<p>根据<a href="https://zipkin.io/pages/quickstart.html" target="_blank" rel="external">zipkin官网中的Quickstart</a>，启动zipkin。</p>
<h2 id="监控告警"><a href="#监控告警" class="headerlink" title="监控告警"></a>监控告警</h2><h3 id="使用-Spring-Boot2-x-Actuator-监控应用"><a href="#使用-Spring-Boot2-x-Actuator-监控应用" class="headerlink" title="使用 Spring Boot2.x Actuator 监控应用"></a>使用 Spring Boot2.x Actuator 监控应用</h3><p>在项目的 POM 中引入</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-actuator<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div></pre></td></tr></table></figure>
<h4 id="Endpoints"><a href="#Endpoints" class="headerlink" title="Endpoints"></a>Endpoints</h4><p>可以通过<code>/actuator</code>查看所有的暴露端点。</p>
<h5 id="暴露配置"><a href="#暴露配置" class="headerlink" title="暴露配置"></a>暴露配置</h5><figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="attr">management:</span></div><div class="line"><span class="attr">  endpoints:</span></div><div class="line"><span class="attr">    web:</span></div><div class="line"><span class="attr">      exposure:</span></div><div class="line"><span class="attr">        include:</span> <span class="string">'*'</span> <span class="comment"># 通过HTTP暴露所有的端点</span></div><div class="line"><span class="attr">      base-path:</span> <span class="string">'/actuator'</span></div><div class="line"><span class="attr">    jmx:</span></div><div class="line"><span class="attr">      exposure:</span></div><div class="line"><span class="attr">        include:</span> <span class="string">'*'</span> <span class="comment"># 通过JMX暴露所有的端点</span></div></pre></td></tr></table></figure>
<h5 id="health端点"><a href="#health端点" class="headerlink" title="health端点"></a>health端点</h5><p>health端点会聚合你程序的健康指标，来检查程序的健康情况。端点公开的应用健康信息取决于：<code>management.endpoint.health.show-details=always</code></p>
<p>show-details的值：</p>
<ul>
<li>never    不展示详细信息，up或者down的状态，默认配置</li>
<li>when-authorized    详细信息将会展示给通过认证的用户。授权的角色可以通过 management.endpoint.health.roles配置</li>
<li>always    对所有用户暴露详细信息</li>
</ul>
<h5 id="metrics端点"><a href="#metrics端点" class="headerlink" title="metrics端点"></a>metrics端点</h5><p>metrics端点用来返回当前应用的各类重要度量指标，比如：内存信息、线程信息、垃圾回收信息、tomcat、数据库连接池等。</p>
<p>调用：<code>&quot;http://localhost:8080/actuator/metrics/{requiredMetricName}&quot;</code> 获得各个指标。如：<code>http://localhost:8080/actuator/metrics/jvm.memory.max</code></p>
<h5 id="loggers端点"><a href="#loggers端点" class="headerlink" title="loggers端点"></a>loggers端点</h5><p>loggers 端点暴露了我们程序内部配置的所有logger的信息。</p>
<p>调用：<code>http://localhost:8080/actuator/loggers</code>获得所有的logger信息。</p>
<p>可以调用对应的POST接口，修改日志级别等。</p>
<h5 id="info端点"><a href="#info端点" class="headerlink" title="info端点"></a>info端点</h5><p>info端点可以用来展示你程序的信息。我理解过来就是一些程序的基础信息。并且你可以按照自己的需求在配置文件 application.properties中个性化配置。</p>
<h5 id="beans端点"><a href="#beans端点" class="headerlink" title="beans端点"></a>beans端点</h5><p>beans端点会返回Spring 容器中所有bean的别名、类型、是否单例、依赖等信息。</p>
<h5 id="heapdump端点"><a href="#heapdump端点" class="headerlink" title="heapdump端点"></a>heapdump端点</h5><p>访问：<code>http://localhost:8080/actuator/heapdump</code>会自动生成一个 Jvm 的堆文件 heapdump。我们可以使用 JDK 自带的 Jvm监控工具，VisualVM 打开此文件查看内存快照。</p>
<h5 id="threaddump-端点"><a href="#threaddump-端点" class="headerlink" title="threaddump 端点"></a>threaddump 端点</h5><p>主要展示了线程名、线程ID、线程的状态、是否等待锁资源、线程堆栈等信息。就是可能查看起来不太直观。访问：<code>http://localhost:8080/actuator/threaddump</code></p>
<h5 id="shutdown端点"><a href="#shutdown端点" class="headerlink" title="shutdown端点"></a>shutdown端点</h5><p>这个端点属于操作控制类端点，可以优雅关闭 Spring Boot 应用。要使用这个功能首先需要在配置文件中开启：<br><figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="attr">management:</span></div><div class="line"><span class="attr">  endpoint:</span></div><div class="line"><span class="attr">    shutdown:</span></div><div class="line"><span class="attr">      enabled:</span> <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>由于 shutdown 接口默认只支持 POST 请求。</p>
<h5 id="env端点"><a href="#env端点" class="headerlink" title="env端点"></a>env端点</h5><p>获取应用所有可用的环境属性报告</p>
<h5 id="mappings端点"><a href="#mappings端点" class="headerlink" title="mappings端点"></a>mappings端点</h5><p>获取应用所有Spring Web的控制器映射关系报告</p>
<h5 id="configprops端点"><a href="#configprops端点" class="headerlink" title="configprops端点"></a>configprops端点</h5><p>获取应用中配置的属性信息报告</p>
<h2 id="SpringCloud-Admin健康检查"><a href="#SpringCloud-Admin健康检查" class="headerlink" title="SpringCloud Admin健康检查"></a>SpringCloud Admin健康检查</h2><h3 id="Admin客户端"><a href="#Admin客户端" class="headerlink" title="Admin客户端"></a>Admin客户端</h3><p>在要被监控的客户端添加pom引用：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>de.codecentric<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-admin-starter-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.2.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></div><div class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div><div class="line"> <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-actuator<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></div><div class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div></pre></td></tr></table></figure>
<p>在配置文件中如下配置：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="attr">spring:</span></div><div class="line"><span class="attr">  boot:</span></div><div class="line"><span class="attr">    admin:</span></div><div class="line"><span class="attr">      client:</span></div><div class="line"><span class="attr">        url:</span> <span class="attr">http://localhost:9030</span></div></pre></td></tr></table></figure>
<h3 id="Admin服务端"><a href="#Admin服务端" class="headerlink" title="Admin服务端"></a>Admin服务端</h3><p>新建一个项目，引入：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">           <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>de.codecentric<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></div><div class="line">           <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-admin-starter-server<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></div><div class="line">           <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.2.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></div><div class="line">       <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div></pre></td></tr></table></figure>
<p>然后添加注解<code>@EnableAdminServer</code></p>
<hr>
<ul>
<li><a href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%80%E3%80%91">从0开始用SpringCloud搭建微服务系统【一】</a></li>
<li><a href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%8C%E3%80%91/">从0开始用SpringCloud搭建微服务系统【二】</a></li>
<li><a href="https://webfuse.cn/2020/04/25/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%89%E3%80%91/">从0开始用SpringCloud搭建微服务系统【三】</a></li>
<li><a href="https://webfuse.cn/2020/04/27/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E5%9B%9B%E3%80%91/">从0开始用SpringCloud搭建微服务系统【四】</a></li>
<li><a href="https://webfuse.cn/2020/04/30/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%94%E3%80%91/">从0开始用SpringCloud搭建微服务系统【五】</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;p&gt;如果能跟踪每个请求，中间请求经过哪些微服务，请求耗时，网络延迟，业务逻辑耗时等。我们就能更好地分析系统瓶颈、解决系统
    
    </summary>
    
      <category term="Spring" scheme="https://webfuse.cn/categories/Spring/"/>
    
      <category term="Spring Cloud" scheme="https://webfuse.cn/categories/Spring/Spring-Cloud/"/>
    
    
      <category term="微服务" scheme="https://webfuse.cn/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Spring Cloud" scheme="https://webfuse.cn/tags/Spring-Cloud/"/>
    
  </entry>
  
  <entry>
    <title>从0开始用SpringCloud搭建微服务系统【四】</title>
    <link href="https://webfuse.cn/2020/04/27/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E5%9B%9B%E3%80%91/"/>
    <id>https://webfuse.cn/2020/04/27/从0开始用SpringCloud搭建微服务系统【四】/</id>
    <published>2020-04-27T08:30:38.000Z</published>
    <updated>2020-04-30T02:25:33.310Z</updated>
    
    <content type="html"><![CDATA[<h2 id="容错"><a href="#容错" class="headerlink" title="容错"></a>容错</h2><p>基本的容错模式有：</p>
<ul>
<li>主动超时：</li>
<li>限流：限制最大并发数</li>
<li>熔断：错误数达到阈值时，类似保险丝熔断</li>
<li>隔离：隔离不同的依赖调用或者隔离不同的线程</li>
<li>降级：服务降低</li>
</ul>
<p>容错理念：</p>
<ul>
<li>凡是依赖都可能会失败</li>
<li>凡是资源都有限制<ul>
<li>CPU/Memory/Threads/Queue</li>
</ul>
</li>
<li>网络并不可靠</li>
<li>延迟是应用稳定性杀手</li>
</ul>
<h3 id="Netflix-Hystrix"><a href="#Netflix-Hystrix" class="headerlink" title="Netflix Hystrix"></a>Netflix Hystrix</h3><p>Hystrix实现了 超时机制和断路器模式。</p>
<p>Hystrix是Netflix开源的一个类库，用于隔离远程系统、服务或者第三方库，防止级联失败，从而提升系统的可用性与容错性。主要有以下几点功能：</p>
<ul>
<li>为系统提供保护机制。在依赖的服务出现高延迟或失败时，为系统提供保护和控制。</li>
<li>防止雪崩。</li>
<li>包裹请求：使用HystrixCommand（或HystrixObservableCommand）包裹对依赖的调用逻辑，每个命令在独立线程中运行。</li>
<li>跳闸机制：当某服务失败率达到一定的阈值时，Hystrix可以自动跳闸，停止请求该服务一段时间。</li>
<li>资源隔离：Hystrix为每个请求都的依赖都维护了一个小型线程池，如果该线程池已满，发往该依赖的请求就被立即拒绝，而不是排队等候，从而加速失败判定。防止级联失败。</li>
<li>快速失败：Fail Fast。同时能快速恢复。侧重点是：（不去真正的请求服务，发生异常再返回），而是直接失败。</li>
<li>监控：Hystrix可以实时监控运行指标和配置的变化，提供近实时的监控、报警、运维控制。</li>
<li>回退机制：fallback，当请求失败、超时、被拒绝，或当断路器被打开时，执行回退逻辑。回退逻辑我们自定义，提供优雅的服务降级。</li>
<li>自我修复：断路器打开一段时间后，会自动进入“半开”状态，可以进行打开，关闭，半开状态的转换。</li>
</ul>
<h3 id="Hystrix设计原理"><a href="#Hystrix设计原理" class="headerlink" title="Hystrix设计原理"></a>Hystrix设计原理</h3><p>Hystrix的工作原理可以可以参考官网的<a href="https://github.com/Netflix/Hystrix/wiki/How-it-Works" target="_blank" rel="external">How it Works</a>。如果要看中文版，可以参考：<a href="https://segmentfault.com/a/1190000012439580" target="_blank" rel="external">Hystrix工作原理</a>。</p>
<h4 id="信号量隔离与线程隔离"><a href="#信号量隔离与线程隔离" class="headerlink" title="信号量隔离与线程隔离"></a>信号量隔离与线程隔离</h4><p>默认情况下hystrix使用线程池控制请求隔离</p>
<p>线程池隔离技术，是用 Hystrix 自己的线程去执行调用；而信号量隔离技术，是直接让 tomcat 线程去调用依赖服务。信号量隔离，只是一道关卡，信号量有多少，就允许多少个 tomcat 线程通过它，然后去执行。</p>
<p>信号量隔离主要维护的是Tomcat的线程，不需要内部线程池，更加轻量级。</p>
<table>
<thead>
<tr>
<th></th>
<th>优点</th>
<th>不足</th>
<th>适用</th>
</tr>
</thead>
<tbody>
<tr>
<td>信号量隔离</td>
<td>轻量，无额外开销</td>
<td>不支持任务排队和主动超时<br>不支持异步调用</td>
<td>受信客户<br>高扇出(网关)<br>高频高速调用(cache)</td>
</tr>
<tr>
<td>线程池隔离</td>
<td>支持排队和超时<br>支持异步调用</td>
<td>线程调用会产生额外的开销</td>
<td>不受信客户<br>有限扇出</td>
</tr>
</tbody>
</table>
<h3 id="使用Hystrix"><a href="#使用Hystrix" class="headerlink" title="使用Hystrix"></a>使用Hystrix</h3><h4 id="独立使用"><a href="#独立使用" class="headerlink" title="独立使用"></a>独立使用</h4><p>独立使用Hystrix可以参考<a href="https://github.com/Netflix/Hystrix/wiki/How-To-Use" target="_blank" rel="external">How To Use</a>。</p>
<p>如果想要中文，可以参考：<a href="https://juejin.im/post/5c009ff6f265da614b11b84d" target="_blank" rel="external">Hystrix都停更了，我为什么还要学？</a></p>
<h4 id="与Resttemplate整合"><a href="#与Resttemplate整合" class="headerlink" title="与Resttemplate整合"></a>与Resttemplate整合</h4><p>引入依赖<code>spring-cloud-starter-netflix-hystrix</code>。</p>
<p>在Service中：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Service</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GreetingService</span> </span>&#123;</div><div class="line">    <span class="meta">@HystrixCommand</span>(fallbackMethod = <span class="string">"defaultGreeting"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getGreeting</span><span class="params">(String username)</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">new</span> RestTemplate()</div><div class="line">          .getForObject(<span class="string">"http://localhost:9090/greeting/&#123;username&#125;"</span>, </div><div class="line">          String.class, username);</div><div class="line">    &#125;</div><div class="line">  </div><div class="line">    <span class="function"><span class="keyword">private</span> String <span class="title">defaultGreeting</span><span class="params">(String username)</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="string">"Hello User!"</span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>在Application中：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@SpringBootApplication</span></div><div class="line"><span class="meta">@EnableCircuitBreaker</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RestConsumerApplication</span> </span>&#123;</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</div><div class="line">        SpringApplication.run(RestConsumerApplication.class, args);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>@EnableCircuitBreaker注释将扫描类路径以查找任何兼容的Circuit Breaker实现。</p>
<h4 id="与Feign整合"><a href="#与Feign整合" class="headerlink" title="与Feign整合"></a>与Feign整合</h4><p>在集成Feign的基础上修改如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@FeignClient</span>(name = <span class="string">"statistics-service"</span>, fallback = StatisticsServiceClientFallback.class)</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">StatisticsServiceClient</span> </span>&#123;</div><div class="line">	<span class="meta">@RequestMapping</span>(method = RequestMethod.PUT, value = <span class="string">"/statistics/&#123;accountName&#125;"</span>)</div><div class="line">	<span class="function"><span class="keyword">void</span> <span class="title">updateStatistics</span><span class="params">(@PathVariable(<span class="string">"accountName"</span>)</span> String accountName, Account account)</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="meta">@Slf</span>4j</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StatisticsServiceClientFallback</span> <span class="keyword">implements</span> <span class="title">StatisticsServiceClient</span> </span>&#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateStatistics</span><span class="params">(String accountName, Account account)</span> </span>&#123;</div><div class="line">        log.error(<span class="string">"Error during update statistics for account: &#123;&#125;"</span>, accountName);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>然后在配置文件中打开：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">feign.hystrix.enabled=true</div></pre></td></tr></table></figure>
<h4 id="使用fallbackFactory检查具体错误"><a href="#使用fallbackFactory检查具体错误" class="headerlink" title="使用fallbackFactory检查具体错误"></a>使用fallbackFactory检查具体错误</h4><p>在一些场景中，简单的触发在 FeignClient 加入 Fallback 属性即可，而另外有一些场景需要访问导致回退触发的原因，那么这个时候可以在 FeignClient 中加入 FallbackFactory 属性即可；</p>
<ol>
<li>定义一个类，实现FallbackFactory</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebError</span> <span class="keyword">implements</span> <span class="title">FallbackFactory</span>&lt;<span class="title">ConsumerApi</span>&gt; </span>&#123;</div><div class="line"></div><div class="line">	<span class="meta">@Override</span></div><div class="line">	<span class="function"><span class="keyword">public</span> ConsumerApi <span class="title">create</span><span class="params">(Throwable cause)</span> </span>&#123;</div><div class="line"></div><div class="line">		<span class="keyword">return</span> <span class="keyword">new</span> ConsumerApi() &#123;</div><div class="line">			<span class="meta">@Override</span></div><div class="line">			<span class="function"><span class="keyword">public</span> String <span class="title">getById</span><span class="params">(Integer id)</span> </span>&#123;</div><div class="line">        <span class="comment">//针对不同异常返回响应</span></div><div class="line">				<span class="keyword">if</span>(cause <span class="keyword">instanceof</span> InternalServerError) &#123;</div><div class="line">					System.out.println(<span class="string">"InternalServerError"</span>);</div><div class="line">					<span class="keyword">return</span> <span class="string">"远程服务报错"</span>;</div><div class="line">				&#125;<span class="keyword">else</span> <span class="keyword">if</span>(cause <span class="keyword">instanceof</span> RuntimeException) &#123;</div><div class="line">					<span class="keyword">return</span> <span class="string">"请求时异常："</span> + cause;</div><div class="line">				&#125;<span class="keyword">else</span> &#123;</div><div class="line">					<span class="keyword">return</span> <span class="string">"都算不上"</span>;</div><div class="line">				&#125;</div><div class="line">				<span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line">			&#125;</div><div class="line">		&#125;;</div><div class="line">	&#125;</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ol>
<li>@Feign中的使用<code>@FeignClient(name = &quot;statistics-service&quot;, fallbackFactory = WebError.class)</code></li>
</ol>
<h3 id="主要配置项"><a href="#主要配置项" class="headerlink" title="主要配置项"></a>主要配置项</h3><table>
<thead>
<tr>
<th>配置项(前缀hystrix.command.*.)</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>execution.isolation.strategy</td>
<td>线程“THREAD”或信号量“SEMAPHORE”隔离(Default: THREAD)</td>
</tr>
<tr>
<td>execution.isolation.thread.timeoutInMilliseconds</td>
<td>run()方法执行超时时间(Default: 1000)</td>
</tr>
<tr>
<td>execution.isolation.semaphore.maxConcurrentRequests</td>
<td>信号量隔离最大并发数(Default:10)</td>
</tr>
<tr>
<td>circuitBreaker.errorThresholdPercentage</td>
<td>熔断的错误百分比阀值(Default:50)</td>
</tr>
<tr>
<td>circuitBreaker.requestVolumeThreshold</td>
<td>断路器生效必须满足的流量阀值(Default:20)</td>
</tr>
<tr>
<td>circuitBreaker.sleepWindowInMilliseconds</td>
<td>熔断后重置断路器的时间间隔(Default:5000)</td>
</tr>
<tr>
<td>circuitBreaker.forceOpen</td>
<td>设true表示强制熔断器进入打开状态(Default: false)</td>
</tr>
<tr>
<td>circuitBreaker.forceClosed</td>
<td>设true表示强制熔断器进入关闭状态(Default: false)</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>配置项(前缀hystrix.threadpool.*.)</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>coreSize</td>
<td>使用线程池时的最大并发请求(Default: 10)</td>
</tr>
<tr>
<td>maxQueueSize</td>
<td>最大LinkedBlockingQueue大小，-1表示用SynchronousQueue(Default:-1)</td>
</tr>
<tr>
<td>default.queueSizeRejectionThreshold</td>
<td>队列大小阀值，超过则拒绝(Default:5)</td>
</tr>
</tbody>
</table>
<h3 id="Hystrix-Dashboard"><a href="#Hystrix-Dashboard" class="headerlink" title="Hystrix Dashboard"></a><strong>Hystrix Dashboard</strong></h3><p>Hystrix有一个不错的可选功能是能够在仪表板上监视其状态。 为了启用它，我们将<code>spring-cloud-starter-hystrix-dashboard</code>和<code>spring-boot-starter-actuator</code>放入项目的pom.xml中。</p>
<p>然后添加<code>@EnableHystrixDashboard</code>注解。</p>
<p>启动应用程序后，将浏览器指向<a href="http://localhost:8080/hystrix，输入“" target="_blank" rel="external">http://localhost:8080/hystrix，输入“</a> hystrix.stream”的指标URL并开始监视。</p>
<h3 id="参考："><a href="#参考：" class="headerlink" title="参考："></a>参考：</h3><ul>
<li><a href="https://www.baeldung.com/spring-cloud-netflix-hystrix" target="_blank" rel="external">A Guide to Spring Cloud Netflix – Hystrix</a></li>
</ul>
<hr>
<ul>
<li><a href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%80%E3%80%91">从0开始用SpringCloud搭建微服务系统【一】</a></li>
<li><a href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%8C%E3%80%91/">从0开始用SpringCloud搭建微服务系统【二】</a></li>
<li><a href="https://webfuse.cn/2020/04/25/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%89%E3%80%91/">从0开始用SpringCloud搭建微服务系统【三】</a></li>
<li><a href="https://webfuse.cn/2020/04/27/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E5%9B%9B%E3%80%91/">从0开始用SpringCloud搭建微服务系统【四】</a></li>
<li><a href="https://webfuse.cn/2020/04/30/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%94%E3%80%91/">从0开始用SpringCloud搭建微服务系统【五】</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;p&gt;基本的容错模式有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主动超时：&lt;/li&gt;
&lt;li&gt;限流：限制最大并发数&lt;/li&gt;
&lt;li&gt;熔断：错误数达到阈值时，
    
    </summary>
    
      <category term="Spring" scheme="https://webfuse.cn/categories/Spring/"/>
    
      <category term="Spring Cloud" scheme="https://webfuse.cn/categories/Spring/Spring-Cloud/"/>
    
    
      <category term="微服务" scheme="https://webfuse.cn/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Spring Cloud" scheme="https://webfuse.cn/tags/Spring-Cloud/"/>
    
  </entry>
  
  <entry>
    <title>从0开始用SpringCloud搭建微服务系统【三】</title>
    <link href="https://webfuse.cn/2020/04/25/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%89%E3%80%91/"/>
    <id>https://webfuse.cn/2020/04/25/从0开始用SpringCloud搭建微服务系统【三】/</id>
    <published>2020-04-25T09:38:59.000Z</published>
    <updated>2020-04-30T02:25:26.833Z</updated>
    
    <content type="html"><![CDATA[<h2 id="服务间通信"><a href="#服务间通信" class="headerlink" title="服务间通信"></a>服务间通信</h2><p>微服务间可以使用 HTTP 协议，RESTful 规范进行通信。Spring Cloud 提供了 2 种 RESTful 调用方式：Ribbon 和 Feign 。</p>
<h3 id="Ribbon"><a href="#Ribbon" class="headerlink" title="Ribbon"></a>Ribbon</h3><p>客户端软负载组件，支持Eureka对接，支持多种可插拔LB策略。依赖 <code>spring-cloud-starter-netflix-eureka-client</code> 中已经默认加载了 Ribbon 的依赖。</p>
<p>Ribbon作为Spring Cloud的负载均衡机制的实现：</p>
<ol>
<li>Ribbon可以单独使用，作为一个独立的负载均衡组件。只是需要我们手动配置 服务地址列表。</li>
<li>Ribbon与Eureka配合使用时，Ribbon可自动从Eureka Server获取服务提供者地址列表（DiscoveryClient），并基于负载均衡算法，请求其中一个服务提供者实例。</li>
<li>Ribbon与OpenFeign和RestTemplate进行无缝对接，让二者具有负载均衡的能力。OpenFeign默认集成了ribbon。</li>
</ol>
<p>Ribbon 的自定义配置以及一些高级使用可以参考官方文档：<a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.2.2.RELEASE/reference/html/#spring-cloud-ribbon" target="_blank" rel="external">Client Side Load Balancer: Ribbon</a></p>
<h4 id="Ribbon组成"><a href="#Ribbon组成" class="headerlink" title="Ribbon组成"></a>Ribbon组成</h4><p>官网首页：<a href="https://github.com/Netflix/ribbon" target="_blank" rel="external">https://github.com/Netflix/ribbon</a></p>
<p>ribbon-core: 核心的通用性代码。api一些配置。</p>
<p>ribbon-eureka：基于eureka封装的模块，能快速集成eureka。</p>
<p>ribbon-examples：学习示例。</p>
<p>ribbon-httpclient：基于apache httpClient封装的rest客户端，集成了负载均衡模块，可以直接在项目中使用。</p>
<p>ribbon-loadbalancer：负载均衡模块。</p>
<p>ribbon-transport：基于netty实现多协议的支持。比如http，tcp，udp等。</p>
<h4 id="调用方式"><a href="#调用方式" class="headerlink" title="调用方式"></a>调用方式</h4><h5 id="使用RestTemplate"><a href="#使用RestTemplate" class="headerlink" title="使用RestTemplate"></a>使用RestTemplate</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Configuration</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LoadBalancerClientConfig</span> </span>&#123;</div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 负载均衡的RestTemplate。</div><div class="line">     */</div><div class="line">    <span class="meta">@LoadBalanced</span></div><div class="line">    <span class="meta">@Bean</span>(name = <span class="string">"loadBalanced"</span>)</div><div class="line">    <span class="function">RestTemplate <span class="title">loadBalanced</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">new</span> RestTemplate();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 常规的RestTemplate。</div><div class="line">     */</div><div class="line">    <span class="meta">@Primary</span></div><div class="line">    <span class="meta">@Bean</span>(name = <span class="string">"restTemplate"</span>)</div><div class="line">    <span class="function">RestTemplate <span class="title">restTemplate</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">new</span> RestTemplate();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>以上代码中使用 <code>@LoadBalanced</code> 注解，这样就可以让 RestTemplate 在请求时拥有客户端负载均衡的能力。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="meta">@Autowired</span></div><div class="line"><span class="meta">@Qualifier</span>(<span class="string">"loadBalanced"</span>)</div><div class="line"><span class="keyword">private</span> RestTemplate loadBalanced;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">public</span> String <span class="title">getTime</span><span class="params">()</span> </span>&#123;</div><div class="line">   <span class="keyword">return</span> loadBalanced.getForEntity(<span class="string">"http://service-producer/get_time"</span>, String.class).getBody();</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h5 id="使用DiscoveryClient"><a href="#使用DiscoveryClient" class="headerlink" title="使用DiscoveryClient"></a>使用DiscoveryClient</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="meta">@Autowired</span></div><div class="line"><span class="keyword">private</span> DiscoveryClient discoveryClient;</div><div class="line">   </div><div class="line">   </div><div class="line"><span class="function"><span class="keyword">public</span> List&lt;String&gt; <span class="title">getAllInstance</span><span class="params">(@RequestParam(<span class="string">"service_id"</span>)</span> String serviceId) </span>&#123;</div><div class="line"></div><div class="line">   List&lt;ServiceInstance&gt; instances = discoveryClient.getInstances(serviceId);</div><div class="line">   <span class="keyword">return</span> instances.stream()</div><div class="line">           .map(instance -&gt; String.format(<span class="string">"http://%s:%s"</span>, instance.getHost(), instance.getPort()))</div><div class="line">           .collect(Collectors.toList());</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h4 id="负载均衡算法"><a href="#负载均衡算法" class="headerlink" title="负载均衡算法"></a>负载均衡算法</h4><p>Ribbon 提供了很多负载均衡的策略。详情可见：</p>
<table>
<thead>
<tr>
<th>策略名称</th>
<th>策略描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>BestAvailableRule（最低并发策略）</td>
<td>会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务，然后选择一个并发量最小的服务。逐个找服务，如果断路器打开，则忽略。</td>
</tr>
<tr>
<td>AvailabilityFilteringRule（可用过滤策略）</td>
<td>会先过滤掉多次访问故障而处于断路器跳闸状态的服务和过滤并发的连接数量超过阀值得服务，然后对剩余的服务列表安装轮询策略进行访问。</td>
</tr>
<tr>
<td>WeightedResponseTimeRule（响应时间加权策略）</td>
<td>据平均响应时间计算所有的服务的权重，响应时间越快服务权重越大，容易被选中的概率就越高。刚启动时，如果统计信息不中，则使用RoundRobinRule(轮询)策略，等统计的信息足够了会自动的切换到WeightedResponseTimeRule。响应时间长，权重低，被选择的概率低。反之，同样道理。此策略综合了各种因素（网络，磁盘，IO等），这些因素直接影响响应时间。</td>
</tr>
<tr>
<td>RetryRule（重试策略）</td>
<td>先按照RoundRobinRule(轮询)的策略获取服务，如果获取的服务失败则在指定的时间会进行重试，进行获取可用的服务。如多次获取某个服务失败，就不会再次获取该服务。主要是在一个时间段内，如果选择一个服务不成功，就继续找可用的服务，直到超时。</td>
</tr>
<tr>
<td>RoundRobinRule（轮询策略）</td>
<td>以简单轮询选择一个服务器。按顺序循环选择一个server。</td>
</tr>
<tr>
<td>RandomRule（随机策略）</td>
<td>随机选择一个服务器。</td>
</tr>
<tr>
<td>ZoneAvoidanceRule（区域权衡策略）【默认实现】</td>
<td>复合判断Server所在区域的性能和Server的可用性，轮询选择服务器。</td>
</tr>
</tbody>
</table>
<h4 id="切换负载均衡策略"><a href="#切换负载均衡策略" class="headerlink" title="切换负载均衡策略"></a>切换负载均衡策略</h4><h5 id="注解方式"><a href="#注解方式" class="headerlink" title="注解方式"></a>注解方式</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Configuration</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultRibbonConfig</span> </span>&#123;</div><div class="line">    <span class="meta">@Bean</span></div><div class="line">    <span class="function"><span class="keyword">public</span> IRule <span class="title">ribbonRule</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">new</span> BestAvailableRule();</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>如上，配置IRule的新值，直接可以切换负载均衡策略</p>
<h5 id="配置文件方式"><a href="#配置文件方式" class="headerlink" title="配置文件方式"></a>配置文件方式</h5><p>给所有的服务指定负载均衡策略：</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="attr">ribbon:</span></div><div class="line"><span class="attr">    NIWSServerListClassName:</span> <span class="string">com.netflix.loadbalancer.ConfigurationBasedServerList</span></div><div class="line"><span class="attr">    NFLoadBalancerRuleClassName:</span> <span class="string">com.netflix.loadbalancer.WeightedResponseTimeRule</span></div></pre></td></tr></table></figure>
<p>给特定的服务指定负载均衡策略：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&lt;服务名&gt;.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule</div></pre></td></tr></table></figure>
<h4 id="Ribbon拦截"><a href="#Ribbon拦截" class="headerlink" title="Ribbon拦截"></a>Ribbon拦截</h4><p>在服务调用的时候，我们可能不仅仅是简单地进行调用，会涉及到一些接口的校验、权限的校验等。要实现这些，可以实现<code>ClientHttpRequestInterceptor</code>接口。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LoggingClientHttpRequestInterceptor</span> <span class="keyword">implements</span> <span class="title">ClientHttpRequestInterceptor</span> </span>&#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> ClientHttpResponse <span class="title">intercept</span><span class="params">(HttpRequest httpRequest, <span class="keyword">byte</span>[] bytes, ClientHttpRequestExecution clientHttpRequestExecution)</span> <span class="keyword">throws</span> IOException </span>&#123;</div><div class="line">        System.out.println(<span class="string">"拦截啦！！！"</span>);</div><div class="line">        System.out.println(httpRequest.getURI());</div><div class="line"></div><div class="line">        ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes);</div><div class="line"></div><div class="line">        System.out.println(response.getHeaders());</div><div class="line">        <span class="keyword">return</span> response;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>添加到resttemplate中</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@LoadBalanced</span></div><div class="line"><span class="meta">@Bean</span>(name = <span class="string">"loadBalanced"</span>)</div><div class="line"><span class="meta">@Primary</span></div><div class="line"><span class="function">RestTemplate <span class="title">loadBalanced</span><span class="params">()</span> </span>&#123;</div><div class="line">    RestTemplate restTemplate = <span class="keyword">new</span> RestTemplate();</div><div class="line">    restTemplate.getInterceptors().add(<span class="keyword">new</span> LoggingClientHttpRequestInterceptor());</div><div class="line">    <span class="keyword">return</span> restTemplate;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h4 id="超时"><a href="#超时" class="headerlink" title="超时"></a>超时</h4><p>Ribbon的配置如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">#连接超时时间(ms)</div><div class="line">ribbon.ConnectTimeout=1000</div><div class="line">#业务逻辑超时时间(ms)</div><div class="line">ribbon.ReadTimeout=6000</div></pre></td></tr></table></figure>
<h4 id="重试"><a href="#重试" class="headerlink" title="重试"></a>重试</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">#同一台实例最大重试次数,不包括首次调用</div><div class="line">ribbon.MaxAutoRetries=1</div><div class="line">#重试负载均衡其他的实例最大重试次数,不包括首次调用</div><div class="line">ribbon.MaxAutoRetriesNextServer=1</div><div class="line">#是否所有操作都重试</div><div class="line">ribbon.OkToRetryOnAllOperations=false</div></pre></td></tr></table></figure>
<p>使用ribbon重试机制，请求失败后，每个6秒会重新尝试</p>
<h3 id="Feign"><a href="#Feign" class="headerlink" title="Feign"></a>Feign</h3><p>Feign 是 Netflix 开发的声明式、模板化的 HTTP 客户端，Feign 的使用非常简单，创建一个接口，在接口上加入一些注解，这样就完成了代码开发。</p>
<p>Feign 是一个 Http 请求调用的轻量级框架，可以以 Java 接口注解的方式调用 Http 请求，而不用像 Java 中通过封装 HTTP 请求报文的方式直接调用。通过处理注解，将请求模板化，当实际调用的时候，传入参数，根据参数再应用到请求上，进而转化成真正的请求，这种请求相对而言比较直观。Feign 封装 了HTTP 调用流程，面向接口编程</p>
<h4 id="Spring-Cloud-OpenFeign"><a href="#Spring-Cloud-OpenFeign" class="headerlink" title="Spring Cloud OpenFeign"></a>Spring Cloud OpenFeign</h4><p>Spring Cloud OpenFeign 通过自动配置并绑定到Spring Environment和其他Spring编程模型习惯用法，为Spring Boot应用程序提供OpenFeign集成。</p>
<p>官方文档：<a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-openfeign/2.2.2.RELEASE/reference/html/" target="_blank" rel="external">Spring Cloud OpenFeign</a></p>
<h4 id="服务调用"><a href="#服务调用" class="headerlink" title="服务调用"></a>服务调用</h4><p>使用 Feign 必须引入 <code>spring-cloud-starter-openfeign</code>。在启动类 <code>Application</code> 上 <code>@EnableFeignClients</code> 注解。</p>
<h5 id="服务提供者"><a href="#服务提供者" class="headerlink" title="服务提供者"></a>服务提供者</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@RequestMapping</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">HelloApi</span> </span>&#123;</div><div class="line">    <span class="meta">@GetMapping</span>(value = <span class="string">"/v0.1/greeting"</span>)</div><div class="line">    <span class="function">String <span class="title">greeting</span><span class="params">()</span></span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="meta">@RestController</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloController</span> <span class="keyword">implements</span> <span class="title">HelloApi</span> </span>&#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">greeting</span><span class="params">()</span></span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="string">"hello world"</span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h5 id="服务消费者：简单使用"><a href="#服务消费者：简单使用" class="headerlink" title="服务消费者：简单使用"></a>服务消费者：简单使用</h5><p>简单使用Feign不需要代码耦合，但是需要硬编码接口的信息。如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@FeignClient</span>(value = <span class="string">"producer-service"</span>)</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">HelloClient</span></span>&#123;</div><div class="line">    <span class="meta">@GetMapping</span>(value = <span class="string">"/v0.1/greeting"</span>)</div><div class="line">    <span class="function">String <span class="title">greeting</span><span class="params">()</span></span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="meta">@Service</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestService</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="meta">@Autowired</span></div><div class="line">    HelloClient helloClient;</div><div class="line">    </div><div class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">test</span><span class="params">()</span></span>&#123;</div><div class="line">        helloClient.greeting();</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><code>@FeignClient</code>也可以脱离Eureka使用，如：<code>@FeignClient(name = &quot;xxx&quot;,url=&quot;&quot;)</code> 这个url就是接口的地址。</p>
<h5 id="服务消费者：接口继承方式"><a href="#服务消费者：接口继承方式" class="headerlink" title="服务消费者：接口继承方式"></a>服务消费者：接口继承方式</h5><p>此方法需要引入<strong>服务提供者</strong>提供的接口jar包。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="meta">@FeignClient</span>(value = <span class="string">"producer-service"</span>)</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">HelloService</span> <span class="keyword">extends</span> <span class="title">HelloApi</span> </span>&#123;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="meta">@Service</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestService</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="meta">@Autowired</span></div><div class="line">    HelloService helloService;</div><div class="line">    </div><div class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">test</span><span class="params">()</span></span>&#123;</div><div class="line">        helloService.greeting();</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>以上代码中，<code>@FeignClient(value = &quot;producer-service&quot;)</code> 指定了使用哪一个服务。</p>
<h4 id="高级用法"><a href="#高级用法" class="headerlink" title="高级用法"></a>高级用法</h4><h5 id="自定义配置"><a href="#自定义配置" class="headerlink" title="自定义配置"></a>自定义配置</h5><p>feign的默认配置类是：org.springframework.cloud.openfeign.FeignClientsConfiguration。默认定义了feign使用的编码器，解码器等。</p>
<p>允许使用@FeignClient的configuration的属性自定义Feign配置。自定义的配置优先级高于上面的FeignClientsConfiguration。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Configuration</span></div><div class="line"><span class="meta">@Slf</span>4j</div><div class="line"><span class="meta">@EnableConfigurationProperties</span>(FeignSecurityProperties.class)</div><div class="line"><span class="meta">@ConditionalOnClass</span>(value = &#123;RequestInterceptor.class, Decoder.class, Encoder.class&#125;)</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FeignAutoConfig</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="meta">@Autowired</span></div><div class="line">    <span class="keyword">private</span> FeignSecurityProperties feignSecurityProperties;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 内部微服务请求，加上timestamp，并且按字典序进行签名，附上sig</div><div class="line">     *</div><div class="line">     * <span class="doctag">@return</span> RequestInterceptor 请求拦截器</div><div class="line">     */</div><div class="line">    <span class="meta">@Bean</span></div><div class="line">    <span class="meta">@ConditionalOnMissingBean</span>(RequestInterceptor.class)</div><div class="line">    <span class="function"><span class="keyword">public</span> RequestInterceptor <span class="title">requestTokenBearerInterceptor</span><span class="params">()</span> </span>&#123;</div><div class="line">        String apiSecretKey = feignSecurityProperties.getApiSignature().getInternalApiKey();</div><div class="line">        <span class="keyword">return</span> requestTemplate -&gt; ApiSigUtil.sig(apiSecretKey, requestTemplate);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Bean</span></div><div class="line">    <span class="function"><span class="keyword">public</span> Decoder <span class="title">feignDecoder</span><span class="params">()</span> </span>&#123;</div><div class="line">        HttpMessageConverter jacksonConverter = <span class="keyword">new</span> MappingJackson2HttpMessageConverter(customObjectMapper());</div><div class="line">        ObjectFactory&lt;HttpMessageConverters&gt; objectFactory = () -&gt; <span class="keyword">new</span> HttpMessageConverters(jacksonConverter);</div><div class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ResponseEntityDecoder(<span class="keyword">new</span> SpringDecoder(objectFactory));</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Bean</span></div><div class="line">    <span class="function"><span class="keyword">public</span> Encoder <span class="title">feignEncoder</span><span class="params">()</span> </span>&#123;</div><div class="line">        HttpMessageConverter jacksonConverter = <span class="keyword">new</span> MappingJackson2HttpMessageConverter(customObjectMapper());</div><div class="line">        ObjectFactory&lt;HttpMessageConverters&gt; objectFactory = () -&gt; <span class="keyword">new</span> HttpMessageConverters(jacksonConverter);</div><div class="line">        <span class="keyword">return</span> <span class="keyword">new</span> SpringEncoder(objectFactory);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> ObjectMapper <span class="title">customObjectMapper</span><span class="params">()</span> </span>&#123;</div><div class="line">        ObjectMapper objectMapper = <span class="keyword">new</span> ObjectMapper();</div><div class="line">        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, <span class="keyword">true</span>);</div><div class="line">        <span class="keyword">return</span> objectMapper;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Bean</span></div><div class="line">    <span class="meta">@ConditionalOnMissingBean</span>(Retryer.class)</div><div class="line">    <span class="function"><span class="keyword">public</span> Retryer <span class="title">feignRetryer</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> Retryer.NEVER_RETRY;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Bean</span></div><div class="line">    <span class="meta">@ConditionalOnMissingBean</span>(Request.Options.class)</div><div class="line">    Request.<span class="function">Options <span class="title">feignOptions</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Request.Options(<span class="number">30</span> * <span class="number">1000</span>, <span class="number">30</span> * <span class="number">1000</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>如上面所示，<strong>在配置类上加上@Configuration注解，且该类在@ComponentScan所扫描的包中，那么该类中的配置信息就会被所有的@FeignClient共享。</strong>如果想要对某些的@FeignClient添加指定的配置，则：不指定@Configuration注解（或者指定configuration，用注解忽略），而是手动使用：<code>@FeignClient(name = &quot;service-valuation&quot;,configuration = FeignAuthConfiguration.class)</code></p>
<h5 id="拦截器"><a href="#拦截器" class="headerlink" title="拦截器"></a>拦截器</h5><p>上面代码中：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Bean</span></div><div class="line">    <span class="meta">@ConditionalOnMissingBean</span>(RequestInterceptor.class)</div><div class="line">    <span class="function"><span class="keyword">public</span> RequestInterceptor <span class="title">requestTokenBearerInterceptor</span><span class="params">()</span> </span>&#123;</div><div class="line">        String apiSecretKey = feignSecurityProperties.getApiSignature().getInternalApiKey();</div><div class="line">        <span class="keyword">return</span> requestTemplate -&gt; ApiSigUtil.sig(apiSecretKey, requestTemplate);</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p><code>RequestInterceptor</code>是一个请求拦截器，我们可以继承它做很多事情，如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> feign.RequestInterceptor;</div><div class="line"><span class="keyword">import</span> feign.RequestTemplate;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyBasicAuthRequestInterceptor</span> <span class="keyword">implements</span> <span class="title">RequestInterceptor</span> </span>&#123;</div><div class="line"></div><div class="line">	<span class="meta">@Override</span></div><div class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">apply</span><span class="params">(RequestTemplate template)</span> </span>&#123;</div><div class="line">		template.header(<span class="string">"Authorization"</span>, <span class="string">"Basic cm9vdDpyb290"</span>);</div><div class="line">	&#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<blockquote>
<p>如果是BasicAuth认证，可以重写BasicAuthRequestInterceptor。</p>
</blockquote>
<p>然后，在配置文件中添加：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="attr">feign:</span></div><div class="line"><span class="attr">  client:</span> </div><div class="line"><span class="attr">    config:</span>  </div><div class="line"><span class="attr">      service-valuation:</span> </div><div class="line"><span class="attr">        request-interceptors:</span></div><div class="line"><span class="bullet">        -</span> <span class="string">cn.webfuse.passenger.feign.interceptor.MyBasicAuthRequestInterceptor</span></div></pre></td></tr></table></figure>
<h5 id="配置文件扩展"><a href="#配置文件扩展" class="headerlink" title="配置文件扩展"></a>配置文件扩展</h5><p>指定服务名配置：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="attr">feign:</span></div><div class="line"><span class="attr">  client:</span> </div><div class="line"><span class="attr">    config:</span>  </div><div class="line"><span class="attr">      service-valuation:</span> </div><div class="line"><span class="attr">        connect-timeout:</span> <span class="number">5000</span></div><div class="line"><span class="attr">        read-timeout:</span> <span class="number">5000</span></div><div class="line"><span class="attr">        logger-level:</span> <span class="string">full</span></div></pre></td></tr></table></figure>
<p>通用配置:</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="attr">feign:</span></div><div class="line"><span class="attr">     client:</span> </div><div class="line"><span class="attr">       config:</span>  </div><div class="line"><span class="attr">         default:</span> </div><div class="line"><span class="attr">           connect-timeout:</span> <span class="number">5000</span></div><div class="line"><span class="attr">           read-timeout:</span> <span class="number">5000</span></div><div class="line"><span class="attr">           logger-level:</span> <span class="string">full</span></div></pre></td></tr></table></figure>
<p>属性配置比Java代码优先级高。也可通过配置设置java代码优先级高：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="attr">feign:</span></div><div class="line">	<span class="attr">client:</span> </div><div class="line">		<span class="attr">default-to-properties:</span> <span class="literal">false</span></div></pre></td></tr></table></figure>
<h5 id="压缩"><a href="#压缩" class="headerlink" title="压缩"></a>压缩</h5><figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="attr">feign:</span></div><div class="line"><span class="attr">  compression:</span>  <span class="comment"># 开启请求与响应的GZIP压缩</span></div><div class="line"><span class="attr">    request:</span></div><div class="line"><span class="attr">      enabled:</span> <span class="literal">true</span></div><div class="line"><span class="attr">      min-request-size:</span> <span class="number">10000</span> <span class="comment"># 单位是B</span></div><div class="line"><span class="attr">    response:</span></div><div class="line"><span class="attr">      enabled:</span> <span class="literal">true</span></div></pre></td></tr></table></figure>
<h5 id="超时-1"><a href="#超时-1" class="headerlink" title="超时"></a>超时</h5><p>Feign默认支持Ribbon；Ribbon的重试机制和Feign的重试机制有冲突，所以源码中默认关闭Feign的重试机制,使用Ribbon的重试机制。</p>
<h5 id="保留原始异常信息"><a href="#保留原始异常信息" class="headerlink" title="保留原始异常信息"></a>保留原始异常信息</h5><p>当调用服务时，如果服务返回的状态码不是200，就会进入到<code>Feign</code>的<code>ErrorDecoder</code>中，因此如果我们要解析异常信息，就要重写<code>ErrorDecoder</code></p>
<h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h4><ol>
<li>主程序入口添加@EnableFeignClients注解开启对Feign Client扫描加载处理。根据Feign Client的开发规范，定义接口并加@FeignClient注解。</li>
<li>当程序启动时，会进行包扫描，扫描所有@FeignClient注解的类，并将这些信息注入Spring IoC容器中。当定义的Feign接口中的方法被调用时，通过JDK的代理方式，来生成具体的RequestTemplate。当生成代理时，Feign会为每个接口方法创建一个RequestTemplate对象，该对象封装了HTTP请求需要的全部信息，如请求参数名、请求方法等信息都在这个过程中确定。</li>
<li>然后由RequestTemplate生成Request，然后把这个Request交给client处理，这里指的Client可以是JDK原生的URLConnection、Apache的Http Client，也可以是Okhttp。最后Client被封装到LoadBalanceClient类，这个类结合Ribbon负载均衡发起服务之间的调用。</li>
</ol>
<h3 id="参考资源"><a href="#参考资源" class="headerlink" title="参考资源"></a>参考资源</h3><ul>
<li><a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.1.0.RELEASE/single/spring-cloud-netflix.html#_service_discovery_eureka_clients" target="_blank" rel="external">Service Discovery: Eureka Clients</a></li>
<li><a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.1.0.RELEASE/single/spring-cloud-netflix.html#spring-cloud-eureka-server" target="_blank" rel="external">Service Discovery: Eureka Server</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/24829766" target="_blank" rel="external">深度剖析服务发现组件Netflix Eureka</a></li>
<li><a href="https://blog.csdn.net/forezp/article/details/73017664" target="_blank" rel="external">深入理解Eureka之源码解析</a></li>
<li><a href="https://blog.csdn.net/forezp/article/details/74820899" target="_blank" rel="external">深入理解Ribbon之源码解析</a></li>
<li><a href="https://blog.csdn.net/forezp/article/details/73480304" target="_blank" rel="external">深入理解Feign之源码解析</a></li>
</ul>
<hr>
<ul>
<li><a href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%80%E3%80%91">从0开始用SpringCloud搭建微服务系统【一】</a></li>
<li><a href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%8C%E3%80%91/">从0开始用SpringCloud搭建微服务系统【二】</a></li>
<li><a href="https://webfuse.cn/2020/04/25/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%89%E3%80%91/">从0开始用SpringCloud搭建微服务系统【三】</a></li>
<li><a href="https://webfuse.cn/2020/04/27/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E5%9B%9B%E3%80%91/">从0开始用SpringCloud搭建微服务系统【四】</a></li>
<li><a href="https://webfuse.cn/2020/04/30/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%94%E3%80%91/">从0开始用SpringCloud搭建微服务系统【五】</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;p&gt;微服务间可以使用 HTTP 协议，RESTful 规范进行通信。Spring Cloud 提供了 2 种 RESTf
    
    </summary>
    
      <category term="Spring" scheme="https://webfuse.cn/categories/Spring/"/>
    
      <category term="Spring Cloud" scheme="https://webfuse.cn/categories/Spring/Spring-Cloud/"/>
    
    
      <category term="微服务" scheme="https://webfuse.cn/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Spring Cloud" scheme="https://webfuse.cn/tags/Spring-Cloud/"/>
    
  </entry>
  
  <entry>
    <title>从0开始用SpringCloud搭建微服务系统【二】</title>
    <link href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%8C%E3%80%91/"/>
    <id>https://webfuse.cn/2020/04/22/从0开始用SpringCloud搭建微服务系统【二】/</id>
    <published>2020-04-22T12:22:35.000Z</published>
    <updated>2020-04-30T02:25:15.091Z</updated>
    
    <content type="html"><![CDATA[<h2 id="服务注册与服务发现"><a href="#服务注册与服务发现" class="headerlink" title="服务注册与服务发现"></a>服务注册与服务发现</h2><ol>
<li><p>背景：在传统应用中，组件之间的调用，通过有规范的约束的接口来实现，从而实现不同模块间良好的协作。但是被拆分成微服务后，每个微服务实例的网络地址都可能动态变化，数量也会变化，使得原来硬编码的地址失去了作用。需要一个中心化的组件来进行服务的登记和管理。</p>
</li>
<li><p>概念：实现服务治理，即管理所有的服务信息和状态。</p>
</li>
<li><p>注册中心好处：不用关心有多少提供方。</p>
</li>
<li><p>注册中心有哪些: Eureka，Nacos，Consul，Zookeeper等。</p>
</li>
<li><p>服务注册与发现包括两部分，一个是服务器端，另一个是客户端。</p>
<ol>
<li>服务端（Server）是一个公共服务，为Client提供服务注册和发现的功能，维护注册到自身的Client的相关信息，同时提供接口给Client获取注册表中其他服务的信息，使得动态变化的Client能够进行服务间的相互调用。</li>
<li>客户端（Client） 将自己的服务信息通过一定的方式登记到 Server上，并在正常范围内维护自己信息一致性，方便其他服务发现自己，同时可以通过Server获取到自己依赖的其他服务信息，完成服务调用，还内置了负载均衡器，用来进行基本的负载均衡。</li>
</ol>
</li>
</ol>
<h3 id="服务端和客户端功能"><a href="#服务端和客户端功能" class="headerlink" title="服务端和客户端功能"></a>服务端和客户端功能</h3><h4 id="server注册中心功能"><a href="#server注册中心功能" class="headerlink" title="server注册中心功能"></a>server注册中心功能</h4><ol>
<li>服务注册表：记录各个微服务信息，例如服务名称，ip，端口等。注册表提供查询 API（查询可用的微服务实例）和管理 API（用于服务的注册和注销）。</li>
<li>服务注册与发现：<ol>
<li>注册：将微服务信息注册到注册中心。</li>
<li>发现：查询可用微服务列表及其网络地址。</li>
</ol>
</li>
<li>服务检查：定时检测已注册的服务，如发现某实例长时间无法访问，就从注册表中移除。</li>
</ol>
<h4 id="client功能"><a href="#client功能" class="headerlink" title="client功能"></a>client功能</h4><ol>
<li>注册：每个微服务启动时，将自己的网络地址等信息注册到注册中心，注册中心会存储（内存中）这些信息。</li>
<li>获取服务注册表：服务消费者从注册中心，查询服务提供者的网络地址，并使用该地址调用服务提供者，为了避免每次都查注册表信息，所以 client 会定时去 serve r拉取注册表信息到缓存到 client 本地。</li>
<li>心跳：各个微服务与注册中心通过某种机制（心跳）通信，若注册中心长时间和服务间没有通信，就会注销该实例。</li>
<li>调用：实际的服务调用，通过注册表，解析服务名和具体地址的对应关系，找到具体服务的地址，进行实际调用。</li>
</ol>
<h3 id="Eureka"><a href="#Eureka" class="headerlink" title="Eureka"></a>Eureka</h3><ul>
<li>Eureka 是 Netflix 开源的一个 Restful 服务，主要用于服务的注册发现。它由两个组件组成：Eureka 服务器 和Eureka 客户端。</li>
<li>Eureka 服务器用作服务注册服务器。各个节点启动后，会在 Eureka Server 中进行注册，这样 Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息。</li>
<li>Eureka 客户端是一个 Java 客户端，用来简化与服务器的交互，作为轮询负载均衡器，发现相关的服务，并提供服务的故障切换。</li>
<li><strong>Eureka 在设计时就优先保证可用性。</strong>即在 CAP 理论中，Eureka 满足 AP。Eureka 各个节点都是平等的，几个节点挂掉不会影响正常节点的工作，剩余的节点依然可以提供注册和查询服务。</li>
<li>Eureka 的几个时间点：<ul>
<li>在应用启动后，将会向 Eureka Server 发送心跳,默认周期为 30 秒。</li>
<li>如果 Eureka Server 在多个心跳周期内没有接收到某个节点的心跳，Eureka Server 将会从服务注册表中把这个服务节点移除(默认90秒)。</li>
<li>Eureka Client 对已经获取到的注册信息做了 30s 缓存。即服务通过 Eureka 客户端第一次查询到可用服务地址后会将结果缓存，下次再调用时就不会真正向 Eureka 发起 HTTP 请求了。</li>
<li>负载均衡组件 Ribbon 也有 30s 缓存。Ribbon 会从上面提到的 Eureka Client 获取服务列表，然后将结果缓存 30s。</li>
<li><strong>最大可能出现2分钟的延迟</strong>：注册延迟30s + Eureka服务器响应延迟30s + Eureka客户端更新延迟30s + Ribbon服务列表更新延迟30s。</li>
</ul>
</li>
<li><p>Eureka 启动保护机制：</p>
<ul>
<li><p>如果在 15 分钟内超过 85% 的节点都没有正常的心跳，那 么Eureka 就认为客户端与注册中心出现了网络故障。</p>
</li>
<li><p>自我保护机制的触发条件： </p>
<p>  条件：当每分钟心跳次数( renewsLastMin ) 小于 numberOfRenewsPerMinThreshold 时，并且开启自动保护模式开关( eureka.server.enable-self-preservation = true ) 时，触发自我保护机制，不再自动过期续约。</p>
<p>  其中：</p>
<ol>
<li><p>numberOfRenewsPerMinThreshold = expectedNumberOfRenewsPerMin * 续租百分比( eureka.server.renewalPercentThreshold, 默认0.85 ) </p>
</li>
<li><p>expectedNumberOfRenewsPerMin = 当前注册的应用实例数 x 2 。为什么乘以 2： 默认情况下，注册的应用实例每半分钟续租一次，那么一分钟心跳两次，因此 x 2 。</p>
<p>解释：服务实例数10个，期望每分钟续约数10 <em> 2=20，期望阈值20</em>0.85=17，自我保护少于17时 触发。】</p>
</li>
</ol>
</li>
<li><p>Eureka 启动保护机制会出现以下情况：</p>
<ul>
<li>Eureka 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。</li>
<li>Eureka 仍然能够接受新服务的注册和查询请求，但是不会被同步到其它节点上(即保证当前节点依然可用)。</li>
<li>当网络稳定时，当前实例新的注册信息会被同步到其它节点中。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="架构原理图"><a href="#架构原理图" class="headerlink" title="架构原理图"></a>架构原理图</h4><img src="/2020/04/22/从0开始用SpringCloud搭建微服务系统【二】/eureka架构图.png" alt="eureka架构图.png" title="">
<h4 id="Eureka-Server"><a href="#Eureka-Server" class="headerlink" title="Eureka Server"></a>Eureka Server</h4><p>引入依赖 <code>spring-cloud-starter-netflix-eureka-server</code>， 在启动类 <code>Application</code> 上，添加 <code>@EnableEurekaServer</code> 注解。</p>
<p>如果是单机服务，可以在 <code>application.yml</code> 中使用以下配置：</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="attr">eureka:</span></div><div class="line"><span class="attr">  environment:</span> <span class="string">dev</span>  <span class="comment"># 设置环境，可选</span></div><div class="line"><span class="attr">  server:</span></div><div class="line"><span class="attr">    enable-self-preservation:</span> <span class="literal">false</span>  <span class="comment"># 中小规模下，自我保护模式坑比好处多，所以关闭它</span></div><div class="line"><span class="attr">    renewal-threshold-update-interval-ms:</span> <span class="number">120000</span>  <span class="comment"># 心跳阈值计算周期，如果开启自我保护模式，可以改一下这个配置</span></div><div class="line"><span class="attr">    eviction-interval-timer-in-ms:</span> <span class="number">5000</span> <span class="comment"># 主动失效检测间隔,配置成5秒</span></div><div class="line"><span class="attr">    use-read-only-response-cache:</span> <span class="literal">false</span> <span class="comment"># 禁用readOnlyCacheMap</span></div><div class="line"><span class="attr">    wait-time-in-ms-when-sync-empty:</span> <span class="number">0</span>  <span class="comment">#在Eureka服务器获取不到集群里对等服务器上的实例时，需要等待的时间，单机模式设置为0</span></div><div class="line"><span class="attr">  client:</span></div><div class="line"><span class="attr">    healthcheck:</span> <span class="literal">true</span></div><div class="line"><span class="attr">    service-url:</span></div><div class="line"><span class="attr">      defaultZone:</span> <span class="attr">http://$&#123;webfuse-security.user.name&#125;:$&#123;webfuse-security.user.password&#125;@localhost:2000/eureka/</span></div><div class="line"><span class="attr">    registry-fetch-interval-seconds:</span> <span class="number">5</span>  <span class="comment"># 定时刷新本地缓存时间</span></div><div class="line"><span class="attr">    register-with-eureka:</span> <span class="literal">false</span> <span class="comment">#表示是否将自己注册到Eureka Server，默认为true。由于当前这个应用就是Eureka Server，故而设为false。</span></div><div class="line"><span class="attr">    fetch-registry:</span> <span class="literal">false</span> <span class="comment">#表示是否从Eureka Server获取注册信息，默认为true。因为这是一个单点的Eureka Server，不需要同步其他的Eureka Server节点的数据，故而设为false。</span></div><div class="line"><span class="attr">  instance:</span></div><div class="line"><span class="attr">    hostname:</span> <span class="string">$&#123;hostname:localhost&#125;</span></div><div class="line"><span class="attr">    instance-id:</span> <span class="string">$&#123;spring.application.name&#125;@$&#123;spring.cloud.client.ip-address&#125;:$&#123;server.port&#125;</span>    <span class="comment"># 自定义实例ID</span></div><div class="line"><span class="attr">    prefer-ip-address:</span> <span class="literal">true</span></div><div class="line"><span class="attr">    lease-expiration-duration-in-seconds:</span> <span class="number">10</span>  <span class="comment">#  没有心跳的淘汰时间，10秒</span></div><div class="line"><span class="attr">    lease-renewal-interval-in-seconds:</span> <span class="number">5</span>  <span class="comment"># 心跳间隔，5秒</span></div></pre></td></tr></table></figure>
<p>启动服务，可在 <code>http://localhost:2000</code> 查看项目页面。</p>
<h4 id="Eureka-Client"><a href="#Eureka-Client" class="headerlink" title="Eureka Client"></a>Eureka Client</h4><p>EurekaClient 可以在客户端获取eureka服务器上的注册者信息。</p>
<p>引入依赖 <code>spring-cloud-starter-netflix-eureka-client</code>, 在启动类 <code>Application</code> 上，添加 <code>@EnableDiscoveryClient</code> 注解。</p>
<p>在 <code>application.yml</code> 中使用以下配置：</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="attr">eureka:</span></div><div class="line"><span class="attr">  environment:</span> <span class="string">dev</span></div><div class="line"><span class="attr">  client:</span></div><div class="line"><span class="attr">    healthcheck:</span></div><div class="line"><span class="attr">      enabled:</span> <span class="literal">true</span></div><div class="line"><span class="attr">    service-url:</span></div><div class="line"><span class="attr">      defaultZone:</span> <span class="attr">http://user:password@192.168.1.16:8010/eureka/</span></div><div class="line"><span class="attr">    registry-fetch-interval-seconds:</span> <span class="number">5</span>  <span class="comment"># 定时刷新本地缓存时间</span></div><div class="line"><span class="attr">  instance:</span></div><div class="line"><span class="attr">    hostname:</span> <span class="string">$&#123;hostname:localhost&#125;</span></div><div class="line"><span class="attr">    instance-id:</span> <span class="string">$&#123;spring.application.name&#125;@$&#123;spring.cloud.client.ip-address&#125;:$&#123;server.port&#125;</span>    <span class="comment"># 自定义实例ID</span></div><div class="line"><span class="attr">    prefer-ip-address:</span> <span class="literal">true</span></div><div class="line"><span class="attr">    lease-expiration-duration-in-seconds:</span> <span class="number">10</span>  <span class="comment">#  没有心跳的淘汰时间，10秒</span></div><div class="line"><span class="attr">    lease-renewal-interval-in-seconds:</span> <span class="number">5</span>  <span class="comment"># 心跳间隔，5秒</span></div></pre></td></tr></table></figure>
<p>启动项目即可。可在 <code>http://localhost:2000</code> 中看到注册的状态。</p>
<h4 id="Eureka-高可用"><a href="#Eureka-高可用" class="headerlink" title="Eureka 高可用"></a>Eureka 高可用</h4><p>Eureka Server 之间是可以互相注册的。</p>
<p>举个例子，我们有 3 个 Eureka 注册中心，端口分别为 2001 、 2002 和 2003 。那么端口为 2001 的最基本的配置如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">eureka:</div><div class="line">  client:</div><div class="line">    service-url:</div><div class="line">      defaultZone: http://localhost:2002/eureka/,http://localhost:2003/eureka/</div></pre></td></tr></table></figure>
<p>端口 2002 和 2003 服务可以根据以上规则配置。</p>
<h4 id="多网卡选择"><a href="#多网卡选择" class="headerlink" title="多网卡选择"></a>多网卡选择</h4><p>服务器有多个网卡，eh0，eh1，eh2，只有eh0可以让外部其他服务访问进来，而Eureka client将eh1和eh2注册到Eureka server上，这样其他服务就无法访问该微服务了。有两种方式：</p>
<ul>
<li><p>指定IP注册</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="attr">eureka:</span></div><div class="line"><span class="attr">  instance:</span></div><div class="line"><span class="attr">    prefer-ip-address:</span> <span class="literal">true</span></div><div class="line"><span class="attr">    ip-address:</span> <span class="string">实际能访问到的IP</span></div></pre></td></tr></table></figure>
</li>
<li><p>使用<strong>spring.cloud.inetutils</strong>配置网卡选择</p>
</li>
</ul>
<h4 id="Eureka健康检查"><a href="#Eureka健康检查" class="headerlink" title="Eureka健康检查"></a>Eureka健康检查</h4><p>由于server和client通过心跳保持 服务状态，而只有状态为UP的服务才能被访问。看eureka界面中的status。</p>
<p>比如心跳一直正常，服务一直UP，但是此服务DB连不上了，无法正常提供服务。</p>
<p>此时，我们需要将 微服务的健康状态也同步到server。只需要启动eureka的健康检查就行。这样微服务就会将自己的健康状态同步到eureka。配置如下即可。</p>
<p>在client端加入Actuator，并配置<code>eureka.client.healthcheck.enabled=true</code>，将自己真正的健康状态传播到server。</p>
<p>通过代码来改动服务的状态：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="meta">@Data</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HealthStatusHandler</span> <span class="keyword">implements</span> <span class="title">HealthIndicator</span> </span>&#123;</div><div class="line">    <span class="keyword">private</span> Boolean status = <span class="keyword">true</span>;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> Health <span class="title">health</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">if</span> (status) &#123;</div><div class="line">            <span class="keyword">return</span> <span class="keyword">new</span> Health.Builder().up().build();</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Health.Builder().down().build();</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>应用场景：比如说短信业务，欠费了等情况，可以暂时下线服务。</p>
<h4 id="Eureka遇到的坑"><a href="#Eureka遇到的坑" class="headerlink" title="Eureka遇到的坑"></a>Eureka遇到的坑</h4><h5 id="available-replicas为空的问题"><a href="#available-replicas为空的问题" class="headerlink" title="available-replicas为空的问题"></a>available-replicas为空的问题</h5><p>如果使用了<code>eureka.instance.prefer-ip-address: true</code>，然后<code>eureka.client.service-url.defaultZone</code>配置的IP与实例IP不一致，会出现available-replicas为空的问题。</p>
<p>解决方法：在<code>eureka.instance.ip-address</code>中强制设置IP，然后在<code>eureka.client.service-url.defaultZone</code>配置对应的IP。</p>
<h4 id="Eureka-最佳实践"><a href="#Eureka-最佳实践" class="headerlink" title="Eureka 最佳实践"></a>Eureka 最佳实践</h4><p>本人所在的公司由于体量小，<strong>生产环境直接使用 Eureka 的默认配置进行高可用性运行</strong>，目前也没有出现太大的问题。</p>
<p>以下是一些实践参考文章（注意：文章中的版本号不是最新的，可能在配置上会有调整）：</p>
<ul>
<li><a href="http://www.itmuch.com/spring-cloud-sum-eureka/" target="_blank" rel="external">Spring Cloud中，Eureka常见问题总结</a></li>
<li><a href="https://github.com/spring-cloud/spring-cloud-netflix/issues/203" target="_blank" rel="external">Eureka Clustering documentation and best practices</a></li>
<li><a href="https://github.com/spring-cloud/spring-cloud-netflix/issues/373" target="_blank" rel="external">Documentation: changing Eureka renewal frequency <em>WILL</em> break the self-preservation feature of the server</a></li>
</ul>
<hr>
<ul>
<li><a href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%80%E3%80%91">从0开始用SpringCloud搭建微服务系统【一】</a></li>
<li><a href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%8C%E3%80%91/">从0开始用SpringCloud搭建微服务系统【二】</a></li>
<li><a href="https://webfuse.cn/2020/04/25/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%89%E3%80%91/">从0开始用SpringCloud搭建微服务系统【三】</a></li>
<li><a href="https://webfuse.cn/2020/04/27/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E5%9B%9B%E3%80%91/">从0开始用SpringCloud搭建微服务系统【四】</a></li>
<li><a href="https://webfuse.cn/2020/04/30/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%94%E3%80%91/">从0开始用SpringCloud搭建微服务系统【五】</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;p&gt;背景：在传统应用中，组件之间的调用，通过有规范的约束的接口来实现，
    
    </summary>
    
      <category term="Spring" scheme="https://webfuse.cn/categories/Spring/"/>
    
      <category term="Spring Cloud" scheme="https://webfuse.cn/categories/Spring/Spring-Cloud/"/>
    
    
      <category term="微服务" scheme="https://webfuse.cn/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Spring Cloud" scheme="https://webfuse.cn/tags/Spring-Cloud/"/>
    
  </entry>
  
  <entry>
    <title>从0开始用SpringCloud搭建微服务系统【一】</title>
    <link href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%80%E3%80%91/"/>
    <id>https://webfuse.cn/2020/04/22/从0开始用SpringCloud搭建微服务系统【一】/</id>
    <published>2020-04-22T07:58:34.000Z</published>
    <updated>2020-04-30T02:24:56.445Z</updated>
    
    <content type="html"><![CDATA[<h2 id="微服务与-Spring-Cloud"><a href="#微服务与-Spring-Cloud" class="headerlink" title="微服务与 Spring Cloud"></a>微服务与 Spring Cloud</h2><h3 id="微服务"><a href="#微服务" class="headerlink" title="微服务"></a>微服务</h3><h4 id="从单体到微服务"><a href="#从单体到微服务" class="headerlink" title="从单体到微服务"></a>从单体到微服务</h4><h5 id="单体服务"><a href="#单体服务" class="headerlink" title="单体服务"></a>单体服务</h5><ul>
<li><p>概念：所有功能全部打包在一起，也就是全部的功能都在一个应用包中。应用大部分是一个 war 包或 jar 包。</p>
</li>
<li><p>优点：容易开发、测试、部署，适合项目初期试错。</p>
</li>
<li><p>缺点：</p>
<p> 随着项目越来越复杂，团队不断扩大。坏处就显现出来了。</p>
<ul>
<li>复杂性高：代码多，十万行，百万行级别。加一个小功能，会带来其他功能的隐患，因为它们在一起。</li>
<li>技术债务：人员流动，不坏不修，因为不敢修。</li>
<li>持续部署困难：由于是全量应用，改一个小功能，全部部署，会导致无关的功能暂停使用。编译部署上线耗时长，不敢随便部署，导致部署频率低，进而又导致两次部署之间 功能修改多，越不敢部署，恶性循环。</li>
<li>可靠性差：某个小问题，比如小功能出现 OOM，会导致整个应用崩溃。</li>
<li>扩展受限：只能整体扩展，无法按照需要进行扩展， 不能根据计算密集型（派单系统）和IO密集型（文件服务） 进行合适的区分。</li>
<li>阻碍创新：单体应用是以一种技术解决所有问题，不容易引入新技术。但在高速的互联网发展过程中，适应的潮流是：用合适的语言做合适的事情。比如在单体应用中，一个项目用 spring MVC，想换成s pring boot，切换成本很高，因为有可能10万，百万行代码都要改。</li>
</ul>
</li>
</ul>
<h5 id="微服务-1"><a href="#微服务-1" class="headerlink" title="微服务"></a>微服务</h5><ul>
<li><p><a href="https://martinfowler.com/" target="_blank" rel="external">Martin Fowler</a> 在 2014年提出 <a href="https://www.martinfowler.com/articles/microservices.html" target="_blank" rel="external">Microservices架构</a></p>
</li>
<li><p>微服务是一种架构风格，将单体应用划分为小型的服务单元。</p>
</li>
<li><p>微服务架构是一种使用一系列粒度较小的服务来开发单个应用的方式。</p>
<ul>
<li>每个服务运行在自己的进程中</li>
<li>服务间采用轻量级的方式进行通信（通常是HTTP API）</li>
<li>这些服务是基于业务逻辑和范围，通过自动化部署的机制来独立部署的，并且服务的集中管理应该是最低限度的，即每个服务可以采用不同的编程语言编写，使用不同的数据存储技术。</li>
</ul>
</li>
<li><p>优点：</p>
<ul>
<li>独立部署。不依赖其他服务，耦合性低，不用管其他服务的部署对自己的影响。</li>
<li>易于开发和维护。关注特定业务，所以业务清晰，代码量少，模块变的易开发、易理解、易维护。</li>
<li>启动快。功能少，代码少，所以启动快，有需要停机维护的服务，不会长时间暂停服务。</li>
<li>局部修改容易。只需要部署 相应的服务即可，适合敏捷开发。</li>
<li>技术栈不受限。java，node.js，go 等</li>
<li>按需伸缩.某个服务受限，可以按需增加内存，cpu 等。</li>
<li>职责专一。专门团队负责专门业务，有利于团队分工。</li>
<li>代码复用。不需要重复写。底层实现通过接口方式提供。</li>
<li>便于团队协作：每个团队只需要提供 API 就行，定义好 API 后，可以并行开发。</li>
</ul>
</li>
<li>缺点：<ul>
<li>分布式固有的复杂性。容错，网络延时，调用关系、分布式事务等，都会带来复杂。</li>
<li>分布式事务的挑战。每个服务有自己的数据库，有点在于不同服务可以选择适合自身业务的数据库。订单用MySQL，评论用Mongodb等。目前最理想解决方案是：柔性事务的最终一致性。</li>
<li>接口调整成本高。改一个接口，调用方都要改。</li>
<li>测试难度提升。一个接口改变，所有调用方都得测。自动化测试就变得重要了，API文档的管理也尤为重要。</li>
<li>运维要求高。需要维护几十上百个服务，监控变的复杂，并且还要关注多个集群。</li>
<li>重复工作。比如 java 的工具类可以在共享 common.ja r中，但在多语言下行不通，C++ 无法直接用 java的 jar 包。</li>
</ul>
</li>
</ul>
<h4 id="微服务组件"><a href="#微服务组件" class="headerlink" title="微服务组件"></a>微服务组件</h4><p>基于微服务的特性，微服务的组件不局限于技术的实现。主要的组件有：</p>
<ul>
<li>服务注册与发现：服务提供方将己方调用地址注册到服务注册中心，让服务调用方能够方便地找到自己；服务调用方从服务注册中心找到自己需要调用的服务的地址。</li>
<li>负载均衡：服务提供方一般以多实例的形式提供服务，负载均衡功能能够让服务调用方连接到合适的服务节点。并且，服务节点选择的过程对服务调用方来说是透明的。</li>
<li>服务网关：服务网关是服务调用的唯一入口，可以在这个组件中实现用户鉴权、动态路由、灰度发布、A/B测试、负载限流等功能。</li>
<li>配置中心：将本地化的配置信息（Properties、XML、YAML等形式）注册到配置中心，实现程序包在开发、测试、生产环境中的无差别性，方便程序包的迁移，也是无状态特性。</li>
<li>集成框架：微服务组件都以职责单一的程序包对外提供服务，集成框架以配置的形式将所有微服务组件(特别是管理端组件)集成到统一的界面框架下，让用户能够在统一的界面中使用系统。Spring Cloud 就是一个集成框架。</li>
<li>调用链监控：记录完成一次请求的先后衔接和调用关系，并将这种串行或并行的调用关系展示出来。在系统出错时，可以方便地找到出错点。</li>
<li>支撑平台：系统微服务化后，各个业务模块经过拆分变得更加细化，系统的部署、运维、监控等都比单体应用架构更加复杂，这就需要将大部分的工作自动化。现在，Docker 等工具可以给微服务架构的部署带来较多的便利，例如持续集成、蓝绿发布、健康检查、性能监控等等。如果没有合适的支撑平台或工具，微服务架构就无法发挥它最大的功效。</li>
</ul>
<h5 id="常见的架构图"><a href="#常见的架构图" class="headerlink" title="常见的架构图"></a>常见的架构图</h5><img src="/2020/04/22/从0开始用SpringCloud搭建微服务系统【一】/基础微服务架构图.png" alt="基础微服务架构图.png" title="">
<h4 id="Spring-Cloud"><a href="#Spring-Cloud" class="headerlink" title="Spring Cloud"></a>Spring Cloud</h4><p>Spring Cloud 是实现微服务架构的一系列框架的有机集合。是在 Spring Boot 基础上构建的，用于简化分布式系统构建的工具集。是拥有众多子项目的项目集合。利用 Spring Boot 的开发便利性，巧妙地简化了分布式系统基础设施（服务注册与发现、熔断机制、网关路由、配置中心、消息总线、负载均衡、链路追踪等）的开发。</p>
<h6 id="Spring-Cloud的基础组件"><a href="#Spring-Cloud的基础组件" class="headerlink" title="Spring Cloud的基础组件"></a>Spring Cloud的基础组件</h6><ul>
<li><p>Eureka：服务注册与发现，用于服务管理。</p>
</li>
<li><p>Feign： web调用客户端，能够简化HTTP接口的调用。</p>
</li>
<li><p>Ribbon：基于客户端的负载均衡。</p>
</li>
<li><p>Hystrix：熔断降级，防止服务雪崩。</p>
</li>
<li><p>Zuul：网关路由，提供路由转发、请求过滤、限流降级等功能。</p>
</li>
<li><p>Config：配置中心，分布式配置管理。</p>
</li>
<li><p>Sleuth：服务链路追踪</p>
</li>
<li><p>Admin：健康管理</p>
</li>
</ul>
<hr>
<ul>
<li><a href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%80%E3%80%91">从0开始用SpringCloud搭建微服务系统【一】</a></li>
<li><a href="https://webfuse.cn/2020/04/22/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%8C%E3%80%91/">从0开始用SpringCloud搭建微服务系统【二】</a></li>
<li><a href="https://webfuse.cn/2020/04/25/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%B8%89%E3%80%91/">从0开始用SpringCloud搭建微服务系统【三】</a></li>
<li><a href="https://webfuse.cn/2020/04/27/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E5%9B%9B%E3%80%91/">从0开始用SpringCloud搭建微服务系统【四】</a></li>
<li><a href="https://webfuse.cn/2020/04/30/%E4%BB%8E0%E5%BC%80%E5%A7%8B%E7%94%A8SpringCloud%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F%E3%80%90%E4%BA%94%E3%80%91/">从0开始用SpringCloud搭建微服务系统【五】</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;微服务与-Spring-Cloud&quot;&gt;&lt;a href=&quot;#微服务与-Spring-Cloud&quot; class=&quot;headerlink&quot; title=&quot;微服务与 Spring Cloud&quot;&gt;&lt;/a&gt;微服务与 Spring Cloud&lt;/h2&gt;&lt;h3 id=&quot;微服务&quot;&gt;
    
    </summary>
    
      <category term="Spring" scheme="https://webfuse.cn/categories/Spring/"/>
    
      <category term="Spring Cloud" scheme="https://webfuse.cn/categories/Spring/Spring-Cloud/"/>
    
    
      <category term="微服务" scheme="https://webfuse.cn/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Spring Cloud" scheme="https://webfuse.cn/tags/Spring-Cloud/"/>
    
  </entry>
  
  <entry>
    <title>Golang简单入门笔记</title>
    <link href="https://webfuse.cn/2020/04/06/Golang%E7%AE%80%E5%8D%95%E5%85%A5%E9%97%A8%E7%AC%94%E8%AE%B0/"/>
    <id>https://webfuse.cn/2020/04/06/Golang简单入门笔记/</id>
    <published>2020-04-06T07:56:40.000Z</published>
    <updated>2020-04-06T07:58:03.468Z</updated>
    
    <content type="html"><![CDATA[<h2 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h2><h3 id="关键字"><a href="#关键字" class="headerlink" title="关键字"></a>关键字</h3><p>关键字有25个。关键字不能用于自定义名字，只能在特定语法结构中使用。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">break      default       func     interface   select</div><div class="line">case       defer         go       map         struct</div><div class="line">chan       else          goto     package     switch</div><div class="line">const      fallthrough   if       range       type</div><div class="line">continue   for           import   return      var</div></pre></td></tr></table></figure>
<h3 id="预定义名字"><a href="#预定义名字" class="headerlink" title="预定义名字"></a>预定义名字</h3><p>大约有30多个预定义的名字，比如int和true等，主要对应内建的常量、类型和函数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">内建常量: true false iota nil</div><div class="line"></div><div class="line">内建类型: int int8 int16 int32 int64</div><div class="line">          uint uint8 uint16 uint32 uint64 uintptr</div><div class="line">          float32 float64 complex128 complex64</div><div class="line">          bool byte rune string error</div><div class="line"></div><div class="line">内建函数: make len cap new append copy close delete</div><div class="line">          complex real imag</div><div class="line">          panic recover</div></pre></td></tr></table></figure>
<p>其中：</p>
<ul>
<li>byte 是 uint8 类型的别名，存储 raw data</li>
<li>rune 是 int32 类型的别名，存储一个 Unicode code point 字符</li>
</ul>
<p>这些内部预先定义的名字并不是关键字，你可以在定义中重新使用它们。在一些特殊的场景中重新定义它们也是有意义的，但是也要注意避免过度而引起语义混乱。</p>
<h3 id="命名规则"><a href="#命名规则" class="headerlink" title="命名规则"></a>命名规则</h3><p>Go语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名，都遵循一个简单的命名规则：<strong>一个名字必须以一个字母（Unicode字母）或下划线开头，后面可以跟任意数量的字母、数字或下划线。</strong></p>
<p>如果一个名字是在函数内部定义，那么它的就只在函数内部有效。如果是在函数外部定义，那么将在当前包的所有文件中都可以访问。名字的开头字母的大小写决定了名字在包外的可见性。如果一个名字是大写字母开头的（必须是在函数外部定义的包级名字；包级函数名本身也是包级名字），那么它将是导出的，也就是说可以被外部的包访问。</p>
<p>命名的建议：</p>
<ul>
<li>推荐使用 <strong>驼峰式</strong> 命名，当名字由几个单词组成时优先使用大小写分隔，而不是优先用下划线分隔。</li>
<li>包本身的名字一般总是用小写字母。</li>
<li>名字的长度没有逻辑限制，但是Go语言的风格是尽量使用短小的名字，对于局部变量尤其是这样。</li>
<li>如果一个名字的作用域比较大，生命周期也比较长，那么用长的名字将会更有意义。</li>
</ul>
<h3 id="声明和变量"><a href="#声明和变量" class="headerlink" title="声明和变量"></a>声明和变量</h3><ul>
<li><strong>var</strong> 用于声明变量</li>
<li><strong>const</strong> 用于声明常量</li>
<li><strong>type</strong> 用于声明一个新的类型</li>
<li><strong>func</strong> 用于声明一个函数</li>
</ul>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> foo <span class="keyword">int</span> 			<span class="comment">// 无初值的声明</span></div><div class="line"><span class="keyword">var</span> foo <span class="keyword">int</span> = <span class="number">42</span> 		<span class="comment">// 带初值的声明</span></div><div class="line"><span class="keyword">var</span> foo, bar <span class="keyword">int</span> = <span class="number">42</span>, <span class="number">1302</span>	<span class="comment">// 一次性声明并初始化多个变量</span></div><div class="line"><span class="keyword">var</span> foo = <span class="number">42</span> 			<span class="comment">// 类型推断，由使用的上下文决定</span></div><div class="line">foo := <span class="number">42</span> 			<span class="comment">// 简短模式，自动推断类型，并且必须在函数体内部</span></div><div class="line"><span class="keyword">const</span> constant = <span class="string">"This is a constant"</span> <span class="comment">//声明一个常量</span></div><div class="line"></div><div class="line"><span class="keyword">type</span> MyInt <span class="keyword">struct</span>&#123; <span class="comment">//声明一个类型</span></div><div class="line">    </div><div class="line">&#125;  </div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">fooFuc</span><span class="params">()</span></span>&#123;  <span class="comment">//声明一个函数</span></div><div class="line">    </div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>var 形式的声明语句往往是用于需要显式指定变量类型地方，或者因为变量稍后会被重新赋值而初始值无关紧要的地方。</li>
<li>简短模式有以下限制：<ul>
<li>定义变量，同时显式初始化。</li>
<li>不能提供数据类型。</li>
<li>只能用在函数内部。</li>
<li>由于使用了:=，而不是赋值的=，因此推导声明写法的左值变量必须是没有定义过的变量。若定义过，将会发生编译错误。</li>
<li>在多个短变量声明和赋值中，至少有一个新声明的变量出现在左值中，即便其他变量名可能是重复声明的，编译器也不会报错。</li>
</ul>
</li>
<li><strong>当一个变量被声明之后，系统自动赋予它该类型的零值：int 为 0，float 为 0.0，bool 为 false，string 为空字符串，指针为 nil 等。<font color="red">所有的内存在 Go 中都是经过初始化的。</font></strong></li>
</ul>
<h4 id="匿名变量"><a href="#匿名变量" class="headerlink" title="匿名变量"></a>匿名变量</h4><p>匿名变量的特点是一个下画线“<em>”，“</em>”本身就是一个特殊的标识符，被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值（任何类型都可以赋值给它），但任何赋给这个标识符的值都将被抛弃，因此这些值不能在后续的代码中使用，也不可以使用这个标识符作为变量对其它变量进行赋值或运算。使用匿名变量时，只需要在变量声明的地方使用下画线替换即可。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">GetData</span><span class="params">()</span> <span class="params">(<span class="keyword">int</span>, <span class="keyword">int</span>)</span></span> &#123;</div><div class="line">    <span class="keyword">return</span> <span class="number">100</span>, <span class="number">200</span></div><div class="line">&#125;</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span>&#123;</div><div class="line">    a, _ := GetData()</div><div class="line">    _, b := GetData()</div><div class="line">    fmt.Println(a, b)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h4 id="变量的生命周期"><a href="#变量的生命周期" class="headerlink" title="变量的生命周期"></a>变量的生命周期</h4><ul>
<li>对于在包一级声明的变量来说，它们的生命周期和整个程序的运行周期是一致的。</li>
<li>局部变量的声明周期则是动态的：每次从创建一个新变量的声明语句开始，直到该变量不再被引用为止，然后变量的存储空间可能被回收。</li>
<li>函数的参数变量和返回值变量都是局部变量。它们在函数每次被调用的时候创建。</li>
</ul>
<h4 id="常量"><a href="#常量" class="headerlink" title="常量"></a>常量</h4><p>常量表达式的值在编译期计算，而不是在运行期。</p>
<h5 id="iota-常量生成器"><a href="#iota-常量生成器" class="headerlink" title="iota 常量生成器"></a>iota 常量生成器</h5><p>常量声明可以使用iota常量生成器初始化，它用于生成一组以相似规则初始化的常量，但是不用每行都写一遍初始化表达式。在一个const声明语句中，在第一个声明的常量所在的行，iota将会被置为0，然后在每一个有常量声明的行加一。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">type</span> Weekday <span class="keyword">int</span></div><div class="line"></div><div class="line"><span class="keyword">const</span> (</div><div class="line">    Sunday Weekday = <span class="literal">iota</span></div><div class="line">    Monday</div><div class="line">    Tuesday</div><div class="line">    Wednesday</div><div class="line">    Thursday</div><div class="line">    Friday</div><div class="line">    Saturday</div><div class="line">)</div><div class="line"></div><div class="line"><span class="keyword">const</span> (</div><div class="line">    _ = <span class="number">1</span> &lt;&lt; (<span class="number">10</span> * <span class="literal">iota</span>)</div><div class="line">    KiB <span class="comment">// 1024</span></div><div class="line">    MiB <span class="comment">// 1048576</span></div><div class="line">    GiB <span class="comment">// 1073741824</span></div><div class="line">    TiB <span class="comment">// 1099511627776             (exceeds 1 &lt;&lt; 32)</span></div><div class="line">    PiB <span class="comment">// 1125899906842624</span></div><div class="line">    EiB <span class="comment">// 1152921504606846976</span></div><div class="line">    ZiB <span class="comment">// 1180591620717411303424    (exceeds 1 &lt;&lt; 64)</span></div><div class="line">    YiB <span class="comment">// 1208925819614629174706176</span></div><div class="line">)</div></pre></td></tr></table></figure>
<h3 id="new-和-make"><a href="#new-和-make" class="headerlink" title="new 和 make"></a>new 和 make</h3><p>new 和 make 是两个内置函数，主要用来创建并分配类型的内存。new 只分配内存，而 make 只能用于 slice、map 和 channel 的初始化。</p>
<ul>
<li>new 函数只接受一个参数，这个参数是一个类型，并且<strong>返回一个指向该类型内存地址的指针</strong>。同时 new 函数会把分配的内存置为零，也就是类型的零值。</li>
</ul>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> sum *<span class="keyword">int</span></div><div class="line">sum = <span class="built_in">new</span>(<span class="keyword">int</span>) <span class="comment">//分配空间</span></div><div class="line">*sum = <span class="number">98</span></div><div class="line">fmt.Println(*sum)</div><div class="line"></div><div class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span> &#123;</div><div class="line">   name <span class="keyword">string</span></div><div class="line">   age <span class="keyword">int</span></div><div class="line">&#125;</div><div class="line"><span class="keyword">var</span> s *Student</div><div class="line">s = <span class="built_in">new</span>(Student) <span class="comment">//分配空间。如果我们不使用 new 函数为自定义类型分配空间，就会报错</span></div><div class="line">s.name =<span class="string">"dequan"</span></div><div class="line">fmt.Println(s)</div></pre></td></tr></table></figure>
<ul>
<li>make 也是用于内存分配的，但是和 new 不同，<strong>它只用于 chan、map 以及 slice 的内存创建</strong>，<strong>而且它返回的类型就是这三个类型本身，而不是他们的指针类型</strong>，因为这三种类型就是引用类型，所以就没有必要返回他们的指针了。</li>
</ul>
<h3 id="运算符"><a href="#运算符" class="headerlink" title="运算符"></a>运算符</h3><table>
<thead>
<tr>
<th style="text-align:center">运算符</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><code>+</code></td>
<td style="text-align:center">加</td>
</tr>
<tr>
<td style="text-align:center"><code>-</code></td>
<td style="text-align:center">减</td>
</tr>
<tr>
<td style="text-align:center"><code>*</code></td>
<td style="text-align:center">乘</td>
</tr>
<tr>
<td style="text-align:center"><code>/</code></td>
<td style="text-align:center">除</td>
</tr>
<tr>
<td style="text-align:center"><code>%</code></td>
<td style="text-align:center">取余</td>
</tr>
<tr>
<td style="text-align:center"><code>&amp;</code></td>
<td style="text-align:center">按位与</td>
</tr>
<tr>
<td style="text-align:center">`</td>
<td style="text-align:center">`</td>
<td>按位或</td>
</tr>
<tr>
<td style="text-align:center"><code>^</code></td>
<td style="text-align:center">按位异或</td>
</tr>
<tr>
<td style="text-align:center"><code>&amp;^</code></td>
<td style="text-align:center">按位清除（AND NOT）<code>&amp;^</code> 即 <code>AND NOT(x, y) = AND(x, NOT(Y))</code></td>
</tr>
<tr>
<td style="text-align:center"><code>&lt;&lt;</code></td>
<td style="text-align:center">左移</td>
</tr>
<tr>
<td style="text-align:center"><code>&gt;&gt;</code></td>
<td style="text-align:center">右移</td>
</tr>
<tr>
<td style="text-align:center"><code>==</code></td>
<td style="text-align:center">相等</td>
</tr>
<tr>
<td style="text-align:center"><code>!=</code></td>
<td style="text-align:center">不等</td>
</tr>
<tr>
<td style="text-align:center"><code>&lt;</code></td>
<td style="text-align:center">小于</td>
</tr>
<tr>
<td style="text-align:center"><code>&lt;=</code></td>
<td style="text-align:center">小于等于</td>
</tr>
<tr>
<td style="text-align:center"><code>&gt;</code></td>
<td style="text-align:center">大于</td>
</tr>
<tr>
<td style="text-align:center"><code>&gt;=</code></td>
<td style="text-align:center">大于等于</td>
</tr>
<tr>
<td style="text-align:center"><code>&amp;&amp;</code></td>
<td style="text-align:center">逻辑与</td>
</tr>
<tr>
<td style="text-align:center">`</td>
<td style="text-align:center"></td>
<td>`</td>
<td>逻辑或</td>
</tr>
<tr>
<td style="text-align:center"><code>!</code></td>
<td style="text-align:center">取反</td>
</tr>
<tr>
<td style="text-align:center"><code>&amp;</code></td>
<td style="text-align:center">寻址（生成指针）</td>
</tr>
<tr>
<td style="text-align:center"><code>*</code></td>
<td style="text-align:center">获取指针指向的数据</td>
</tr>
<tr>
<td style="text-align:center"><code>&lt;-</code></td>
<td style="text-align:center">向 channel 中发送 / 接收数据</td>
</tr>
</tbody>
</table>
<h3 id="包（-package-）"><a href="#包（-package-）" class="headerlink" title="包（ package ）"></a>包（ package ）</h3><ul>
<li>main package 才是可执行文件</li>
<li>package 名字与 import 路径的最后一个单词一致（如导入 math/rand 则 package 叫 rand）</li>
<li>写开头的标识符（变量名、函数名…），对其他 package 是可访问的</li>
<li>小写开头的标识符，对其他 package 是不可见的</li>
</ul>
<h4 id="包的初始化"><a href="#包的初始化" class="headerlink" title="包的初始化"></a>包的初始化</h4><ul>
<li>包的初始化首先是解决包级变量的依赖顺序，然后按照包级变量声明出现的顺序依次初始化。</li>
<li>如果包中含有多个.go源文件，它们将按照发给编译器的顺序进行初始化，Go语言的构建工具首先会将.go文件根据文件名排序，然后依次调用编译器编译。</li>
<li>每个包在解决依赖的前提下，以导入声明的顺序初始化，每个包只会被初始化一次。</li>
<li>初始化工作是自下而上进行的，main包最后被初始化。以这种方式，可以确保在main函数执行之前，所有依赖的包都已经完成初始化工作了。</li>
</ul>
<p>init初始化函数</p>
<p>对于在包级别声明的变量，如果有初始化表达式则用表达式初始化，还有一些没有初始化表达式的，例如某些表格数据初始化并不是一个简单的赋值过程。在这种情况下，我们可以用一个特殊的init初始化函数来简化初始化工作。每个文件都可以包含多个init初始化函数。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> popcount</div><div class="line"></div><div class="line"><span class="comment">// pc[i] is the population count of i.</span></div><div class="line"><span class="keyword">var</span> pc [<span class="number">256</span>]<span class="keyword">byte</span></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> &#123;</div><div class="line">    <span class="keyword">for</span> i := <span class="keyword">range</span> pc &#123;</div><div class="line">        pc[i] = pc[i/<span class="number">2</span>] + <span class="keyword">byte</span>(i&amp;<span class="number">1</span>)</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// PopCount returns the population count (number of set bits) of x.</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">PopCount</span><span class="params">(x <span class="keyword">uint64</span>)</span> <span class="title">int</span></span> &#123;</div><div class="line">    <span class="keyword">return</span> <span class="keyword">int</span>(pc[<span class="keyword">byte</span>(x&gt;&gt;(<span class="number">0</span>*<span class="number">8</span>))] +</div><div class="line">        pc[<span class="keyword">byte</span>(x&gt;&gt;(<span class="number">1</span>*<span class="number">8</span>))] +</div><div class="line">        pc[<span class="keyword">byte</span>(x&gt;&gt;(<span class="number">2</span>*<span class="number">8</span>))] +</div><div class="line">        pc[<span class="keyword">byte</span>(x&gt;&gt;(<span class="number">3</span>*<span class="number">8</span>))] +</div><div class="line">        pc[<span class="keyword">byte</span>(x&gt;&gt;(<span class="number">4</span>*<span class="number">8</span>))] +</div><div class="line">        pc[<span class="keyword">byte</span>(x&gt;&gt;(<span class="number">5</span>*<span class="number">8</span>))] +</div><div class="line">        pc[<span class="keyword">byte</span>(x&gt;&gt;(<span class="number">6</span>*<span class="number">8</span>))] +</div><div class="line">        pc[<span class="keyword">byte</span>(x&gt;&gt;(<span class="number">7</span>*<span class="number">8</span>))])</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><p>数据类型分为：基础类型、复合类型、引用类型和接口类型。</p>
<ul>
<li>基础类型，包括：数字、字符串和布尔型等</li>
<li>复合数据类型：数组和结构体等</li>
<li>引用类型包括：指针、切片、字典、函数、通道等</li>
</ul>
<h3 id="基础类型"><a href="#基础类型" class="headerlink" title="基础类型"></a>基础类型</h3><ul>
<li><strong>uint</strong> 32 位或 64 位</li>
<li><strong>uint8</strong> 无符号 8 位整型 (0 到 255)</li>
<li><strong>uint16</strong> 无符号 16 位整型 (0 到 65535)</li>
<li><strong>uint32</strong> 无符号 32 位整型 (0 到 4294967295)</li>
<li><strong>uint64</strong> 无符号 64 位整型 (0 到 18446744073709551615)</li>
<li><strong>int</strong> 32 位或 64 位</li>
<li><strong>int8</strong> 有符号 8 位整型 (-128 到 127)</li>
<li><strong>int16</strong> 有符号 16 位整型 (-32768 到 32767)</li>
<li><strong>int32</strong> 有符号 32 位整型 (-2147483648 到 2147483647)</li>
<li><strong>int64</strong> 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)</li>
<li><strong>byte</strong> uint8 的别名(type byte = uint8)</li>
<li><strong>rune</strong> int32 的别名(type rune = int32)，表示一个 unicode 码</li>
<li><strong>uintptr</strong> “无符号整型，用于存放一个指针是一种无符号的整数类型，没有指定具体的 bit 大小但是足以容纳指针。uintptr 类型只有在底层编程是才需要，特别是Go 语言和 C 语言函数库或操作系统接口相交互的地方。”</li>
<li><strong>float32</strong> IEEE-754 32 位浮点型数</li>
<li><strong>float64</strong> IEEE-754 64 位浮点型数</li>
<li><strong>complex64</strong> 32 位实数和虚数</li>
<li><strong>complex128</strong> 64 位实数和虚数</li>
<li><strong>string</strong> 字符串，默认值是空字符串，而非 NULL</li>
</ul>
<h3 id="复合数据类型"><a href="#复合数据类型" class="headerlink" title="复合数据类型"></a>复合数据类型</h3><p>复合数据类型类型包括数组、结构体、切片、字典等。</p>
<h4 id="struct"><a href="#struct" class="headerlink" title="struct"></a>struct</h4><p>Go 语言中没有 class 类的概念，取而代之的是 struct，struct 的方法对应到类的成员函数。</p>
<p>结构类型可以用来描述一组数据值，这组值的本质既可以是原始的，也可以是非原始的。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// struct 是一种类型，也是字段成员的集合体</span></div><div class="line"></div><div class="line"><span class="comment">// 声明 struct</span></div><div class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> &#123;</div><div class="line">    X, Y <span class="keyword">int</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 结构体的定义只是一种内存布局的描述，只有当结构体实例化时，才会真正地分配内存，因此必须在定义结构体并实例化后才能使用结构体的字段。</span></div><div class="line"><span class="comment">// 初始化 struct</span></div><div class="line"><span class="keyword">var</span> v = Vertex&#123;<span class="number">1</span>, <span class="number">2</span>&#125;			<span class="comment">// 字段名有序对应值</span></div><div class="line"><span class="keyword">var</span> v = Vertex&#123;X: <span class="number">1</span>, Y: <span class="number">2</span>&#125; 		<span class="comment">// 字段名对应值</span></div><div class="line"><span class="keyword">var</span> v = []Vertex&#123;&#123;<span class="number">1</span>,<span class="number">2</span>&#125;,&#123;<span class="number">5</span>,<span class="number">2</span>&#125;,&#123;<span class="number">5</span>,<span class="number">5</span>&#125;&#125;	<span class="comment">// 初始化多个 struct 组成的 slice</span></div><div class="line"></div><div class="line"><span class="comment">//用键值对填充结构体的例子</span></div><div class="line"><span class="keyword">type</span> People <span class="keyword">struct</span> &#123;</div><div class="line">    name  <span class="keyword">string</span></div><div class="line">    child *People</div><div class="line">&#125;</div><div class="line">relation := &amp;People&#123;</div><div class="line">    name: <span class="string">"爷爷"</span>,</div><div class="line">    child: &amp;People&#123;</div><div class="line">        name: <span class="string">"爸爸"</span>,</div><div class="line">        child: &amp;People&#123;</div><div class="line">                name: <span class="string">"我"</span>,</div><div class="line">        &#125;,</div><div class="line">    &#125;,</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 创建指针类型的结构体。实例化以后，实例的类型为 *T，属于指针。</span></div><div class="line">vertex := <span class="built_in">new</span>(Vertex)</div><div class="line"></div><div class="line"><span class="comment">// 取结构体的地址实例化。对结构体进行&amp;取地址操作时，视为对该类型进行一次 new 的实例化操作。</span></div><div class="line"><span class="comment">// 实例化以后，实例的类型为 *T，属于指针。</span></div><div class="line">v := &amp;Vertex&#123;&#125;</div><div class="line"></div><div class="line"><span class="comment">// 访问成员</span></div><div class="line">v.X = <span class="number">4</span></div><div class="line"></div><div class="line"><span class="comment">// 在 func 关键字和函数名之间，声明接收者是 struct</span></div><div class="line"><span class="comment">// 在方法内部，struct 实例被复制，传值引用</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v Vertex)</span> <span class="title">Abs</span><span class="params">()</span> <span class="title">float64</span></span> &#123;</div><div class="line">    <span class="keyword">return</span> math.Sqrt(v.X*v.X + v.Y*v.Y)</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 调用方法(有接收者的函数)</span></div><div class="line">v.Abs()</div><div class="line"></div><div class="line"><span class="comment">// 有的方法接收者是指向 struct 的指针</span></div><div class="line"><span class="comment">// 此时在方法内调用实例，将是传址引用</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v *Vertex)</span> <span class="title">add</span><span class="params">(n <span class="keyword">float64</span>)</span></span> &#123;</div><div class="line">    v.X += n</div><div class="line">    v.Y += n</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">//取地址实例化是最广泛的一种结构体实例化方式，可以使用函数封装上面的初始化过程</span></div><div class="line"><span class="keyword">type</span> Command <span class="keyword">struct</span> &#123;</div><div class="line">    Name    <span class="keyword">string</span>    <span class="comment">// 指令名称</span></div><div class="line">    Var     *<span class="keyword">int</span>      <span class="comment">// 指令绑定的变量</span></div><div class="line">    Comment <span class="keyword">string</span>    <span class="comment">// 指令的注释</span></div><div class="line">&#125;</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">newCommand</span><span class="params">(name <span class="keyword">string</span>, varref *<span class="keyword">int</span>, comment <span class="keyword">string</span>)</span> *<span class="title">Command</span></span> &#123;</div><div class="line">    <span class="keyword">return</span> &amp;Command&#123;</div><div class="line">        Name:    name,</div><div class="line">        Var:     varref,</div><div class="line">        Comment: comment,</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line">cmd = newCommand(</div><div class="line">    <span class="string">"version"</span>,</div><div class="line">    &amp;version,</div><div class="line">    <span class="string">"show version"</span>,</div><div class="line">)</div></pre></td></tr></table></figure>
<p>匿名结构体</p>
<p>使用 <code>map[string]interface{}</code> 开销更小且更为安全。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">point := <span class="keyword">struct</span> &#123;</div><div class="line">	X, Y <span class="keyword">int</span></div><div class="line">&#125;&#123;<span class="number">1</span>, <span class="number">2</span>&#125;</div></pre></td></tr></table></figure>
<h4 id="array"><a href="#array" class="headerlink" title="array"></a>array</h4><figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a [<span class="number">10</span>]<span class="keyword">int</span> <span class="comment">// 声明长度为 10 的 int 型数组，注意数组类型 = （元素类型 int，元素个数 10）</span></div><div class="line">a[<span class="number">3</span>] = <span class="number">42</span>     <span class="comment">// 设置元素值</span></div><div class="line">i := a[<span class="number">3</span>]     <span class="comment">// 读取元素值</span></div><div class="line"></div><div class="line"><span class="comment">// 声明并初始化数组</span></div><div class="line"><span class="keyword">var</span> a = [<span class="number">2</span>]<span class="keyword">int</span>&#123;<span class="number">1</span>, <span class="number">2</span>&#125;</div><div class="line">a := [<span class="number">2</span>]<span class="keyword">int</span>&#123;<span class="number">1</span>, <span class="number">2</span>&#125; 	<span class="comment">// 简短声明</span></div><div class="line">a := [...]<span class="keyword">int</span>&#123;<span class="number">1</span>, <span class="number">2</span>&#125;	<span class="comment">// 数组长度使用 ... 代替，编译器会自动计算元素个数</span></div><div class="line"></div><div class="line"><span class="comment">//遍历数组</span></div><div class="line"><span class="keyword">for</span> k, v := <span class="keyword">range</span> a &#123;</div><div class="line">    fmt.Println(k, v)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h4 id="list"><a href="#list" class="headerlink" title="list"></a>list</h4><p>在Go语言中，列表使用 container/list 包来实现，内部的实现原理是双链表，列表能够高效地进行任意位置的元素插入和删除操作。</p>
<p>初始化：</p>
<ul>
<li>变量名 := list.New()</li>
<li>var 变量名 list.List</li>
</ul>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">l := list.New()</div><div class="line">l.PushBack(<span class="string">"fist"</span>)  <span class="comment">//在尾部插入</span></div><div class="line">l.PushFront(<span class="number">67</span>) <span class="comment">//在头部插入</span></div><div class="line"></div><div class="line"> </div><div class="line">l.Remove(element) <span class="comment">// 移除 element 变量对应的元素。</span></div><div class="line"></div><div class="line"><span class="comment">//遍历</span></div><div class="line"><span class="keyword">for</span> i := l.Front(); i != <span class="literal">nil</span>; i = i.Next() &#123;</div><div class="line">    fmt.Println(i.Value)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h4 id="slice"><a href="#slice" class="headerlink" title="slice"></a>slice</h4><p>切片是有三个字段的数据结构，<strong>指向底层数组的指针，切片访问元素的个数（长度），切片允许增长到的元素的个数（容量）</strong>,这种数据结构便于使用和管理数据集。切片围绕动态数组的概念构建的，可以按需自动增长和缩小。切片是一个很小的对象，对底层数组进行了抽象，并提供相关的操作方法，切片的底层内存是在连续块中分配的。</p>
<p>语法：</p>
<p>slice [开始位置 : 结束位置]</p>
<ul>
<li>slice：表示目标切片对象；</li>
<li>开始位置：对应目标切片对象的索引；</li>
<li>结束位置：对应目标切片的结束索引。</li>
</ul>
<p>从数组或切片生成新的切片拥有如下特性：</p>
<ul>
<li>取出的元素数量为：结束位置 - 开始位置；</li>
<li>取出元素不包含结束位置对应的索引，切片最后一个元素使用 slice[len(slice)] 获取；</li>
<li>当缺省开始位置时，表示从连续区域开头到结束位置；</li>
<li>当缺省结束位置时，表示从开始位置到整个连续区域末尾；</li>
<li>两者同时缺省时，与切片本身等效；</li>
<li>两者同时为 0 时，等效于空切片，一般用于切片复位。</li>
</ul>
<p>根据索引位置取切片 slice 元素值时，取值范围是（0～len(slice)-1），超界会报运行时错误，生成切片时，结束位置可以填写 len(slice) 但不会报错。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a []<span class="keyword">int</span>           		<span class="comment">// 声明 slice，相当于声明未指定长度的数组</span></div><div class="line"><span class="keyword">var</span> a = []<span class="keyword">int</span> &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;	<span class="comment">// 声明并初始化 slice (基于 &#123;&#125; 中给出的底层数组)</span></div><div class="line">a := []<span class="keyword">int</span>&#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;		<span class="comment">// 简短声明</span></div><div class="line">chars := []<span class="keyword">string</span>&#123;<span class="number">0</span>:<span class="string">"a"</span>, <span class="number">2</span>:<span class="string">"c"</span>, <span class="number">1</span>: <span class="string">"b"</span>&#125;  <span class="comment">// ["a", "b", "c"]</span></div><div class="line"></div><div class="line"><span class="keyword">var</span> b = a[lo:hi]	<span class="comment">// 创建从 lo 到 hi-1 的 slice </span></div><div class="line"><span class="keyword">var</span> b = a[<span class="number">1</span>:<span class="number">4</span>]		<span class="comment">// 创建从 1  到 3    的 slice</span></div><div class="line"><span class="keyword">var</span> b = a[:<span class="number">3</span>]		<span class="comment">// 缺省 start index 则默认为 0 </span></div><div class="line"><span class="keyword">var</span> b = a[<span class="number">3</span>:]		<span class="comment">// 缺省 end   index 则默认为 len(a)</span></div><div class="line">a =  <span class="built_in">append</span>(a,<span class="number">17</span>,<span class="number">3</span>)	<span class="comment">// 向 slice a 中追加 17 和 3</span></div><div class="line">c := <span class="built_in">append</span>(a,b...)	<span class="comment">// 合并两个 slice</span></div><div class="line"></div><div class="line"><span class="comment">// 使用 make 创建 slice</span></div><div class="line">a = <span class="built_in">make</span>([]<span class="keyword">byte</span>, <span class="number">5</span>, <span class="number">5</span>)	<span class="comment">// 第一个参数是长度，第二个参数是容量</span></div><div class="line">a = <span class="built_in">make</span>([]<span class="keyword">byte</span>, <span class="number">5</span>)	<span class="comment">// 容量参数是可选的</span></div><div class="line"></div><div class="line"><span class="comment">// 从数组创建 slice</span></div><div class="line">x := [<span class="number">3</span>]<span class="keyword">string</span>&#123;<span class="string">"Лайка"</span>, <span class="string">"Белка"</span>, <span class="string">"Стрелка"</span>&#125;</div><div class="line">s := x[:] 		<span class="comment">// slice s 指向底层数组 x</span></div><div class="line"></div><div class="line"><span class="comment">// 内置函数 copy() 可以将一个数组切片复制到另一个数组切片中，如果加入的两个数组切片不一样大，就会按照其中较小的那个数组切片的元素个数进行复制。</span></div><div class="line">slice1 := []<span class="keyword">int</span>&#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;</div><div class="line">slice2 := []<span class="keyword">int</span>&#123;<span class="number">5</span>, <span class="number">4</span>, <span class="number">3</span>&#125;</div><div class="line"><span class="built_in">copy</span>(slice2, slice1) <span class="comment">// 只会复制slice1的前3个元素到slice2中</span></div><div class="line"><span class="built_in">copy</span>(slice1, slice2) <span class="comment">// 只会复制slice2的3个元素到slice1的前3个位置</span></div></pre></td></tr></table></figure>
<h4 id="map"><a href="#map" class="headerlink" title="map"></a>map</h4><p>map (映射)是一种数据结构，用于存储一系列无序的键值对。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> m <span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">int</span></div><div class="line">m = <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">int</span>)</div><div class="line">m[<span class="string">"key"</span>] = <span class="number">42</span></div><div class="line">fmt.Println(m[<span class="string">"key"</span>])</div><div class="line"></div><div class="line"><span class="built_in">delete</span>(m, <span class="string">"key"</span>)</div><div class="line"></div><div class="line">elem, ok := m[<span class="string">"key"</span>] <span class="comment">// 检查 m 中是否键为 key 的元素，如果有 ok 才为 true</span></div><div class="line"></div><div class="line"><span class="comment">// 使用键值对的形式来初始化 map</span></div><div class="line"><span class="keyword">var</span> m = <span class="keyword">map</span>[<span class="keyword">string</span>]Vertex&#123;</div><div class="line">    <span class="string">"Bell Labs"</span>: &#123;<span class="number">40.68433</span>, <span class="number">-74.39967</span>&#125;,</div><div class="line">    <span class="string">"Google"</span>:    &#123;<span class="number">37.42202</span>, <span class="number">-122.08408</span>&#125;,</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 从映射获取值并判断键是否存在</span></div><div class="line">value, exits := colors[<span class="string">"blue"</span>]</div><div class="line"><span class="keyword">if</span> exits &#123;</div><div class="line">    <span class="comment">// blue</span></div><div class="line">    fmt.Println(value)</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 使用range迭代映射</span></div><div class="line"><span class="keyword">for</span> key, value := <span class="keyword">range</span> colors &#123;</div><div class="line">    <span class="comment">// key:red value:red</span></div><div class="line">    fmt.Printf(<span class="string">"key:%s value:%s\n"</span>, key, value)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>Go语言中的 map 在并发情况下，只读是线程安全的，同时读写是线程不安全的。1.9 版本中提供了一种效率较高的并发安全的 sync.Map。</p>
<h3 id="指针"><a href="#指针" class="headerlink" title="指针"></a>指针</h3><p>一个指针变量可以指向任何一个值的内存地址，它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 或 8 个字节，占用字节的大小与所指向的值的大小无关。</p>
<p>当一个指针被定义后没有分配到任何变量时，它的默认值为 nil。指针变量通常缩写为 ptr。</p>
<p>每个变量在运行时都拥有一个地址，这个地址代表变量在内存中的位置。Go语言中使用在变量名前面添加 &amp;操作符（前缀）来获取变量的内存地址（取地址操作），格式如下：</p>
<p><code>ptr := &amp;v    // v 的类型为 T</code></p>
<p>==其中 v 代表被取地址的变量，变量 v 的地址使用变量 ptr 进行接收，ptr 的类型为*T，称做 T 的指针类型，*代表指针。==</p>
<blockquote>
<p><strong>变量、指针和地址三者的关系是，每个变量都拥有地址，指针的值就是地址。</strong></p>
</blockquote>
<p><strong>从指针获取指针指向的值：</strong> 当使用<code>&amp;</code>操作符对普通变量进行取地址操作并得到变量的指针后，可以对指针使用<code>*</code>操作符，也就是指针取值。</p>
<font color="red"><strong>取地址操作符&amp;和取值操作符*是一对互补操作符，&amp;取出地址，*根据地址取出地址指向的值。</strong></font>

<p>变量、指针地址、指针变量、取地址、取值的相互关系和特性如下：</p>
<ul>
<li>对变量进行取地址操作使用&amp;操作符，可以获得这个变量的指针变量。</li>
<li>指针变量的值是指针地址。</li>
<li>对指针变量进行取值操作使用*操作符，可以获得指针变量指向的原变量的值。</li>
</ul>
<p>*操作符的根本意义就是操作指针指向的变量。当操作在右值时，就是取指向变量的值，当操作在左值时，就是将值设置给指向的变量。</p>
<p>new() 函数可以创建一个对应类型的指针，创建过程会分配内存，被创建的指针指向默认值。</p>
<h3 id="通道"><a href="#通道" class="headerlink" title="通道"></a>通道</h3><p>如果说goroutine是Go语言程序的并发体的话，那么channels则是它们之间的通信机制。一个channel是一个通信机制，它可以让一个goroutine通过它给另一个goroutine发送值信息。每个channel都有一个特殊的类型，也就是channels可发送数据的类型。</p>
<p>通道的声明如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">ch := make(chan 类型)</div></pre></td></tr></table></figure>
<p>如： <code>ch := make(chan int)  // ch has type &#39;chan int&#39;</code></p>
<p>两个相同类型的channel可以使用==运算符比较。如果两个channel引用的是相同的对象，那么比较的结果为真。一个channel也可以和nil进行比较。</p>
<p>channel的发送和接受操作：</p>
<ul>
<li>一个发送语句将一个值从一个goroutine通过channel发送到另一个执行接收操作的goroutine</li>
<li>发送和接收两个操作都使用<code>&lt;-</code>运算符</li>
<li>在发送语句中，&lt;-运算符分割channel和要发送的值</li>
<li>在接收语句中，&lt;-运算符写在channel对象之前。一个不使用接收结果的接收操作也是合法的</li>
</ul>
<p>示例如下：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">ch &lt;- x  <span class="comment">// a send statement</span></div><div class="line">x = &lt;-ch <span class="comment">// a receive expression in an assignment statement</span></div><div class="line">&lt;-ch     <span class="comment">// a receive statement; result is discarded</span></div></pre></td></tr></table></figure>
<h4 id="不带缓存的channel"><a href="#不带缓存的channel" class="headerlink" title="不带缓存的channel"></a>不带缓存的channel</h4><ul>
<li>一个基于无缓存Channels的发送操作将导致发送者goroutine阻塞，直到另一个goroutine在相同的Channels上执行接收操作。</li>
<li>如果接收操作先发生，那么接收者goroutine也将阻塞，直到有另一个goroutine在相同的Channels上执行发送操作。</li>
<li>无缓存Channels有时候也被称为同步Channels。当通过一个无缓存Channels发送数据时，接收者收到数据发生在唤醒发送者goroutine之前。</li>
</ul>
<h4 id="串联的Channels（Pipeline）"><a href="#串联的Channels（Pipeline）" class="headerlink" title="串联的Channels（Pipeline）"></a>串联的Channels（Pipeline）</h4><p>Channels也可以用于将多个goroutine连接在一起，一个Channel的输出作为下一个Channel的输入。这种串联的Channels就是所谓的管道（pipeline）。</p>
<p>示例：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">    naturals := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</div><div class="line">    squares := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</div><div class="line"></div><div class="line">    <span class="comment">// Counter</span></div><div class="line">    <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</div><div class="line">        <span class="keyword">for</span> x := <span class="number">0</span>; x &lt; <span class="number">100</span>; x++ &#123;</div><div class="line">            naturals &lt;- x</div><div class="line">        &#125;</div><div class="line">        <span class="built_in">close</span>(naturals)</div><div class="line">    &#125;()</div><div class="line"></div><div class="line">    <span class="comment">// Squarer</span></div><div class="line">    <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</div><div class="line">        <span class="keyword">for</span> x := <span class="keyword">range</span> naturals &#123;</div><div class="line">            squares &lt;- x * x</div><div class="line">        &#125;</div><div class="line">        <span class="built_in">close</span>(squares)</div><div class="line">    &#125;()</div><div class="line"></div><div class="line">    <span class="comment">// Printer (in main goroutine)</span></div><div class="line">    <span class="keyword">for</span> x := <span class="keyword">range</span> squares &#123;</div><div class="line">        fmt.Println(x)</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h4 id="单方向的Channel"><a href="#单方向的Channel" class="headerlink" title="单方向的Channel"></a>单方向的Channel</h4><p>Go语言的类型系统提供了单方向的channel类型，分别用于只发送或只接收的channel。箭头<code>&lt;-</code>和关键字chan的相对位置表明了channel的方向。</p>
<p>示例：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">counter</span><span class="params">(out <span class="keyword">chan</span>&lt;- <span class="keyword">int</span>)</span></span> &#123;</div><div class="line">    <span class="keyword">for</span> x := <span class="number">0</span>; x &lt; <span class="number">100</span>; x++ &#123;</div><div class="line">        out &lt;- x</div><div class="line">    &#125;</div><div class="line">    <span class="built_in">close</span>(out)</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">squarer</span><span class="params">(out <span class="keyword">chan</span>&lt;- <span class="keyword">int</span>, in &lt;-<span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> &#123;</div><div class="line">    <span class="keyword">for</span> v := <span class="keyword">range</span> in &#123;</div><div class="line">        out &lt;- v * v</div><div class="line">    &#125;</div><div class="line">    <span class="built_in">close</span>(out)</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">printer</span><span class="params">(in &lt;-<span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> &#123;</div><div class="line">    <span class="keyword">for</span> v := <span class="keyword">range</span> in &#123;</div><div class="line">        fmt.Println(v)</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">    naturals := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</div><div class="line">    squares := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</div><div class="line">    <span class="keyword">go</span> counter(naturals)</div><div class="line">    <span class="keyword">go</span> squarer(squares, naturals)</div><div class="line">    printer(squares)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h4 id="带缓存的Channels"><a href="#带缓存的Channels" class="headerlink" title="带缓存的Channels"></a>带缓存的Channels</h4><p>带缓存的Channel内部持有一个元素队列。队列的最大容量是在调用make函数创建channel时通过第二个参数指定的。</p>
<p>向缓存Channel的发送操作就是向内部缓存队列的尾部插入元素，接收操作则是从队列的头部删除元素。如果内部缓存队列是满的，那么发送操作将阻塞直到因另一个goroutine执行接收操作而释放了新的队列空间。相反，如果channel是空的，接收操作将阻塞直到有另一个goroutine执行发送操作而向队列插入元素。</p>
<h3 id="数据类型转换"><a href="#数据类型转换" class="headerlink" title="数据类型转换"></a>数据类型转换</h3><p>由于Go语言不存在隐式类型转换，因此所有的类型转换都必须显式的声明：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">xvalueOfTypeB = typeB(valueOfTypeA)</div><div class="line"></div><div class="line">类型 B 的值 = 类型 B(类型 A 的值)</div></pre></td></tr></table></figure>
<p>只有相同底层类型的变量之间可以进行相互转换（如将 int16 类型转换成 int32 类型），不同底层类型的变量相互转换时会引发编译错误（如将 bool 类型转换为 int 类型）。</p>
<h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><p>函数的基本语法为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">func 函数名(形式参数列表)(返回值列表)&#123;</div><div class="line">    函数体</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="基本函数"><a href="#基本函数" class="headerlink" title="基本函数"></a>基本函数</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 最简单的函数</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">functionName</span><span class="params">()</span></span> &#123;&#125;</div><div class="line"></div><div class="line"><span class="comment">// 带参数的函数(注意类型也是放在标识符之后的)</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">functionName</span><span class="params">(param1 <span class="keyword">string</span>, param2 <span class="keyword">int</span>)</span></span> &#123;&#125;</div><div class="line"></div><div class="line"><span class="comment">// 类型相同的多个参数</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">functionName</span><span class="params">(param1, param2 <span class="keyword">int</span>)</span></span> &#123;&#125;</div><div class="line"></div><div class="line"><span class="comment">// 声明返回值的类型</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">functionName</span><span class="params">()</span> <span class="title">int</span></span> &#123;</div><div class="line">    <span class="keyword">return</span> <span class="number">42</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 定义多个变量接受，多返回值</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">div</span><span class="params">(a, b <span class="keyword">int</span>)</span> <span class="params">(<span class="keyword">int</span>, error)</span></span> &#123;</div><div class="line">    <span class="keyword">if</span> b == <span class="number">0</span> &#123;</div><div class="line">        <span class="keyword">return</span> <span class="number">0</span>, errors.New(<span class="string">"division by zero"</span>)</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">return</span> a / b, <span class="literal">nil</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 一次返回多个值</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">returnMulti</span><span class="params">()</span> <span class="params">(<span class="keyword">int</span>, <span class="keyword">string</span>)</span></span> &#123;</div><div class="line">    <span class="keyword">return</span> <span class="number">42</span>, <span class="string">"foobar"</span></div><div class="line">&#125;</div><div class="line"><span class="keyword">var</span> x, str = returnMulti()</div><div class="line"></div><div class="line"><span class="comment">// 可以在函数体中直接对函数返回值进行赋值，在命名的返回值方式的函数体中，在函数结束前需要显式地使用 return 语句进行返回</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">returnMulti2</span><span class="params">()</span> <span class="params">(n <span class="keyword">int</span>, s <span class="keyword">string</span>)</span></span> &#123;</div><div class="line">    n = <span class="number">42</span></div><div class="line">    s = <span class="string">"foobar"</span></div><div class="line">    <span class="comment">// n 和 s 会被返回</span></div><div class="line">    <span class="keyword">return</span></div><div class="line">&#125;</div><div class="line"><span class="keyword">var</span> x, str = returnMulti2()</div><div class="line"></div><div class="line"><span class="comment">// 函数作为第一类型，可作为参数或返回值</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">test</span><span class="params">(x <span class="keyword">int</span>)</span> <span class="title">func</span><span class="params">()</span></span> &#123;</div><div class="line">    <span class="comment">// 匿名函数</span></div><div class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</div><div class="line">        fmt.Println(x)</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="匿名函数（函数作为值和回调使用）"><a href="#匿名函数（函数作为值和回调使用）" class="headerlink" title="匿名函数（函数作为值和回调使用）"></a>匿名函数（函数作为值和回调使用）</h3><p>匿名函数语法为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">func(参数列表)(返回参数列表)&#123;</div><div class="line">    函数体</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>一些示例：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="comment">// 在定义时调用匿名函数</span></div><div class="line"><span class="function"><span class="keyword">func</span><span class="params">(data <span class="keyword">int</span>)</span></span> &#123;</div><div class="line">    fmt.Println(<span class="string">"hello"</span>, data)</div><div class="line">&#125;(<span class="number">100</span>)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">    <span class="comment">// 将函数作为值，赋给变量</span></div><div class="line">    add := <span class="function"><span class="keyword">func</span><span class="params">(a, b <span class="keyword">int</span>)</span> <span class="title">int</span></span> &#123;</div><div class="line">        <span class="keyword">return</span> a + b</div><div class="line">    &#125;</div><div class="line">    <span class="comment">// 使用变量直接调用函数</span></div><div class="line">    fmt.Println(add(<span class="number">3</span>, <span class="number">4</span>))</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 使用匿名函数实现操作封装</span></div><div class="line"><span class="keyword">var</span> skillParam = flag.String(<span class="string">"skill"</span>, <span class="string">""</span>, <span class="string">"skill to perform"</span>)</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">    flag.Parse()</div><div class="line">    <span class="keyword">var</span> skill = <span class="keyword">map</span>[<span class="keyword">string</span>]<span class="function"><span class="keyword">func</span><span class="params">()</span></span>&#123;</div><div class="line">        <span class="string">"fire"</span>: <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</div><div class="line">            fmt.Println(<span class="string">"chicken fire"</span>)</div><div class="line">        &#125;,</div><div class="line">        <span class="string">"run"</span>: <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</div><div class="line">            fmt.Println(<span class="string">"soldier run"</span>)</div><div class="line">        &#125;,</div><div class="line">        <span class="string">"fly"</span>: <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</div><div class="line">            fmt.Println(<span class="string">"angel fly"</span>)</div><div class="line">        &#125;,</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">if</span> f, ok := skill[*skillParam]; ok &#123;</div><div class="line">        f()</div><div class="line">    &#125; <span class="keyword">else</span> &#123;</div><div class="line">        fmt.Println(<span class="string">"skill not found"</span>)</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 回调函数作用域：在定义回调函数时能访问外部函数的值</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">scope</span><span class="params">()</span> <span class="title">func</span><span class="params">()</span> <span class="title">int</span></span>&#123;</div><div class="line">    outer_var := <span class="number">2</span></div><div class="line">    foo := <span class="function"><span class="keyword">func</span><span class="params">()</span> <span class="title">int</span></span> &#123; <span class="keyword">return</span> outer_var&#125;</div><div class="line">    <span class="keyword">return</span> foo</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">another_scope</span><span class="params">()</span> <span class="title">func</span><span class="params">()</span> <span class="title">int</span></span>&#123;</div><div class="line">    <span class="comment">// 编译错误，两个变量不在此函数作用域内</span></div><div class="line">    <span class="comment">// undefined: outer_var</span></div><div class="line">    outer_var = <span class="number">444</span></div><div class="line">    <span class="keyword">return</span> foo</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 回调函数不会修改外部作用域的数据</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">outer</span><span class="params">()</span> <span class="params">(<span class="keyword">func</span>()</span> <span class="title">int</span>, <span class="title">int</span>)</span> &#123;</div><div class="line">    outer_var := <span class="number">2</span></div><div class="line">    inner := <span class="function"><span class="keyword">func</span><span class="params">()</span> <span class="title">int</span></span> &#123;</div><div class="line">        outer_var += <span class="number">99</span> 	<span class="comment">// 试着使用外部作用域的 outer_var 变量</span></div><div class="line">        <span class="keyword">return</span> outer_var 	<span class="comment">// 返回值是 101，但只在 inner() 内部有效</span></div><div class="line">    &#125;</div><div class="line">    <span class="keyword">return</span> inner, outer_var	<span class="comment">// 返回值是 inner, 2 (outer_var 仍是 2）</span></div><div class="line">&#125;</div><div class="line"></div><div class="line">inner, outer_var := outer();	<span class="comment">// inner, 2</span></div><div class="line">inner();	<span class="comment">// 返回 101</span></div><div class="line">inner();	<span class="comment">// 返回 200	// 回调函数的特性</span></div></pre></td></tr></table></figure>
<h3 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h3><p>函数 + 引用环境 = 闭包</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">package main</div><div class="line">import (</div><div class="line">    &quot;fmt&quot;</div><div class="line">)</div><div class="line">// 创建一个玩家生成器, 输入名称, 输出生成器</div><div class="line">func playerGen(name string) func() (string, int) &#123;</div><div class="line">    // 血量一直为150</div><div class="line">    hp := 150</div><div class="line">    // 返回创建的闭包</div><div class="line">    return func() (string, int) &#123;</div><div class="line">        // 将变量引用到闭包中</div><div class="line">        return name, hp</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line">func main() &#123;</div><div class="line">    // 创建一个玩家生成器</div><div class="line">    generator := playerGen(&quot;high noon&quot;)</div><div class="line">    // 返回玩家的名字和血量</div><div class="line">    name, hp := generator()</div><div class="line">    // 打印值</div><div class="line">    fmt.Println(name, hp)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="可变参数函数"><a href="#可变参数函数" class="headerlink" title="可变参数函数"></a>可变参数函数</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">	fmt.Println(adder(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)) 	<span class="comment">// 6</span></div><div class="line">	fmt.Println(adder(<span class="number">9</span>, <span class="number">9</span>))	<span class="comment">// 18</span></div><div class="line">	</div><div class="line">	nums := []<span class="keyword">int</span>&#123;<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>&#125;</div><div class="line">	fmt.Println(adder(nums...))	<span class="comment">// 60</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 在函数的最后一个参数类型前，使用 ... 可表明函数还能接收 0 到多个此种类型的参数</span></div><div class="line"><span class="comment">// 下边的函数在调用时传多少个参数都可以</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">adder</span><span class="params">(args ...<span class="keyword">int</span>)</span> <span class="title">int</span></span> &#123;</div><div class="line">	total := <span class="number">0</span></div><div class="line">	<span class="keyword">for</span> _, v := <span class="keyword">range</span> args &#123;	<span class="comment">// 使用迭代器逐个访问参数</span></div><div class="line">		total += v</div><div class="line">	&#125;</div><div class="line">	<span class="keyword">return</span> total</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="range-函数"><a href="#range-函数" class="headerlink" title="range 函数"></a>range 函数</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">// 迭代数组或 slice</div><div class="line">for i, e := range a &#123;</div><div class="line">    // i 是索引</div><div class="line">    // e 是元素值</div><div class="line">&#125;</div><div class="line"></div><div class="line">// 如果你只要值，可用 _ 来丢弃返回的索引</div><div class="line">for _, e := range a &#123;</div><div class="line">&#125;</div><div class="line"></div><div class="line">// 如果你只要索引</div><div class="line">for i := range a &#123;</div><div class="line">&#125;</div><div class="line"></div><div class="line">// 在 Go 1.4 以前的版本，如果 i 和 e 你都不用，直接 range 编译器会报错</div><div class="line">for range time.Tick(time.Second) &#123;</div><div class="line">    // 每隔 1s 执行一次</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="流程控制"><a href="#流程控制" class="headerlink" title="流程控制"></a>流程控制</h2><h3 id="if…else…"><a href="#if…else…" class="headerlink" title="if…else…"></a>if…else…</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="comment">//一般的条件判断</span></div><div class="line">x := <span class="number">1</span></div><div class="line"><span class="keyword">if</span> x &gt; <span class="number">0</span> &#123;</div><div class="line">	<span class="keyword">return</span> x</div><div class="line">&#125; <span class="keyword">else</span> &#123;</div><div class="line">	<span class="keyword">return</span> -x</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 在条件判断语句前可以有一条语句</span></div><div class="line"><span class="keyword">if</span> a := b + c; a &lt; <span class="number">42</span> &#123;</div><div class="line">	<span class="keyword">return</span> a</div><div class="line">&#125; <span class="keyword">else</span> &#123;</div><div class="line">	<span class="keyword">return</span> a - <span class="number">42</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="switch…case…"><a href="#switch…case…" class="headerlink" title="switch…case…"></a>switch…case…</h3><p>case 语句自带 break，想执行所有 case 需要手动 fallthrough</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">// switch 分支语句</div><div class="line">switch arg &#123;</div><div class="line">    case &quot;string&quot;:</div><div class="line">        fmt.Println(&quot;string&quot;)</div><div class="line">    case 1</div><div class="line">        fmt.Println(&quot;number&quot;)</div><div class="line">    case arg &lt; 1:</div><div class="line">        fmt.Println(&quot;运算&quot;)</div><div class="line">    case case &apos; &apos;, &apos;?&apos;, &apos;&amp;&apos;, &apos;=&apos;, &apos;#&apos;, &apos;+&apos;, &apos;%&apos;:    // 多个 case 可使用逗号分隔统一处理</div><div class="line">        fmt.Println(&quot;Should escape&quot;)</div><div class="line">    default:</div><div class="line">        fmt.Println(&quot;Other&quot;)</div><div class="line">&#125;</div><div class="line"></div><div class="line">//  可在判断变量之前加入一条赋值语句</div><div class="line">switch os := runtime.GOOS; os &#123;</div><div class="line">    case &quot;darwin&quot;: ...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="for"><a href="#for" class="headerlink" title="for"></a>for</h3><p>Go 语言中循环结构只有 for，没有 do、while、foreach 等</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="keyword">for</span> i := <span class="number">1</span>; i &lt; <span class="number">10</span>; i++ &#123;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 等效于 while 循环</span></div><div class="line"><span class="keyword">for</span> ; i &lt; <span class="number">10</span>;  &#123;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 只有一个判断条件时可省去分号</span></div><div class="line"><span class="keyword">for</span> i &lt; <span class="number">10</span>  &#123;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 无条件循环时，等效于 while(true)</span></div><div class="line"><span class="keyword">for</span> &#123; 		</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h2><h3 id="处理运行时错误"><a href="#处理运行时错误" class="headerlink" title="处理运行时错误"></a>处理运行时错误</h3><p>Go 中没有异常处理机制，函数在调用时在有可能会产生错误，可返回一个 <code>Error</code> 类型的值，<code>Error</code> 接口：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">type</span> error <span class="keyword">interface</span> &#123;</div><div class="line">    Error() <span class="keyword">string</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>自定义一个错误：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">var err = errors.New(&quot;this is an error&quot;)</div><div class="line"></div><div class="line">// 创建错误对象</div><div class="line">func New(text string) error &#123;</div><div class="line">    return &amp;errorString&#123;text&#125;</div><div class="line">&#125;</div><div class="line">// 错误字符串</div><div class="line">type errorString struct &#123;</div><div class="line">    s string</div><div class="line">&#125;</div><div class="line">// 返回发生何种错误</div><div class="line">func (e *errorString) Error() string &#123;</div><div class="line">    return e.s</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>一个可能产生错误的函数：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">doStuff</span><span class="params">()</span> <span class="params">(<span class="keyword">int</span>, error)</span></span> &#123;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">    result, err := doStuff()</div><div class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</div><div class="line">        <span class="comment">// 错误处理</span></div><div class="line">    &#125;</div><div class="line">    <span class="comment">// 使用 result 处理正常逻辑</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="defer-延迟错误处理"><a href="#defer-延迟错误处理" class="headerlink" title="defer(延迟错误处理)"></a>defer(延迟错误处理)</h3><p>Go语言的 defer 语句会将其后面跟随的语句进行延迟处理，在 defer 归属的函数即将返回时，将延迟处理的语句按 defer 的逆序进行执行，也就是说，先被 defer 的语句最后被执行，最后被 defer 的语句，最先被执行。</p>
<p>关键字 defer 的用法类似于面向对象编程语言 Java 的 finally 语句块，它一般用于释放某些已分配的资源，典型的例子就是对一个互斥解锁，或者关闭一个文件。因为 defer 语句正好是在函数退出时执行的语句，所以使用 defer 能非常方便地处理资源释放问题。</p>
<h3 id="宕机（panic）与宕机恢复（recover）"><a href="#宕机（panic）与宕机恢复（recover）" class="headerlink" title="宕机（panic）与宕机恢复（recover）"></a>宕机（panic）与宕机恢复（recover）</h3><p>Go语言的 panic 机制类似于其他语言的异常，但 panic 的适用场景有一些不同，由于 panic 会引起程序的崩溃，因此 panic 一般用于严重错误，如程序内部的逻辑不一致。任何崩溃都表明了我们的代码中可能存在漏洞，所以对于大部分漏洞，我们应该使用Go语言提供的错误机制，而不是 panic。</p>
<p>手动触发宕机： <code>panic(&quot;crash&quot;)</code></p>
<p>Recover 是一个Go语言的内建函数，可以让进入宕机流程中的 goroutine 恢复过来，recover 仅在延迟函数 defer 中有效，在正常的执行过程中，调用 recover 会返回 nil 并且没有其他任何效果，如果当前的 goroutine 陷入恐慌，调用 recover 可以捕获到 panic 的输入值，并且恢复正常的执行。</p>
<h3 id="panic-和-recover-的关系"><a href="#panic-和-recover-的关系" class="headerlink" title="panic 和 recover 的关系"></a>panic 和 recover 的关系</h3><p>panic 和 recover 的组合有如下特性：</p>
<ul>
<li>有 panic 没 recover，程序宕机。</li>
<li>有 panic 也有 recover，程序不会宕机，执行完对应的 defer 后，从宕机点退出当前函数后继续执行。</li>
</ul>
<p>提示</p>
<p>虽然 panic/recover 能模拟其他语言的异常机制，但并不建议在编写普通函数时也经常性使用这种特性。</p>
<p>在 panic 触发的 defer 函数内，可以继续调用 panic，进一步将错误外抛，直到程序整体崩溃。</p>
<p>如果想在捕获错误时设置当前函数的返回值，可以对返回值使用命名返回值方式直接进行设置。</p>
<h2 id="OOP"><a href="#OOP" class="headerlink" title="OOP"></a>OOP</h2><h3 id="对象"><a href="#对象" class="headerlink" title="对象"></a>对象</h3><p>见 <a href="#struct">struct</a></p>
<h3 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h3><p>Go语言中的继承是通过内嵌或组合来实现的</p>
<ul>
<li>1) 内嵌的结构体可以直接访问其成员变量</li>
<li>2) 内嵌结构体的字段名是它的类型名</li>
</ul>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> main</div><div class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></div><div class="line"><span class="comment">// 可飞行的</span></div><div class="line"><span class="keyword">type</span> Flying <span class="keyword">struct</span>&#123;&#125;</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(f *Flying)</span> <span class="title">Fly</span><span class="params">()</span></span> &#123;</div><div class="line">    fmt.Println(<span class="string">"can fly"</span>)</div><div class="line">&#125;</div><div class="line"><span class="comment">// 可行走的</span></div><div class="line"><span class="keyword">type</span> Walkable <span class="keyword">struct</span>&#123;&#125;</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(f *Walkable)</span> <span class="title">Walk</span><span class="params">()</span></span> &#123;</div><div class="line">    fmt.Println(<span class="string">"can calk"</span>)</div><div class="line">&#125;</div><div class="line"><span class="comment">// 人类</span></div><div class="line"><span class="keyword">type</span> Human <span class="keyword">struct</span> &#123;</div><div class="line">    Walkable <span class="comment">// 人类能行走</span></div><div class="line">&#125;</div><div class="line"><span class="comment">// 鸟类</span></div><div class="line"><span class="keyword">type</span> Bird <span class="keyword">struct</span> &#123;</div><div class="line">    Walkable <span class="comment">// 鸟类能行走</span></div><div class="line">    Flying   <span class="comment">// 鸟类能飞行</span></div><div class="line">&#125;</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">    <span class="comment">// 实例化鸟类</span></div><div class="line">    b := <span class="built_in">new</span>(Bird)</div><div class="line">    fmt.Println(<span class="string">"Bird: "</span>)</div><div class="line">    b.Fly()</div><div class="line">    b.Walk()</div><div class="line">    <span class="comment">// 实例化人类</span></div><div class="line">    h := <span class="built_in">new</span>(Human)</div><div class="line">    fmt.Println(<span class="string">"Human: "</span>)</div><div class="line">    h.Walk()</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h3><p>Go语言的类型或结构体没有构造函数的功能，但是我们可以使用结构体初始化的过程来模拟实现构造函数。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//多种方式创建和初始化结构体——模拟构造函数重载</span></div><div class="line"><span class="keyword">type</span> Cat <span class="keyword">struct</span> &#123;</div><div class="line">    Color <span class="keyword">string</span></div><div class="line">    Name  <span class="keyword">string</span></div><div class="line">&#125;</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewCatByName</span><span class="params">(name <span class="keyword">string</span>)</span> *<span class="title">Cat</span></span> &#123;</div><div class="line">    <span class="keyword">return</span> &amp;Cat&#123; <span class="comment">//定义用名字构造猫结构的函数，返回 Cat 指针。</span></div><div class="line">        Name: name,</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewCatByColor</span><span class="params">(color <span class="keyword">string</span>)</span> *<span class="title">Cat</span></span> &#123;</div><div class="line">    <span class="keyword">return</span> &amp;Cat&#123;    <span class="comment">//定义用颜色构造猫结构的函数，返回 Cat 指针。</span></div><div class="line">        Color: color,</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">//带有父子关系的结构体的构造和初始化——模拟父级构造调用</span></div><div class="line"><span class="keyword">type</span> BlackCat <span class="keyword">struct</span> &#123;</div><div class="line">    Cat  <span class="comment">// 嵌入Cat, 类似于继承</span></div><div class="line">&#125;</div><div class="line"><span class="comment">// “构造基类”</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewCat</span><span class="params">(name <span class="keyword">string</span>)</span> *<span class="title">Cat</span></span> &#123;</div><div class="line">    <span class="keyword">return</span> &amp;Cat&#123;</div><div class="line">        Name: name,</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"><span class="comment">// “构造子类”</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewBlackCat</span><span class="params">(color <span class="keyword">string</span>)</span> *<span class="title">BlackCat</span></span> &#123;</div><div class="line">    cat := &amp;BlackCat&#123;&#125;</div><div class="line">    cat.Color = color</div><div class="line">    <span class="keyword">return</span> cat</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h3><p>Go 方法是作用在接收器（receiver）上的一个函数，接收器是某种类型的变量，因此方法是一种特殊类型的函数。</p>
<p>接收器语法格式：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">func (接收器变量 接收器类型) 方法名(参数列表) (返回参数) &#123;</div><div class="line">    函数体</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>对各部分的说明：</p>
<ul>
<li>接收器变量：接收器中的参数变量名在命名时，官方建议使用接收器类型名的第一个小写字母，而不是 self、this 之类的命名。例如，Socket 类型的接收器变量应该命名为 s，Connector 类型的接收器变量应该命名为 c 等。</li>
<li>接收器类型：接收器类型和参数类似，可以是指针类型和非指针类型。</li>
<li>方法名、参数列表、返回参数：格式与函数定义一致。</li>
</ul>
<p>接收器根据接收器的类型可以分为<strong>指针接收器、非指针接收器</strong>，两种接收器在使用时会产生不同的效果，根据效果的不同，两种接收器会被用于不同性能和功能要求的代码中。</p>
<h4 id="指针接收器"><a href="#指针接收器" class="headerlink" title="指针接收器"></a>指针接收器</h4><p>指针类型的接收器由一个结构体的指针组成，更接近于面向对象中的 this 或者 self。</p>
<p>由于指针的特性，<strong>调用方法时，修改接收器指针的任意成员变量，在方法结束后，修改都是有效的。</strong></p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> main</div><div class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></div><div class="line"><span class="comment">// 定义属性结构</span></div><div class="line"><span class="keyword">type</span> Property <span class="keyword">struct</span> &#123;</div><div class="line">    value <span class="keyword">int</span>  <span class="comment">// 属性值</span></div><div class="line">&#125;</div><div class="line"><span class="comment">// 设置属性值</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p *Property)</span> <span class="title">SetValue</span><span class="params">(v <span class="keyword">int</span>)</span></span> &#123;</div><div class="line">    <span class="comment">// 修改p的成员变量</span></div><div class="line">    p.value = v</div><div class="line">&#125;</div><div class="line"><span class="comment">// 取属性值</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p *Property)</span> <span class="title">Value</span><span class="params">()</span> <span class="title">int</span></span> &#123;</div><div class="line">    <span class="keyword">return</span> p.value</div><div class="line">&#125;</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">    <span class="comment">// 实例化属性</span></div><div class="line">    p := <span class="built_in">new</span>(Property)</div><div class="line">    <span class="comment">// 设置值</span></div><div class="line">    p.SetValue(<span class="number">100</span>)</div><div class="line">    <span class="comment">// 打印值</span></div><div class="line">    fmt.Println(p.Value())</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h4 id="非指针接收器"><a href="#非指针接收器" class="headerlink" title="非指针接收器"></a>非指针接收器</h4><p>当方法作用于非指针接收器时，Go语言会在代码运行时<strong>将接收器的值复制一份</strong>，在非指针接收器的方法中可以获取接收器的成员值，但<strong>修改后无效</strong>。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">package main</div><div class="line">import (</div><div class="line">    &quot;fmt&quot;</div><div class="line">)</div><div class="line">// 定义点结构</div><div class="line">type Point struct &#123;</div><div class="line">    X int</div><div class="line">    Y int</div><div class="line">&#125;</div><div class="line">// 非指针接收器的加方法</div><div class="line">func (p Point) Add(other Point) Point &#123;</div><div class="line">    // 成员值与参数相加后返回新的结构</div><div class="line">    return Point&#123;p.X + other.X, p.Y + other.Y&#125;</div><div class="line">&#125;</div><div class="line">func main() &#123;</div><div class="line">    // 初始化点</div><div class="line">    p1 := Point&#123;1, 1&#125;</div><div class="line">    p2 := Point&#123;2, 2&#125;</div><div class="line">    // 与另外一个点相加</div><div class="line">    result := p1.Add(p2)</div><div class="line">    // 输出结果</div><div class="line">    fmt.Println(result)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="指针和非指针接收器的使用"><a href="#指针和非指针接收器的使用" class="headerlink" title="指针和非指针接收器的使用"></a>指针和非指针接收器的使用</h3><ul>
<li><p>在计算机中，小对象由于值复制时的速度较快，所以适合使用非指针接收器，大对象因为复制性能较低，适合使用指针接收器，在接收器和参数间传递时不进行复制，只是传递指针。</p>
</li>
<li><p>不管你的method的receiver是指针类型还是非指针类型，都是可以通过指针/非指针类型进行调用的，编译器会帮你做类型转换。</p>
</li>
<li><p>在声明一个method的receiver该是指针还是非指针类型时，你需要考虑两方面的内部，第一方面是这个对象本身是不是特别大，如果声明为非指针变量时，调用会产生一次拷贝；第二方面是如果你用指针类型作为receiver，那么你一定要注意，这种指针类型指向的始终是一块内存地址，就算你对其进行了拷贝。熟悉C或者C艹的人这里应该很快能明白。</p>
</li>
</ul>
<h3 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h3><p>接口是用来定义行为的类型，这些被定义的行为不由接口直接实现，而是通过方法由用户定义的类型实现。<strong>Go 接口实现机制很简洁，只要目标类型方法包含接口声明的全部方法，就被视为实现了该接口，无需做显示声明。当然，目标类可以实现多个接口。如果接口没有任何方法声明，那么就是一个空接口（interface{}），它的用途类似 Object，可被赋值为任何类型的对象。</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">// 声明接口</div><div class="line">type Awesomizer interface &#123;</div><div class="line">    Awesomize() string</div><div class="line">&#125;</div><div class="line"></div><div class="line">// 无需手动声明 implement 接口</div><div class="line">type Foo struct &#123;&#125;</div><div class="line"></div><div class="line">// 自定义类型如果实现了接口的所有方法，那它就自动实现了该接口</div><div class="line">func (foo Foo) Awesomize() string &#123;</div><div class="line">    return &quot;Awesome!&quot;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>类型断言（Type Assertion）是一个使用在接口值上的操作，用于检查接口类型变量所持有的值是否实现了期望的接口或者具体的类型。</p>
<p><code>value, ok := x.(T)</code></p>
<p>其中，x 表示一个接口的类型，T 表示一个具体的类型（也可为接口类型）。</p>
<p>该断言表达式会返回 x 的值（也就是 value）和一个布尔值（也就是 ok），可根据该布尔值判断 x 是否为 T 类型：</p>
<ul>
<li>如果 T 是具体某个类型，类型断言会检查 x 的动态类型是否等于具体类型 T。如果检查成功，类型断言返回的结果是 x 的动态值，其类型是 T。</li>
<li>如果 T 是接口类型，类型断言会检查 x 的动态类型是否满足 T。如果检查成功，x 的动态值不会被提取，返回值是一个类型为 T 的接口值。</li>
<li>无论 T 是什么类型，如果 x 是 nil 接口值，类型断言都会失败。</li>
</ul>
<h2 id="并发"><a href="#并发" class="headerlink" title="并发"></a>并发</h2><h3 id="goroutine"><a href="#goroutine" class="headerlink" title="goroutine"></a>goroutine</h3><p>goroutine 是 Go语言中的轻量级线程实现，由 Go 运行时（runtime）管理。Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU。</p>
<p>Go 程序从 main 包的 main() 函数开始，在程序启动时，Go 程序就会为 main() 函数创建一个默认的 goroutine。</p>
<p>使用普通函数创建 goroutine：</p>
<p>Go 程序中使用 go 关键字为一个函数创建一个 goroutine。一个函数可以被创建多个 goroutine，一个 goroutine 必定对应一个函数。语法：<code>go 函数名( 参数列表 )</code>。使用 go 关键字创建 goroutine 时，被调用函数的返回值会被忽略。如果需要在 goroutine 中返回数据，请使用后面介绍的通道（channel）特性，通过通道把数据从 goroutine 中作为返回值传出。</p>
<p>也可以使用匿名函数创建：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">go func( 参数列表 )&#123;</div><div class="line">    函数体</div><div class="line">&#125;( 调用参数列表 )</div></pre></td></tr></table></figure>
<p>示例：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> main</div><div class="line"><span class="keyword">import</span> (</div><div class="line">	<span class="string">"fmt"</span></div><div class="line">	<span class="string">"time"</span></div><div class="line">)</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">running</span><span class="params">()</span></span> &#123;</div><div class="line">	<span class="keyword">var</span> times <span class="keyword">int</span></div><div class="line">	<span class="comment">// 构建一个无限循环</span></div><div class="line">	<span class="keyword">for</span> &#123;</div><div class="line">		times++</div><div class="line">		fmt.Println(<span class="string">"tick"</span>, times)</div><div class="line">		<span class="comment">// 延时1秒</span></div><div class="line">		time.Sleep(time.Second)</div><div class="line">	&#125;</div><div class="line">&#125;</div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">	<span class="comment">// 并发执行程序</span></div><div class="line">	<span class="keyword">go</span> running()</div><div class="line">	<span class="comment">// 接受命令行输入, 不做任何事情</span></div><div class="line">	<span class="keyword">var</span> input <span class="keyword">string</span></div><div class="line">	fmt.Scanln(&amp;input)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="反射"><a href="#反射" class="headerlink" title="反射"></a>反射</h2><p>Go语言提供了一种机制，能够在运行时更新变量和检查它们的值、调用它们的方法和它们支持的内在操作，而不需要在编译时就知道这些变量的具体类型。这种机制被称为反射。</p>
<p>反射是由 reflect 包提供的。 它定义了两个重要的类型, Type 和 Value。 </p>
<ul>
<li>一个 Type 表示一个Go类型。函数 reflect.TypeOf 接受任意的 interface{} 类型, 并以reflect.Type形式返回其动态类型</li>
<li>一个 reflect.Value 可以装载任意类型的值。函数 reflect.ValueOf 接受任意的 interface{} 类型, 并返回一个装载着其动态值的 reflect.Value。</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;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
    
    </summary>
    
      <category term="Go" scheme="https://webfuse.cn/categories/Go/"/>
    
    
      <category term="Go" scheme="https://webfuse.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Golang之禅</title>
    <link href="https://webfuse.cn/2020/02/28/Golang%E4%B9%8B%E7%A6%85/"/>
    <id>https://webfuse.cn/2020/02/28/Golang之禅/</id>
    <published>2020-02-28T03:55:12.000Z</published>
    <updated>2020-02-28T03:59:52.437Z</updated>
    
    <content type="html"><![CDATA[<p>文章转载自 OSCHINA 社区 [<a href="http://www.oschina.net" target="_blank" rel="external">http://www.oschina.net</a>]</p>
<p>标题：Golang 之禅</p>
<p>地址：<a href="https://www.oschina.net/news/113606/the-zen-of-go" target="_blank" rel="external">https://www.oschina.net/news/113606/the-zen-of-go</a></p>
<hr>
<p>在本月初的 GopherCon 上，知名 Go 语言贡献者与布道师 Dave Cheney 发表了名为《The Zen of Go》的演讲，之后他整理了演讲内容在博客中分享，由于内容过长，他又写了一个简洁版本：</p>
<p>完整版：<a href="https://dave.cheney.net/2020/02/23/the-zen-of-go" target="_blank" rel="external">https://dave.cheney.net/2020/02/23/the-zen-of-go</a><br>简洁版：<a href="https://the-zen-of-go.netlify.com" target="_blank" rel="external">https://the-zen-of-go.netlify.com</a><br>这里简单翻译一下简洁版本的内容：</p>
<p>编写简单、可读、可维护的 Go 代码的十个工程要点。</p>
<p><strong>每个包实现单一目标</strong></p>
<p>设计良好的 Go 软件包提供一个单一的思路，以及一系列相关的行为。一个好的 Go 软件包首先需要选择一个好名字，使用电梯法则（30 秒内向客户讲清楚一个方案），仅用一个词来思考你的软件包要提供什么功能。</p>
<p><strong>明确处理错误</strong></p>
<p>健壮的程序其实是由处理故障案例的片段组成的，并且需要在故障出现之前处理好。冗余的if err != nil { return err }比出了故障再一个个去处理更有价值。panic 和 recover 也一样。</p>
<p><strong>尽早 return，不要深陷</strong></p>
<p>每次缩进时都会在程序员的堆栈中添加另一个先决条件，这会占用他们短期内存中的 7±2 个片段。避免需要深层缩进的控制流。与其深入嵌套，不如使用守卫子句将成功路径保持在左侧。</p>
<p><strong>并发权留给调用者</strong></p>
<p>让调用者选择是否要异步运行你的库或函数，不要强制他们使用异步。</p>
<p><strong>在启动 goroutine 之前，要知道它什么时候会停止</strong></p>
<p>goroutines 拥有资源、锁、变量与内存等，释放这些资源的可靠方法是停止 goroutine。</p>
<p><strong>避免包级别的状态</strong></p>
<p>要完成明确和减少耦合的操作，需要通过提供类型需要的依赖项作为该类型上的字段，而不是使用包变量。</p>
<p><strong>简单性很重要</strong></p>
<p>简单性不是老练的代名词。简单并不意味着粗糙，它意味着可读性和可维护性。如果可以选择，请遵循较简单的解决方案。</p>
<p><strong>编写测试以确认包 API 的行为</strong></p>
<p>软件包的 API 是与使用者的一份合约，不管先后，不管多少，一定要进行测试。测试是确定合约的保证。要确保测试使用者可以观察和依赖的行为。</p>
<p><strong>如果你认为速度缓慢，先通过基准测试进行验证</strong></p>
<p>以性能之名会犯下许多危害可维护性的罪行。优化会破坏抽象、暴露内部和紧密耦合。如果要付出这样的代价，请确保有充分理由这样做。</p>
<p><strong>节制是一种美德</strong></p>
<p>适度使用 goroutine、通道、锁、接口与嵌套。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;文章转载自 OSCHINA 社区 [&lt;a href=&quot;http://www.oschina.net&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://www.oschina.net&lt;/a&gt;]&lt;/p&gt;
&lt;p&gt;标题：Golang 之禅&lt;/p&gt;
&lt;p&gt;
    
    </summary>
    
      <category term="Go" scheme="https://webfuse.cn/categories/Go/"/>
    
    
      <category term="Go" scheme="https://webfuse.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Python的logging实践</title>
    <link href="https://webfuse.cn/2019/11/13/Python%E7%9A%84logging%E5%AE%9E%E8%B7%B5/"/>
    <id>https://webfuse.cn/2019/11/13/Python的logging实践/</id>
    <published>2019-11-13T01:51:18.000Z</published>
    <updated>2019-11-22T03:17:50.518Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在使用 Python 的时候，logging 一度让我头疼。因为，从 Java 转过来以后，总是想着 logback、log4j 那样的统一配置。在使用过程中折腾了些时候，算是勉强给出了自己比较满意的效果。</p>
<h2 id="logging-使用"><a href="#logging-使用" class="headerlink" title="logging 使用"></a>logging 使用</h2><p>日志的简单使用可以参考官方的<a href="https://docs.python.org/zh-cn/3.7/howto/logging-cookbook.html#" target="_blank" rel="external">《日志操作手册》</a></p>
<p>其他的学习资料有：</p>
<ul>
<li><a href="https://rgb-24bit.github.io/blog/2018/python-logging.html" target="_blank" rel="external">Python 日志模块</a></li>
<li><a href="https://www.doudou0o.com/archives/fe118cd8.html" target="_blank" rel="external">python logging日志模块以及多进程日志</a></li>
<li><a href="https://www.cnblogs.com/yyds/p/6901864.html" target="_blank" rel="external">Python之日志处理（logging模块）</a></li>
</ul>
<h2 id="我的-logging-配置"><a href="#我的-logging-配置" class="headerlink" title="我的 logging 配置"></a>我的 logging 配置</h2><p>我的 logging 配置主要基于 Tornado 的日志模块进行了修改。代码如下：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div><div class="line">125</div><div class="line">126</div><div class="line">127</div><div class="line">128</div><div class="line">129</div><div class="line">130</div><div class="line">131</div><div class="line">132</div><div class="line">133</div><div class="line">134</div><div class="line">135</div><div class="line">136</div><div class="line">137</div><div class="line">138</div><div class="line">139</div><div class="line">140</div><div class="line">141</div><div class="line">142</div><div class="line">143</div><div class="line">144</div><div class="line">145</div><div class="line">146</div><div class="line">147</div><div class="line">148</div><div class="line">149</div><div class="line">150</div><div class="line">151</div><div class="line">152</div><div class="line">153</div><div class="line">154</div><div class="line">155</div><div class="line">156</div><div class="line">157</div><div class="line">158</div><div class="line">159</div><div class="line">160</div><div class="line">161</div><div class="line">162</div><div class="line">163</div><div class="line">164</div><div class="line">165</div><div class="line">166</div><div class="line">167</div><div class="line">168</div><div class="line">169</div><div class="line">170</div><div class="line">171</div><div class="line">172</div><div class="line">173</div><div class="line">174</div><div class="line">175</div><div class="line">176</div><div class="line">177</div><div class="line">178</div><div class="line">179</div><div class="line">180</div><div class="line">181</div><div class="line">182</div><div class="line">183</div><div class="line">184</div><div class="line">185</div><div class="line">186</div><div class="line">187</div><div class="line">188</div><div class="line">189</div><div class="line">190</div><div class="line">191</div><div class="line">192</div><div class="line">193</div><div class="line">194</div><div class="line">195</div><div class="line">196</div><div class="line">197</div><div class="line">198</div><div class="line">199</div><div class="line">200</div><div class="line">201</div><div class="line">202</div><div class="line">203</div><div class="line">204</div><div class="line">205</div><div class="line">206</div><div class="line">207</div><div class="line">208</div><div class="line">209</div><div class="line">210</div><div class="line">211</div><div class="line">212</div><div class="line">213</div><div class="line">214</div><div class="line">215</div><div class="line">216</div><div class="line">217</div><div class="line">218</div><div class="line">219</div><div class="line">220</div><div class="line">221</div><div class="line">222</div><div class="line">223</div><div class="line">224</div><div class="line">225</div><div class="line">226</div><div class="line">227</div><div class="line">228</div><div class="line">229</div><div class="line">230</div><div class="line">231</div><div class="line">232</div><div class="line">233</div><div class="line">234</div><div class="line">235</div><div class="line">236</div><div class="line">237</div><div class="line">238</div><div class="line">239</div><div class="line">240</div><div class="line">241</div><div class="line">242</div><div class="line">243</div><div class="line">244</div><div class="line">245</div><div class="line">246</div><div class="line">247</div><div class="line">248</div><div class="line">249</div><div class="line">250</div><div class="line">251</div><div class="line">252</div><div class="line">253</div><div class="line">254</div><div class="line">255</div><div class="line">256</div><div class="line">257</div><div class="line">258</div><div class="line">259</div><div class="line">260</div><div class="line">261</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="comment"># coding=utf-8</span></div><div class="line"></div><div class="line"><span class="string">"""</span></div><div class="line">基于Tornado Log的logger实现.</div><div class="line">"""</div><div class="line"><span class="keyword">import</span> logging</div><div class="line"><span class="keyword">import</span> logging.handlers</div><div class="line"><span class="keyword">import</span> os</div><div class="line"><span class="keyword">import</span> sys</div><div class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Dict, Any, cast, Union, Optional</div><div class="line"></div><div class="line">bytes_type = bytes</div><div class="line">unicode_type = str</div><div class="line">basestring_type = str</div><div class="line"></div><div class="line"><span class="keyword">try</span>:</div><div class="line">    <span class="keyword">import</span> colorama  <span class="comment"># type: ignore</span></div><div class="line"><span class="keyword">except</span> ImportError:</div><div class="line">    colorama = <span class="keyword">None</span></div><div class="line"></div><div class="line"><span class="keyword">try</span>:</div><div class="line">    <span class="keyword">import</span> curses</div><div class="line"><span class="keyword">except</span> ImportError:</div><div class="line">    curses = <span class="keyword">None</span>  <span class="comment"># type: ignore</span></div><div class="line"></div><div class="line">_TO_UNICODE_TYPES = (unicode_type, type(<span class="keyword">None</span>))</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">_unicode</span><span class="params">(value: Union[None, str, bytes])</span> -&gt; Optional[str]:</span>  <span class="comment"># noqa: F811</span></div><div class="line">    <span class="string">"""Converts a string argument to a unicode string.</span></div><div class="line"></div><div class="line">    If the argument is already a unicode string or None, it is returned</div><div class="line">    unchanged.  Otherwise it must be a byte string and is decoded as utf8.</div><div class="line">    """</div><div class="line">    <span class="keyword">if</span> isinstance(value, _TO_UNICODE_TYPES):</div><div class="line">        <span class="keyword">return</span> value</div><div class="line">    <span class="keyword">if</span> <span class="keyword">not</span> isinstance(value, bytes):</div><div class="line">        <span class="keyword">raise</span> TypeError(<span class="string">"Expected bytes, unicode, or None; got %r"</span> % type(value))</div><div class="line">    <span class="keyword">return</span> value.decode(<span class="string">"utf-8"</span>)</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">_stderr_supports_color</span><span class="params">()</span> -&gt; bool:</span></div><div class="line">    <span class="keyword">try</span>:</div><div class="line">        <span class="keyword">if</span> hasattr(sys.stderr, <span class="string">"isatty"</span>) <span class="keyword">and</span> sys.stderr.isatty():</div><div class="line">            <span class="keyword">if</span> curses:</div><div class="line">                curses.setupterm()</div><div class="line">                <span class="keyword">if</span> curses.tigetnum(<span class="string">"colors"</span>) &gt; <span class="number">0</span>:</div><div class="line">                    <span class="keyword">return</span> <span class="keyword">True</span></div><div class="line">            <span class="keyword">elif</span> colorama:</div><div class="line">                <span class="keyword">if</span> sys.stderr <span class="keyword">is</span> getattr(</div><div class="line">                        colorama.initialise, <span class="string">"wrapped_stderr"</span>, object()</div><div class="line">                ):</div><div class="line">                    <span class="keyword">return</span> <span class="keyword">True</span></div><div class="line">    <span class="keyword">except</span> Exception:</div><div class="line">        <span class="comment"># Very broad exception handling because it's always better to</span></div><div class="line">        <span class="comment"># fall back to non-colored logs than to break at startup.</span></div><div class="line">        <span class="keyword">pass</span></div><div class="line">    <span class="keyword">return</span> <span class="keyword">False</span></div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">_safe_unicode</span><span class="params">(s: Any)</span> -&gt; str:</span></div><div class="line">    <span class="keyword">try</span>:</div><div class="line">        <span class="keyword">return</span> _unicode(s)</div><div class="line">    <span class="keyword">except</span> UnicodeDecodeError:</div><div class="line">        <span class="keyword">return</span> repr(s)</div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">LogFormatter</span><span class="params">(logging.Formatter)</span>:</span></div><div class="line">    <span class="string">"""</span></div><div class="line">    Log formatter used in Tornado.</div><div class="line">    """</div><div class="line"></div><div class="line">    DEFAULT_FORMAT = <span class="string">"%(color)s[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]%(end_color)s %(message)s"</span>  <span class="comment"># noqa: E501</span></div><div class="line">    DEFAULT_DATE_FORMAT = <span class="string">"%Y-%m-%d %H:%M:%S"</span></div><div class="line">    DEFAULT_COLORS = &#123;</div><div class="line">        logging.DEBUG: <span class="number">4</span>,  <span class="comment"># Blue</span></div><div class="line">        logging.INFO: <span class="number">2</span>,  <span class="comment"># Green</span></div><div class="line">        logging.WARNING: <span class="number">3</span>,  <span class="comment"># Yellow</span></div><div class="line">        logging.ERROR: <span class="number">1</span>,  <span class="comment"># Red</span></div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(</span></span></div><div class="line">            self,</div><div class="line">            fmt: str = DEFAULT_FORMAT,</div><div class="line">            datefmt: str = DEFAULT_DATE_FORMAT,</div><div class="line">            color: bool = True,</div><div class="line">            colors: Dict[int, int] = DEFAULT_COLORS,</div><div class="line">    ) -&gt; <span class="keyword">None</span>:</div><div class="line">        <span class="string">r"""</span></div><div class="line">        :arg bool color: Enables color support.</div><div class="line">        :arg str fmt: Log message format.</div><div class="line">          It will be applied to the attributes dict of log records. The</div><div class="line">          text between ``%(color)s`` and ``%(end_color)s`` will be colored</div><div class="line">          depending on the level if color support is on.</div><div class="line">        :arg dict colors: color mappings from logging level to terminal color</div><div class="line">          code</div><div class="line">        :arg str datefmt: Datetime format.</div><div class="line">          Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.</div><div class="line">        """</div><div class="line">        logging.Formatter.__init__(self, datefmt=datefmt)</div><div class="line">        self._fmt = fmt</div><div class="line"></div><div class="line">        self._colors = &#123;&#125;  <span class="comment"># type: Dict[int, str]</span></div><div class="line">        <span class="keyword">if</span> color <span class="keyword">and</span> _stderr_supports_color():</div><div class="line">            <span class="keyword">if</span> curses <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:</div><div class="line">                fg_color = curses.tigetstr(<span class="string">"setaf"</span>) <span class="keyword">or</span> curses.tigetstr(<span class="string">"setf"</span>) <span class="keyword">or</span> <span class="string">b""</span></div><div class="line"></div><div class="line">                <span class="keyword">for</span> levelno, code <span class="keyword">in</span> colors.items():</div><div class="line">                    <span class="comment"># Convert the terminal control characters from</span></div><div class="line">                    <span class="comment"># bytes to unicode strings for easier use with the</span></div><div class="line">                    <span class="comment"># logging module.</span></div><div class="line">                    self._colors[levelno] = unicode_type(</div><div class="line">                        curses.tparm(fg_color, code), <span class="string">"ascii"</span></div><div class="line">                    )</div><div class="line">                self._normal = unicode_type(curses.tigetstr(<span class="string">"sgr0"</span>), <span class="string">"ascii"</span>)</div><div class="line">            <span class="keyword">else</span>:</div><div class="line">                <span class="comment"># If curses is not present (currently we'll only get here for</span></div><div class="line">                <span class="comment"># colorama on windows), assume hard-coded ANSI color codes.</span></div><div class="line">                <span class="keyword">for</span> levelno, code <span class="keyword">in</span> colors.items():</div><div class="line">                    self._colors[levelno] = <span class="string">"\033[2;3%dm"</span> % code</div><div class="line">                self._normal = <span class="string">"\033[0m"</span></div><div class="line">        <span class="keyword">else</span>:</div><div class="line">            self._normal = <span class="string">""</span></div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">def</span> <span class="title">format</span><span class="params">(self, record: Any)</span> -&gt; str:</span></div><div class="line">        <span class="keyword">try</span>:</div><div class="line">            message = record.getMessage()</div><div class="line">            <span class="keyword">assert</span> isinstance(message, basestring_type)  <span class="comment"># guaranteed by logging</span></div><div class="line">            record.message = _safe_unicode(message)</div><div class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</div><div class="line">            record.message = <span class="string">"Bad message (%r): %r"</span> % (e, record.__dict__)</div><div class="line"></div><div class="line">        record.asctime = self.formatTime(record, cast(str, self.datefmt))</div><div class="line"></div><div class="line">        <span class="keyword">if</span> record.levelno <span class="keyword">in</span> self._colors:</div><div class="line">            record.color = self._colors[record.levelno]</div><div class="line">            record.end_color = self._normal</div><div class="line">        <span class="keyword">else</span>:</div><div class="line">            record.color = record.end_color = <span class="string">""</span></div><div class="line"></div><div class="line">        formatted = self._fmt % record.__dict__</div><div class="line"></div><div class="line">        <span class="keyword">if</span> record.exc_info:</div><div class="line">            <span class="keyword">if</span> <span class="keyword">not</span> record.exc_text:</div><div class="line">                record.exc_text = self.formatException(record.exc_info)</div><div class="line">        <span class="keyword">if</span> record.exc_text:</div><div class="line">            lines = [formatted.rstrip()]</div><div class="line">            lines.extend(_safe_unicode(ln) <span class="keyword">for</span> ln <span class="keyword">in</span> record.exc_text.split(<span class="string">"\n"</span>))</div><div class="line">            formatted = <span class="string">"\n"</span>.join(lines)</div><div class="line">        <span class="keyword">return</span> formatted.replace(<span class="string">"\n"</span>, <span class="string">"\n    "</span>)</div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">NullHandler</span><span class="params">(logging.Handler)</span>:</span></div><div class="line">    <span class="function"><span class="keyword">def</span> <span class="title">emit</span><span class="params">(self, record)</span>:</span></div><div class="line">        <span class="keyword">pass</span></div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">ExactLogLevelFilter</span><span class="params">(logging.Filter)</span>:</span></div><div class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, level)</span>:</span></div><div class="line">        self.__level = level</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">def</span> <span class="title">filter</span><span class="params">(self, log_record)</span>:</span></div><div class="line">        <span class="keyword">return</span> log_record.levelno == self.__level</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">_pretty_logging</span><span class="params">(options: Dict, logger: logging.Logger)</span> -&gt; <span class="keyword">None</span>:</span></div><div class="line">    <span class="comment"># 如果没有设置日志级别</span></div><div class="line">    <span class="keyword">if</span> options[<span class="string">'logging_level'</span>] <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">or</span> options[<span class="string">'logging_level'</span>].lower() == <span class="string">"none"</span>:</div><div class="line">        <span class="keyword">return</span></div><div class="line"></div><div class="line">    <span class="keyword">if</span> options[<span class="string">'log_file_path'</span>]:</div><div class="line">        rotate_mode = options[<span class="string">'log_rotate_mode'</span>]</div><div class="line">        <span class="keyword">if</span> rotate_mode == <span class="string">"size"</span>:</div><div class="line">            channel = logging.handlers.RotatingFileHandler(</div><div class="line">                filename=options[<span class="string">'log_file_path'</span>],</div><div class="line">                maxBytes=options[<span class="string">'log_file_max_size'</span>],</div><div class="line">                backupCount=options[<span class="string">'log_file_num_backups'</span>],</div><div class="line">                encoding=<span class="string">"utf-8"</span>,</div><div class="line">            )  <span class="comment"># type: logging.Handler</span></div><div class="line">        <span class="keyword">elif</span> rotate_mode == <span class="string">"time"</span>:</div><div class="line">            channel = logging.handlers.TimedRotatingFileHandler(</div><div class="line">                filename=options[<span class="string">'log_file_path'</span>],</div><div class="line">                when=options[<span class="string">'log_rotate_when'</span>],</div><div class="line">                interval=options[<span class="string">'log_rotate_interval'</span>],</div><div class="line">                backupCount=options[<span class="string">'log_file_num_backups'</span>],</div><div class="line">                encoding=<span class="string">"utf-8"</span>,</div><div class="line">            )</div><div class="line">        <span class="keyword">else</span>:</div><div class="line">            error_message = (</div><div class="line">                    <span class="string">"The value of log_rotate_mode option should be "</span></div><div class="line">                    + <span class="string">'"size" or "time", not "%s".'</span> % rotate_mode</div><div class="line">            )</div><div class="line">            <span class="keyword">raise</span> ValueError(error_message)</div><div class="line">        channel.setFormatter(LogFormatter(color=<span class="keyword">False</span>))</div><div class="line">        <span class="comment"># 添加通过级别过滤</span></div><div class="line">        channel.addFilter(ExactLogLevelFilter(logging.getLevelName(options[<span class="string">'logging_level'</span>])))</div><div class="line">        logger.addHandler(channel)</div><div class="line"></div><div class="line">    <span class="keyword">if</span> options[<span class="string">'log_to_stderr'</span>] <span class="keyword">or</span> (options[<span class="string">'log_to_stderr'</span>] <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">and</span> <span class="keyword">not</span> logger.handlers):</div><div class="line">        <span class="comment"># Set up color if we are in a tty and curses is installed</span></div><div class="line">        channel = logging.StreamHandler()</div><div class="line">        channel.setFormatter(LogFormatter())</div><div class="line">        logger.addHandler(channel)</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">initialize_logging</span><span class="params">(logger: logging.Logger = None, options: Dict = None)</span>:</span></div><div class="line">    <span class="string">"""</span></div><div class="line"></div><div class="line">    :param logger:</div><div class="line">    :param options:</div><div class="line">        &#123;</div><div class="line">        'log_file_path':'日志文件路径',</div><div class="line">        'logging_level':'日志级别（DEBUG/INFO/WARN/ERROR）',</div><div class="line">        'log_to_stderr':'将日志输出发送到stderr（如果可能的话，将其着色）。如果未设置--log_file_prefix并且未配置其他日志记录，则默认使用stderr。',</div><div class="line">        'log_file_max_size':'每个文件最大的大小，默认：100 * 1000 * 1000',</div><div class="line">        'log_file_num_backups':'要保留的日志文件数',</div><div class="line">        'log_rotate_when':'时间间隔的类型（'S', 'M', 'H', 'D', 'W0'-'W6'）',</div><div class="line">        'log_rotate_interval':'TimedRotatingFileHandler的interval值',</div><div class="line">        'log_rotate_mode':'类型（size/time）'</div><div class="line">        &#125;</div><div class="line">    :return:</div><div class="line">    """</div><div class="line"></div><div class="line">    <span class="keyword">if</span> logger <span class="keyword">is</span> <span class="keyword">None</span>:</div><div class="line">        logger = logging.getLogger()</div><div class="line"></div><div class="line">    <span class="keyword">if</span> options <span class="keyword">is</span> <span class="keyword">None</span>:</div><div class="line">        options = &#123;&#125;</div><div class="line"></div><div class="line">    default_options = &#123;</div><div class="line">        <span class="string">'log_file_path'</span>: <span class="string">''</span>,</div><div class="line">        <span class="string">'logging_level'</span>: <span class="string">'INFO'</span>,</div><div class="line">        <span class="string">'log_to_stderr'</span>: <span class="keyword">None</span>,</div><div class="line">        <span class="string">'log_file_max_size'</span>: <span class="number">100</span> * <span class="number">1000</span> * <span class="number">1000</span>,</div><div class="line">        <span class="string">'log_file_num_backups'</span>: <span class="number">10</span>,</div><div class="line">        <span class="string">'log_rotate_when'</span>: <span class="string">'M'</span>,</div><div class="line">        <span class="string">'log_rotate_interval'</span>: <span class="number">1</span>,</div><div class="line">        <span class="string">'log_rotate_mode'</span>: <span class="string">'time'</span></div><div class="line">    &#125;</div><div class="line">    default_options.update(options)</div><div class="line"></div><div class="line">    <span class="comment"># 设置日志级别</span></div><div class="line">    logger.setLevel(getattr(logging, default_options[<span class="string">'logging_level'</span>].upper()))</div><div class="line"></div><div class="line">    log_level_path = &#123;</div><div class="line">        <span class="string">'DEBUG'</span>: os.path.join(default_options[<span class="string">'log_file_path'</span>], <span class="string">'debug/debug.log'</span>),</div><div class="line">        <span class="string">'INFO'</span>: os.path.join(default_options[<span class="string">'log_file_path'</span>], <span class="string">'info/info.log'</span>),</div><div class="line">        <span class="string">'WARNING'</span>: os.path.join(default_options[<span class="string">'log_file_path'</span>], <span class="string">'warning/warning.log'</span>),</div><div class="line">        <span class="string">'ERROR'</span>: os.path.join(default_options[<span class="string">'log_file_path'</span>], <span class="string">'error/error.log'</span>)</div><div class="line">    &#125;</div><div class="line">    log_levels = log_level_path.keys()</div><div class="line">    <span class="keyword">for</span> level <span class="keyword">in</span> log_levels:</div><div class="line">        log_path = os.path.abspath(log_level_path[level])</div><div class="line"></div><div class="line">        <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(os.path.dirname(log_path)):</div><div class="line">            os.makedirs(os.path.dirname(log_path))</div><div class="line"></div><div class="line">        default_options.update(&#123;<span class="string">'log_file_path'</span>: log_path, <span class="string">'logging_level'</span>: level&#125;)</div><div class="line"></div><div class="line">        _pretty_logging(options=default_options, logger=logger)</div></pre></td></tr></table></figure>
<p>以上的代码没有考虑到多线程情况下的使用场景，且暂时考虑的是在应用程序中使用的场景。</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;p&gt;在使用 Python 的时候，logging 一度让我头疼。因为，从 Java 转过来以后，总是想着 logback、log4j 那样的统一
    
    </summary>
    
      <category term="Python" scheme="https://webfuse.cn/categories/Python/"/>
    
    
      <category term="Python" scheme="https://webfuse.cn/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>Java中AES加解密</title>
    <link href="https://webfuse.cn/2019/07/09/Java%E4%B8%ADAES%E5%8A%A0%E8%A7%A3%E5%AF%86/"/>
    <id>https://webfuse.cn/2019/07/09/Java中AES加解密/</id>
    <published>2019-07-09T11:44:52.000Z</published>
    <updated>2019-07-09T12:33:06.672Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Java-SDK-对-AES-的实现"><a href="#Java-SDK-对-AES-的实现" class="headerlink" title="Java SDK 对 AES 的实现"></a>Java SDK 对 AES 的实现</h2><p>AES 是 Advanced Encryption Standard 的缩写，也就是 高级加密标准 。具体可以见：<a href="https://zh.wikipedia.org/wiki/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86" target="_blank" rel="external">高级加密标准</a>。本文主要讨论使用 Java SDK中对 AES 的实现。</p>
<p>从 <a href="https://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html" target="_blank" rel="external">JDK 的 Cipher 文档</a> 中，知道它支持 4 种 AES 加密模式：</p>
<ul>
<li>AES/CBC/NoPadding (128)</li>
<li>AES/CBC/PKCS5Padding (128)</li>
<li>AES/ECB/NoPadding (128)</li>
<li>AES/ECB/PKCS5Padding (128)</li>
</ul>
<p>AES 是一种加解密算法，那么 CBC, ECB, NoPadding 和 PKCS5Padding 是什么呢？</p>
<p>CBC, CBC 是分组密码工作模式，是对于按块处理密码的加密方式的一种扩充。NoPadding, PKCS5Padding 是填充(Padding)，是对需要按块处理的数据，当数据长度不符合块处理需求时，按照一定方法填充满块长的一种规则。</p>
<blockquote>
<p>关于分组密码工作模式，可以参考： <a href="https://zh.wikipedia.org/wiki/%E5%88%86%E7%BB%84%E5%AF%86%E7%A0%81%E5%B7%A5%E4%BD%9C%E6%A8%A1%E5%BC%8F" target="_blank" rel="external">分组密码工作模式</a> 和 <a href="https://www.cnblogs.com/starwolf/p/3365834.html" target="_blank" rel="external">AES五种加密模式（CBC、ECB、CTR、OCF、CFB）</a><br>关于填充，可以参考：<a href="https://en.wikipedia.org/wiki/Padding_%28cryptography%29" target="_blank" rel="external">Padding (cryptography)</a></p>
</blockquote>
<p>参考，<a href="https://blog.51cto.com/qiaofengzxq/1748687" target="_blank" rel="external">JAVA AES算法</a> 知道（以下部分的内容为该文章里的内容）：</p>
<ul>
<li>(1)缺省模式和填充为“AES/ECB/PKCS5Padding”，Cipher.getInstance(“AES”)与Cipher.getInstance(“AES/ECB/PKCS5Padding”)等效。</li>
<li>(2)JDK的PKCS5Padding实际是上述的PKCS7的实现。</li>
<li>(3)由于AES是按照16Byte为块进行处理，对于NoPadding而言，如果需要加密的原文长度不是16Byte的倍数，将无法处理抛出异常，其实是由用户自己选择Padding的算法。密文则必然是16Byte的倍数，否则密文肯定异常。</li>
<li>(4)如果加密为PKCS5Padding，解密可以选择NoPadding，也能解密成功，内容为原文加上PKCS5Padding之后的结果。</li>
<li>(5)如果原文最后一个字符为&gt;=0x00&amp;&amp;&lt;=0x10的内容，PKCS5Padding的解密将会出现异常，要么是符合PKCS5Padding，最后的内容被删除，要么不符合，则解密失败抛出异常。对此有两种思路，一是原文通过Base64编码为可见字符，二是原文自带长度使用NoPadding解密。</li>
</ul>
<h2 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> javax.crypto.Cipher;</div><div class="line"><span class="keyword">import</span> javax.crypto.spec.IvParameterSpec;</div><div class="line"><span class="keyword">import</span> javax.crypto.spec.SecretKeySpec;</div><div class="line"><span class="keyword">import</span> java.security.SecureRandom;</div><div class="line"><span class="keyword">import</span> java.util.Base64;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AesUtil</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * AES/CBC/PKCS5Padding加密，然后进行base64加密</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> plainText</div><div class="line">     * <span class="doctag">@param</span> key</div><div class="line">     * <span class="doctag">@return</span></div><div class="line">     * <span class="doctag">@throws</span> Exception</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">encryptBase64AESCBC</span><span class="params">(String plainText, String key)</span> <span class="keyword">throws</span> Exception </span>&#123;</div><div class="line"></div><div class="line">        <span class="keyword">byte</span>[] clean = plainText.getBytes();</div><div class="line"></div><div class="line">        <span class="comment">//Cipher</span></div><div class="line">        Cipher cipher = Cipher.getInstance(<span class="string">"AES/CBC/PKCS5Padding"</span>);</div><div class="line"></div><div class="line">        <span class="comment">// Generating IV.</span></div><div class="line">        <span class="keyword">int</span> ivSize = cipher.getBlockSize();</div><div class="line">        <span class="keyword">byte</span>[] iv = <span class="keyword">new</span> <span class="keyword">byte</span>[ivSize];</div><div class="line">        SecureRandom random = <span class="keyword">new</span> SecureRandom();</div><div class="line">        random.nextBytes(iv);</div><div class="line">        IvParameterSpec ivParameterSpec = <span class="keyword">new</span> IvParameterSpec(iv);</div><div class="line"></div><div class="line">        <span class="comment">// Encrypt.</span></div><div class="line">        SecretKeySpec secretKeySpec = <span class="keyword">new</span> SecretKeySpec(key.getBytes(<span class="string">"UTF-8"</span>), <span class="string">"AES"</span>);</div><div class="line">        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);</div><div class="line">        <span class="keyword">byte</span>[] encrypted = cipher.doFinal(clean);</div><div class="line"></div><div class="line">        <span class="comment">// Combine IV and encrypted part.</span></div><div class="line">        <span class="keyword">byte</span>[] encryptedIVAndText = <span class="keyword">new</span> <span class="keyword">byte</span>[ivSize + encrypted.length];</div><div class="line">        System.arraycopy(iv, <span class="number">0</span>, encryptedIVAndText, <span class="number">0</span>, ivSize);</div><div class="line">        System.arraycopy(encrypted, <span class="number">0</span>, encryptedIVAndText, ivSize, encrypted.length);</div><div class="line"></div><div class="line">        <span class="keyword">return</span> Base64.getEncoder().encodeToString(encryptedIVAndText);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * base64解密后再进行AES/CBC/PKCS5Padding解密</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> encryptedIvText</div><div class="line">     * <span class="doctag">@param</span> key</div><div class="line">     * <span class="doctag">@return</span></div><div class="line">     * <span class="doctag">@throws</span> Exception</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">decryptBase64AESCBC</span><span class="params">(String encryptedIvText, String key)</span> <span class="keyword">throws</span> Exception </span>&#123;</div><div class="line"></div><div class="line">        <span class="keyword">byte</span>[] encryptedIvTextBytes = Base64.getDecoder().decode(encryptedIvText);</div><div class="line"></div><div class="line">        <span class="comment">//Cipher</span></div><div class="line">        Cipher cipher = Cipher.getInstance(<span class="string">"AES/CBC/PKCS5Padding"</span>);</div><div class="line"></div><div class="line">        <span class="comment">// Extract IV.</span></div><div class="line">        <span class="keyword">int</span> ivSize = cipher.getBlockSize();</div><div class="line">        <span class="keyword">byte</span>[] iv = <span class="keyword">new</span> <span class="keyword">byte</span>[ivSize];</div><div class="line">        System.arraycopy(encryptedIvTextBytes, <span class="number">0</span>, iv, <span class="number">0</span>, iv.length);</div><div class="line">        IvParameterSpec ivParameterSpec = <span class="keyword">new</span> IvParameterSpec(iv);</div><div class="line"></div><div class="line">        <span class="comment">// Extract encrypted part.</span></div><div class="line">        <span class="keyword">int</span> encryptedSize = encryptedIvTextBytes.length - ivSize;</div><div class="line">        <span class="keyword">byte</span>[] encryptedBytes = <span class="keyword">new</span> <span class="keyword">byte</span>[encryptedSize];</div><div class="line">        System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, <span class="number">0</span>, encryptedSize);</div><div class="line"></div><div class="line">        <span class="comment">// Decrypt.</span></div><div class="line">        SecretKeySpec secretKeySpec = <span class="keyword">new</span> SecretKeySpec(key.getBytes(<span class="string">"UTF-8"</span>), <span class="string">"AES"</span>);</div><div class="line">        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);</div><div class="line">        <span class="keyword">byte</span>[] original = cipher.doFinal(encryptedBytes);</div><div class="line">        String originalString = <span class="keyword">new</span> String(original);</div><div class="line"></div><div class="line">        <span class="keyword">return</span> originalString;</div><div class="line">    &#125;</div><div class="line"></div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>以上代码中 <code>iv</code>的长度为 <code>cipher.getBlockSize()</code>，然后将 <code>iv</code> 放在加密文本的前部分。解密的时候一样先获得 <code>iv</code>，再进行加密内容的解密。</p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;Java-SDK-对-AES-的实现&quot;&gt;&lt;a href=&quot;#Java-SDK-对-AES-的实现&quot; class=&quot;headerlink&quot; title=&quot;Java SDK 对 AES 的实现&quot;&gt;&lt;/a&gt;Java SDK 对 AES 的实现&lt;/h2&gt;&lt;p&gt;AES 是 
    
    </summary>
    
      <category term="Java" scheme="https://webfuse.cn/categories/Java/"/>
    
    
      <category term="Java" scheme="https://webfuse.cn/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot中使用Redis</title>
    <link href="https://webfuse.cn/2019/07/09/SpringBoot%E4%B8%AD%E4%BD%BF%E7%94%A8Redis/"/>
    <id>https://webfuse.cn/2019/07/09/SpringBoot中使用Redis/</id>
    <published>2019-07-09T11:10:56.000Z</published>
    <updated>2019-07-09T11:39:53.006Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍SpringBoot Redis的<strong>基本使用</strong>和<strong>多Redis数据源配置</strong></p>
<h2 id="基本使用"><a href="#基本使用" class="headerlink" title="基本使用"></a>基本使用</h2><p>SpringBoot 2.x项目中引入<code>spring-boot-starter-data-redis</code>：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div></pre></td></tr></table></figure>
<p>查看引入包，发现SpringBoot 2.x 使用的是： <code>lettuce</code> 。</p>
<p>配置 <code>application.yml</code></p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="attr">spring:</span></div><div class="line"><span class="attr">  redis:</span></div><div class="line"><span class="attr">    host:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span></div><div class="line"><span class="attr">    port:</span> <span class="number">6379</span></div><div class="line"><span class="attr">    password:</span></div><div class="line"><span class="attr">    database:</span> <span class="number">0</span></div></pre></td></tr></table></figure>
<p>使用：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Autowired</span></div><div class="line"><span class="keyword">private</span> RedisTemplate&lt;String, String&gt; redisTemplate;</div><div class="line"></div><div class="line"><span class="meta">@Autowired</span></div><div class="line"><span class="keyword">private</span> StringRedisTemplate stringRedisTemplate;</div></pre></td></tr></table></figure>
<p>SpringBoot Redis的自动化配置位于：<code>RedisAutoConfiguration</code></p>
<h2 id="多Redis数据源配置"><a href="#多Redis数据源配置" class="headerlink" title="多Redis数据源配置"></a>多Redis数据源配置</h2><p>项目中常常用到不止一个Redis数据源，如果按照上面的简单配置，那么很难满足需要。需要我们自己来声明 <code>ConnectionFactory</code> 和 <code>RedisTemplate</code> 。</p>
<p><code>application.yml</code>中配置如下：</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="attr">spring:</span></div><div class="line"><span class="attr">  redis:</span>  <span class="comment">#redis配置</span></div><div class="line"><span class="attr">    redis-a:</span></div><div class="line"><span class="attr">      host:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span></div><div class="line"><span class="attr">      port:</span> <span class="number">6379</span></div><div class="line"><span class="attr">      password:</span> <span class="string">pwd@local</span></div><div class="line"><span class="attr">      ssl:</span> <span class="literal">true</span></div><div class="line"><span class="attr">      database:</span> <span class="number">0</span></div><div class="line"><span class="attr">    redis-b:</span></div><div class="line"><span class="attr">      host:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span></div><div class="line"><span class="attr">      port:</span> <span class="number">6479</span></div><div class="line"><span class="attr">      password:</span> <span class="string">pwd@local</span></div><div class="line"><span class="attr">      ssl:</span> <span class="literal">true</span></div><div class="line"><span class="attr">      database:</span> <span class="number">0</span></div></pre></td></tr></table></figure>
<p>以上配置是在<code>spring.redis</code>下做区分两个数据源，这是为了能够方便地查找配置位置。</p>
<p>添加<code>RedisConfig.java</code>，如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="meta">@Configuration</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisConfig</span> </span>&#123;</div><div class="line"></div><div class="line"></div><div class="line">    <span class="meta">@Bean</span>(name = <span class="string">"aRedisTemplate"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> StringRedisTemplate <span class="title">aRedisTemplate</span><span class="params">(</span></span></div><div class="line">            @Qualifier(<span class="string">"aLettuceConnectionFactory"</span>) RedisConnectionFactory aLettuceConnectionFactory) &#123;</div><div class="line">        StringRedisTemplate template = <span class="keyword">new</span> StringRedisTemplate();</div><div class="line">        template.setConnectionFactory(aLettuceConnectionFactory);</div><div class="line">        <span class="keyword">return</span> template;</div><div class="line">    &#125;</div><div class="line"></div><div class="line"></div><div class="line">    <span class="meta">@Bean</span>(name = <span class="string">"bRedisTemplate"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> StringRedisTemplate <span class="title">bRedisTemplate</span><span class="params">(</span></span></div><div class="line">            @Qualifier(<span class="string">"bConnectionFactory"</span>) RedisConnectionFactory bConnectionFactory) &#123;</div><div class="line">        StringRedisTemplate template = <span class="keyword">new</span> StringRedisTemplate();</div><div class="line">        template.setConnectionFactory(bConnectionFactory);</div><div class="line">        <span class="keyword">return</span> template;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Bean</span>(name = <span class="string">"aLettuceConnectionFactory"</span>)</div><div class="line">    <span class="meta">@Primary</span></div><div class="line">    <span class="function"><span class="keyword">public</span> LettuceConnectionFactory <span class="title">aLettuceConnectionFactory</span><span class="params">(@Value(<span class="string">"$&#123;spring.redis.redis-a.host&#125;"</span>)</span> String host,</span></div><div class="line">                                                                   @<span class="title">Value</span><span class="params">(<span class="string">"$&#123;spring.redis.redis-a.port&#125;"</span>)</span> <span class="keyword">int</span> port,</div><div class="line">                                                                   @<span class="title">Value</span><span class="params">(<span class="string">"$&#123;spring.redis.redis-a.password&#125;"</span>)</span> String password,</div><div class="line">                                                                   @<span class="title">Value</span><span class="params">(<span class="string">"$&#123;spring.redis.redis-a.ssl&#125;"</span>)</span> <span class="keyword">boolean</span> ssl,</div><div class="line">                                                                   @<span class="title">Value</span><span class="params">(<span class="string">"$&#123;spring.redis.redis-a.database&#125;"</span>)</span> <span class="keyword">int</span> database) &#123;</div><div class="line"></div><div class="line">        LettuceClientConfiguration.LettuceClientConfigurationBuilder builder = LettuceClientConfiguration.builder();</div><div class="line">        <span class="keyword">if</span> (ssl) &#123;</div><div class="line">            builder.useSsl();</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        RedisStandaloneConfiguration standaloneConfiguration = getRedisStandaloneConfiguration(host, port, password, database);</div><div class="line">        <span class="keyword">return</span> <span class="keyword">new</span> LettuceConnectionFactory(standaloneConfiguration, builder.build());</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Bean</span>(name = <span class="string">"bConnectionFactory"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> LettuceConnectionFactory <span class="title">bConnectionFactory</span><span class="params">(@Value(<span class="string">"$&#123;spring.redis.redis-b.host&#125;"</span>)</span> String host,</span></div><div class="line">                                                                   @<span class="title">Value</span><span class="params">(<span class="string">"$&#123;spring.redis.redis-b.port&#125;"</span>)</span> <span class="keyword">int</span> port,</div><div class="line">                                                                   @<span class="title">Value</span><span class="params">(<span class="string">"$&#123;spring.redis.redis-b.password&#125;"</span>)</span> String password,</div><div class="line">                                                                   @<span class="title">Value</span><span class="params">(<span class="string">"$&#123;spring.redis.redis-b.ssl&#125;"</span>)</span> <span class="keyword">boolean</span> ssl,</div><div class="line">                                                                   @<span class="title">Value</span><span class="params">(<span class="string">"$&#123;spring.redis.redis-b.database&#125;"</span>)</span> <span class="keyword">int</span> database) &#123;</div><div class="line"></div><div class="line">        LettuceClientConfiguration.LettuceClientConfigurationBuilder builder = LettuceClientConfiguration.builder();</div><div class="line">        <span class="keyword">if</span> (ssl) &#123;</div><div class="line">            builder.useSsl();</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        RedisStandaloneConfiguration standaloneConfiguration = getRedisStandaloneConfiguration(host, port, password, database);</div><div class="line">        <span class="keyword">return</span> <span class="keyword">new</span> LettuceConnectionFactory(standaloneConfiguration, builder.build());</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">private</span> RedisStandaloneConfiguration <span class="title">getRedisStandaloneConfiguration</span><span class="params">(String host, <span class="keyword">int</span> port, String password, <span class="keyword">int</span> database)</span> </span>&#123;</div><div class="line">        RedisStandaloneConfiguration standaloneConfiguration = <span class="keyword">new</span> RedisStandaloneConfiguration();</div><div class="line">        standaloneConfiguration.setHostName(host);</div><div class="line">        standaloneConfiguration.setPort(port);</div><div class="line">        standaloneConfiguration.setPassword(password);</div><div class="line">        standaloneConfiguration.setDatabase(database);</div><div class="line">        <span class="keyword">return</span> standaloneConfiguration;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>使用：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="meta">@Autowired</span></div><div class="line"><span class="meta">@Qualifier</span>(<span class="string">"aRedisTemplate"</span>)</div><div class="line"><span class="keyword">private</span> StringRedisTemplate aRedisTemplate;</div><div class="line"></div><div class="line"></div><div class="line"><span class="meta">@Autowired</span></div><div class="line"><span class="meta">@Qualifier</span>(<span class="string">"bRedisTemplate"</span>)</div><div class="line"><span class="keyword">private</span> StringRedisTemplate bRedisTemplate;</div></pre></td></tr></table></figure>
<p>如果想要忽略掉默认的Redis配置，那么可以将 <code>RedisAutoConfiguration</code> 和 <code>RedisReactiveAutoConfiguration</code> 加入忽略：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@EnableAutoConfiguration</span>(exclude = &#123;RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class&#125;)</div></pre></td></tr></table></figure>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本文介绍SpringBoot Redis的&lt;strong&gt;基本使用&lt;/strong&gt;和&lt;strong&gt;多Redis数据源配置&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;基本使用&quot;&gt;&lt;a href=&quot;#基本使用&quot; class=&quot;headerlink&quot; title=&quot;基本使用&quot;
    
    </summary>
    
      <category term="Spring" scheme="https://webfuse.cn/categories/Spring/"/>
    
      <category term="Spring Boot" scheme="https://webfuse.cn/categories/Spring/Spring-Boot/"/>
    
    
      <category term="实战" scheme="https://webfuse.cn/tags/%E5%AE%9E%E6%88%98/"/>
    
      <category term="Spring Boot" scheme="https://webfuse.cn/tags/Spring-Boot/"/>
    
  </entry>
  
  <entry>
    <title>PMP必须掌握的知识——PMI主义</title>
    <link href="https://webfuse.cn/2019/07/04/PMP%E5%BF%85%E9%A1%BB%E6%8E%8C%E6%8F%A1%E7%9A%84%E7%9F%A5%E8%AF%86%E2%80%94%E2%80%94PMI%E4%B8%BB%E4%B9%89/"/>
    <id>https://webfuse.cn/2019/07/04/PMP必须掌握的知识——PMI主义/</id>
    <published>2019-07-04T13:50:53.000Z</published>
    <updated>2019-07-04T14:02:35.609Z</updated>
    
    <content type="html"><![CDATA[<ol>
<li>你是专业的项目经理，管理是你的核心职能</li>
<li>必须以专业的方法做项目，即遵循 PMBOK 的要求</li>
<li>强调事业环境因素和组织过程资产</li>
<li>强调历史信息，强调经验教训总结，强调记录</li>
<li>问题重在预防，而非解决</li>
<li>必须有明确的目标，必须有正式的计划，才可行动</li>
<li>利害关系者很重要，尽早识别全部并让其参与</li>
<li>项目经理必须被任命，PM 是管理工作的核心责任点</li>
<li>项目是系统工程，PM 是整合者， 三重约束牢记在心</li>
<li>项目管理以结果为导向，项目成功是 PM 最终责任</li>
<li>变更影响项目成功，PM 应影响变更变化，管理变更</li>
<li>整合通过沟通实现，PM 要花 75% ~ 90% 时间用于沟通</li>
<li>PM 应拒绝提供不重要的信息要求</li>
<li>工作必须被详细描述，责任必须明确</li>
<li>任何情况下，质量都要达到客户满意</li>
<li>消减费用的前提是消减项目范围</li>
<li>PM 必须遵守职业道德（个人、公司、社会、道德、法律）</li>
<li>PM 必须主动，PM 是神器、伟大的，可以拯救世界</li>
<li>一切决策必须以事实为依据，以程序为准绳，正确的程序优先于正确的结果</li>
<li>防止范围潜变，杜绝质量镀金</li>
<li>项目必须收尾</li>
<li>公正、公平、公开，勇敢、诚实地面对现实</li>
<li>双赢理念</li>
<li>决策要遵守四大价值观：责任、尊重、公平、诚信</li>
</ol>
]]></content>
    
    <summary type="html">
    
      &lt;ol&gt;
&lt;li&gt;你是专业的项目经理，管理是你的核心职能&lt;/li&gt;
&lt;li&gt;必须以专业的方法做项目，即遵循 PMBOK 的要求&lt;/li&gt;
&lt;li&gt;强调事业环境因素和组织过程资产&lt;/li&gt;
&lt;li&gt;强调历史信息，强调经验教训总结，强调记录&lt;/li&gt;
&lt;li&gt;问题重在预防，而非解决&lt;
    
    </summary>
    
      <category term="PMP" scheme="https://webfuse.cn/categories/PMP/"/>
    
    
      <category term="PMP" scheme="https://webfuse.cn/tags/PMP/"/>
    
  </entry>
  
  <entry>
    <title>web3.js编译Solidity，发布，调用全部流程</title>
    <link href="https://webfuse.cn/2019/04/11/web3-js%E7%BC%96%E8%AF%91Solidity%EF%BC%8C%E5%8F%91%E5%B8%83%EF%BC%8C%E8%B0%83%E7%94%A8%E5%85%A8%E9%83%A8%E6%B5%81%E7%A8%8B/"/>
    <id>https://webfuse.cn/2019/04/11/web3-js编译Solidity，发布，调用全部流程/</id>
    <published>2019-04-11T07:53:53.000Z</published>
    <updated>2019-04-11T07:56:46.975Z</updated>
    
    <content type="html"><![CDATA[<p>根据 <a href="http://web3.tryblockchain.org/web3-js-in-action.html" target="_blank" rel="external">web3.js编译Solidity，发布，调用全部流程（手把手教程）</a>进行。因为这篇文章中的一些方法和最新版本的不一样了，中间遇到了一些坑，现把解决方法记录如下：</p>
<h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><p>安装 Node.js 、安装以太节点以及合约代码等与<a href="http://web3.tryblockchain.org/web3-js-in-action.html" target="_blank" rel="external">web3.js编译Solidity，发布，调用全部流程（手把手教程）</a>中的一致。</p>
<ul>
<li>安装好的NodeJs版本为：v10.15.3</li>
<li>安装好的以太节点版本为：ethereumjs-testrpc@6.0.3</li>
</ul>
<h2 id="遇到的坑"><a href="#遇到的坑" class="headerlink" title="遇到的坑"></a>遇到的坑</h2><h3 id="第一个坑：Solidity安装"><a href="#第一个坑：Solidity安装" class="headerlink" title="第一个坑：Solidity安装"></a>第一个坑：Solidity安装</h3><p>按照官方文档使用 <code>npm install -g solc</code> 安装。</p>
<p>安装后验证版本应该使用 <code>solcjs --version</code> ，而不是 <code>solc --version</code> 。而且这边安装的是最新版本，也为后面的步骤埋下了坑。</p>
<h3 id="第二个坑：TypeError-Cannot-read-property-‘solidity’-of-undefined"><a href="#第二个坑：TypeError-Cannot-read-property-‘solidity’-of-undefined" class="headerlink" title="第二个坑：TypeError: Cannot read property ‘solidity’ of undefined"></a>第二个坑：TypeError: Cannot read property ‘solidity’ of undefined</h3><p>使用以下代码：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> source = <span class="string">"pragma solidity ^0.4.0;contract Calc&#123;  /*区块链存储*/  uint count;  /*执行会写入数据，所以需要`transaction`的方式执行。*/  function add(uint a, uint b) returns(uint)&#123;    count++;    return a + b;  &#125;  /*执行不会写入数据，所以允许`call`的方式执行。*/  function getCount() returns (uint)&#123;    return count;  &#125;&#125;"</span>;</div><div class="line"><span class="keyword">let</span> calc = web3.eth.compile.solidity(source);</div></pre></td></tr></table></figure>
<p>运行的时候报：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">let calc = web3.eth.compile.solidity(source);</div><div class="line">                            ^</div><div class="line"></div><div class="line">TypeError: Cannot read property 'solidity' of undefined</div><div class="line">    at Object.&lt;anonymous&gt; (/data/eth-contract/src/index.js:15:29)</div><div class="line">    at Module._compile (internal/modules/cjs/loader.js:701:30)</div><div class="line">    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)</div><div class="line">    at Module.load (internal/modules/cjs/loader.js:600:32)</div><div class="line">    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)</div><div class="line">    at Function.Module._load (internal/modules/cjs/loader.js:531:3)</div><div class="line">    at Function.Module.runMain (internal/modules/cjs/loader.js:754:12)</div><div class="line">    at startup (internal/bootstrap/node.js:283:19)</div><div class="line">    at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)</div></pre></td></tr></table></figure>
<p>这个是因为 Geth 在 1.5.9 版本以后就不支持此功能。见：<a href="https://medium.com/@ksin751119/ethereum-dapp%E5%88%9D%E5%BF%83%E8%80%85%E4%B9%8B%E8%B7%AF-7-web3-eth-compile-solidity-%E6%9B%BF%E4%BB%A3%E6%96%B9%E6%A1%88-a020a6763fbd" target="_blank" rel="external">Ethereum Dapp初心者之路(7): web3.eth.compile.solidity()替代方案</a>。</p>
<p>所以参考 <a href="https://ethereum.stackexchange.com/questions/6346/solc-version-command-not-found" target="_blank" rel="external">https://ethereum.stackexchange.com/questions/6346/solc-version-command-not-found</a> 文章，将代码改为：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">"fs"</span>);</div><div class="line"><span class="keyword">const</span> solc = <span class="built_in">require</span>(<span class="string">'solc'</span>);</div><div class="line"><span class="keyword">let</span> source = fs.readFileSync(<span class="string">'Calc.sol'</span>, <span class="string">'UTF-8'</span>);</div><div class="line"><span class="keyword">let</span> compilationData = solc.compile(source);</div></pre></td></tr></table></figure>
<h3 id="第三个坑：编译报错"><a href="#第三个坑：编译报错" class="headerlink" title="第三个坑：编译报错"></a>第三个坑：编译报错</h3><p>编译的时候，如果遇到以下报错：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&#123;"errors":[&#123;"component":"general","formattedMessage":"* Line 1, Column 1\n  Syntax error: value, object or array expected.\n* Line 1, Column 2\n  Extra non-whitespace after JSON value.\n","message":"* Line 1, Column 1\n  Syntax error: value, object or array expected.\n* Line 1, Column 2\n  Extra non-whitespace after JSON value.\n","severity":"error","type":"JSONError"&#125;]&#125;</div></pre></td></tr></table></figure>
<p>这个是因为 solc 的版本不对，我们的代码中的版本是 <code>^0.4.0</code> ，而安装的 solc 的版本为 <code>^0.5.7</code> 。应该用一样的版本。</p>
<p>代码中的版本改为<code>^0.5.7</code>，即<code>pragma solidity ^0.5.7;</code>。运行后，报：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">assert.js:350</div><div class="line">    throw err;</div><div class="line">    ^</div><div class="line"></div><div class="line">AssertionError [ERR_ASSERTION]: Invalid callback specified.</div><div class="line">    at wrapCallback (/data/eth-contract/node_modules/solc/wrapper.js:16:5)</div><div class="line">    at runWithReadCallback (/data/eth-contract/node_modules/solc/wrapper.js:42:26)</div><div class="line">    at compileStandard (/data/eth-contract/node_modules/solc/wrapper.js:83:14)</div><div class="line">    at Object.compileStandardWrapper (/data/eth-contract/node_modules/solc/wrapper.js:90:14)</div><div class="line">    at Object.&lt;anonymous&gt; (/data/eth-contract/src/index.js:8:28)</div><div class="line">    at Module._compile (internal/modules/cjs/loader.js:701:30)</div><div class="line">    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)</div><div class="line">    at Module.load (internal/modules/cjs/loader.js:600:32)</div><div class="line">    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)</div><div class="line">    at Function.Module._load (internal/modules/cjs/loader.js:531:3)</div><div class="line">    at Function.Module.runMain (internal/modules/cjs/loader.js:754:12)</div><div class="line">    at startup (internal/bootstrap/node.js:283:19)</div><div class="line">    at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)</div></pre></td></tr></table></figure>
<p>参考 <a href="https://stackoverflow.com/questions/53353167/npm-solc-assertionerror-err-assertion-invalid-callback-specified" target="_blank" rel="external">https://stackoverflow.com/questions/53353167/npm-solc-assertionerror-err-assertion-invalid-callback-specified</a> 后，如下操作：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">npm uninstall -g solc</div><div class="line">npm uninstall solc</div><div class="line">npm install solc@0.4.25</div></pre></td></tr></table></figure>
<p>然后将代码中的版本号改为：<code>pragma solidity ^0.4.25;</code></p>
<h2 id="完整源码"><a href="#完整源码" class="headerlink" title="完整源码"></a>完整源码</h2><p>index.js:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> Web3 = <span class="built_in">require</span>(<span class="string">'web3'</span>);</div><div class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">"fs"</span>);</div><div class="line"><span class="keyword">const</span> solc = <span class="built_in">require</span>(<span class="string">'solc'</span>);</div><div class="line"></div><div class="line"><span class="keyword">let</span> web3;</div><div class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> web3 !== <span class="string">'undefined'</span>) &#123;</div><div class="line">    web3 = <span class="keyword">new</span> Web3(web3.currentProvider);</div><div class="line">&#125; <span class="keyword">else</span> &#123;</div><div class="line">    <span class="comment">// set the provider you want from Web3.providers</span></div><div class="line">    web3 = <span class="keyword">new</span> Web3(<span class="keyword">new</span> Web3.providers.HttpProvider(<span class="string">"http://localhost:8545"</span>));</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">//编译合约</span></div><div class="line"><span class="keyword">let</span> source = fs.readFileSync(<span class="string">'Calc.sol'</span>, <span class="string">'UTF-8'</span>).toString();</div><div class="line"><span class="keyword">let</span> calcCompiled = solc.compile(source);</div><div class="line"></div><div class="line"><span class="comment">//得到合约对象</span></div><div class="line"><span class="keyword">let</span> jsonInterface = calcCompiled[<span class="string">'contracts'</span>][<span class="string">':Calc'</span>][<span class="string">'interface'</span>];</div><div class="line"></div><div class="line"><span class="comment">//获得abi</span></div><div class="line"><span class="keyword">let</span> abi = <span class="built_in">JSON</span>.parse(jsonInterface);</div><div class="line"></div><div class="line"><span class="comment">//获取合约的代码</span></div><div class="line"><span class="keyword">let</span> bytecode = calcCompiled[<span class="string">'contracts'</span>][<span class="string">':Calc'</span>][<span class="string">'bytecode'</span>]</div><div class="line"></div><div class="line"><span class="comment">//得到合约对象</span></div><div class="line"><span class="keyword">const</span> calcContract = <span class="keyword">new</span> web3.eth.Contract(abi,<span class="literal">null</span>,&#123;</div><div class="line">    <span class="attr">data</span>: <span class="string">'0x'</span>+bytecode, </div><div class="line">    <span class="attr">defaultGas</span>:<span class="string">'4700000'</span></div><div class="line">&#125;);</div><div class="line"></div><div class="line"><span class="comment">//部署者的地址，当前取默认账户的第一个地址</span></div><div class="line"><span class="keyword">let</span> deployeAddr = web3.eth.accounts[<span class="number">0</span>];</div><div class="line"></div><div class="line"><span class="comment">//部署合约</span></div><div class="line">calcContract.deploy().send(&#123;</div><div class="line">    <span class="attr">from</span>: deployeAddr</div><div class="line">&#125;)</div><div class="line">.on(<span class="string">'error'</span>, (error) =&gt; &#123; </div><div class="line">    <span class="built_in">console</span>.error(error)</div><div class="line"> &#125;)</div><div class="line">.on(<span class="string">'transactionHash'</span>, (transactionHash) =&gt; &#123; </div><div class="line">    <span class="built_in">console</span>.log(<span class="string">"transactionHash :"</span> + transactionHash)</div><div class="line"> &#125;)</div><div class="line">.on(<span class="string">'receipt'</span>, (receipt) =&gt; &#123;</div><div class="line">   <span class="built_in">console</span>.log(<span class="string">"receipt:"</span>) </div><div class="line">   <span class="built_in">console</span>.log(receipt) </div><div class="line">&#125;)</div><div class="line">.on(<span class="string">'confirmation'</span>, (confirmationNumber, receipt) =&gt; &#123; </div><div class="line">   <span class="built_in">console</span>.log(<span class="string">"confirmationNumber:"</span>+confirmationNumber)</div><div class="line"> &#125;).then(<span class="function">(<span class="params">newContractInstance</span>) =&gt;</span> &#123;</div><div class="line">    <span class="built_in">console</span>.log(newContractInstance)</div><div class="line">    <span class="built_in">console</span>.log(newContractInstance.options.address) <span class="comment">// instance with the new contract address</span></div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>Calc.sol:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">pragma solidity ^0.4.25;</div><div class="line"></div><div class="line">contract Calc&#123;</div><div class="line">  /*区块链存储*/</div><div class="line">  uint count;</div><div class="line"></div><div class="line">  /*执行会写入数据，所以需要`transaction`的方式执行。*/</div><div class="line">  function add(uint a, uint b) returns(uint)&#123;</div><div class="line">    count++;</div><div class="line">    return a + b;</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  /*执行不会写入数据，所以允许`call`的方式执行。*/</div><div class="line">  function getCount() constant returns (uint)&#123;</div><div class="line">    return count;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>package.json:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">  <span class="attr">"name"</span>: <span class="string">"eth-contract"</span>,</div><div class="line">  <span class="attr">"version"</span>: <span class="string">"1.0.0"</span>,</div><div class="line">  <span class="attr">"description"</span>: <span class="string">""</span>,</div><div class="line">  <span class="attr">"main"</span>: <span class="string">"index.js"</span>,</div><div class="line">  <span class="attr">"scripts"</span>: &#123;</div><div class="line">    <span class="attr">"test"</span>: <span class="string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span></div><div class="line">  &#125;,</div><div class="line">  <span class="attr">"author"</span>: <span class="string">""</span>,</div><div class="line">  <span class="attr">"license"</span>: <span class="string">"ISC"</span>,</div><div class="line">  <span class="attr">"dependencies"</span>: &#123;</div><div class="line">    <span class="attr">"solc"</span>: <span class="string">"^0.4.25"</span>,</div><div class="line">    <span class="attr">"web3"</span>: <span class="string">"^1.0.0-beta.52"</span></div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul>
<li><a href="http://web3.tryblockchain.org/web3-js-in-action.html" target="_blank" rel="external">web3.js编译Solidity，发布，调用全部流程（手把手教程）</a></li>
<li><a href="https://web3js.readthedocs.io/en/1.0/index.html" target="_blank" rel="external">web3.js - Ethereum JavaScript API</a></li>
<li><a href="https://solidity-cn.readthedocs.io/zh/develop/index.html" target="_blank" rel="external">Solidity 开发指南</a></li>
<li><a href="http://remix.ethereum.org" target="_blank" rel="external">Remix - Solidity IDE</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;根据 &lt;a href=&quot;http://web3.tryblockchain.org/web3-js-in-action.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;web3.js编译Solidity，发布，调用全部流程（手把手教程）&lt;/a&gt;进行
    
    </summary>
    
      <category term="区块链" scheme="https://webfuse.cn/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
    
      <category term="区块链" scheme="https://webfuse.cn/tags/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
  </entry>
  
  <entry>
    <title>初识比特闪电网络</title>
    <link href="https://webfuse.cn/2019/04/08/%E5%88%9D%E8%AF%86%E6%AF%94%E7%89%B9%E9%97%AA%E7%94%B5%E7%BD%91%E7%BB%9C/"/>
    <id>https://webfuse.cn/2019/04/08/初识比特闪电网络/</id>
    <published>2019-04-08T07:41:59.000Z</published>
    <updated>2019-04-08T07:44:19.740Z</updated>
    
    <content type="html"><![CDATA[<p>闪电网络在今年的区块链世界是一个很热门的词。这个词乍听起来很酷炫，很深奥，它到底是什么呢？本文带你了解它。</p>
<h2 id="闪电网络是什么"><a href="#闪电网络是什么" class="headerlink" title="闪电网络是什么"></a>闪电网络是什么</h2><p>官方网站：<a href="http://lightning.network/" target="_blank" rel="external">http://lightning.network/</a></p>
<p>闪电网络（Lightning Network）是一个去中心化的系统。闪电网络的卓越之处在于，无需信任对方以及第三方即可实现实时的、海量的交易网络。这也就解决了比特币支付存在着拥堵的问题。</p>
<p>闪电网络是基于微支付通道演进而来，创造性的设计出了两种类型的交易合约：序列到期可撤销合约RSMC（Revocable Sequence Maturity Contract），哈希时间锁定合约HTLC（Hashed Timelock Contract）。</p>
<p>RSMC解决了通道中币单向流动问题，也就是链下交易的确认问题；HTLC解决了币跨节点传递的问题，也就是支付通道问题。这两个类型的交易组合构成了闪电网络。</p>
<p><strong>1. 入门级文字：</strong></p>
<ul>
<li><a href="https://www.8btc.com/article/375131" target="_blank" rel="external">用大白话解释：闪电网络是什么？</a></li>
<li><a href="https://github.com/diaojibo/StudyNote_Rocklct/blob/master/%E5%8C%BA%E5%9D%97%E9%93%BE/%E9%97%AA%E7%94%B5%E7%BD%91%E7%BB%9C.md" target="_blank" rel="external">闪电网络详解</a></li>
<li><a href="https://www.8btc.com/article/242957" target="_blank" rel="external">闪电网络入门：什么是闪电网络？它是如何运作的？</a></li>
<li><a href="https://www.jianshu.com/p/233b192dc4dc" target="_blank" rel="external">什么是闪电网络？干货都在这了！</a></li>
<li><a href="https://www.jianshu.com/p/2cd6113b2668" target="_blank" rel="external">区块链学堂——闪电网络是什么鬼（故事篇）？</a></li>
<li><a href="https://www.jianshu.com/p/e326802294e1" target="_blank" rel="external">区块链学堂——闪电网络是个什么鬼（技术篇）？</a></li>
</ul>
<p><strong>2. 如果不喜欢文字资料，还可以通过以下两个视频简单入门：</strong></p>
<ul>
<li><a href="https://www.8btc.com/video/345682" target="_blank" rel="external">中心化魔鬼or 扩容灵药，6分钟带你看懂闪电网络！</a></li>
<li><a href="https://www.8btc.com/video/352353" target="_blank" rel="external">闪电网络五大误区，你踩雷了吗？</a></li>
</ul>
<p><strong>3. 白皮书：</strong></p>
<ul>
<li><a href="https://wenku.baidu.com/view/f3bd55ea763231126fdb1150.html" target="_blank" rel="external">比特币闪电网络白皮书：可扩展的 off-chain 即时支付</a></li>
<li><a href="https://lightning.network/lightning-network-paper.pdf" target="_blank" rel="external">The Bitcoin Lightning Network: Scalable Off-Chain Instant Payments</a></li>
</ul>
<p><strong>4. 其他资料：</strong></p>
<ul>
<li><a href="https://lnroute.com/" target="_blank" rel="external">BITCOIN LIGHTNING NETWORK - Resource and Information Guide</a></li>
</ul>
<h2 id="闪电网络的优缺点"><a href="#闪电网络的优缺点" class="headerlink" title="闪电网络的优缺点"></a>闪电网络的优缺点</h2><h3 id="闪电网络的优点"><a href="#闪电网络的优点" class="headerlink" title="闪电网络的优点"></a>闪电网络的优点</h3><ul>
<li>可以即时支付</li>
<li>对节点的性能需求并不高</li>
<li>通过路由系统自动寻径(寻找花费最少的路径)，网络中的节点充分竞争使得交易费用可以低</li>
<li>匿名性(每一个节点只记录该通道内的交易)</li>
</ul>
<h3 id="闪电网络的缺点"><a href="#闪电网络的缺点" class="headerlink" title="闪电网络的缺点"></a>闪电网络的缺点</h3><ul>
<li>如果换一种代币就需要重新开启通道，节点和用户的成本都相当的高</li>
<li>没有足够的商家支持闪电网络，使得作为节点的收益并不高，由于没有足够的商家支持，用户使用闪电网络的需求并不高</li>
<li>闪电网络是小额支付，开启通道是需要使用公网的，使用公网意味着要发起一次交易，如果只转入几百块，一次交易就需要几十块，可能就得不偿失了</li>
<li>关闭通道时,也需要发起交易，将通道中的币分配给通道以及用户，这又需要发起一次交易，又需要发起一次交易</li>
<li>其次每个节点都需要抵押一定的比特币，才能开启节点，长时间暴露在公网中，可能会存在私钥泄露的问题</li>
<li>现阶段闪电网络并不完善处于一个初始版本</li>
</ul>
<h2 id="闪电网络的浏览器"><a href="#闪电网络的浏览器" class="headerlink" title="闪电网络的浏览器"></a>闪电网络的浏览器</h2><ul>
<li><a href="https://1ml.com/" target="_blank" rel="external">1ML</a></li>
<li><a href="https://explorer.acinq.co/" target="_blank" rel="external">Lightning network explorer</a></li>
<li><a href="https://lightblock.me/" target="_blank" rel="external">Lightblock</a></li>
<li><a href="https://www.robtex.com/lightning/node/" target="_blank" rel="external">Robtex Bitcoin Lightning Explorer</a></li>
<li><a href="https://explore.casa/" target="_blank" rel="external">Lightning Explorer (explore casa)</a></li>
<li><a href="https://graph.lndexplorer.com/" target="_blank" rel="external">lndexplorer</a></li>
<li><a href="https://lightning.chaintools.io/" target="_blank" rel="external">Lightning Explorerc(chaintools)</a></li>
<li><a href="https://bitcoinvisuals.com/lightning" target="_blank" rel="external">Bitcoin Visuals</a></li>
<li><a href="https://lnmainnet.rompert.com/" target="_blank" rel="external">rompert.com</a></li>
<li><a href="https://bitcoinexchangerate.org/lightning" target="_blank" rel="external">Bitcoin Exchange Rate</a></li>
</ul>
<p>其他：</p>
<ul>
<li><a href="https://github.com/xsb/lngraph" target="_blank" rel="external">lngraph - Personal private Lightning Network explorer using Neo4j Browser</a></li>
<li><a href="https://gist.github.com/bretton/798ec38165ffabc719d91e0f4f67552d#a-mostly-visual-collection-of-the-lightning-network" target="_blank" rel="external">A (mostly) visual collection of the Lightning Network</a></li>
</ul>
<h2 id="闪电网络的钱包"><a href="#闪电网络的钱包" class="headerlink" title="闪电网络的钱包"></a>闪电网络的钱包</h2><p>闪电网络的钱包可以参考<a href="https://lightningnetworkstores.com/wallets" target="_blank" rel="external">Lightning mobile wallets</a>列表。</p>
<p>GitHub上开源的钱包项目：</p>
<p><strong>桌面</strong></p>
<ul>
<li><a href="https://github.com/lightninglabs/lightning-app" target="_blank" rel="external">lightning-app</a> - NodeJs实现</li>
<li><a href="https://github.com/alexbosworth/lnd-gui" target="_blank" rel="external">lnd-gui</a> - Swift实现</li>
<li><a href="https://github.com/ACINQ/eclair" target="_blank" rel="external">eclair-node-gui</a> - Java实现</li>
<li><a href="https://github.com/LN-Zap/zap-desktop" target="_blank" rel="external">zap-desktop</a> -  Electron+React+Redux实现</li>
<li><a href="https://github.com/icota/presto" target="_blank" rel="external">Presto</a> - C++实现</li>
<li><a href="https://github.com/LightningPeach/lightning-peach-wallet" target="_blank" rel="external">Lightning Peach Wallet</a> - Electron+React+Redux实现</li>
<li><a href="https://github.com/shesek/spark-wallet" target="_blank" rel="external">spark-wallet</a> - NodeJs实现</li>
</ul>
<p><strong>Web</strong></p>
<ul>
<li><a href="https://github.com/Lightning-Family/Discovery-Wallet" target="_blank" rel="external">Discovery wallet</a> - NodeJs实现</li>
<li><a href="https://github.com/marzig76/fulmo" target="_blank" rel="external">fulmo</a> - Python实现</li>
<li><a href="https://github.com/cdecker/kugelblitz" target="_blank" rel="external">kugelblitz</a> - Go实现</li>
<li><a href="https://github.com/mably/lncli-web" target="_blank" rel="external">lncli-web</a> - NodeJS+Angular实现</li>
<li><a href="https://github.com/ShahanaFarooqui/RTL" target="_blank" rel="external">Ride The Lightning (RTL)</a> - NodeJS+Angular7实现</li>
</ul>
<p><strong>移动端</strong></p>
<p>可以参考<a href="https://lightningnetworkstores.com/wallets" target="_blank" rel="external">Lightning mobile wallets</a>中<code>Open source</code>为<code>yes</code>的项目。</p>
<h2 id="闪电网络协议的实现"><a href="#闪电网络协议的实现" class="headerlink" title="闪电网络协议的实现"></a>闪电网络协议的实现</h2><p>在闪电网络的实现方面，现在比较主流的有<a href="https://github.com/lightningnetwork/lnd" target="_blank" rel="external">Lightning Network Daemon</a>、<a href="https://github.com/ACINQ/eclair" target="_blank" rel="external">eclair</a>和<a href="https://github.com/ElementsProject/lightning" target="_blank" rel="external">c-lightning</a>。这三个实现是兼容的。</p>
<h3 id="Lightning-Network-Daemon-LND"><a href="#Lightning-Network-Daemon-LND" class="headerlink" title="Lightning Network Daemon (LND)"></a>Lightning Network Daemon (LND)</h3><p>LND是论文《The Bitcoin Lightning Network: Scalable Off-Chain Instant Payments》中提出者的项目，使用Go语言编写，它比较适用于社区和开发者方面。</p>
<ul>
<li>项目地址：<a href="https://github.com/lightningnetwork/lnd" target="_blank" rel="external">https://github.com/lightningnetwork/lnd</a></li>
<li>LND Developer Site： <a href="https://dev.lightning.community/" target="_blank" rel="external">https://dev.lightning.community/</a></li>
</ul>
<p>开发支持：</p>
<ul>
<li><a href="http://www.lightningj.org/" target="_blank" rel="external">LightningJ - Lightning APIs for Java </a></li>
</ul>
<blockquote>
<p>风险：目前LND还不支持导出私钥<a href="https://github.com/lightningnetwork/lnd/issues/732" target="_blank" rel="external">https://github.com/lightningnetwork/lnd/issues/732</a></p>
</blockquote>
<h3 id="eclair"><a href="#eclair" class="headerlink" title="eclair"></a>eclair</h3><p>Eclair(法语闪电)是闪电网络的Scala实现。它可以运行GUI，也可以不运行GUI，还可以使用JSON-RPC API。因为是Scala开发的，所以适用于所有的java语言，适合商业场景。</p>
<ul>
<li>项目地址：<a href="https://github.com/ACINQ/eclair" target="_blank" rel="external">https://github.com/ACINQ/eclair</a></li>
</ul>
<h3 id="c-lightning"><a href="#c-lightning" class="headerlink" title="c-lightning"></a>c-lightning</h3><p>用C实现的一个规范兼容的闪电网络。</p>
<ul>
<li>项目地址：<a href="https://github.com/ElementsProject/lightning" target="_blank" rel="external">https://github.com/ElementsProject/lightning</a></li>
</ul>
<h3 id="其他实现"><a href="#其他实现" class="headerlink" title="其他实现"></a>其他实现</h3><ul>
<li><a href="https://github.com/raiden-network/raiden" target="_blank" rel="external">Raiden Network</a> - Python实现</li>
<li><a href="https://github.com/mit-dci/lit" target="_blank" rel="external">lit</a> - Go实现</li>
<li><a href="https://github.com/lightningnetwork/lightning-onion" target="_blank" rel="external">lightning-onion</a> - Go实现</li>
<li><a href="https://github.com/nayutaco/ptarmigan" target="_blank" rel="external">ptarmigan</a> - C实现</li>
</ul>
<h2 id="其他资料"><a href="#其他资料" class="headerlink" title="其他资料"></a>其他资料</h2><ul>
<li><a href="https://github.com/bcongdon/awesome-lightning-network" target="_blank" rel="external">Awesome-lightning-network</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&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;闪电网络
    
    </summary>
    
      <category term="区块链" scheme="https://webfuse.cn/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
    
      <category term="区块链" scheme="https://webfuse.cn/tags/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
      <category term="比特币" scheme="https://webfuse.cn/tags/%E6%AF%94%E7%89%B9%E5%B8%81/"/>
    
      <category term="闪电网络" scheme="https://webfuse.cn/tags/%E9%97%AA%E7%94%B5%E7%BD%91%E7%BB%9C/"/>
    
  </entry>
  
  <entry>
    <title>使用Docker容器构建Bitcoin</title>
    <link href="https://webfuse.cn/2019/03/21/%E4%BD%BF%E7%94%A8Docker%E5%AE%B9%E5%99%A8%E6%9E%84%E5%BB%BABitcoin/"/>
    <id>https://webfuse.cn/2019/03/21/使用Docker容器构建Bitcoin/</id>
    <published>2019-03-21T13:53:32.000Z</published>
    <updated>2019-03-21T13:56:42.695Z</updated>
    
    <content type="html"><![CDATA[<p>虽然说Bitcoin Core有<a href="https://bitcoin.org/zh_CN/download" target="_blank" rel="external">编译好的客户端</a>。但如果能自己编译下源码，感觉也还是不错的。所以就有了以下的图文：</p>
<h2 id="前置条件"><a href="#前置条件" class="headerlink" title="前置条件"></a>前置条件</h2><ul>
<li>已经安装好Docker</li>
<li>能够科学上网</li>
</ul>
<h2 id="构建Bitcoin客户端"><a href="#构建Bitcoin客户端" class="headerlink" title="构建Bitcoin客户端"></a>构建Bitcoin客户端</h2><p>从源码构建bitcoin客户端，我们分为以下几步：</p>
<ul>
<li>选择一个Linux，并安装系统依赖库</li>
<li>从github下载代码库，并切换最新的版本</li>
<li>运行autogen.sh脚本</li>
<li>运行configure脚本</li>
<li>运行make和make install</li>
<li>验证bitcoin安装成功</li>
</ul>
<p>所以，我们的Dockerfile如下：</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># 基于alpine linux构建</span></div><div class="line"><span class="keyword">FROM</span> alpine</div><div class="line"></div><div class="line"><span class="comment"># 安装安装系统依赖库</span></div><div class="line"><span class="keyword">RUN</span> apk update &amp;&amp; apk add git \ </div><div class="line">                          make \</div><div class="line">                          file \</div><div class="line">                          autoconf \</div><div class="line">                          automake \ </div><div class="line">                          build-base \</div><div class="line">                          libtool \</div><div class="line">                          db-c++ \</div><div class="line">                          db-dev \</div><div class="line">                          boost-system \</div><div class="line">                          boost-program_options \</div><div class="line">                          boost-filesystem \ </div><div class="line">                          boost-dev \ </div><div class="line">                          libressl-dev \ </div><div class="line">                          libevent-dev</div><div class="line"></div><div class="line"><span class="comment"># 下载源码，并切换到最新的分支</span></div><div class="line"><span class="keyword">RUN</span> git clone https://github.com/bitcoin/bitcoin --branch v0.17.0.1 --single-branch</div><div class="line"></div><div class="line"><span class="comment"># 运行autogen.sh脚本 以及 运行configure脚本 以及 运行make和make install</span></div><div class="line"><span class="keyword">RUN</span> (cd bitcoin  &amp;&amp; ./autogen.sh &amp;&amp; \</div><div class="line">                      ./configure --disable-tests \</div><div class="line">                      --disable-bench --disable-static  \</div><div class="line">                      --without-gui --disable-zmq \ </div><div class="line">                      --with-incompatible-bdb \</div><div class="line">                      CFLAGS=<span class="string">'-w'</span> CXXFLAGS=<span class="string">'-w'</span> &amp;&amp; \</div><div class="line">                      make -j <span class="number">4</span> &amp;&amp; \</div><div class="line">                      strip src/bitcoind &amp;&amp; \</div><div class="line">                      strip src/bitcoin-cli &amp;&amp; \</div><div class="line">                      strip src/bitcoin-tx &amp;&amp; \</div><div class="line">                      make install )</div></pre></td></tr></table></figure>
<p>运行<br><code>docker build -f bitcoin.dockerfile -t bitcoin-alpine .</code>构建一个docker镜像。</p>
<p>在成功构建镜像后，通过命令运行镜像<code>docker run -it bitcoin-alpine-bin</code>。</p>
<p>bitcoind 默认的安装位置是/usr/local/bin。你可以通过询问系统下面2个可执行文件的路径，来确认bitcoin是否安装成功。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">/ #  which bitcoind</div><div class="line">/usr/local/bin/bitcoind</div><div class="line"></div><div class="line">/ # which bitcoin-cli</div><div class="line">/usr/local/bin/bitcoin-cli</div></pre></td></tr></table></figure>
<p>然后，在运行的容器中运行bitcoin的后台程序：</p>
<p><code>bitcoind -server=1 -rest=1 -regtest -txindex=1 -daemon</code></p>
<p>通过运行<code>bitcoin-cli -regtest getblockchaininfo</code>，<code>bitcoin-cli -regtest getnetworkinfo</code>，<code>bitcoin-cli -regtest getwalletinfo</code>可以获得到相关的状态信息。</p>
<p>其中：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">- getblockchaininfo: blocks, difficulty, chain</div><div class="line">- getnetworkinfo: version, protocolversion, timeoffset, connections, proxy, relayfee, warnings</div><div class="line">- getwalletinfo: balance, keypoololdest, keypoolsize, paytxfee, unlocked_until, walletversion</div></pre></td></tr></table></figure>
<hr>
<p>参考：</p>
<ul>
<li><a href="https://leftasexercise.com/2018/04/12/building-a-bitcoin-container-with-docker/" target="_blank" rel="external">https://leftasexercise.com/2018/04/12/building-a-bitcoin-container-with-docker/</a></li>
<li><a href="https://www.codeooze.com/blockchain/bitcoind-running-in-docker/" target="_blank" rel="external">https://www.codeooze.com/blockchain/bitcoind-running-in-docker/</a></li>
<li><a href="http://blog.fens.me/bitcoin-install/" target="_blank" rel="external">http://blog.fens.me/bitcoin-install/</a></li>
<li><a href="https://github.com/alexfoster/bitcoin-dockerfile/blob/master/Dockerfile" target="_blank" rel="external">https://github.com/alexfoster/bitcoin-dockerfile/blob/master/Dockerfile</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;虽然说Bitcoin Core有&lt;a href=&quot;https://bitcoin.org/zh_CN/download&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;编译好的客户端&lt;/a&gt;。但如果能自己编译下源码，感觉也还是不错的。所以就有了以下的图文：&lt;
    
    </summary>
    
      <category term="区块链" scheme="https://webfuse.cn/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
    
      <category term="Docker" scheme="https://webfuse.cn/tags/Docker/"/>
    
      <category term="区块链" scheme="https://webfuse.cn/tags/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
      <category term="比特币" scheme="https://webfuse.cn/tags/%E6%AF%94%E7%89%B9%E5%B8%81/"/>
    
  </entry>
  
  <entry>
    <title>基于Docker搭建比特币测试网络</title>
    <link href="https://webfuse.cn/2019/03/21/%E5%9F%BA%E4%BA%8EDocker%E6%90%AD%E5%BB%BA%E6%AF%94%E7%89%B9%E5%B8%81%E6%B5%8B%E8%AF%95%E7%BD%91%E7%BB%9C/"/>
    <id>https://webfuse.cn/2019/03/21/基于Docker搭建比特币测试网络/</id>
    <published>2019-03-21T13:50:00.000Z</published>
    <updated>2019-03-21T13:56:46.022Z</updated>
    
    <content type="html"><![CDATA[<h2 id="搭建比特币测试网络"><a href="#搭建比特币测试网络" class="headerlink" title="搭建比特币测试网络"></a>搭建比特币测试网络</h2><h3 id="下载比特币测试网络镜像"><a href="#下载比特币测试网络镜像" class="headerlink" title="下载比特币测试网络镜像"></a>下载比特币测试网络镜像</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">~  docker pull freewill/bitcoin-testnet-box</div></pre></td></tr></table></figure>
<p>运行<code>docker images</code>查看：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE</div><div class="line">freewil/bitcoin-testnet-box          latest              4dac47b76f09        4 months ago        262MB</div></pre></td></tr></table></figure>
<h3 id="运行Docker镜像"><a href="#运行Docker镜像" class="headerlink" title="运行Docker镜像"></a>运行Docker镜像</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">docker run -t -i -p 19001:19001 -p 19011:19011 freewil/bitcoin-testnet-box</div></pre></td></tr></table></figure>
<p>上述命令中的19001 和 19011是配置给两个节点提供RPC服务的端口。</p>
<h3 id="启动比特币测试网络"><a href="#启动比特币测试网络" class="headerlink" title="启动比特币测试网络"></a>启动比特币测试网络</h3><p>运行<code>make start</code>命令启动。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">tester@84fdf4019179 ~/bitcoin-testnet-box$ make start</div><div class="line">bitcoind -datadir=1  -daemon</div><div class="line">Bitcoin server starting</div><div class="line">bitcoind -datadir=2  -daemon</div><div class="line">Bitcoin server starting</div></pre></td></tr></table></figure>
<h3 id="查看节点信息"><a href="#查看节点信息" class="headerlink" title="查看节点信息"></a>查看节点信息</h3><p>使用<code>make getinfo</code>命令获得节点信息</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div></pre></td><td class="code"><pre><div class="line">tester@84fdf4019179 ~/bitcoin-testnet-box$ make getinfo</div><div class="line">bitcoin-cli -datadir=1  -getinfo</div><div class="line">&#123;</div><div class="line">  &quot;version&quot;: 160200,</div><div class="line">  &quot;protocolversion&quot;: 70015,</div><div class="line">  &quot;walletversion&quot;: 159900,</div><div class="line">  &quot;balance&quot;: 0.00000000,</div><div class="line">  &quot;blocks&quot;: 0,</div><div class="line">  &quot;timeoffset&quot;: 0,</div><div class="line">  &quot;connections&quot;: 1,</div><div class="line">  &quot;proxy&quot;: &quot;&quot;,</div><div class="line">  &quot;difficulty&quot;: 4.656542373906925e-10,</div><div class="line">  &quot;testnet&quot;: false,</div><div class="line">  &quot;keypoololdest&quot;: 1544519547,</div><div class="line">  &quot;keypoolsize&quot;: 1000,</div><div class="line">  &quot;paytxfee&quot;: 0.00000000,</div><div class="line">  &quot;relayfee&quot;: 0.00001000,</div><div class="line">  &quot;warnings&quot;: &quot;&quot;</div><div class="line">&#125;</div><div class="line">bitcoin-cli -datadir=2  -getinfo</div><div class="line">&#123;</div><div class="line">  &quot;version&quot;: 160200,</div><div class="line">  &quot;protocolversion&quot;: 70015,</div><div class="line">  &quot;walletversion&quot;: 159900,</div><div class="line">  &quot;balance&quot;: 0.00000000,</div><div class="line">  &quot;blocks&quot;: 0,</div><div class="line">  &quot;timeoffset&quot;: 0,</div><div class="line">  &quot;connections&quot;: 1,</div><div class="line">  &quot;proxy&quot;: &quot;&quot;,</div><div class="line">  &quot;difficulty&quot;: 4.656542373906925e-10,</div><div class="line">  &quot;testnet&quot;: false,</div><div class="line">  &quot;keypoololdest&quot;: 1544519547,</div><div class="line">  &quot;keypoolsize&quot;: 1000,</div><div class="line">  &quot;paytxfee&quot;: 0.00000000,</div><div class="line">  &quot;relayfee&quot;: 0.00001000,</div><div class="line">  &quot;warnings&quot;: &quot;&quot;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="初始化和测试区块链数据"><a href="#初始化和测试区块链数据" class="headerlink" title="初始化和测试区块链数据"></a>初始化和测试区块链数据</h2><h3 id="产生区块"><a href="#产生区块" class="headerlink" title="产生区块"></a>产生区块</h3><p>使用<code>make generate</code>命令产生一个区块。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">tester@84fdf4019179 ~/bitcoin-testnet-box$ make generate</div><div class="line">bitcoin-cli -datadir=1  generate 1</div><div class="line">[</div><div class="line">  &quot;793a86b807ac6595529bfcbd58118fe09706aa64e736fd9448f63cb7d38a9eb1&quot;</div><div class="line">]</div></pre></td></tr></table></figure>
<p>使用<code>make generate BLOCKS=数量</code>命令批量产生区块。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">tester@84fdf4019179 ~/bitcoin-testnet-box$ make generate BLOCKS=10</div><div class="line">bitcoin-cli -datadir=1  generate 10</div><div class="line">[</div><div class="line">  &quot;3a9d94273198bee2c448fcdeb2925a8e2685adc8f239f77fc64ce8b6f637b9aa&quot;,</div><div class="line">  &quot;3bd20765fa0162e1a17b2051c5b5711bfb271b25c88238f42a4351301d9e5ce6&quot;,</div><div class="line">  &quot;308e612999026677733ffd2954833e58d456e663e982037880f455ee5932c59c&quot;,</div><div class="line">  &quot;0b62ecc7990ac6770848655f10d4277d89df4ef1092fb7e25c0b2aa87a2bcbec&quot;,</div><div class="line">  &quot;5de9fbe62f7dc499568b41300610e3aa8b489a61375b3fa26717f65eab85392a&quot;,</div><div class="line">  &quot;14a000cae88c1bd881c06ba24a7a31364ff369578912e5a5abcf4023a0a28557&quot;,</div><div class="line">  &quot;414b9361debb64366cdf61b1acf5211e17b5f4f978ae5bfcad1aac916503e8a9&quot;,</div><div class="line">  &quot;75d1176835a12cf09cd51663fd7bd40e8e4fef15b392bf7e6ff94188398da041&quot;,</div><div class="line">  &quot;3d71ee9885fc9586013ce1c2954e197d726b25981c6977195e184fa90cdfa71d&quot;,</div><div class="line">  &quot;149dfcdc4cdfcbeeaf432bab90deca9a7b2b3935d359366ce652b071d6b72bb4&quot;</div><div class="line">]</div></pre></td></tr></table></figure>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://blog.csdn.net/yzpbright/article/details/81004202" target="_blank" rel="external">https://blog.csdn.net/yzpbright/article/details/81004202</a></p>
<p><a href="https://www.codeooze.com/blockchain/bitcoind-running-in-docker/" target="_blank" rel="external">https://www.codeooze.com/blockchain/bitcoind-running-in-docker/</a></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;h3 id=&quot;下载比特币测试网络镜像&quot;&gt;&lt;a href=&quot;#下载比特币测试网络镜像&quot; c
    
    </summary>
    
      <category term="区块链" scheme="https://webfuse.cn/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
    
      <category term="Docker" scheme="https://webfuse.cn/tags/Docker/"/>
    
      <category term="区块链" scheme="https://webfuse.cn/tags/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
      <category term="比特币" scheme="https://webfuse.cn/tags/%E6%AF%94%E7%89%B9%E5%B8%81/"/>
    
  </entry>
  
  <entry>
    <title>WebMvcConfigurationSupport和WebMvcConfigurer</title>
    <link href="https://webfuse.cn/2019/01/20/WebMvcConfigurationSupport%E5%92%8CWebMvcConfigurer/"/>
    <id>https://webfuse.cn/2019/01/20/WebMvcConfigurationSupport和WebMvcConfigurer/</id>
    <published>2019-01-20T07:41:33.000Z</published>
    <updated>2019-04-10T05:43:32.269Z</updated>
    
    <content type="html"><![CDATA[<p>SpringBoot帮我们做了很多的事情，但是有的时候会有自定义的Handler，Interceptor，ViewResolver，MessageConverter等，该怎么配置呢？为什么继承了WebMvcConfigurationSupport后有些配置会不生效呢？WebMvcConfigurer又是什么呢？</p>
<h2 id="WebMvcConfigurationSupport"><a href="#WebMvcConfigurationSupport" class="headerlink" title="WebMvcConfigurationSupport"></a>WebMvcConfigurationSupport</h2><p>我们继承WebMvcConfigurationSupport可以自定义SpringMvc的配置。</p>
<p>跟踪发现DelegatingWebMvcConfiguration类是WebMvcConfigurationSupport的一个实现类，DelegatingWebMvcConfiguration类的setConfigurers方法可以收集所有的WebMvcConfigurer实现类中的配置组合起来，组成一个超级配置（这些配置会覆盖掉默认的配置）。而@EnableWebMvc又引入了DelegatingWebMvcConfiguration。</p>
<p>所以，我们继承了WebMvcConfigurationSupport，而后使用@EnableWebMvc会覆盖掉原来的配置。</p>
<h2 id="WebMvcConfigurer"><a href="#WebMvcConfigurer" class="headerlink" title="WebMvcConfigurer"></a>WebMvcConfigurer</h2><p>WebMvcConfigurer配置类其实是Spring内部的一种配置方式，采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制。</p>
<p>在官方文档中有这么一段话：</p>
<p>&gt;</p>
<blockquote>
<p>If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.</p>
</blockquote>
<p>所以，如果我们想要在Auto-configuration的基础上配置自定义的interceptors, formatters, view controllers等功能话，我们可以实现WebMvcConfigurer，并用@Configuration注释。</p>
<p>WebMvcConfigurer的主要方法有：</p>
<ul>
<li>configurePathMatch：配置路由请求规则</li>
<li>configureContentNegotiation：内容协商配置</li>
<li>configureAsyncSupport</li>
<li>configureDefaultServletHandling：默认静态资源处理器</li>
<li>addFormatters：注册自定义转化器</li>
<li>addInterceptors：拦截器配置</li>
<li>addResourceHandlers：资源处理</li>
<li>addCorsMappings：CORS配置</li>
<li>addViewControllers：视图跳转控制器</li>
<li>configureViewResolvers：配置视图解析</li>
<li>addArgumentResolvers：添加自定义方法参数处理器</li>
<li>addReturnValueHandlers：添加自定义返回结果处理器</li>
<li>configureMessageConverters：配置消息转换器。重载会覆盖默认注册的HttpMessageConverter</li>
<li>extendMessageConverters：配置消息转换器。仅添加一个自定义的HttpMessageConverter.</li>
<li>configureHandlerExceptionResolvers：配置异常转换器</li>
<li>extendHandlerExceptionResolvers：添加异常转化器</li>
<li>getValidator</li>
<li>getMessageCodesResolver</li>
</ul>
<h2 id="使用方式"><a href="#使用方式" class="headerlink" title="使用方式"></a>使用方式</h2><ul>
<li>使用@EnableWebMvc注解 等于 扩展了WebMvcConfigurationSupport，但是没有重写任何方法</li>
<li>使用“extends WebMvcConfigurationSupport”方式（需要添加@EnableWebMvc），会屏蔽掉springBoot的@EnableAutoConfiguration中的设置</li>
<li>使用“implement WebMvcConfigurer”可以配置自定义的配置，同时也使用了@EnableAutoConfiguration中的设置</li>
<li>使用“implement WebMvcConfigurer + @EnableWebMvc”，会屏蔽掉springBoot的@EnableAutoConfiguration中的设置</li>
</ul>
<blockquote>
<p>这里的“@EnableAutoConfiguration中的设置”是指，读取 application.properties 或 application.yml 文件中的配置。</p>
</blockquote>
<p>所以，如果需要使用springBoot的@EnableAutoConfiguration中的设置，那么就只需要“implement WebMvcConfigurer”即可。如果，需要自己扩展同时不使用@EnableAutoConfiguration中的设置，可以选择另外的方式。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;SpringBoot帮我们做了很多的事情，但是有的时候会有自定义的Handler，Interceptor，ViewResolver，MessageConverter等，该怎么配置呢？为什么继承了WebMvcConfigurationSupport后有些配置会不生效呢？Web
    
    </summary>
    
      <category term="Spring" scheme="https://webfuse.cn/categories/Spring/"/>
    
      <category term="Spring Boot" scheme="https://webfuse.cn/categories/Spring/Spring-Boot/"/>
    
    
      <category term="实战" scheme="https://webfuse.cn/tags/%E5%AE%9E%E6%88%98/"/>
    
      <category term="Spring Boot" scheme="https://webfuse.cn/tags/Spring-Boot/"/>
    
  </entry>
  
  <entry>
    <title>基于Docker搭建以太坊私有网络</title>
    <link href="https://webfuse.cn/2018/12/18/%E5%9F%BA%E4%BA%8EDocker%E6%90%AD%E5%BB%BA%E4%BB%A5%E5%A4%AA%E5%9D%8A%E7%A7%81%E6%9C%89%E7%BD%91%E7%BB%9C/"/>
    <id>https://webfuse.cn/2018/12/18/基于Docker搭建以太坊私有网络/</id>
    <published>2018-12-18T09:37:38.000Z</published>
    <updated>2018-12-18T09:40:57.779Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Running-in-Docker"><a href="#Running-in-Docker" class="headerlink" title="Running in Docker"></a>Running in Docker</h2><p>参考<a href="https://github.com/ethereum/go-ethereum/wiki/Running-in-Docker" target="_blank" rel="external">官方的Running in Docker</a>，具体的解释如下：</p>
<h3 id="运行节点"><a href="#运行节点" class="headerlink" title="运行节点"></a>运行节点</h3><p>拉取镜像：</p>
<p><code>docker pull ethereum/client-go</code></p>
<p>启动一个节点：</p>
<p><code>docker run -it -p 30303:30303 ethereum/client-go</code></p>
<p>启动一个节点并在8545上运行JSON-RPC接口：</p>
<p><code>docker run -it -p 8545:8545 -p 30303:30303 ethereum/client-go --rpc --rpcaddr &quot;0.0.0.0&quot;</code></p>
<p>注意：“0.0.0.0”参数会在8545接口上接收所有主机发送的请求，公共网络慎用！</p>
<p>使用javascript控制台进行交互操作，可运行下命令启动节点：</p>
<p><code>docker run -it -p 30303:30303 ethereum/client-go console</code></p>
<h3 id="指定区块链数据存储位置"><a href="#指定区块链数据存储位置" class="headerlink" title="指定区块链数据存储位置"></a>指定区块链数据存储位置</h3><p><code>docker run -it -p 30303:30303 -v /path/on/host:/root/.ethereum ethereum/client-go</code></p>
<p>-v参数指定了存储的路径。上面的命令会将/root/.ethereum挂载到本地路径/path/on/host下面。这样，容器启动以后，数据的实际存储路径就在/path/on/host下。</p>
<h3 id="使用启动的节点"><a href="#使用启动的节点" class="headerlink" title="使用启动的节点"></a>使用启动的节点</h3><p>运行上面的命令后，节点会自动去拉取主网的区块数据。我们可以连接到运行的容器中去执行各个操作。</p>
<p>运行<code>docker ps -a</code>查看容器实例。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">~ docker ps -a</div><div class="line">CONTAINER ID        IMAGE                COMMAND             CREATED             STATUS                     PORTS               NAMES</div><div class="line">3791b8c1df57        ethereum/client-go   &quot;geth&quot;              19 minutes ago      Exited (0) 6 minutes ago                       eloquent_wiles</div></pre></td></tr></table></figure>
<p>执行命令<code>docker exec -it eloquent_wiles /bin/sh</code>，其中eloquent_wiles是容器的名字。这样，它就连接到了现在运行的容器了。</p>
<p>示例操作：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div></pre></td><td class="code"><pre><div class="line">/ # geth attach</div><div class="line">WARN [12-18|07:49:15.868] Sanitizing cache to Go&apos;s GC limits       provided=1024 updated=666</div><div class="line">Welcome to the Geth JavaScript console!</div><div class="line"></div><div class="line">instance: Geth/v1.8.19-unstable-9a000601/linux-amd64/go1.11.2</div><div class="line"> modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0</div><div class="line"></div><div class="line">&gt; eth.syncing</div><div class="line">&#123;</div><div class="line">  currentBlock: 11504,</div><div class="line">  highestBlock: 6907887,</div><div class="line">  knownStates: 40307,</div><div class="line">  pulledStates: 17241,</div><div class="line">  startingBlock: 725</div><div class="line">&#125;</div><div class="line">&gt; eth.blockNumber</div><div class="line">0</div><div class="line">&gt; net.peerCount</div><div class="line">2</div><div class="line">&gt; eth.syncing</div><div class="line">&#123;</div><div class="line">  currentBlock: 27043,</div><div class="line">  highestBlock: 6907887,</div><div class="line">  knownStates: 61031,</div><div class="line">  pulledStates: 40281,</div><div class="line">  startingBlock: 725</div><div class="line">&#125;</div><div class="line">&gt; exit</div></pre></td></tr></table></figure>
<p>默认情况下，当节点启动时会在 datadir 指定的目录之下，生成一个名字为 geth.ipc 的文件，当程序关闭时此文件随之消失。可以使用–ipcpath参数修改掉路径。可以配合 attach 命令来进入与 geth 节点进行 js 交互的窗口。基本命令为：<code>/geth attach rpc:/path/on/host/geth.ipc</code></p>
<p>关于eth.blockNumber返回0的原因可以查看：</p>
<ul>
<li><a href="https://github.com/ethereum/go-ethereum/issues/14338" target="_blank" rel="external">eth.blockNumber is 0 while syncing #14338</a></li>
<li><a href="https://github.com/ethereum/go-ethereum/issues/16147" target="_blank" rel="external">eth.getBlock(“latest”).number is always 0 #16147</a></li>
</ul>
<h3 id="个性化Docker启动命令"><a href="#个性化Docker启动命令" class="headerlink" title="个性化Docker启动命令"></a>个性化Docker启动命令</h3><p>先来一条执行的命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">docker run -it -d --name ethereum-node -p 8545:8545 -p 30303:30303 --network eth-network --ip 192.168.0.34 -v /path/on/host:/root/.ethereum ethereum/client-go --rinkeby --rpc --rpcaddr &quot;0.0.0.0&quot; --rpcapi &quot;admin,debug,eth,miner,net,personal,shh,txpool,web3,db&quot; --nodiscover --networkid 15 --fast --cache=512 --dev console 3&gt;&gt;eth.log&quot;</div></pre></td></tr></table></figure>
<h4 id="Docker参数"><a href="#Docker参数" class="headerlink" title="Docker参数"></a>Docker参数</h4><p>-it：交互式运行模式，-i 标准输入给容器，-t 分配一个虚拟终端</p>
<p>-d：以守护进程方式运行（后台）</p>
<p>-p：指定端口号</p>
<p>-P：随机分配端口号</p>
<p>–name：指定容器名称</p>
<p>–network：指定网络连接</p>
<p>–ip：分配ip地址</p>
<h4 id="Ethereum参数"><a href="#Ethereum参数" class="headerlink" title="Ethereum参数"></a>Ethereum参数</h4><p>–rinkeby：运行rinkeby网络，不设置默认为主网。</p>
<p>–rpc：启用HTTP-RPC服务</p>
<p>–rpcaddr：HTTP-RPC服务监听接口（默认：localhost）</p>
<p>–rpcapi：HTTP-RPC接口提供的api（默认：eth、net、web3）</p>
<p>–fast：快速同步模式启动Geth</p>
<p>–cache=512：内存容量分配</p>
<p>–dev：开发模式</p>
<p>–nodiscover：关闭节点自动发现，允许手动连接</p>
<p>–networkid：设置隔离网络（主网络id为1）</p>
<p>–verbosity 日志等级：0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail (default: 3)</p>
<p>console：进入JavaScript控制台</p>
<p>更多的参数可以见下节。</p>
<h2 id="geth命令"><a href="#geth命令" class="headerlink" title="geth命令"></a>geth命令</h2><p>geth的命令可以在客户端运行<code>geth -help</code>或者在<a href="https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options" target="_blank" rel="external">Command Line Options</a>中查看。</p>
<p>中文资料可以参考：<a href="https://learnblockchain.cn/2017/11/29/geth_cmd_options/" target="_blank" rel="external">以太坊客户端Geth命令用法-参数详解</a></p>
<h2 id="实战经验"><a href="#实战经验" class="headerlink" title="实战经验"></a>实战经验</h2><ul>
<li>关于数据目录。启动时一定要指定一个足够大硬盘的路径，不建议使用默认路径，此路径在后面使用的过程中会频繁用到，如果私钥文件没有单独存储，那么也将存储在此目录下。</li>
<li>keystore 目录下的文件一定要慎重保管，一旦丢失将永远丢失对应地址上的资产，它们和创建账户时设置的密码一一匹配。</li>
<li>在真实生产环境中 rpcapi 的 personal、admin 等级别较高的操作权限慎重开启，一旦使用不当会导致系统安全问题。</li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;Running-in-Docker&quot;&gt;&lt;a href=&quot;#Running-in-Docker&quot; class=&quot;headerlink&quot; title=&quot;Running in Docker&quot;&gt;&lt;/a&gt;Running in Docker&lt;/h2&gt;&lt;p&gt;参考&lt;a href=
    
    </summary>
    
      <category term="区块链" scheme="https://webfuse.cn/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
    
      <category term="Docker" scheme="https://webfuse.cn/tags/Docker/"/>
    
      <category term="区块链" scheme="https://webfuse.cn/tags/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
      <category term="以太坊" scheme="https://webfuse.cn/tags/%E4%BB%A5%E5%A4%AA%E5%9D%8A/"/>
    
  </entry>
  
  <entry>
    <title>《精通比特币》第二版</title>
    <link href="https://webfuse.cn/2018/12/14/%E3%80%8A%E7%B2%BE%E9%80%9A%E6%AF%94%E7%89%B9%E5%B8%81%E3%80%8B%E7%AC%AC%E4%BA%8C%E7%89%88/"/>
    <id>https://webfuse.cn/2018/12/14/《精通比特币》第二版/</id>
    <published>2018-12-14T06:33:06.000Z</published>
    <updated>2018-12-14T07:18:39.588Z</updated>
    
    <content type="html"><![CDATA[<p>为了方便查阅，将<a href="https://github.com/tianmingyun/MasterBitcoin2CN" target="_blank" rel="external">精通比特币</a>整理到这边。</p>
<p>目录如下：</p>
<ul>
<li><a href="/books/MasterBitcoin2CN/README.html">Introduction</a></li>
<li><a href="/books/MasterBitcoin2CN/cn-preface.html">中文版序言</a></li>
<li><a href="/books/MasterBitcoin2CN/trans-preface.html">译者序</a></li>
<li><a href="/books/MasterBitcoin2CN/preface.html">序言</a></li>
<li><a href="/books/MasterBitcoin2CN/second_edition_changes.html">第二版更新内容</a></li>
<li><a href="/books/MasterBitcoin2CN/glossary.html">词汇表</a></li>
<li><a href="/books/MasterBitcoin2CN/ch01.html">第一章介绍</a></li>
<li><a href="/books/MasterBitcoin2CN/ch02.html">第二章比特币原理</a></li>
<li><a href="/books/MasterBitcoin2CN/ch03.html">第三章比特币核心</a></li>
<li><a href="/books/MasterBitcoin2CN/ch04.html">第四章密钥和地址</a></li>
<li><a href="/books/MasterBitcoin2CN/ch05.html">第五章钱包</a></li>
<li><a href="/books/MasterBitcoin2CN/ch06.html">第六章交易</a></li>
<li><a href="/books/MasterBitcoin2CN/ch07.html">第七章高级交易和脚本</a></li>
<li><a href="/books/MasterBitcoin2CN/ch08.html">第八章比特币网络</a></li>
<li><a href="/books/MasterBitcoin2CN/ch09.html">第九章区块链</a></li>
<li><a href="/books/MasterBitcoin2CN/ch10.html">第十章挖矿和共识</a></li>
<li><a href="/books/MasterBitcoin2CN/ch11.html">第十一章比特币安全</a></li>
<li><a href="/books/MasterBitcoin2CN/ch12.html">第十二章比特币应用</a></li>
<li><a href="/books/MasterBitcoin2CN/appdx-bips.html">附录1：比特币改进提案（BIP）</a></li>
<li><a href="/books/MasterBitcoin2CN/appdx-bitcore.html">附录2：Bitcore</a></li>
<li><a href="/books/MasterBitcoin2CN/appdx-bx.html">附录3：Bitcoin Explorer（bx）命令</a></li>
<li><a href="/books/MasterBitcoin2CN/fappdx-pycoin.md">附录4：pycoin库、实用密钥程序ku和交易程序tx</a></li>
<li><a href="/books/MasterBitcoin2CN/appdx-scriptops.html">附录5 交易脚本的操作符、常量和符号</a></li>
<li><a href="/books/MasterBitcoin2CN/appdx-bitcoinwhitepaper.html">附录6 比特币白皮书</a></li>
<li><a href="/books/MasterBitcoin2CN/appdx-segwit.html">附录7 隔离见证</a></li>
<li><a href="/books/MasterBitcoin2CN/appdx-colored_coins.html">附录8 染色币</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;为了方便查阅，将&lt;a href=&quot;https://github.com/tianmingyun/MasterBitcoin2CN&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;精通比特币&lt;/a&gt;整理到这边。&lt;/p&gt;
&lt;p&gt;目录如下：&lt;/p&gt;
&lt;ul&gt;
&lt;l
    
    </summary>
    
      <category term="区块链" scheme="https://webfuse.cn/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
    
      <category term="区块链" scheme="https://webfuse.cn/tags/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
      <category term="BTC" scheme="https://webfuse.cn/tags/BTC/"/>
    
  </entry>
  
  <entry>
    <title>Docker基本命令</title>
    <link href="https://webfuse.cn/2018/11/14/Docker%E5%9F%BA%E6%9C%AC%E5%91%BD%E4%BB%A4/"/>
    <id>https://webfuse.cn/2018/11/14/Docker基本命令/</id>
    <published>2018-11-14T01:47:06.000Z</published>
    <updated>2019-04-10T01:45:15.224Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Docker基本命令"><a href="#Docker基本命令" class="headerlink" title="Docker基本命令"></a>Docker基本命令</h1><p>Docker命令分为两大类：客户端命令和服务端命令。前者主要是操作接口，后者用来启动Docker daemon。</p>
<p>客户端命令基本格式：<code>docker [OPTIONS] COMMAND [arg...]</code></p>
<p>服务端命令基本格式：<code>docker daemon  [OPTIONS]</code></p>
<h2 id="Docker客户端命令帮助"><a href="#Docker客户端命令帮助" class="headerlink" title="Docker客户端命令帮助"></a>Docker客户端命令帮助</h2><p>执行 <code>docker --help</code>命令可以列出Docker的使用帮助，具体如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div></pre></td><td class="code"><pre><div class="line">Usage: docker [OPTIONS] COMMAND</div><div class="line"></div><div class="line">A self-sufficient runtime for containers</div><div class="line"></div><div class="line">Options:</div><div class="line">      --config string      设置docker客户端的配置文件地址</div><div class="line">  -D, --debug              开启调试模式，true|false</div><div class="line">  -H, --host list          需要连接的虚拟机的接口</div><div class="line">  -l, --log-level string   设置日志的等级</div><div class="line">      --tls                是否使用TLS协议（安全传输层协议），true | false</div><div class="line">      --tlscacert string   tls CA签名的可信证书文件路径 </div><div class="line">      --tlscert string     TLS可信证书文件路径 </div><div class="line">      --tlskey string      TLS秘钥文件路径 </div><div class="line">      --tlsverify          是否启用TLS校验 true|false</div><div class="line">  -v, --version            输出版本信息并退出</div><div class="line"></div><div class="line">管理命令:</div><div class="line">  checkpoint  管理检查点</div><div class="line">  config      管理集群中的配置信息</div><div class="line">  container   管理容器</div><div class="line">  image       管理镜像</div><div class="line">  network     管理容器的网络，包括查看、创建、删除、挂载、卸载等</div><div class="line">  node        管理Docker的集群结点，包括查看、更新、删除、提升/取消管理节点等</div><div class="line">  plugin      管理插件</div><div class="line">  secret      管理Docker敏感数据</div><div class="line">  service     管理Docker服务，包括创建、更新、删除等</div><div class="line">  stack       管理Docker堆栈</div><div class="line">  swarm       管理docker集群，包括创建、加入、退出、更新等</div><div class="line">  system      管理Docker</div><div class="line">  trust       管理Docker镜像的信任问题</div><div class="line">  volume      管理docker的数据容器，包括查看、创建、删除等</div><div class="line"></div><div class="line">命令:</div><div class="line">  attach      添加到一个正在运行的容器</div><div class="line">  build       通过dockerfile创建一个镜像</div><div class="line">  commit      从容器的变更中生成一个新镜像</div><div class="line">  cp          在容器和宿主文件系统中复制文件和文件夹</div><div class="line">  create      创建一个新的容器(只创建不允许)</div><div class="line">  deploy      部署新堆栈或更新现有堆栈</div><div class="line">  diff        在容器中进行文件对比</div><div class="line">  events      获取服务器的实时事件</div><div class="line">  exec        在容器中运行命令</div><div class="line">  export      将一个容器的文件以tar压缩包的形式导出</div><div class="line">  history     显示镜像的历史记录</div><div class="line">  images      显示镜像列表</div><div class="line">  import      从本地文件中导入镜像</div><div class="line">  info        显示系统层的信息</div><div class="line">  inspect     显示更底层的容器、镜像和任务信息</div><div class="line">  kill        杀掉一个或多个正在运行的容器</div><div class="line">  load        加载容器</div><div class="line">  login       登录到一个镜像仓库</div><div class="line">  logout      退出镜像仓库</div><div class="line">  logs        获取容器的日志</div><div class="line">  pause       停止容器的进程</div><div class="line">  port        显示出容器的所有端口</div><div class="line">  ps          显示容器列表</div><div class="line">  pull        从镜像仓库中拉取一个镜像</div><div class="line">  push        将一个镜像推送到一个镜像仓库</div><div class="line">  rename      重命名一个容器</div><div class="line">  restart     重新启动容器</div><div class="line">  rm          移除容器</div><div class="line">  rmi         移除镜像</div><div class="line">  run         在容器中运行命令</div><div class="line">  save        将容器保存为一个压缩包</div><div class="line">  search      在docker hub中搜索镜像</div><div class="line">  start       启动容器</div><div class="line">  stats       显示实时的容器状态</div><div class="line">  stop        停止容器</div><div class="line">  tag         给镜像加标签</div><div class="line">  top         显示容器正在运行的进程</div><div class="line">  unpause     恢复容器中的所有进程</div><div class="line">  update      更新容器中的配置</div><div class="line">  version     显示docker的版本信息</div><div class="line">  wait        阻塞进程一直到容器被停止</div><div class="line"></div><div class="line">Run &apos;docker COMMAND --help&apos; for more information on a command.</div></pre></td></tr></table></figure>
<p>参考：</p>
<p><a href="https://blog.csdn.net/itguangit/article/details/80246071" target="_blank" rel="external">docker常用命令总结</a></p>
<p><a href="https://segmentfault.com/a/1190000012063374" target="_blank" rel="external">Docker常用命令</a></p>
<p><a href="https://www.cnblogs.com/hcduguo/p/5150807.html" target="_blank" rel="external">Docker常用命令大全</a></p>
<p><a href="https://docker_practice.gitee.io/" target="_blank" rel="external">Docker从入门到实践</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;Docker基本命令&quot;&gt;&lt;a href=&quot;#Docker基本命令&quot; class=&quot;headerlink&quot; title=&quot;Docker基本命令&quot;&gt;&lt;/a&gt;Docker基本命令&lt;/h1&gt;&lt;p&gt;Docker命令分为两大类：客户端命令和服务端命令。前者主要是操作接口，后者
    
    </summary>
    
      <category term="Docker" scheme="https://webfuse.cn/categories/Docker/"/>
    
    
      <category term="Docker" scheme="https://webfuse.cn/tags/Docker/"/>
    
  </entry>
  
  <entry>
    <title>SpringFramework常用的注解说明</title>
    <link href="https://webfuse.cn/2018/09/28/SpringFramework%E5%B8%B8%E7%94%A8%E7%9A%84%E6%B3%A8%E8%A7%A3%E8%AF%B4%E6%98%8E/"/>
    <id>https://webfuse.cn/2018/09/28/SpringFramework常用的注解说明/</id>
    <published>2018-09-28T10:59:34.000Z</published>
    <updated>2019-04-10T05:43:24.055Z</updated>
    
    <content type="html"><![CDATA[<p>在进行SpringBoot开发的时候经常要用到注解，有的时候很容易忘记某个注解的意思，现将一些常用的注解说明归纳如下：</p>
<table>
<thead>
<tr>
<th>注解</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>@Required</td>
<td>@Required注解检查。但它只检查属性是否已经设置而不会测试属性是否非空。@Required只能设置在setter方法上</td>
</tr>
<tr>
<td>@Autowired</td>
<td>Spring提供的注入工具【由Spring的依赖注入工具（BeanPostProcessor、BeanFactoryPostProcessor）自动注入】</td>
</tr>
<tr>
<td>@Qualifier</td>
<td>如果需要byName（byName就是通过id去标识）注入，增加@Qualifier注释。一般在候选Bean数目不为1时应该加@Qualifier注释。</td>
</tr>
<tr>
<td>@Configuration</td>
<td>在用于指定配置信息的类上加上 @Configuration 注解，以明确指出该类是 Bean 配置的信息源。</td>
</tr>
<tr>
<td>@ComponentScan</td>
<td>@ComponentScan告诉Spring 哪个packages 的用注解标识的类 会被spring自动扫描并且装入bean容器</td>
</tr>
<tr>
<td>@Bean</td>
<td>@Bean注解用于告诉方法，产生一个Bean对象，然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次，随后这个Spring将会将这个Bean对象放在自己的IOC容器中。</td>
</tr>
<tr>
<td>@Lazy</td>
<td>如果我们想要 Spring 在启动的时候延迟加载 bean，即在调用某个 bean 的时候再去初始化，那么就可以使用 @Lazy 注解。</td>
</tr>
<tr>
<td>@Value</td>
<td>使用@Value注解，可以直接将属性值注入到beans中。</td>
</tr>
<tr>
<td>@Resource</td>
<td>@Resource用法与@Autowired 用法 用法相似，也是做依赖注入的，从容器中自动获取bean。</td>
</tr>
<tr>
<td>@Inject</td>
<td>这是jsr330中的规范，通过‘AutowiredAnnotationBeanPostProcessor’ 类实现的依赖注入。</td>
</tr>
<tr>
<td>@PropertySource</td>
<td>指定文件地址。提供了一种方便的、声明性的机制，用于向Spring的环境添加PropertySource。与@configuration类一起使用。</td>
</tr>
<tr>
<td>@PostConstruct</td>
<td>标注在方法上，该方法在构造函数执行完成之后执行。</td>
</tr>
<tr>
<td>@PreDestroy</td>
<td>标注在方法上，该方法在对象销毁之前执行。</td>
</tr>
<tr>
<td>@ActiveProfiles</td>
<td>用来声明活动的profile–@ActiveProfiles(“prod”(这个prod定义在配置类中))</td>
</tr>
<tr>
<td>@Profile</td>
<td>表示当一个或多个指定的文件是活动的时，一个组件是有资格注册的。使用@Profile注解类或者方法，达到在不同情况下选择实例化不同的Bean。@Profile(“dev”)表示为dev时实例化。</td>
</tr>
<tr>
<td>@Component</td>
<td>表示一个带注释的类是一个“组件”，成为Spring管理的Bean。当使用基于注解的配置和类路径扫描时，这些类被视为自动检测的候选对象。同时@Component还是一个元注解。</td>
</tr>
<tr>
<td>@Controller</td>
<td>组合注解（组合了@Component注解），应用在MVC层（控制层）,DispatcherServlet会自动扫描注解了此注解的类，然后将web请求映射到注解了@RequestMapping的方法上。</td>
</tr>
<tr>
<td>@Service</td>
<td>组合注解（组合了@Component注解），应用在service层（业务逻辑层）</td>
</tr>
<tr>
<td>@Reponsitory</td>
<td>组合注解（组合了@Component注解），应用在dao层（数据访问层）</td>
</tr>
<tr>
<td>@RestController</td>
<td>@RestController注解相当于@ResponseBody ＋ @Controller合在一起的作用。</td>
</tr>
<tr>
<td>@ResponseBody</td>
<td>将返回值放在response体内。返回的是数据而不是页面</td>
</tr>
<tr>
<td>@RequestBody</td>
<td>允许request的参数在request体中，而不是在直接链接在地址的后面。此注解放置在参数前。</td>
</tr>
<tr>
<td>@PathVariable</td>
<td>放置在参数前，用来接受路径参数。</td>
</tr>
<tr>
<td>@ModelAttribute</td>
<td>将键值对添加到全局，所有注解了@RequestMapping的方法可获得次键值对（就是在请求到达之前，往model里addAttribute一对name-value而已）。</td>
</tr>
<tr>
<td>@RequestAttribute</td>
<td>注解<code>@RequestAttribute</code>可以被用于访问由过滤器或拦截器创建的、预先存在的请求属性</td>
</tr>
<tr>
<td>@RequestHeader</td>
<td>获得指定的请求中的Header信息</td>
</tr>
<tr>
<td>@RequestParam</td>
<td>请求参数绑定</td>
</tr>
<tr>
<td>@ResponseStatus</td>
<td>带有@ResponseStatus注解的异常类会被ResponseStatusExceptionResolver 解析。可以实现自定义的一些异常,同时在页面上进行显示。</td>
</tr>
<tr>
<td>@CookieValue</td>
<td>用来获取Cookie中的值</td>
</tr>
<tr>
<td>@CrossOrigin</td>
<td>@CrossOrigin是用来处理跨域请求的注解</td>
</tr>
<tr>
<td>@RequestMapping</td>
<td>用来映射web请求（访问路径和参数），处理类和方法的。可以注解在类和方法上，注解在方法上的@RequestMapping路径会继承注解在类上的路径。同时支持Serlvet的request和response作为参数，也支持对request和response的媒体类型进行配置。其中有value(路径)，produces(定义返回的媒体类型和字符集)，method(指定请求方式)等属性。</td>
</tr>
<tr>
<td>@GetMapping</td>
<td>GET请求</td>
</tr>
<tr>
<td>@PostMapping</td>
<td>POST请求</td>
</tr>
<tr>
<td>@PutMapping</td>
<td>PUT请求</td>
</tr>
<tr>
<td>@PatchMapping</td>
<td>PATCH请求</td>
</tr>
<tr>
<td>@DeleteMapping</td>
<td>DELETE请求</td>
</tr>
<tr>
<td>@ExceptionHandler</td>
<td>用在方法上定义全局处理，通过他的value属性可以过滤拦截的条件：@ExceptionHandler(value=Exception.class)–表示拦截所有的Exception。</td>
</tr>
<tr>
<td>@ControllerAdvice</td>
<td>用在类上，声明一个控制器建言，它也组合了@Component注解，会自动注册为Spring的Bean</td>
</tr>
<tr>
<td>@InitBinder</td>
<td>通过@InitBinder注解定制WebDataBinder（用在方法上，方法有一个WebDataBinder作为参数，用WebDataBinder在方法内定制数据绑定，例如可以忽略request传过来的参数Id等）。</td>
</tr>
<tr>
<td>@SessionAttribute</td>
<td>@SessionAttribute作用于处理器类上，用于在多个请求之间传递参数，类似于Session的Attribute，但不完全一样，一般来说@SessionAttribute设置的参数只用于暂时的传递，而不是长期的保存，长期保存的数据还是要放到Session中。</td>
</tr>
<tr>
<td>@SessionAttributes</td>
<td>@sessionattributes注解应用到Controller上面，可以将Model中的属性同步到session当中。</td>
</tr>
<tr>
<td>@Aspect</td>
<td>声明一个切面（就是说这是一个额外功能）</td>
</tr>
<tr>
<td>@After</td>
<td>后置建言（advice），在原方法前执行。</td>
</tr>
<tr>
<td>@Before</td>
<td>前置建言（advice），在原方法后执行。</td>
</tr>
<tr>
<td>@Around</td>
<td>环绕建言（advice），在原方法执行前执行，在原方法执行后再执行（@Around可以实现其他两种advice）</td>
</tr>
<tr>
<td>@PointCut</td>
<td>声明切点，即定义拦截规则，确定有哪些方法会被切入</td>
</tr>
<tr>
<td>@EnableAspectJAutoProxy</td>
<td>开启Spring对AspectJ的支持</td>
</tr>
<tr>
<td>@SpingBootApplication</td>
<td>SpringBoot的核心注解，主要目的是开启自动配置。它也是一个组合注解，主要组合了@Configurer，@EnableAutoConfiguration（核心）和@ComponentScan。可以通过@SpringBootApplication(exclude={想要关闭的自动配置的类名.class})来关闭特定的自动配置。</td>
</tr>
<tr>
<td>@Async</td>
<td>注解在方法上标示这是一个异步方法，在类上标示这个类所有的方法都是异步方法。</td>
</tr>
<tr>
<td>@EnableAsync</td>
<td>开启异步任务支持。注解在配置类上。</td>
</tr>
<tr>
<td>@Scheduled</td>
<td>注解在方法上，声明该方法是计划任务。支持多种类型的计划任务：cron,fixDelay,fixRate</td>
</tr>
<tr>
<td>@EnableScheduling</td>
<td>注解在配置类上，开启对计划任务的支持。</td>
</tr>
<tr>
<td>@EnableAutoConfiguration</td>
<td>此注释自动载入应用程序所需的所有Bean——这依赖于Spring Boot在类路径中的查找。该注解组合了@Import注解，@Import注解导入了EnableAutoCofigurationImportSelector类，它使用SpringFactoriesLoader.loaderFactoryNames方法来扫描具有META-INF/spring.factories文件的jar包。而spring.factories里声明了有哪些自动配置。</td>
</tr>
<tr>
<td>@WebAppConfiguration</td>
<td>一般用在测试上，注解在类上，用来声明加载的ApplicationContext是一个WebApplicationContext。他的属性指定的是Web资源的位置，默认为src/main/webapp,我们可以修改为：@WebAppConfiguration(“src/main/resources”)。</td>
</tr>
<tr>
<td>@Cacheable</td>
<td>声明数据缓存</td>
</tr>
<tr>
<td>@EnableWebMvc</td>
<td>用在配置类上，开启SpringMvc的Mvc的一些默认配置：如ViewResolver，MessageConverter等。同时在自己定制SpringMvc的相关配置时需要做到两点：1.配置类继承WebMvcConfigurerAdapter类<br>2.就是必须使用这个@EnableWebMvc注解。</td>
</tr>
<tr>
<td>@BeforeTransaction</td>
<td>@BeforeTransaction在事务之前执行</td>
</tr>
<tr>
<td>@AfterTransaction</td>
<td>@AfterTransaction在事务之后执行</td>
</tr>
<tr>
<td>@Transactional</td>
<td>声明事务（一般默认配置即可满足要求，当然也可以自定义）</td>
</tr>
<tr>
<td>@ImportResource</td>
<td>虽然Spring提倡零配置，但是还是提供了对xml文件的支持，这个注解就是用来加载xml配置的。</td>
</tr>
<tr>
<td>@ConfigurationProperties</td>
<td>将properties属性与一个Bean及其属性相关联，从而实现类型安全的配置。</td>
</tr>
<tr>
<td>@Conditional</td>
<td>根据满足某一特定条件创建特定的Bean</td>
</tr>
<tr>
<td>@ConditionalOnBean</td>
<td>条件注解。当容器里有指定Bean的条件下。</td>
</tr>
<tr>
<td>@ConditionalOnClass</td>
<td>条件注解。当类路径下有指定的类的条件下。</td>
</tr>
<tr>
<td>@ConditionalOnExpression</td>
<td>条件注解。基于SpEL表达式作为判断条件。</td>
</tr>
<tr>
<td>@ConditionalOnJava</td>
<td>条件注解。基于JVM版本作为判断条件。</td>
</tr>
<tr>
<td>@ConditionalOnJndi</td>
<td>条件注解。在JNDI存在的条件下查找指定的位置。</td>
</tr>
<tr>
<td>@ConditionalOnMissingBean</td>
<td>条件注解。当容器里没有指定Bean的情况下。</td>
</tr>
<tr>
<td>@ConditionalOnMissingClass</td>
<td>条件注解。当类路径下没有指定的类的情况下。</td>
</tr>
<tr>
<td>@ConditionalOnNotWebApplication</td>
<td>条件注解。当前项目不是web项目的条件下。</td>
</tr>
<tr>
<td>@ConditionalOnResource</td>
<td>条件注解。类路径是否有指定的值。</td>
</tr>
<tr>
<td>@ConditionalOnSingleCandidate</td>
<td>条件注解。当指定Bean在容器中只有一个，后者虽然有多个但是指定首选的Bean。</td>
</tr>
<tr>
<td>@ConditionalOnWebApplication</td>
<td>条件注解。当前项目是web项目的情况下。</td>
</tr>
<tr>
<td>@EnableConfigurationProperties</td>
<td>注解在类上，声明开启属性注入，使用@Autowired注入。</td>
</tr>
<tr>
<td>@AutoConfigureAfter</td>
<td>在指定的自动配置类之后再配置。</td>
</tr>
<tr>
<td>@AutoConfigureBefore</td>
<td>在指定的自动配置类之前进行配置。</td>
</tr>
<tr>
<td>@RunWith</td>
<td>这个是Junit的注解，springboot集成了junit。一般在测试类里使用。</td>
</tr>
<tr>
<td>@ContextConfiguration</td>
<td>用来加载配置ApplicationContext，其中classes属性用来加载配置</td>
</tr>
</tbody>
</table>
<hr>
<p>参考：</p>
<ul>
<li><a href="https://blog.csdn.net/weixin_37490221/article/details/78406810" target="_blank" rel="external">Spring 注解大全与详解</a></li>
<li><a href="https://skyao.gitbooks.io/learning-spring-boot/content/" target="_blank" rel="external">Spring Boot学习笔记</a></li>
<li><a href="https://springframework.guru/spring-framework-annotations/" target="_blank" rel="external">SPRING FRAMEWORK ANNOTATIONS</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在进行SpringBoot开发的时候经常要用到注解，有的时候很容易忘记某个注解的意思，现将一些常用的注解说明归纳如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;注解&lt;/th&gt;
&lt;th&gt;解释&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;

    
    </summary>
    
      <category term="Spring" scheme="https://webfuse.cn/categories/Spring/"/>
    
      <category term="Spring Boot" scheme="https://webfuse.cn/categories/Spring/Spring-Boot/"/>
    
    
      <category term="Spring" scheme="https://webfuse.cn/tags/Spring/"/>
    
      <category term="Spring Boot" scheme="https://webfuse.cn/tags/Spring-Boot/"/>
    
  </entry>
  
</feed>
