<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>欣欣然不知所以</title>
  
  <subtitle>......</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://ipiao.top/"/>
  <updated>2020-06-24T03:33:12.025Z</updated>
  <id>http://ipiao.top/</id>
  
  <author>
    <name>ipiao</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>github居然被fork了！</title>
    <link href="http://ipiao.top/2020/06/24/github%E5%B1%85%E7%84%B6%E8%A2%ABfork%E4%BA%86%EF%BC%81/"/>
    <id>http://ipiao.top/2020/06/24/github居然被fork了！/</id>
    <published>2020-06-24T03:26:00.000Z</published>
    <updated>2020-06-24T03:33:12.025Z</updated>
    
    <content type="html"><![CDATA[<blockquote>  <p>没有任何的宣传（上论坛社区贴链接什么的），居然有人fork或star了我的github项目。没什么特别的意思，就是有点意外的高兴，虽然人数不多</p></blockquote><ul>  <li><img src="/images/pasted-20.png" alt="upload successful"></li></ul><p><img src="/images/pasted-21.png" alt="upload successful"></p><ul>  <li>    <p><img src="/images/pasted-22.png" alt="upload successful"><br><img src="/images/pasted-23.png" alt="upload successful"></p>  </li></ul><p>-</p><p><img src="/images/pasted-24.png" alt="upload successful"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
  &lt;p&gt;没有任何的宣传（上论坛社区贴链接什么的），居然有人fork或star了我的github项目。没什么特别的意思，就是有点意外的高兴，虽然人数不多&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;img src=&quot;/images/pas
      
    
    </summary>
    
      <category term="闲文野曲" scheme="http://ipiao.top/categories/%E9%97%B2%E6%96%87%E9%87%8E%E6%9B%B2/"/>
    
    
      <category term="其他" scheme="http://ipiao.top/tags/%E5%85%B6%E4%BB%96/"/>
    
  </entry>
  
  <entry>
    <title>开发过程遇到的一些问题排查</title>
    <link href="http://ipiao.top/2020/06/22/%E5%BC%80%E5%8F%91%E8%BF%87%E7%A8%8B%E9%81%87%E5%88%B0%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/"/>
    <id>http://ipiao.top/2020/06/22/开发过程遇到的一些问题排查/</id>
    <published>2020-06-22T09:41:00.000Z</published>
    <updated>2020-06-22T09:42:07.470Z</updated>
    
    <content type="html"><![CDATA[<h4 id="开发过程中遇到的问题"><a href="#开发过程中遇到的问题" class="headerlink" title="开发过程中遇到的问题"></a>开发过程中遇到的问题</h4><ol>  <li>    <p>Kafka日志传输网络延迟导致的服务假死。</p>    <blockquote>      <p>原因：zap框架挂载了kafka日志传输，是同步模式。由于网络延迟，kafka日志不能及时完成传输，导致业务代码产生延迟。</p>      <p>场景: 同事首先发现问题是在某个TCP写的位置，本地正常，初步怀疑是TCP写延迟，但是并未明确</p>      <p>排查过程:</p>      <ol>        <li>          <p>TCP读顺利无延迟,写最终能被接收，初步排除网络连接方面问题</p>          <ol start="2">            <li>pprof, 火焰图heap,cpufile均无明显异常，goroutine没有明显增高(实际有异常增多，但是当时用户量不高，没有发现，只是以为用户量引起的goroutine变化)</li>            <li>前后打日志（本地正常，所以无法对服务器做断点测试），标准输出或文本输出都正常,但是在write之后的日志出来的慢</li>            <li>疑惑…仍然怀疑TCP问题，但是稍加思考后还是否定了</li>            <li>只有日志模块（当时基本没有怀疑日志），没有查看ELK日志，因为一开始不知道使用了kafka日志传输</li>            <li>查看日志模块代码，有挂载kafka日志。查看配置，生产的连接写成了测试的连接地址，服务器在国外。</li>          </ol>        </li>      </ol>      <p>解决方案: 修改连接，顺便将kafka的传输方式改成异步。 </p>    </blockquote>  </li>  <li>    <p>Kafka日志传输造成的服务“停止”</p>    <blockquote>      <p>原因：kafka服务没有开启</p>      <p>场景：客户端大量接收不到回包，日志稀少(但是阅读代码发现服务在大部分情况下确实不打日志)。但是过一段时间（半小时到1小时），几乎所有用户连接不上，telnet通过。</p>      <p>排查过程：</p>      <pre><code>1. 查询逻辑，发现没有明显会造成阻塞的情况2. 在相关消息入口增加日志，重启服务后发现过一段时间后重现，日志不打印3. 怀疑日志问题，查看日志模块4. 发现kafka挂载，查看kafka异步代码，阻塞通道满</code></pre>      <p>总结: 这两次其实通过异常goroutine的排场是可以直接从pprof中定位到问题所在的，但是由于对他人项目的不熟，加上一眼看上去没有问题的心理，没有充分使用到pprof分析，导致后来依赖阅读代码的来排查。</p>    </blockquote>  </li>  <li>    <p>连接指针未释放造成的最终内存持续累积</p>    <blockquote>      <p>原因: 连接未释放，早期read方法有点奇葩，好像是先分配一个指定大小的 byte 数组再去读数据的（具体不记得了）</p>      <p>场景: 内存持续缓慢增长，heap数量只增不减</p>      <p>排查过程: </p>      <pre><code>1. 通过pprof生成heap文件，图形化显示下查询到内存占用最多的地方累积在read方法。2. read的奇葩读法，但是read每次分配好像是4M,,依照当时的用户量以及15超时清理分钟，也不应该达到那么大累积量3. 继续查看pprof文件，发现有大量的Client连接，考虑到15分钟断开可能有所累积，但是结合web图的引导，确实是连接下的read积累了最多的内存4. 怀疑连接没有正确释放造成(这里是无法直接得出哪块代码有问题的)5. 查询代码，发现客户端和用户管理混乱，让其自己按照这个方向排查代码6. 得出问题，是在服务器异常断开后，没有EOF，新连接替代了旧连接，然后在用户管理下的超市清理处理不到这部分链接，被一直保存到connMap中，其read goroutine 也无法断开</code></pre>    </blockquote>  </li>  <li>    <p>客户端允许body过长造成的内存突增</p>    <blockquote>      <p>原因:socket分包的时候通过协议header的bodyLen分配内存，客户端由于某些错误传递了很大的bodyLen使得内存突增</p>      <p>场景:监控服务报警，内存突增。</p>      <p>排查过程:</p>      <pre><code>1. 由于有上述的经验，首先怀疑是read时候分配内存堆积造成的，实际上确实如此，但有所不同的是当前是徒增，而且没有出现指针不释放的问题2. 直接查看源码，发现bodyLen的判断是小于MaxInt323. 直接依靠经验判断是bodyLen传入过大(而且基于最近出现了客户端连接异常的情况，发数据无响应等)。</code></pre>    </blockquote>  </li>  <li>    <p>客户端总是因心跳超时而被清理(早期设置心跳超时是30秒，没有设置readTimeout而是服务器根据心跳时间判断清理)</p>    <blockquote>      <p>原因: mars框架对相同数据发送的处理</p>      <p>排查过程: </p>      <pre><code>1. debug模式下查看心跳日志，日志显示心跳在一段时间后(每五秒)会停止，客户端怀疑是服务端业务处理延迟。2. 通过tcpdum抓包，明确服务端业务处理没有问题3. 怀疑是tcp纳格算法造成的小数据包延迟4. 设置了TCPNODELAY依然会出现5. 敦促客户端自查源码，发现mars框架相同的数据传送多少次之后就不再传送</code></pre>    </blockquote>  </li></ol><h4 id="总结"><a href="#总结" class="headerlink" title="总结:"></a>总结:</h4><pre><code>1.  服务端的代码框架是自己开发的，所以流程十分清晰，日志齐全，排查定位问题简单2.  设计TCP的知识略有疑惑，但所幸通过baidu能及时解决3.  当时和客户端商议提出一起写一份属于自己公司的通用框架模版，结果自己花费月余时间收集整理，客户端却直套了第三方，只是做业务处理上的框架，导致开发过程中不断要兼容客户端的处理方式，十分痛苦。但是也正是因为是自己开发的框架，在变更处理逻辑的时候十分顺畅。4.  日志是很重要的，尽管有pprof可以排查问题，但是很多时候自己还是忽略了pprof的某些信息导致没有排查到问题，这样清晰的日志也可以很快帮忙定位问题。</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;开发过程中遇到的问题&quot;&gt;&lt;a href=&quot;#开发过程中遇到的问题&quot; class=&quot;headerlink&quot; title=&quot;开发过程中遇到的问题&quot;&gt;&lt;/a&gt;开发过程中遇到的问题&lt;/h4&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Kafka日志传输网络延迟导致的服务假死。
      
    
    </summary>
    
      <category term="golang大法好" scheme="http://ipiao.top/categories/golang%E5%A4%A7%E6%B3%95%E5%A5%BD/"/>
    
    
      <category term="go" scheme="http://ipiao.top/tags/go/"/>
    
  </entry>
  
  <entry>
    <title>GODEBUG</title>
    <link href="http://ipiao.top/2020/03/30/GODEBUG/"/>
    <id>http://ipiao.top/2020/03/30/GODEBUG/</id>
    <published>2020-03-30T08:19:00.000Z</published>
    <updated>2020-03-30T08:21:11.126Z</updated>
    
    <content type="html"><![CDATA[<h3 id="GODEBUG"><a href="#GODEBUG" class="headerlink" title="GODEBUG"></a>GODEBUG</h3><h4 id="GODEBUG-gctrace-1-打印gc信息"><a href="#GODEBUG-gctrace-1-打印gc信息" class="headerlink" title="GODEBUG=gctrace=1 打印gc信息"></a>GODEBUG=gctrace=1 打印gc信息</h4><figure class="highlight sh">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br></pre>      </td>      <td class="code">        <pre><span class="line">gc <span class="comment"># @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #-&gt;#-&gt;# MB, # MB goal, # P</span></span><br></pre>      </td>    </tr>  </table></figure><h3 id="含义"><a href="#含义" class="headerlink" title="含义"></a>含义</h3><ul>  <li><code>gc#</code>：GC 执行次数的编号，每次叠加。</li>  <li><code>@#s</code>：自程序启动后到当前的具体秒数。</li>  <li><code>#%</code>：自程序启动以来在GC中花费的时间百分比。</li>  <li><code>#+...+#</code>：GC 的标记工作共使用的 CPU 时间占总 CPU 时间的百分比。</li>  <li><code>#-&gt;#-&gt;# MB</code>：分别表示 GC 启动时, GC 结束时, GC 活动时的堆大小.</li>  <li><code>#MB goal</code>：下一次触发 GC 的内存占用阈值。</li>  <li><code>#P</code>：当前使用的处理器 P 的数量。</li></ul><h4 id="schedtrace"><a href="#schedtrace" class="headerlink" title="schedtrace"></a>schedtrace</h4><ol>  <li>设置 <code>schedtrace=X</code> 参数可以使运行时在每 X 毫秒发出一行调度器的摘要信息到标准 err 输出中。</li></ol><blockquote>  <p>输出:</p>  <p>sched：每一行都代表调度器的调试信息，后面提示的毫秒数表示启动到现在的运行时间，输出的时间间隔受 <code>schedtrace</code> 的值影响。</p>  <p>gomaxprocs：当前的 CPU 核心数（GOMAXPROCS 的当前值）。</p>  <p>idleprocs：空闲的处理器数量，后面的数字表示当前的空闲数量。</p>  <p>threads：OS 线程数量，后面的数字表示当前正在运行的线程数量。</p>  <p>spinningthreads：自旋状态的 OS 线程数量。</p>  <p>idlethreads：空闲的线程数量。</p>  <p>runqueue：全局队列中中的 Goroutine 数量，而后面的 [0 0 1 1] 则分别代表这 4 个 P 的本地队列正在运行的 Goroutine 数量。</p></blockquote><ol>  <li>设置 <code>schedtrace=X</code> 和 <code>scheddetail=1</code> 可以使运行时在每 X 毫秒发出一次详细的多行信息，信息内容主要包括调度程序、处理器、OS 线程 和 Goroutine 的状态</li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;GODEBUG&quot;&gt;&lt;a href=&quot;#GODEBUG&quot; class=&quot;headerlink&quot; title=&quot;GODEBUG&quot;&gt;&lt;/a&gt;GODEBUG&lt;/h3&gt;
&lt;h4 id=&quot;GODEBUG-gctrace-1-打印gc信息&quot;&gt;&lt;a href=&quot;#GODEBUG-
      
    
    </summary>
    
      <category term="golang大法好" scheme="http://ipiao.top/categories/golang%E5%A4%A7%E6%B3%95%E5%A5%BD/"/>
    
    
      <category term="go" scheme="http://ipiao.top/tags/go/"/>
    
  </entry>
  
  <entry>
    <title>Raft算法</title>
    <link href="http://ipiao.top/2020/03/19/Raft%E7%AE%97%E6%B3%95/"/>
    <id>http://ipiao.top/2020/03/19/Raft算法/</id>
    <published>2020-03-19T10:05:00.000Z</published>
    <updated>2020-03-24T02:00:37.248Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Raft"><a href="#Raft" class="headerlink" title="Raft"></a>Raft</h2><blockquote>  <ol>    <li>raft基本原理演示请参考 <a href="http://thesecretlivesofdata.com/raft/" target="_blank" rel="noopener">http://thesecretlivesofdata.com/raft/</a></li>    <li>go语言的raft实现 <a href="https://github.com/goraft/raft" target="_blank" rel="noopener">https://github.com/goraft/raft</a>, 但是该项目早已不再维护，原作者将raft融入到etcd和influxdb(现在貌似不是了)里了，这里摘出etcd的raft实现进行研究</li>  </ol></blockquote><a id="more"></a><h4 id="这是etcd-raft包的文档文件"><a href="#这是etcd-raft包的文档文件" class="headerlink" title="这是etcd/raft包的文档文件"></a>这是etcd/raft包的文档文件</h4><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">// Copyright 2015 The etcd Authors</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Licensed under the Apache License, Version 2.0 (the "License");</span></span><br><span class="line"><span class="comment">// you may not use this file except in compliance with the License.</span></span><br><span class="line"><span class="comment">// You may obtain a copy of the License at</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//     http://www.apache.org/licenses/LICENSE-2.0</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Unless required by applicable law or agreed to in writing, software</span></span><br><span class="line"><span class="comment">// distributed under the License is distributed on an "AS IS" BASIS,</span></span><br><span class="line"><span class="comment">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span></span><br><span class="line"><span class="comment">// See the License for the specific language governing permissions and</span></span><br><span class="line"><span class="comment">// limitations under the License.</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">Package raft sends and receives messages in the Protocol Buffer format</span></span><br><span class="line"><span class="comment">defined in the raftpb package.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">Raft is a protocol with which a cluster of nodes can maintain a replicated state machine.</span></span><br><span class="line"><span class="comment">The state machine is kept in sync through the use of a replicated log.</span></span><br><span class="line"><span class="comment">For more details on Raft, see "In Search of an Understandable Consensus Algorithm"</span></span><br><span class="line"><span class="comment">(https://ramcloud.stanford.edu/raft.pdf) by Diego Ongaro and John Ousterhout.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">A simple example application, _raftexample_, is also available to help illustrate</span></span><br><span class="line"><span class="comment">how to use this package in practice:</span></span><br><span class="line"><span class="comment">https://github.com/coreos/etcd/tree/master/contrib/raftexample</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">Usage</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># raft里面最的基础对象是Node</span></span><br><span class="line"><span class="comment">The primary object in raft is a Node. You either start a Node from scratch</span></span><br><span class="line"><span class="comment">using raft.StartNode or start a Node from some initial state using raft.RestartNode.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">To start a node from scratch:</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">  storage := raft.NewMemoryStorage()</span></span><br><span class="line"><span class="comment">  c := &amp;Config&#123;</span></span><br><span class="line"><span class="comment">    ID:              0x01,</span></span><br><span class="line"><span class="comment">    ElectionTick:    10,</span></span><br><span class="line"><span class="comment">    HeartbeatTick:   1,</span></span><br><span class="line"><span class="comment">    Storage:         storage,</span></span><br><span class="line"><span class="comment">    MaxSizePerMsg:   4096,</span></span><br><span class="line"><span class="comment">    MaxInflightMsgs: 256,</span></span><br><span class="line"><span class="comment">  &#125;</span></span><br><span class="line"><span class="comment">  n := raft.StartNode(c, []raft.Peer&#123;&#123;ID: 0x02&#125;, &#123;ID: 0x03&#125;&#125;)</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">To restart a node from previous state:</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">  storage := raft.NewMemoryStorage()</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">  // recover the in-memory storage from persistent</span></span><br><span class="line"><span class="comment">  // snapshot, state and entries.</span></span><br><span class="line"><span class="comment">  storage.ApplySnapshot(snapshot)</span></span><br><span class="line"><span class="comment">  storage.SetHardState(state)</span></span><br><span class="line"><span class="comment">  storage.Append(entries)</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">  c := &amp;Config&#123;</span></span><br><span class="line"><span class="comment">    ID:              0x01,</span></span><br><span class="line"><span class="comment">    ElectionTick:    10,</span></span><br><span class="line"><span class="comment">    HeartbeatTick:   1,</span></span><br><span class="line"><span class="comment">    Storage:         storage,</span></span><br><span class="line"><span class="comment">    MaxSizePerMsg:   4096,</span></span><br><span class="line"><span class="comment">    MaxInflightMsgs: 256,</span></span><br><span class="line"><span class="comment">  &#125;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">  // restart raft without peer information.</span></span><br><span class="line"><span class="comment">  // peer information is already included in the storage.</span></span><br><span class="line"><span class="comment">  n := raft.RestartNode(c)</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 现在你掌握了一个Node，你有些任务需要完成</span></span><br><span class="line"><span class="comment">Now that you are holding onto a Node you have a few responsibilities:</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 第一，你必须从 Node.Ready()通道读取数据，并要对其包含的更新进行操作，这步操作一般</span></span><br><span class="line"><span class="comment">是并发的，除非遇到步骤二</span></span><br><span class="line"><span class="comment">First, you must read from the Node.Ready() channel and process the updates</span></span><br><span class="line"><span class="comment">it contains. These steps may be performed in parallel, except as noted in step</span></span><br><span class="line"><span class="comment">2.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 1. 将硬件状态，Entries(一个个数据实体)和Snapshot(快照)写入到持久化数据库，如果这</span></span><br><span class="line"><span class="comment">些东西不是空的。注意，如果写入一个Entry的index是i，那之前持久的index大于i的Entries</span></span><br><span class="line"><span class="comment">都将被忽略掉。</span></span><br><span class="line"><span class="comment">1. Write HardState, Entries, and Snapshot to persistent storage if they are</span></span><br><span class="line"><span class="comment">not empty. Note that when writing an Entry with Index i, any</span></span><br><span class="line"><span class="comment">previously-persisted entries with Index &gt;= i must be discarded.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 2. 将所有的消息发送给字段to里面的节点。这里关键的点是，在硬件状态同步到数据库之前，</span></span><br><span class="line"><span class="comment">并且所有的Entries已经写入到之前准备好的batch中，消息将无法发送。为了减少I/O延迟，可以</span></span><br><span class="line"><span class="comment">通过并发操作，将leader的数据同步给follwers。如果某条消息的类型是MsgSnap，在将数据发</span></span><br><span class="line"><span class="comment">送出去之后需要调用 Node.ReportSnapshot</span></span><br><span class="line"><span class="comment">2. Send all Messages to the nodes named in the To field. It is important that</span></span><br><span class="line"><span class="comment">no messages be sent until the latest HardState has been persisted to disk,</span></span><br><span class="line"><span class="comment">and all Entries written by any previous Ready batch (Messages may be sent while</span></span><br><span class="line"><span class="comment">entries from the same batch are being persisted). To reduce the I/O latency, an</span></span><br><span class="line"><span class="comment">optimization can be applied to make leader write to disk in parallel with its</span></span><br><span class="line"><span class="comment">followers (as explained at section 10.2.1 in Raft thesis). If any Message has type</span></span><br><span class="line"><span class="comment">MsgSnap, call Node.ReportSnapshot() after it has been sent (these messages may be</span></span><br><span class="line"><span class="comment">large).</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 注意: 编码消息不是并发安全的，所以很重要的一点是你要保证在消息编码的时候没有Entries</span></span><br><span class="line"><span class="comment">持久化。最简单的处理方式就死直接在你的raft主循环里序列化消息</span></span><br><span class="line"><span class="comment">Note: Marshalling messages is not thread-safe; it is important that you</span></span><br><span class="line"><span class="comment">make sure that no new entries are persisted while marshalling.</span></span><br><span class="line"><span class="comment">The easiest way to achieve this is to serialise the messages directly inside</span></span><br><span class="line"><span class="comment">your main raft loop.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 3. 将Snapshot和已提交的Entries应用到状态机。如果已提交的Entry是类型EntryConfChange，</span></span><br><span class="line"><span class="comment">调用Node.ApplyConfChange()来将它应用到节点。如果在调用ApplyConfChange之前将NodeID设置</span></span><br><span class="line"><span class="comment">成0，那么配置的更改将被取消(但是ApplyConfChange必须通过某种方式调用，并且取消配置变更只有</span></span><br><span class="line"><span class="comment">状态机能做，即使是健康检查也不行)</span></span><br><span class="line"><span class="comment">3. Apply Snapshot (if any) and CommittedEntries to the state machine.</span></span><br><span class="line"><span class="comment">If any committed Entry has Type EntryConfChange, call Node.ApplyConfChange()</span></span><br><span class="line"><span class="comment">to apply it to the node. The configuration change may be cancelled at this point</span></span><br><span class="line"><span class="comment">by setting the NodeID field to zero before calling ApplyConfChange</span></span><br><span class="line"><span class="comment">(but ApplyConfChange must be called one way or the other, and the decision to cancel</span></span><br><span class="line"><span class="comment">must be based solely on the state machine and not external information such as</span></span><br><span class="line"><span class="comment">the observed health of the node).</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 4.调用 Node.Advance()标志下一个batch已经准备好发送。这在步骤1之后的任何时候都有可</span></span><br><span class="line"><span class="comment">能完成，不过所有的更新必须在按照Ready返回的顺序执行。</span></span><br><span class="line"><span class="comment">4. Call Node.Advance() to signal readiness for the next batch of updates.</span></span><br><span class="line"><span class="comment">This may be done at any time after step 1, although all updates must be processed</span></span><br><span class="line"><span class="comment">in the order they were returned by Ready.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 第二，持久化的实现需要保证所有的日志实体是可读取的的，比如提供的MemoryStorage，或者</span></span><br><span class="line"><span class="comment">你可以自己实现</span></span><br><span class="line"><span class="comment">Second, all persisted log entries must be made available via an</span></span><br><span class="line"><span class="comment">implementation of the Storage interface. The provided MemoryStorage</span></span><br><span class="line"><span class="comment">type can be used for this (if you repopulate its state upon a</span></span><br><span class="line"><span class="comment">restart), or you can supply your own disk-backed implementation.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 第三， 当你从其他节点中收到消息的时候，将消息传递给 Node.Step:</span></span><br><span class="line"><span class="comment">Third, when you receive a message from another node, pass it to Node.Step:</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">   func recvRaftRPC(ctx context.Context, m raftpb.Message) &#123;</span></span><br><span class="line"><span class="comment">      n.Step(ctx, m)</span></span><br><span class="line"><span class="comment">   &#125;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 最后，你需要定时调用 Node.Tick()。Raft有两种超时: 心跳超时和选举超时。他们在raft</span></span><br><span class="line"><span class="comment">包被抽象成同一个</span></span><br><span class="line"><span class="comment">Finally, you need to call Node.Tick() at regular intervals (probably</span></span><br><span class="line"><span class="comment">via a time.Ticker). Raft has two important timeouts: heartbeat and the</span></span><br><span class="line"><span class="comment">election timeout. However, internally to the raft package time is</span></span><br><span class="line"><span class="comment">represented by an abstract "tick".</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 综上，状态机的处理循环就像下面这样:</span></span><br><span class="line"><span class="comment">The total state machine handling loop will look something like this:</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">  for &#123;</span></span><br><span class="line"><span class="comment">    select &#123;</span></span><br><span class="line"><span class="comment">    case &lt;-s.Ticker:</span></span><br><span class="line"><span class="comment">      n.Tick()</span></span><br><span class="line"><span class="comment">    case rd := &lt;-s.Node.Ready():</span></span><br><span class="line"><span class="comment">      saveToStorage(rd.State, rd.Entries, rd.Snapshot)</span></span><br><span class="line"><span class="comment">      send(rd.Messages)</span></span><br><span class="line"><span class="comment">      if !raft.IsEmptySnap(rd.Snapshot) &#123;</span></span><br><span class="line"><span class="comment">        processSnapshot(rd.Snapshot)</span></span><br><span class="line"><span class="comment">      &#125;</span></span><br><span class="line"><span class="comment">      for _, entry := range rd.CommittedEntries &#123;</span></span><br><span class="line"><span class="comment">        process(entry)</span></span><br><span class="line"><span class="comment">        if entry.Type == raftpb.EntryConfChange &#123;</span></span><br><span class="line"><span class="comment">          var cc raftpb.ConfChange</span></span><br><span class="line"><span class="comment">          cc.Unmarshal(entry.Data)</span></span><br><span class="line"><span class="comment">          s.Node.ApplyConfChange(cc)</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">      &#125;</span></span><br><span class="line"><span class="comment">      s.Node.Advance()</span></span><br><span class="line"><span class="comment">    case &lt;-s.done:</span></span><br><span class="line"><span class="comment">      return</span></span><br><span class="line"><span class="comment">    &#125;</span></span><br><span class="line"><span class="comment">  &#125;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 要向状态机发起议案，需要将你的节点应用数据序列化成byte数组，然后调用n.Propose(ctx, data)</span></span><br><span class="line"><span class="comment">To propose changes to the state machine from your node take your application</span></span><br><span class="line"><span class="comment">data, serialize it into a byte slice and call:</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">   n.Propose(ctx, data)</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 如果完成提交，数据将变成具有EntryNormal类型的已提交数据。一个提议命令如果已经提交是</span></span><br><span class="line"><span class="comment">没有过期时间的，你必须在超时之后重新发起</span></span><br><span class="line"><span class="comment">If the proposal is committed, data will appear in committed entries with type</span></span><br><span class="line"><span class="comment">raftpb.EntryNormal. There is no guarantee that a proposed command will be</span></span><br><span class="line"><span class="comment">committed; you may have to re-propose after a timeout.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 添加或移除节点，构建配置然后调用ProposeConfChange</span></span><br><span class="line"><span class="comment">To add or remove node in a cluster, build ConfChange struct 'cc' and call:</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">   n.ProposeConfChange(ctx, cc)</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">#  在配置更改提交之后，一些有着EntryConfChange类型的Entries会被返回，解析后通过</span></span><br><span class="line"><span class="comment">ApplyConfChange应用它们</span></span><br><span class="line"><span class="comment">After config change is committed, some committed entry with type</span></span><br><span class="line"><span class="comment">raftpb.EntryConfChange will be returned. You must apply it to node through:</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">   var cc raftpb.ConfChange</span></span><br><span class="line"><span class="comment">   cc.Unmarshal(data)</span></span><br><span class="line"><span class="comment">   n.ApplyConfChange(cc)</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 注意，节点ID必须总是唯一</span></span><br><span class="line"><span class="comment">Note: An ID represents a unique node in a cluster for all time. A</span></span><br><span class="line"><span class="comment">given ID MUST be used only once even if the old node has been removed.</span></span><br><span class="line"><span class="comment">This means that for example IP addresses make poor node IDs since they</span></span><br><span class="line"><span class="comment">may be reused. Node IDs must be non-zero.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 实现笔记</span></span><br><span class="line"><span class="comment">Implementation notes</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 实现基本是跟着Raft thesis 同步更新的，但是还是与第4章节描述的有所差异。关键的差异</span></span><br><span class="line"><span class="comment">是，保留在节点memberships一次改变时，我们的实现时在entry被应用的时候，而不是它被加</span></span><br><span class="line"><span class="comment">到log中的时候(因此，entry在提交后还是原来的成员而不是新的)。这是为了安全性</span></span><br><span class="line"><span class="comment">This implementation is up to date with the final Raft thesis</span></span><br><span class="line"><span class="comment">(https://ramcloud.stanford.edu/~ongaro/thesis.pdf), although our</span></span><br><span class="line"><span class="comment">implementation of the membership change protocol differs somewhat from</span></span><br><span class="line"><span class="comment">that described in chapter 4. The key invariant that membership changes</span></span><br><span class="line"><span class="comment">happen one node at a time is preserved, but in our implementation the</span></span><br><span class="line"><span class="comment">membership change takes effect when its entry is applied, not when it</span></span><br><span class="line"><span class="comment">is added to the log (so the entry is committed under the old</span></span><br><span class="line"><span class="comment">membership instead of the new). This is equivalent in terms of safety,</span></span><br><span class="line"><span class="comment">since the old and new configurations are guaranteed to overlap.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 为了确保不会一次性提交两次成员变化，我们在leader节点没有提交完成的时候，不允许提起成</span></span><br><span class="line"><span class="comment">员变化</span></span><br><span class="line"><span class="comment">To ensure that we do not attempt to commit two membership changes at</span></span><br><span class="line"><span class="comment">once by matching log positions (which would be unsafe since they</span></span><br><span class="line"><span class="comment">should have different quorum requirements), we simply disallow any</span></span><br><span class="line"><span class="comment">proposed membership change while any uncommitted change appears in</span></span><br><span class="line"><span class="comment">the leader's log.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 这里介绍了这样一个问题: 当只有两个节点的时候，如果一个成员在另一个节点收到配置变化</span></span><br><span class="line"><span class="comment">提交之前死亡，那么集群将会完全失效，因此强烈建议构建三个以上节点的集群</span></span><br><span class="line"><span class="comment">This approach introduces a problem when you try to remove a member</span></span><br><span class="line"><span class="comment">from a two-member cluster: If one of the members dies before the</span></span><br><span class="line"><span class="comment">other one receives the commit of the confchange entry, then the member</span></span><br><span class="line"><span class="comment">cannot be removed any more since the cluster cannot make progress.</span></span><br><span class="line"><span class="comment">For this reason it is highly recommended to use three or more nodes in</span></span><br><span class="line"><span class="comment">every cluster.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 消息类型</span></span><br><span class="line"><span class="comment">MessageType</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">Package raft sends and receives message in Protocol Buffer format (defined</span></span><br><span class="line"><span class="comment">in raftpb package). Each state (follower, candidate, leader) implements its</span></span><br><span class="line"><span class="comment">own 'step' method ('stepFollower', 'stepCandidate', 'stepLeader') when</span></span><br><span class="line"><span class="comment">advancing with the given raftpb.Message. Each step is determined by its</span></span><br><span class="line"><span class="comment">raftpb.MessageType. Note that every step is checked by one common method</span></span><br><span class="line"><span class="comment">'Step' that safety-checks the terms of node and incoming message to prevent</span></span><br><span class="line"><span class="comment">stale log entries:</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># MsgHup用于选举过程。如果在一个tickElection中，节点没有收到心跳，就发送MsgHup</span></span><br><span class="line"><span class="comment">    给Step，并成为候选者</span></span><br><span class="line"><span class="comment">   'MsgHup' is used for election. If a node is a follower or candidate, the</span></span><br><span class="line"><span class="comment">   'tick' function in 'raft' struct is set as 'tickElection'. If a follower or</span></span><br><span class="line"><span class="comment">   candidate has not received any heartbeat before the election timeout, it</span></span><br><span class="line"><span class="comment">   passes 'MsgHup' to its Step method and becomes (or remains) a candidate to</span></span><br><span class="line"><span class="comment">   start a new election.</span></span><br><span class="line"><span class="comment">    </span></span><br><span class="line"><span class="comment">   # MsgBeat 是节点内部的心跳消息类型，由主节点定时向各follew发起</span></span><br><span class="line"><span class="comment">   'MsgBeat' is an internal type that signals the leader to send a heartbeat of</span></span><br><span class="line"><span class="comment">   the 'MsgHeartbeat' type. If a node is a leader, the 'tick' function in</span></span><br><span class="line"><span class="comment">   the 'raft' struct is set as 'tickHeartbeat', and triggers the leader to</span></span><br><span class="line"><span class="comment">   send periodic 'MsgHeartbeat' messages to its followers.</span></span><br><span class="line"><span class="comment">   </span></span><br><span class="line"><span class="comment">   # MsgProp 发起提议，用来将议案转发给主节点。这里send方法用硬件状态的Term重写了</span></span><br><span class="line"><span class="comment">   Message的Term以避免直接将本地的term附加给MsgProp。当MsgProp传递给主节点，主节</span></span><br><span class="line"><span class="comment">   点立即将消息append到日志队列，然后调用bcastAppend将这些entried广播给各个节点。</span></span><br><span class="line"><span class="comment">   如果发送给了候选者，会被候选者丢掉，当发给跟随者，跟随者将会通过send发放将消息存</span></span><br><span class="line"><span class="comment">   入mailbox(消息)。消息存储的时候会带上发送者的ID然后通过raftHttp发送给leader。</span></span><br><span class="line"><span class="comment">   'MsgProp' proposes to append data to its log entries. This is a special</span></span><br><span class="line"><span class="comment">   type to redirect proposals to leader. Therefore, send method overwrites</span></span><br><span class="line"><span class="comment">   raftpb.Message's term with its HardState's term to avoid attaching its</span></span><br><span class="line"><span class="comment">   local term to 'MsgProp'. When 'MsgProp' is passed to the leader's 'Step'</span></span><br><span class="line"><span class="comment">   method, the leader first calls the 'appendEntry' method to append entries</span></span><br><span class="line"><span class="comment">   to its log, and then calls 'bcastAppend' method to send those entries to</span></span><br><span class="line"><span class="comment">   its peers. When passed to candidate, 'MsgProp' is dropped. When passed to</span></span><br><span class="line"><span class="comment">   follower, 'MsgProp' is stored in follower's mailbox(msgs) by the send</span></span><br><span class="line"><span class="comment">   method. It is stored with sender's ID and later forwarded to leader by</span></span><br><span class="line"><span class="comment">   rafthttp package.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># MsgApp 包含了要复制的日志，主节点bcastAppend的时候会通过sendAppend发送MsgApp</span></span><br><span class="line"><span class="comment">    消息，意味着消息需要尽快发送出去。当MsgApp发送给候选者，候选者会重新变回跟随者，因为</span></span><br><span class="line"><span class="comment">    这意味着这是合法leader发过来的消息，就不需要再选举了。候选者和跟随者会回</span></span><br><span class="line"><span class="comment">    MsgAppResp消息</span></span><br><span class="line"><span class="comment">   'MsgApp' contains log entries to replicate. A leader calls bcastAppend,</span></span><br><span class="line"><span class="comment">   which calls sendAppend, which sends soon-to-be-replicated logs in 'MsgApp'</span></span><br><span class="line"><span class="comment">   type. When 'MsgApp' is passed to candidate's Step method, candidate reverts</span></span><br><span class="line"><span class="comment">   back to follower, because it indicates that there is a valid leader sending</span></span><br><span class="line"><span class="comment">   'MsgApp' messages. Candidate and follower respond to this message in</span></span><br><span class="line"><span class="comment">   'MsgAppResp' type.</span></span><br><span class="line"><span class="comment">   </span></span><br><span class="line"><span class="comment">   # MsgAppResp，日志复制回复</span></span><br><span class="line"><span class="comment">   'MsgAppResp' is response to log replication request('MsgApp'). When</span></span><br><span class="line"><span class="comment">   'MsgApp' is passed to candidate or follower's Step method, it responds by</span></span><br><span class="line"><span class="comment">   calling 'handleAppendEntries' method, which sends 'MsgAppResp' to raft</span></span><br><span class="line"><span class="comment">   mailbox.</span></span><br><span class="line"><span class="comment">   </span></span><br><span class="line"><span class="comment">   # MsgVote 是用来请求选票的。当跟随者或者候选人发送MsgHup消息给它的Step方法之后，</span></span><br><span class="line"><span class="comment">   节点会调用campaign方法参加竞选，使自己成为候选人。一旦campaign调用，节点及腰发</span></span><br><span class="line"><span class="comment">   送MsgVote给集群的每个成员要求选票。当消息被传递给leader或者候选人的Step方法，并且</span></span><br><span class="line"><span class="comment">   消息的term比他们自己term小，选举会被拒绝(MsgVoteResp回复附带拒绝标志)。如果</span></span><br><span class="line"><span class="comment">   leader或者候选者收到的选举消息附带的Term更大，他们会转化成follower。当消息传递</span></span><br><span class="line"><span class="comment">   给follower，只有当发送者当term和lastCommitted index大于等于接收节点的时候，才</span></span><br><span class="line"><span class="comment">   会投票</span></span><br><span class="line"><span class="comment">   'MsgVote' requests votes for election. When a node is a follower or</span></span><br><span class="line"><span class="comment">   candidate and 'MsgHup' is passed to its Step method, then the node calls</span></span><br><span class="line"><span class="comment">   'campaign' method to campaign itself to become a leader. Once 'campaign'</span></span><br><span class="line"><span class="comment">   method is called, the node becomes candidate and sends 'MsgVote' to peers</span></span><br><span class="line"><span class="comment">   in cluster to request votes. When passed to leader or candidate's Step</span></span><br><span class="line"><span class="comment">   method and the message's Term is lower than leader's or candidate's,</span></span><br><span class="line"><span class="comment">   'MsgVote' will be rejected ('MsgVoteResp' is returned with Reject true).</span></span><br><span class="line"><span class="comment">   If leader or candidate receives 'MsgVote' with higher term, it will revert</span></span><br><span class="line"><span class="comment">   back to follower. When 'MsgVote' is passed to follower, it votes for the</span></span><br><span class="line"><span class="comment">   sender only when sender's last term is greater than MsgVote's term or</span></span><br><span class="line"><span class="comment">   sender's last term is equal to MsgVote's term but sender's last committed</span></span><br><span class="line"><span class="comment">   index is greater than or equal to follower's.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">   # 选票结果，候选人接收到后统计票数，超过半数则成为leader，broadcast广告给所有节点。</span></span><br><span class="line"><span class="comment">   否则从新变成follower</span></span><br><span class="line"><span class="comment">   'MsgVoteResp' contains responses from voting request. When 'MsgVoteResp' is</span></span><br><span class="line"><span class="comment">   passed to candidate, the candidate calculates how many votes it has won. If</span></span><br><span class="line"><span class="comment">   it's more than majority (quorum), it becomes leader and calls 'bcastAppend'.</span></span><br><span class="line"><span class="comment">   If candidate receives majority of votes of denials, it reverts back to</span></span><br><span class="line"><span class="comment">   follower.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># MsgPreVote 和 MsgPreVoteResp 用于二阶段选举。当配置的PreVote是true的时</span></span><br><span class="line"><span class="comment">    候，二阶段选举才会派上用场。并且节点不会增加term，除非pre-election已经告知了竞选</span></span><br><span class="line"><span class="comment">    节点胜出。这样减少当分区节点重新加入时候的混乱。</span></span><br><span class="line"><span class="comment">   'MsgPreVote' and 'MsgPreVoteResp' are used in an optional two-phase election</span></span><br><span class="line"><span class="comment">   protocol. When Config.PreVote is true, a pre-election is carried out first</span></span><br><span class="line"><span class="comment">   (using the same rules as a regular election), and no node increases its term</span></span><br><span class="line"><span class="comment">   number unless the pre-election indicates that the campaigining node would win.</span></span><br><span class="line"><span class="comment">   This minimizes disruption when a partitioned node rejoins the cluster.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># MsgSnap 要求加载消息。当一个节点刚成为leader的时候，或者leader收到MsgProp的</span></span><br><span class="line"><span class="comment">    时候，它调用bcastAppend方法给个节点同步消息的时候，如果获取不到term或者</span></span><br><span class="line"><span class="comment">    entries，leader就会发送MsgSnap获取快照。</span></span><br><span class="line"><span class="comment">   'MsgSnap' requests to install a snapshot message. When a node has just</span></span><br><span class="line"><span class="comment">   become a leader or the leader receives 'MsgProp' message, it calls</span></span><br><span class="line"><span class="comment">   'bcastAppend' method, which then calls 'sendAppend' method to each</span></span><br><span class="line"><span class="comment">   follower. In 'sendAppend', if a leader fails to get term or entries,</span></span><br><span class="line"><span class="comment">   the leader requests snapshot by sending 'MsgSnap' type message.</span></span><br><span class="line"><span class="comment">   </span></span><br><span class="line"><span class="comment">    # MsgSnapStatus 告知快照结果。当follower拒绝返回，就意味着主节点与之网络连接</span></span><br><span class="line"><span class="comment">    失败，主节点就会考虑对该节点进行网络探测。当返回了，意味着foller接收了快照并恢复了</span></span><br><span class="line"><span class="comment">    日志复制</span></span><br><span class="line"><span class="comment">   'MsgSnapStatus' tells the result of snapshot install message. When a</span></span><br><span class="line"><span class="comment">   follower rejected 'MsgSnap', it indicates the snapshot request with</span></span><br><span class="line"><span class="comment">   'MsgSnap' had failed from network issues which causes the network layer</span></span><br><span class="line"><span class="comment">   to fail to send out snapshots to its followers. Then leader considers</span></span><br><span class="line"><span class="comment">   follower's progress as probe. When 'MsgSnap' were not rejected, it</span></span><br><span class="line"><span class="comment">   indicates that the snapshot succeeded and the leader sets follower's</span></span><br><span class="line"><span class="comment">   progress to probe and resumes its log replication.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># MsgHeartbeat leader给follwer发心跳。当心跳发送给候选人，如果term高于候选</span></span><br><span class="line"><span class="comment">    人，候选人重新成为follower并更新提交索引。如果发送给follower，候选人更新</span></span><br><span class="line"><span class="comment">    leaderId</span></span><br><span class="line"><span class="comment">   'MsgHeartbeat' sends heartbeat from leader. When 'MsgHeartbeat' is passed</span></span><br><span class="line"><span class="comment">   to candidate and message's term is higher than candidate's, the candidate</span></span><br><span class="line"><span class="comment">   reverts back to follower and updates its committed index from the one in</span></span><br><span class="line"><span class="comment">   this heartbeat. And it sends the message to its mailbox. When</span></span><br><span class="line"><span class="comment">   'MsgHeartbeat' is passed to follower's Step method and message's term is</span></span><br><span class="line"><span class="comment">   higher than follower's, the follower updates its leaderID with the ID</span></span><br><span class="line"><span class="comment">   from the message.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># MsgHeartbeatResp， 心跳返回，附带follower的日志id，点那个日志id比leader大的</span></span><br><span class="line"><span class="comment">    时候，发送append</span></span><br><span class="line"><span class="comment">   'MsgHeartbeatResp' is a response to 'MsgHeartbeat'. When 'MsgHeartbeatResp'</span></span><br><span class="line"><span class="comment">   is passed to leader's Step method, the leader knows which follower</span></span><br><span class="line"><span class="comment">   responded. And only when the leader's last committed index is greater than</span></span><br><span class="line"><span class="comment">   follower's Match index, the leader runs 'sendAppend` method.</span></span><br><span class="line"><span class="comment">   </span></span><br><span class="line"><span class="comment">   # MsgUnreachable 表示消息发送失败。当主节点收到该消息的时候，主节点会发现发送该消</span></span><br><span class="line"><span class="comment">   息的节点不可达，基本意味着MsgApp消息丢失。</span></span><br><span class="line"><span class="comment">   'MsgUnreachable' tells that request(message) wasn't delivered. When</span></span><br><span class="line"><span class="comment">   'MsgUnreachable' is passed to leader's Step method, the leader discovers</span></span><br><span class="line"><span class="comment">   that the follower that sent this 'MsgUnreachable' is not reachable, often</span></span><br><span class="line"><span class="comment">   indicating 'MsgApp' is lost. When follower's progress state is replicate,</span></span><br><span class="line"><span class="comment">   the leader sets it back to probe.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">package</span> raft</span><br></pre>      </td>    </tr>  </table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;Raft&quot;&gt;&lt;a href=&quot;#Raft&quot; class=&quot;headerlink&quot; title=&quot;Raft&quot;&gt;&lt;/a&gt;Raft&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;raft基本原理演示请参考 &lt;a href=&quot;http://thesecretlivesofdata.com/raft/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://thesecretlivesofdata.com/raft/&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;go语言的raft实现 &lt;a href=&quot;https://github.com/goraft/raft&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/goraft/raft&lt;/a&gt;, 但是该项目早已不再维护，原作者将raft融入到etcd和influxdb(现在貌似不是了)里了，这里摘出etcd的raft实现进行研究&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="reading" scheme="http://ipiao.top/categories/reading/"/>
    
    
      <category term="go" scheme="http://ipiao.top/tags/go/"/>
    
      <category term="算法" scheme="http://ipiao.top/tags/%E7%AE%97%E6%B3%95/"/>
    
      <category term="分布式" scheme="http://ipiao.top/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
    
  </entry>
  
  <entry>
    <title>Go Plugin动态加载库</title>
    <link href="http://ipiao.top/2020/03/11/Go-Plugin%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E5%BA%93/"/>
    <id>http://ipiao.top/2020/03/11/Go-Plugin动态加载库/</id>
    <published>2020-03-11T10:25:00.000Z</published>
    <updated>2020-03-16T06:12:35.038Z</updated>
    
    <content type="html"><![CDATA[<h4 id="利用Go-plugin实现动态加载库文件"><a href="#利用Go-plugin实现动态加载库文件" class="headerlink" title="利用Go plugin实现动态加载库文件"></a>利用Go plugin实现动态加载库文件</h4><ul>  <li>调用文件</li></ul><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">"fmt"</span></span><br><span class="line"><span class="string">"os"</span></span><br><span class="line"><span class="string">"plugin"</span></span><br><span class="line"><span class="string">"strings"</span></span><br><span class="line"><span class="string">"sync"</span></span><br><span class="line"><span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">fn     <span class="function"><span class="keyword">func</span><span class="params">()</span> // <span class="title">plugin</span> 动态加载的函数</span></span><br><span class="line"><span class="function"><span class="title">fnLock</span> <span class="title">sync</span>.<span class="title">RWMutex</span></span></span><br><span class="line"><span class="function">)</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"></span><br><span class="line">tick := time.NewTicker(time.Second)</span><br><span class="line">inputCh := readInput()</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line"><span class="keyword">select</span> &#123;</span><br><span class="line"><span class="keyword">case</span> &lt;-tick.C:</span><br><span class="line">execFn()</span><br><span class="line"><span class="keyword">case</span> file := &lt;-inputCh:</span><br><span class="line">fmt.Printf(<span class="string">"loading plugin file : %s\n"</span>, file)</span><br><span class="line">load(file)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">readInput</span><span class="params">()</span> &lt;-<span class="title">chan</span> <span class="title">string</span></span> &#123;</span><br><span class="line">ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">string</span>, <span class="number">1</span>)</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">b := <span class="built_in">make</span>([]<span class="keyword">byte</span>, <span class="number">16</span>)</span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line">n, err := os.Stdin.Read(b)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="comment">//</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> n &gt; <span class="number">0</span> &#123;</span><br><span class="line">ch &lt;- <span class="keyword">string</span>(b[:n])</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;()</span><br><span class="line"><span class="keyword">return</span> ch</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">load</span><span class="params">(file <span class="keyword">string</span>)</span></span> &#123;</span><br><span class="line">file = strings.TrimSpace(file)</span><br><span class="line">p, err := plugin.Open(<span class="string">"./myplugin/"</span> + file)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">"Open plugin error: %v\n"</span>, err)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line">sb, err := p.Lookup(<span class="string">"Print"</span>)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">"plugin lookup failed, error: %v\n"</span>, err)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line">nfn, _ := sb.(<span class="function"><span class="keyword">func</span><span class="params">()</span>)</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">fnLock</span>.<span class="title">Lock</span><span class="params">()</span></span></span><br><span class="line"><span class="function"><span class="title">fn</span> = <span class="title">nfn</span></span></span><br><span class="line"><span class="function"><span class="title">fnLock</span>.<span class="title">Unlock</span><span class="params">()</span></span></span><br><span class="line"><span class="function">&#125;</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">func</span> <span class="title">execFn</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="keyword">if</span> fn != <span class="literal">nil</span> &#123;</span><br><span class="line">fnLock.RLock()</span><br><span class="line"><span class="keyword">defer</span> fnLock.RUnlock()</span><br><span class="line">fn()</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><ul>  <li>上面的代码模拟了一个简单的服务场景。<ol>      <li>监听用户输入，模拟库文件的更新（库文件不能同名，否则会当作同一个文件）</li>      <li>在库文件加载进来后实现库函数的调用</li>    </ol>  </li></ul><ul>  <li>库文件</li></ul><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Print</span><span class="params">()</span></span> &#123;</span><br><span class="line">fmt.Println(<span class="string">"这是p1加载的plugin“）</span></span><br><span class="line"><span class="string">   // fmt.Println("</span>这是p2加载的plugin<span class="string">")</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre>      </td>    </tr>  </table></figure><ul>  <li>    <p>编译成库文件</p>    <figure class="highlight vim">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="keyword">go</span> build -buildmode=plugin -<span class="keyword">o</span> <span class="keyword">print</span>.<span class="keyword">so</span> p1.<span class="keyword">go</span></span><br><span class="line"># <span class="keyword">go</span> build -buildmode=plugin -<span class="keyword">o</span> print2.<span class="keyword">so</span> p2.<span class="keyword">go</span></span><br></pre>          </td>        </tr>      </table>    </figure>  </li>  <li>    <p>终端输入编译后的文件路径，获取终端的输出</p>  </li></ul><p><img src="/images/pasted-19.png" alt="upload successful"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;利用Go-plugin实现动态加载库文件&quot;&gt;&lt;a href=&quot;#利用Go-plugin实现动态加载库文件&quot; class=&quot;headerlink&quot; title=&quot;利用Go plugin实现动态加载库文件&quot;&gt;&lt;/a&gt;利用Go plugin实现动态加载库文件&lt;/h4&gt;

      
    
    </summary>
    
      <category term="golang大法好" scheme="http://ipiao.top/categories/golang%E5%A4%A7%E6%B3%95%E5%A5%BD/"/>
    
    
      <category term="go" scheme="http://ipiao.top/tags/go/"/>
    
  </entry>
  
  <entry>
    <title>mysql并发调试</title>
    <link href="http://ipiao.top/2019/10/24/mysql%E5%B9%B6%E5%8F%91%E8%B0%83%E8%AF%95%E5%91%BD%E5%90%8D/"/>
    <id>http://ipiao.top/2019/10/24/mysql并发调试命名/</id>
    <published>2019-10-24T03:00:00.000Z</published>
    <updated>2019-10-29T06:25:15.392Z</updated>
    
    <content type="html"><![CDATA[<h4 id="常用命令列举"><a href="#常用命令列举" class="headerlink" title="常用命令列举"></a>常用命令列举</h4><ol>  <li>    <p>主要是 INFORMATION_SCHEMA 库里的表</p>    <figure class="highlight sql">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="comment">-- 包含事务正在等待的锁的信息</span></span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> INFORMATION_SCHEMA.INNODB_LOCK_WAITS;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 包含当前运行的所有事务的列表</span></span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> INFORMATION_SCHEMA.INNODB_TRX;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 包含事务持有的当前锁的相关信息以及每个事务等待的锁的信息</span></span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> INFORMATION_SCHEMA.INNODB_LOCKS;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 阻塞的事务列表</span></span><br><span class="line"><span class="keyword">SELECT</span> INNODB_LOCKS.* <span class="keyword">FROM</span> INFORMATION_SCHEMA.INNODB_LOCKS <span class="keyword">JOIN</span> INFORMATION_SCHEMA.INNODB_LOCK_WAITS <span class="keyword">ON</span> (</span><br><span class="line">INFORMATION_SCHEMA.INNODB_LOCKS.LOCK_TRX_ID = INFORMATION_SCHEMA.INNODB_LOCK_WAITS.BLOCKING_TRX_ID)</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 特定表上的锁的列表</span></span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> INFORMATION_SCHEMA.INNODB_LOCKS <span class="keyword">WHERE</span> LOCK_TABLE = <span class="string">'db_name.tables_name'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 等待锁的事务列表</span></span><br><span class="line"><span class="keyword">SELECT</span> TRX_ID, TRX_REQUESTED_LOCK_ID, TRX_MYSQL_THREAD_ID, TRX_QUERY <span class="keyword">FROM</span> INFORMATION_SCHEMA.INNODB_TRX <span class="keyword">WHERE</span> TRX_STATE = <span class="string">'LOCK WAIT'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- INNODB 监控器</span></span><br><span class="line"><span class="keyword">show</span> <span class="keyword">engine</span> <span class="keyword">innodb</span> <span class="keyword">status</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">show</span> <span class="keyword">full</span> <span class="keyword">processlist</span> ;</span><br><span class="line"><span class="keyword">kill</span> <span class="number">600678</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">show</span> <span class="keyword">status</span> <span class="keyword">like</span> <span class="string">'innodb_row_lock%'</span>;  <span class="comment">-- 行锁</span></span><br><span class="line"><span class="keyword">show</span> <span class="keyword">status</span> <span class="keyword">like</span> <span class="string">'%lock%'</span>;         </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">show</span> <span class="keyword">OPEN</span> <span class="keyword">TABLES</span> <span class="keyword">where</span> In_use &gt; <span class="number">0</span>;</span><br></pre>          </td>        </tr>      </table>    </figure>  </li>  <li>    <p>PERFORMANCE_SCHEMA 中的表(*_INSTANCES)</p>    <blockquote>      <p>所有的*_INSTANCES表都包含 NAME和OBJECT_INSTANCE_BEGIN字段，这两个字段分别代表实例的名称和对象检测时的内存地址</p>    </blockquote>  </li></ol><ul>  <li>    <p>COND_INSTANCES: 表包含等待条件列表，这些条件是在服务器启动后生成的。条件是指使一个线程等待其他线程的方式方法。</p>  </li>  <li>    <p>FILE_INSTANCES: 表包含性能架构可见的文件列表。当服务器首次打开文件的时候就将文件名插入该表，并且在文件从磁盘删除之前都会保存在该表之中。目前（…）打开文件会有一个正的 OPEN_COUNT计数。Number字段保存当前使用该文件的文件句柄数量。</p>  </li>  <li>    <p>MUTEX_INSTANCES: 表包含性能架构可见的互斥列表。互斥记录中 LOCKED_BY_THREAD_IS 的值为 NOT NULL 部分是当前锁定的互斥。</p>  </li>  <li>    <p>RWLOCK_INSTANCE: 表包含所有读/写锁实例的列表。WRITE_LOCKED_BY_THREAD_IS 字段代表持有锁的线程ID。READ_LOCKED_BYCOUNT字段代表当前在实例上获取了多少读锁。</p>  </li>  <li>    <p>EVENTS_WAITS_*： 系列表包含每个线程等待的事件的信息。</p>  </li>  <li>    <p>*<em>SUMMARY</em>*: 表包含被终止事件的聚合信息。</p>  </li></ul><ol start="3">  <li>其他命令<figure class="highlight plain">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>          </td>          <td class="code">            <pre><span class="line">-- 查看变量</span><br><span class="line">show variables like &apos;%xxx%&apos;;</span><br><span class="line">-- 查系统变量</span><br><span class="line">select @@xxx;</span><br></pre>          </td>        </tr>      </table>    </figure>  </li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;常用命令列举&quot;&gt;&lt;a href=&quot;#常用命令列举&quot; class=&quot;headerlink&quot; title=&quot;常用命令列举&quot;&gt;&lt;/a&gt;常用命令列举&lt;/h4&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;主要是 INFORMATION_SCHEMA 库里的表&lt;/p&gt;
    &lt;
      
    
    </summary>
    
      <category term="不务正业" scheme="http://ipiao.top/categories/%E4%B8%8D%E5%8A%A1%E6%AD%A3%E4%B8%9A/"/>
    
    
      <category term="mysql" scheme="http://ipiao.top/tags/mysql/"/>
    
  </entry>
  
  <entry>
    <title>TCP Delayed Ack(Ack确认延迟) &amp;&amp; Nagle Algorithm(纳格算法)</title>
    <link href="http://ipiao.top/2019/09/11/TCP-Delayed-Ack-Ack%E7%A1%AE%E8%AE%A4%E5%BB%B6%E8%BF%9F-Nagle-Algorithm-%E7%BA%B3%E6%A0%BC%E7%AE%97%E6%B3%95/"/>
    <id>http://ipiao.top/2019/09/11/TCP-Delayed-Ack-Ack确认延迟-Nagle-Algorithm-纳格算法/</id>
    <published>2019-09-11T06:51:00.000Z</published>
    <updated>2019-09-11T07:08:05.309Z</updated>
    
    <content type="html"><![CDATA[<p>[原文连接: <a href="https://www.jianshu.com/p/bc3c1d6dafe8?from=singlemessage]" target="_blank" rel="noopener">https://www.jianshu.com/p/bc3c1d6dafe8?from=singlemessage]</a></p><p>如果一个 TCP 连接的一端启用了 Nagle‘s Algorithm，而另一端启用了 TCP Delayed<br>Ack，而发送的数据包又比较小，则可能会出现这样的情况：发送端在等 待接收端对上<br>一个packet 的 Ack 才发送当前的 packet，而接收端则正好延迟了 此 Ack 的发送，那么<br>这个正要被发送的 packet 就会同样被延迟。当然 Delayed Ack 是有个超时机制的，而默<br>认的超时正好就是40ms。</p><a id="more"></a><h5 id="1-Delayed-Ack"><a href="#1-Delayed-Ack" class="headerlink" title="1.Delayed Ack"></a>1.Delayed Ack</h5><p>tcp协议规定在接受到数据段时需要向对方发送一个确认,但如果只是单纯的发送一个确<br>认,代价会比较高(20字节的ip首部,20字节的tcp首部),最好能附带响应数据一起发送给<br>对 方.所以tcp在何时发送ack给对方有以下规定:</p><p>1) 当有响应数据要发送时,ack会随响数据立即发送给对方.<br>2) 如果没有响应数据,ack的发 送将会有一个延迟,以等待看是否有响应数据可以一起发送 ,这称是”Delayed Ack”.但这个延迟最多不会超过500ms,一般为200ms.如果在200ms内有数据要发送,那么ack会随数据一起立即发送给对方.注意这里的延迟200ms,不是指的从接受到对方数据到发送ack的最长等待时间差.而是指的内核启动的一个定时器,它每隔200ms就查看下是否有ack要发送.例如:假设定时器在0ms时启动,对方的数据段在185ms时到达,那么ack最迟会在200ms时发送,而不是385ms时发送.</p><p>3) 如果在等待发送ack期间,对方的第二个数据段又到达了,这时要立即发送ack.但是如果对方的三个数据段相继 到达,那么第二个数据段到达时ack立即发送,但第三个数据段到达时是否立即发送,则取决于上面两条.</p><h5 id="2-Nagle-Algorithm"><a href="#2-Nagle-Algorithm" class="headerlink" title="2.Nagle Algorithm"></a>2.Nagle Algorithm</h5><p>当tcp协议用来传输小的数据段时代码是很高的,并且如果传输是在广域网上,那可能就会引起网络拥塞.Nagle算法就是用来解决这个问题.该算法要求一个TCP连接上最多只能有一个未被确认(未收到Ack确认)的未完成的小分组，在该分组的确认到达之前不能发送其他的小分组。相反TCP收集这些少量的分组，并在确认到来时以一 个分组的方式发出去.Host Requirements RFC声明TCP必须实现Nagle算法，但必须为应用提供一种方法来关闭该算法在某个连接上执行。</p><p>纳格算法是合并(coalescing)一定数量的输出资料后一次送出。特别的是，只要有已送出的<a href="https://link.jianshu.com?t=http://baike.baidu.com/view/111761.htm" target="_blank" rel="noopener">封包</a>尚未确认，传送者会持续缓冲封包，直到累积一定数量的<a href="https://link.jianshu.com?t=http://baike.baidu.com/view/18710.htm" target="_blank" rel="noopener">资料</a>才送出。</p><p>算法如下如下：</p><figure class="highlight livecodeserver">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="keyword">if</span> 有新资料要传送</span><br><span class="line"><span class="keyword">if</span> 讯窗大小 &gt;= MSS <span class="keyword">and</span> 可传送的资料 &gt;= MSS</span><br><span class="line"> 立刻传送完整MSS大小的<span class="keyword">segment</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">if</span> 管线中有尚未确认的资料</span><br><span class="line"> 在下一个确认(ACK)封包收到前，将资料排进缓冲区伫列</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> ( MSS=最大<span class="keyword">segment</span>大小)</span><br></pre>      </td>    </tr>  </table></figure><p>为什么要同时介绍这两个知识呢？因为这两个技术同时使用的话会出现问题，下面来看一下问题的出现场景:<br>A 和B进行数据传输:<br> A运行Nagle算法，<br> B运行delayed ACK算法</p><ol>  <li>A-&gt;B 发一个packet(数据包), B不回应，delay ACK</li>  <li>A-&gt; 再发一个packet(数据包)</li>  <li>B收到第二个packet(数据包)，这时候会回应第一个packet(数据包)，即第一个ACK</li>  <li>假设这时候A里的数据已经发送完</li></ol><p>此时问题就来了，因为A没有收到第二个packet的ACK确认，同时数据当然我们从上面可以看到这种等待机制还是有副作用的，那就是需要等待：一项数据表明：在以太网上,传输100000字节仅需1ms,但由于delayed ack和nagle的作用却要花费201ms,这显然对程序的效率产生了很大影响.TCP/IP协议中，无论发送多少数据，总是要在数据前面加上协议头，同时，对方接收到数据，也需要发送ACK表示确认。为了尽可能的利用网络带宽，TCP总是希望尽可能的发送足够大的数据。（一个连接会设置MSS参数，因此，TCP/IP希望每次都能够以MSS尺寸的数据块来发送数据）。<br>Nagle算法就是为了尽可能发送大块数据，避免网络中充斥着许多小数据块。<br>Nagle算法的基本定义是任意时刻，最多只能有一个未被确认的小段。<br>所谓“小段”，指的是小于MSS尺寸的数据块，<br>所谓“未被确认”，是指一个数据块发送出去后，没有收到对方发送的ACK确认该数据已收到。</p><p>举个例子，比如之前的blog中的实验，一开始client端调用socket的write操作将一个int型数据(称为A块)写入到网络中，由于此时连接是空闲的（也就是说还没有未被确认的小段），因此这个int型数据会被马上发送到server端，接着，client端又调用write操作写入‘/r/n’（简称B块），这个时候，A块的ACK没有返回，所以可以认为已经存在了一个未被确认的小段，所以B块没有立即被发送，一直等待A块的ACK收到（大概40ms之后），B块才被发送。整个过程如图所示：</p><p><img src="/images/pasted-18.png" alt="upload successful"></p><p>这里还隐藏了一个问题，就是A块数据的ACK为什么40ms之后才收到？这是因为TCP/IP中不仅仅有nagle算法，还有一个ACK延迟机制。当Server端收到数据之后，它并不会马上向client端发送ACK，而是会将ACK的发送延迟一段时间（假设为t），它希望在t时间内server端会向client端发送应答数据，这样ACK就能够和应答数据一起发送，就像是应答数据捎带着ACK过去。在我之前的时间中，t大概就是40ms。这就解释了为什么’/r/n’(B块)总是在A块之后40ms才发出。</p><p>如果你觉着nagle算法太捣乱了，那么可以通过设置TCP_NODELAY将其禁用。当然，更合理的方案还是应该使用一次大数据的写操作，而不是多次小数据的写操作。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;[原文连接: &lt;a href=&quot;https://www.jianshu.com/p/bc3c1d6dafe8?from=singlemessage]&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.jianshu.com/p/bc3c1d6dafe8?from=singlemessage]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;如果一个 TCP 连接的一端启用了 Nagle‘s Algorithm，而另一端启用了 TCP Delayed&lt;br&gt;Ack，而发送的数据包又比较小，则可能会出现这样的情况：发送端在等 待接收端对上&lt;br&gt;一个packet 的 Ack 才发送当前的 packet，而接收端则正好延迟了 此 Ack 的发送，那么&lt;br&gt;这个正要被发送的 packet 就会同样被延迟。当然 Delayed Ack 是有个超时机制的，而默&lt;br&gt;认的超时正好就是40ms。&lt;/p&gt;
    
    </summary>
    
      <category term="reading" scheme="http://ipiao.top/categories/reading/"/>
    
    
      <category term="tcp" scheme="http://ipiao.top/tags/tcp/"/>
    
  </entry>
  
  <entry>
    <title>Go语言TCP网络编程</title>
    <link href="http://ipiao.top/2019/09/11/Go%E8%AF%AD%E8%A8%80TCP%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
    <id>http://ipiao.top/2019/09/11/Go语言TCP网络编程/</id>
    <published>2019-09-11T06:41:00.000Z</published>
    <updated>2019-09-11T06:49:45.731Z</updated>
    
    <content type="html"><![CDATA[<p>[原文链接：<a href="https://blog.csdn.net/hacker00011000/article/details/53910367]" target="_blank" rel="noopener">https://blog.csdn.net/hacker00011000/article/details/53910367]</a></p><h5 id="一、序言"><a href="#一、序言" class="headerlink" title="一、序言"></a>一、序言</h5><p>Golang的主要 设计目标之一就是面向大规模后端服务程序，网络通信这块是服务端 程序必不可少也是至关重要的一部分。在日常应用中，我们也可以看到Go中的net以及其subdirectories下的包均是“高频+刚需”，而TCP socket则是网络编程的主流，即便您没有直接使用到net中有关TCP Socket方面的接口，但net/http总是用到了吧，http底层依旧是用tcp socket实现的</p><p>网络编程方面，我们最常用的就是tcp socket编程了，在posix标准出来后，socket在各大主流OS平台上都得到了很好的支持。关于tcp programming，最好的资料莫过于W. Richard Stevens 的网络编程圣经《UNIX网络 编程 卷1：套接字联网API》 了，书中关于tcp socket接口的各种使用、行为模式、异常处理讲解的十分细致。Go是自带runtime的跨平台编程语言，Go中暴露给语言使用者的tcp socket api是建立OS原生tcp socket接口之上的。由于Go runtime调度的需要，golang tcp socket接口在行为特点与异常处理方面与OS原生接口有着一些差别。这篇博文的目标就是整理出关于Go tcp socket在各个场景下的使用方法、行为特点以及注意事项<br>  <a id="more"></a></p><h5 id="二、模型"><a href="#二、模型" class="headerlink" title="二、模型"></a>二、模型</h5><p>从tcp socket诞生后，网络编程架构模型也几经演化，大致是：“每进程一个连接” –&gt; “每线程一个连接” –&gt; “Non-Block + I/O多路复用(linux epoll/windows iocp/freebsd darwin kqueue/solaris Event Port)”。伴随着模型的演化，服务程序愈加强大，可以支持更多的连接，获得更好的处理性能</p><p>目前主流web server一般均采用的都是”Non-Block + I/O多路复用”（有的也结合了多线程、多进程）。不过I/O多路复用也给使用者带来了不小的复杂度，以至于后续出现了许多高性能的I/O多路复用框架， 比如libevent、libev、libuv等，以帮助开发者简化开发复杂性，降低心智负担。不过Go的设计者似乎认为I/O多路复用的这种通过回调机制割裂控制流 的方式依旧复杂，且有悖于“一般逻辑”设计，为此Go语言将该“复杂性”隐藏在Runtime中了：Go开发者无需关注socket是否是 non-block的，也无需亲自注册文件描述符的回调，只需在每个连接对应的goroutine中以“block I/O”的方式对待socket处理即可，这可以说大大降低了开发人员的心智负担。一个典型的Go server端程序大致如下</p><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">//go-tcpsock/server.go</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">HandleConn</span><span class="params">(conn net.Conn)</span></span> &#123;</span><br><span class="line">    <span class="keyword">defer</span> conn.Close()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> &#123;</span><br><span class="line">        <span class="comment">// read from the connection</span></span><br><span class="line">        <span class="comment">// ... ...</span></span><br><span class="line">        <span class="comment">// write to the connection</span></span><br><span class="line">        <span class="comment">//... ...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    listen, err := net.Listen(<span class="string">"tcp"</span>, <span class="string">":8888"</span>)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        fmt.Println(<span class="string">"listen error: "</span>, err)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> &#123;</span><br><span class="line">        conn, err := listen.Accept()</span><br><span class="line">        <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">            fmt.Println(<span class="string">"accept error: "</span>, err)</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// start a new goroutine to handle the new connection</span></span><br><span class="line">        <span class="keyword">go</span> HandleConn(conn)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><p>用户层眼中看到的goroutine中的“block socket”，实际上是通过Go runtime中的netpoller通过Non-block socket + I/O多路复用机制“模拟”出来的，真实的underlying socket实际上是non-block的，只是runtime拦截了底层socket系统调用的错误码，并通过netpoller和goroutine 调度让goroutine“阻塞”在用户层得到的Socket fd上。比如：当用户层针对某个socket fd发起read操作时，如果该socket fd中尚无数据，那么runtime会将该socket fd加入到netpoller中监听，同时对应的goroutine被挂起，直到runtime收到socket fd 数据ready的通知，runtime才会重新唤醒等待在该socket fd上准备read的那个Goroutine。而这个过程从Goroutine的视角来看，就像是read操作一直block在那个socket fd上似的。具体实现细节在后续场景中会有补充描述</p><h5 id="三、TCP连接的建立"><a href="#三、TCP连接的建立" class="headerlink" title="三、TCP连接的建立"></a>三、TCP连接的建立</h5><p>众所周知，TCP Socket的连接的建立需要经历客户端和服务端的三次握手的过程。连接建立过程中，服务端是一个标准的Listen + Accept的结构(可参考上面的代码)，而在客户端Go语言使用net.Dial()或net.DialTimeout()进行连接建立</p><p>阻塞Dial：<br>  <figure class="highlight go">    <table>      <tr>        <td class="gutter">          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>        </td>        <td class="code">          <pre><span class="line">conn, err := net.Dial(<span class="string">"tcp"</span>, <span class="string">"www.baidu.com:80"</span>)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">    <span class="comment">//handle error</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//read or write on conn</span></span><br></pre>        </td>      </tr>    </table>  </figure></p><p>超时机制的Dial：<br>  <figure class="highlight go">    <table>      <tr>        <td class="gutter">          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>        </td>        <td class="code">          <pre><span class="line">conn, err := net.DialTimeout(<span class="string">"tcp"</span>, <span class="string">"www.baidu.com:80"</span>, <span class="number">2</span>*time.Second)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">    <span class="comment">//handle error</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//read or write on conn</span></span><br></pre>        </td>      </tr>    </table>  </figure></p><p>对于客户端而言，连接的建立会遇到如下几种情形：</p><p>1、网络不可达或对方服务未启动<br>如果传给Dial的Addr是可以立即判断出网络不可达，或者Addr中端口对应的服务没有启动，端口未被监听，Dial会几乎立即返回错误，比如：</p><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">//go-tcpsock/conn_establish/client1.go</span></span><br><span class="line">... ...</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    log.Println(<span class="string">"begin dial..."</span>)</span><br><span class="line">    conn, err := net.Dial(<span class="string">"tcp"</span>, <span class="string">":8888"</span>)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        log.Println(<span class="string">"dial error:"</span>, err)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">defer</span> conn.Close()</span><br><span class="line">    log.Println(<span class="string">"dial ok"</span>)</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><p>如果本机8888端口未有服务程序监听，那么执行上面程序，Dial会很快返回错误：</p><figure class="highlight sh">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="variable">$go</span> run client1.go</span><br><span class="line">2015/11/16 14:37:41 begin dial...</span><br><span class="line">2015/11/16 14:37:41 dial error: dial tcp :8888: getsockopt: connection refused</span><br></pre>      </td>    </tr>  </table></figure><p>2、对方服务的listen backlog满<br>还有一种场景就是对方服务器很忙，瞬间有大量client端连接尝试向server建立，server端的listen backlog队列满，server accept不及时((即便不accept，那么在backlog数量范畴里面，connect都会是成功的，因为new conn已经加入到server side的listen queue中了，accept只是从queue中取出一个conn而已)，这将导致client端Dial阻塞。我们还是通过例子感受Dial的行为特点：<br>服务端代码：</p><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">//go-tcpsock/conn_establish/server2.go</span></span><br><span class="line">... ...</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    l, err := net.Listen(<span class="string">"tcp"</span>, <span class="string">":8888"</span>)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        log.Println(<span class="string">"error listen:"</span>, err)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">defer</span> l.Close()</span><br><span class="line">    log.Println(<span class="string">"listen ok"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> i <span class="keyword">int</span></span><br><span class="line">    <span class="keyword">for</span> &#123;</span><br><span class="line">        time.Sleep(time.Second * <span class="number">10</span>)</span><br><span class="line">        <span class="keyword">if</span> _, err := l.Accept(); err != <span class="literal">nil</span> &#123;</span><br><span class="line">            log.Println(<span class="string">"accept error:"</span>, err)</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        &#125;</span><br><span class="line">        i++</span><br><span class="line">        log.Printf(<span class="string">"%d: accept a new connection\n"</span>, i)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><p>客户端代码：</p><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">//go-tcpsock/conn_establish/client2.go</span></span><br><span class="line">... ...</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">establishConn</span><span class="params">(i <span class="keyword">int</span>)</span> <span class="title">net</span>.<span class="title">Conn</span></span> &#123;</span><br><span class="line">    conn, err := net.Dial(<span class="string">"tcp"</span>, <span class="string">":8888"</span>)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        log.Printf(<span class="string">"%d: dial error: %s"</span>, i, err)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">    &#125;</span><br><span class="line">    log.Println(i, <span class="string">":connect to server ok"</span>)</span><br><span class="line">    <span class="keyword">return</span> conn</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    <span class="keyword">var</span> sl []net.Conn</span><br><span class="line">    <span class="keyword">for</span> i := <span class="number">1</span>; i &lt; <span class="number">1000</span>; i++ &#123;</span><br><span class="line">        conn := establishConn(i)</span><br><span class="line">        <span class="keyword">if</span> conn != <span class="literal">nil</span> &#123;</span><br><span class="line">            sl = <span class="built_in">append</span>(sl, conn)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    time.Sleep(time.Second * <span class="number">10000</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="string">``</span><span class="string">` </span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">从程序可以看出，服务端在listen成功后，每隔10s钟accept一次。客户端则是串行的尝试建立连接。这两个程序在Darwin下的执行 结果：</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">`</span><span class="string">``</span> sh</span><br><span class="line">$<span class="keyword">go</span> run server2.<span class="keyword">go</span></span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">55</span>:<span class="number">41</span> listen ok</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">55</span>:<span class="number">51</span> <span class="number">1</span>: accept a <span class="built_in">new</span> connection</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">56</span>:<span class="number">01</span> <span class="number">2</span>: accept a <span class="built_in">new</span> connection</span><br><span class="line">... ...</span><br><span class="line"></span><br><span class="line">$<span class="keyword">go</span> run client2.<span class="keyword">go</span></span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">55</span>:<span class="number">44</span> <span class="number">1</span> :connect to server ok</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">55</span>:<span class="number">44</span> <span class="number">2</span> :connect to server ok</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">55</span>:<span class="number">44</span> <span class="number">3</span> :connect to server ok</span><br><span class="line">... ...</span><br><span class="line"></span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">55</span>:<span class="number">44</span> <span class="number">126</span> :connect to server ok</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">55</span>:<span class="number">44</span> <span class="number">127</span> :connect to server ok</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">55</span>:<span class="number">44</span> <span class="number">128</span> :connect to server ok</span><br><span class="line"></span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">55</span>:<span class="number">52</span> <span class="number">129</span> :connect to server ok</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">56</span>:<span class="number">03</span> <span class="number">130</span> :connect to server ok</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">16</span> <span class="number">21</span>:<span class="number">56</span>:<span class="number">14</span> <span class="number">131</span> :connect to server ok</span><br><span class="line">... ...</span><br></pre>      </td>    </tr>  </table></figure><p>可以看出Client初始时成功地一次性建立了128个连接，然后后续每阻塞近10s才能成功建立一条连接。也就是说在server端 backlog满时(未及时accept)，客户端将阻塞在Dial上，直到server端进行一次accept。至于为什么是128，这与darwin 下的默认设置有关：<br>如果我在ubuntu 14.04上运行上述server程序，我们的client端初始可以成功建立499条连接。</p><p>如果server一直不accept，client端会一直阻塞么？我们去掉accept后的结果是：在Darwin下，client端会阻塞大 约1分多钟才会返回timeout：<br>而如果server运行在ubuntu 14.04上，client似乎一直阻塞，我等了10多分钟依旧没有返回。 阻塞与否看来与server端的网络实现和设置有关</p><p>3、网络延迟较大，Dial阻塞并超时<br>如果网络延迟较大，TCP握手过程将更加艰难坎坷（各种丢包），时间消耗的自然也会更长。Dial这时会阻塞，如果长时间依旧无法建立连接，则Dial也会返回“ getsockopt: operation timed out”错误</p><p>在连接建立阶段，多数情况下，Dial是可以满足需求的，即便阻塞一小会儿。但对于某些程序而言，需要有严格的连接时间限定，如果一定时间内没能成功建立连接，程序可能会需要执行一段“异常”处理逻辑，为此我们就需要DialTimeout了。下面的例子将Dial的最长阻塞时间限制在2s内，超出这个时长，Dial将返回timeout error：</p><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">//go-tcpsock/conn_establish/client3.go</span></span><br><span class="line">... ...</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    log.Println(<span class="string">"begin dial..."</span>)</span><br><span class="line">    conn, err := net.DialTimeout(<span class="string">"tcp"</span>, <span class="string">"104.236.176.96:80"</span>, <span class="number">2</span>*time.Second)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        log.Println(<span class="string">"dial error:"</span>, err)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">defer</span> conn.Close()</span><br><span class="line">    log.Println(<span class="string">"dial ok"</span>)</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><p>执行结果如下，需要模拟一个网络延迟大的环境</p><figure class="highlight sh">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="variable">$go</span> run client3.go</span><br><span class="line">2015/11/17 09:28:34 begin dial...</span><br><span class="line">2015/11/17 09:28:36 dial error: dial tcp 104.236.176.96:80: i/o timeout</span><br></pre>      </td>    </tr>  </table></figure><h5 id="四、Socket读写"><a href="#四、Socket读写" class="headerlink" title="四、Socket读写"></a>四、Socket读写</h5><p>连接建立起来后，我们就要在conn上进行读写，以完成业务逻辑。前面说过Go runtime隐藏了I/O多路复用的复杂性。语言使用者只需采用goroutine+Block I/O的模式即可满足大部分场景需求。Dial成功后，方法返回一个net.Conn接口类型变量值，这个接口变量的动态类型为一个*TCPConn：</p><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">//$GOROOT/src/net/tcpsock_posix.go</span></span><br><span class="line"><span class="keyword">type</span> TCPConn <span class="keyword">struct</span> &#123;</span><br><span class="line">    conn</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><p>TCPConn内嵌了一个unexported类型：conn，因此TCPConn”继承”了conn的Read和Write方法，后续通过Dial返回值调用的Write和Read方法均是net.conn的方法：</p><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">//$GOROOT/src/net/net.go</span></span><br><span class="line"><span class="keyword">type</span> conn <span class="keyword">struct</span> &#123;</span><br><span class="line">    fd *netFD</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *conn)</span> <span class="title">ok</span><span class="params">()</span> <span class="title">bool</span></span> &#123; <span class="keyword">return</span> c != <span class="literal">nil</span> &amp;&amp; c.fd != <span class="literal">nil</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Implementation of the Conn interface.</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Read implements the Conn Read method.</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *conn)</span> <span class="title">Read</span><span class="params">(b []<span class="keyword">byte</span>)</span> <span class="params">(<span class="keyword">int</span>, error)</span></span> &#123;</span><br><span class="line">    <span class="keyword">if</span> !c.ok() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>, syscall.EINVAL</span><br><span class="line">    &#125;</span><br><span class="line">    n, err := c.fd.Read(b)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &amp;&amp; err != io.EOF &#123;</span><br><span class="line">        err = &amp;OpError&#123;Op: <span class="string">"read"</span>, Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n, err</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Write implements the Conn Write method.</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *conn)</span> <span class="title">Write</span><span class="params">(b []<span class="keyword">byte</span>)</span> <span class="params">(<span class="keyword">int</span>, error)</span></span> &#123;</span><br><span class="line">    <span class="keyword">if</span> !c.ok() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>, syscall.EINVAL</span><br><span class="line">    &#125;</span><br><span class="line">    n, err := c.fd.Write(b)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        err = &amp;OpError&#123;Op: <span class="string">"write"</span>, Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n, err</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><p>1、conn.Read的行为特点</p><p>1.1、Socket中无数据<br>连接建立后，如果对方未发送数据到socket，接收方(Server)会阻塞在Read操作上，这和前面提到的“模型”原理是一致的。执行该Read操作的goroutine也会被挂起。runtime会监视该socket，直到其有数据才会重新<br>调度该socket对应的Goroutine完成read。由于篇幅原因，这里就不列代码了，例子对应的代码文件：go-tcpsock/read_write下的client1.go和server1.go。</p><p>1.2、Socket中有部分数据<br>如果socket中有部分数据，且长度小于一次Read操作所期望读出的数据长度，那么Read将会成功读出这部分数据并返回，而不是等待所有期望数据全部读取后再返回。</p><p>1.3、Socket中有足够数据<br>如果socket中有数据，且长度大于等于一次Read操作所期望读出的数据长度，那么Read将会成功读出这部分数据并返回。这个情景是最符合我们对Read的期待的了：Read将用Socket中的数据将我们传入的slice填满后返回：n = 10, err = nil</p><p>1.4、Socket关闭<br>如果client端主动关闭了socket，那么Server的Read将会读到什么呢？<br>这里分为“有数据关闭”和“无数据关闭”。</p><p>有数据关闭是指在client关闭时，socket中还有server端未读取的数据。当client端close socket退出后，server依旧没有开始Read，10s后第一次Read成功读出了所有的数据，当第二次Read时，由于client端 socket关闭，Read返回EOF error</p><p>无数据关闭情形下的结果，那就是Read直接返回EOF error</p><p>1.5、读取操作超时<br>有些场合对Read的阻塞时间有严格限制，在这种情况下，Read的行为到底是什么样的呢？在返回超时错误时，是否也同时Read了一部分数据了呢？<br>不会出现“读出部分数据且返回超时错误”的情况</p><p>2、conn.Write的行为特点</p><p>2.1、成功写<br>前面例子着重于Read，client端在Write时并未判断Write的返回值。所谓“成功写”指的就是Write调用返回的n与预期要写入的数据长度相等，且error = nil。这是我们在调用Write时遇到的最常见的情形，这里不再举例了</p><p>2.2、写阻塞<br>TCP连接通信两端的OS都会为该连接保留数据缓冲，一端调用Write后，实际上数据是写入到OS的协议栈的数据缓冲的。TCP是全双工通信，因此每个方向都有独立的数据缓冲。当发送方将对方的接收缓冲区以及自身的发送缓冲区写满后，Write就会阻塞</p><p>2.3、写入部分数据<br>Write操作存在写入部分数据的情况。没有按照预期的写入所有数据。这时候循环写入便是</p><p>综上例子，虽然Go给我们提供了阻塞I/O的便利，但在调用Read和Write时依旧要综合需要方法返回的n和err的结果，以做出正确处理。net.conn实现了io.Reader和io.Writer接口，因此可以试用一些wrapper包进行socket读写，比如bufio包下面的Writer和Reader、io/ioutil下的函数等</p><h5 id="五、Goroutine-safe"><a href="#五、Goroutine-safe" class="headerlink" title="五、Goroutine safe"></a>五、Goroutine safe</h5><p>基于goroutine的网络架构模型，存在在不同goroutine间共享conn的情况，那么conn的读写是否是goroutine safe的呢？在深入这个问题之前，我们先从应用意义上来看read操作和write操作的goroutine-safe必要性。</p><p>对于read操作而言，由于TCP是面向字节流，conn.Read无法正确区分数据的业务边界，因此多个goroutine对同一个conn进行read的意义不大，goroutine读到不完整的业务包反倒是增加了业务处理的难度。对与Write操作而言，倒是有多个goroutine并发写的情况。</p><p>每次Write操作都是受lock保护，直到此次数据全部write完。因此在应用层面，要想保证多个goroutine在一个conn上write操作的Safe，需要一次write完整写入一个“业务包”；一旦将业务包的写入拆分为多次write，那就无法保证某个Goroutine的某“业务包”数据在conn发送的连续性。</p><p>同时也可以看出即便是Read操作，也是lock保护的。多个Goroutine对同一conn的并发读不会出现读出内容重叠的情况，但内容断点是依 runtime调度来随机确定的。存在一个业务包数据，1/3内容被goroutine-1读走，另外2/3被另外一个goroutine-2读 走的情况。比如一个完整包：world，当goroutine的read slice size &lt; 5时，存在可能：一个goroutine读到 “worl”,另外一个goroutine读出”d”。</p><h5 id="六、Socket属性"><a href="#六、Socket属性" class="headerlink" title="六、Socket属性"></a>六、Socket属性</h5><p>原生Socket API提供了丰富的sockopt设置接口，但Golang有自己的网络架构模型，golang提供的socket options接口也是基于上述模型的必要的属性设置。包括<br>SetKeepAlive<br>SetKeepAlivePeriod<br>SetLinger<br>SetNoDelay （默认no delay）<br>SetWriteBuffer<br>SetReadBuffer</p><p>不过上面的Method是TCPConn的，而不是Conn的，要使用上面的Method的，需要type assertion：</p><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>      </td>      <td class="code">        <pre><span class="line">tcpConn, ok := conn.(*TCPConn)</span><br><span class="line"><span class="keyword">if</span> !ok &#123;</span><br><span class="line">    <span class="comment">//error handle</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">tcpConn.SetNoDelay(<span class="literal">true</span>)</span><br></pre>      </td>    </tr>  </table></figure><p>对于listener socket, golang默认采用了 SO_REUSEADDR，这样当你重启 listener程序时，不会因为address in use的错误而启动失败。而listen backlog的默认值是通过获取系统的设置值得到的。不同系统不同：mac 128, linux 512等</p><p>七、关闭连接<br>和前面的方法相比，关闭连接算是最简单的操作了。由于socket是全双工的，client和server端在己方已关闭的socket和对方关闭的socket上操作的结果有不同。看下面例子：</p><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">//go-tcpsock/conn_close/client1.go</span></span><br><span class="line">... ...</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    log.Println(<span class="string">"begin dial..."</span>)</span><br><span class="line">    conn, err := net.Dial(<span class="string">"tcp"</span>, <span class="string">":8888"</span>)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        log.Println(<span class="string">"dial error:"</span>, err)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">    conn.Close()</span><br><span class="line">    log.Println(<span class="string">"close ok"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> buf = <span class="built_in">make</span>([]<span class="keyword">byte</span>, <span class="number">32</span>)</span><br><span class="line">    n, err := conn.Read(buf)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        log.Println(<span class="string">"read error:"</span>, err)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        log.Printf(<span class="string">"read % bytes, content is %s\n"</span>, n, <span class="keyword">string</span>(buf[:n]))</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    n, err = conn.Write(buf)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        log.Println(<span class="string">"write error:"</span>, err)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        log.Printf(<span class="string">"write % bytes, content is %s\n"</span>, n, <span class="keyword">string</span>(buf[:n]))</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    time.Sleep(time.Second * <span class="number">1000</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//go-tcpsock/conn_close/server1.go</span></span><br><span class="line">... ...</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">handleConn</span><span class="params">(c net.Conn)</span></span> &#123;</span><br><span class="line">    <span class="keyword">defer</span> c.Close()</span><br><span class="line"></span><br><span class="line">    <span class="comment">// read from the connection</span></span><br><span class="line">    <span class="keyword">var</span> buf = <span class="built_in">make</span>([]<span class="keyword">byte</span>, <span class="number">10</span>)</span><br><span class="line">    log.Println(<span class="string">"start to read from conn"</span>)</span><br><span class="line">    n, err := c.Read(buf)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        log.Println(<span class="string">"conn read error:"</span>, err)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        log.Printf(<span class="string">"read %d bytes, content is %s\n"</span>, n, <span class="keyword">string</span>(buf[:n]))</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    n, err = c.Write(buf)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        log.Println(<span class="string">"conn write error:"</span>, err)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        log.Printf(<span class="string">"write %d bytes, content is %s\n"</span>, n, <span class="keyword">string</span>(buf[:n]))</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">... ...</span><br></pre>      </td>    </tr>  </table></figure><p>执行结果如下</p><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre>      </td>      <td class="code">        <pre><span class="line">$<span class="keyword">go</span> run server1.<span class="keyword">go</span></span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">17</span> <span class="number">17</span>:<span class="number">00</span>:<span class="number">51</span> accept a <span class="built_in">new</span> connection</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">17</span> <span class="number">17</span>:<span class="number">00</span>:<span class="number">51</span> start to read from conn</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">17</span> <span class="number">17</span>:<span class="number">00</span>:<span class="number">51</span> conn read error: EOF</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">17</span> <span class="number">17</span>:<span class="number">00</span>:<span class="number">51</span> write <span class="number">10</span> bytes, content is</span><br><span class="line"></span><br><span class="line">$<span class="keyword">go</span> run client1.<span class="keyword">go</span></span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">17</span> <span class="number">17</span>:<span class="number">00</span>:<span class="number">51</span> begin dial...</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">17</span> <span class="number">17</span>:<span class="number">00</span>:<span class="number">51</span> <span class="built_in">close</span> ok</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">17</span> <span class="number">17</span>:<span class="number">00</span>:<span class="number">51</span> read error: read tcp <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">64195</span>-&gt;<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">8888</span>: use of closed network connection</span><br><span class="line"><span class="number">2015</span>/<span class="number">11</span>/<span class="number">17</span> <span class="number">17</span>:<span class="number">00</span>:<span class="number">51</span> write error: write tcp <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">64195</span>-&gt;<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">8888</span>: use of closed network connection</span><br></pre>      </td>    </tr>  </table></figure><p>从client的结果来看，在己方已经关闭的socket上再进行read和write操作，会得到”use of closed network connection” error；</p><p>从server的执行结果来看，在对方关闭的socket上执行read操作会得到EOF error，但write操作会成功，因为数据会成功写入己方的内核socket缓冲区中，即便最终发不到对方socket缓冲区了，因为己方socket并未关闭。因此当发现对方socket关闭后，己方应该正确合理处理自己的socket，再继续write已经无任何意义了</p><h5 id="八、小结"><a href="#八、小结" class="headerlink" title="八、小结"></a>八、小结</h5><p>本文比较基础，但却很重要，毕竟golang是面向大规模服务后端的，对通信环节的细节的深入理解会大有裨益。另外Go的goroutine+阻塞通信的网络通信模型降低了开发者心智负担，简化了通信的复杂性，这点尤为重要</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;[原文链接：&lt;a href=&quot;https://blog.csdn.net/hacker00011000/article/details/53910367]&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.csdn.net/hacker00011000/article/details/53910367]&lt;/a&gt;&lt;/p&gt;
&lt;h5 id=&quot;一、序言&quot;&gt;&lt;a href=&quot;#一、序言&quot; class=&quot;headerlink&quot; title=&quot;一、序言&quot;&gt;&lt;/a&gt;一、序言&lt;/h5&gt;
&lt;p&gt;Golang的主要 设计目标之一就是面向大规模后端服务程序，网络通信这块是服务端 程序必不可少也是至关重要的一部分。在日常应用中，我们也可以看到Go中的net以及其subdirectories下的包均是“高频+刚需”，而TCP socket则是网络编程的主流，即便您没有直接使用到net中有关TCP Socket方面的接口，但net/http总是用到了吧，http底层依旧是用tcp socket实现的&lt;/p&gt;
&lt;p&gt;网络编程方面，我们最常用的就是tcp socket编程了，在posix标准出来后，socket在各大主流OS平台上都得到了很好的支持。关于tcp programming，最好的资料莫过于W. Richard Stevens 的网络编程圣经《UNIX网络 编程 卷1：套接字联网API》 了，书中关于tcp socket接口的各种使用、行为模式、异常处理讲解的十分细致。Go是自带runtime的跨平台编程语言，Go中暴露给语言使用者的tcp socket api是建立OS原生tcp socket接口之上的。由于Go runtime调度的需要，golang tcp socket接口在行为特点与异常处理方面与OS原生接口有着一些差别。这篇博文的目标就是整理出关于Go tcp socket在各个场景下的使用方法、行为特点以及注意事项&lt;br&gt;
    
    </summary>
    
      <category term="golang大法好" scheme="http://ipiao.top/categories/golang%E5%A4%A7%E6%B3%95%E5%A5%BD/"/>
    
    
      <category term="go" scheme="http://ipiao.top/tags/go/"/>
    
      <category term="tcp" scheme="http://ipiao.top/tags/tcp/"/>
    
  </entry>
  
  <entry>
    <title>redis_其他</title>
    <link href="http://ipiao.top/2019/07/23/redis-%E5%85%B6%E4%BB%96/"/>
    <id>http://ipiao.top/2019/07/23/redis-其他/</id>
    <published>2019-07-23T02:21:00.000Z</published>
    <updated>2019-07-23T08:25:29.493Z</updated>
    
    <content type="html"><![CDATA[<h4 id="命令参考"><a href="#命令参考" class="headerlink" title="命令参考"></a>命令参考</h4><ul>  <li><a href="http://redisdoc.com/index.html" target="_blank" rel="noopener">http://redisdoc.com/index.html</a></li></ul><a id="more"></a><h4 id="redis的一些技术点"><a href="#redis的一些技术点" class="headerlink" title="redis的一些技术点"></a>redis的一些技术点</h4><ul>  <li>ae事件模型<ul>      <li><a href="https://www.jianshu.com/p/da092472080e" target="_blank" rel="noopener">Redis AE异步事件库实例分析</a></li>      <li><a href="http://ju.outofmemory.cn/entry/129612" target="_blank" rel="noopener">Redis的事件循环与定时器模型</a></li>    </ul>  </li>  <li>anet网络连接<ul>      <li><a href="https://blog.csdn.net/weixin_39209889/article/details/78660374" target="_blank" rel="noopener">结合redis设计与实现的redis源码学习-15-TCP网络连接（anet.c）        </a></li>    </ul>  </li>  <li>bio<ul>      <li><a href="https://www.cnblogs.com/learn-my-life/p/5662607.html" target="_blank" rel="noopener">redis BIO详解</a></li>      <li><a href="https://yq.aliyun.com/articles/58703?utm_source=tool.lu" target="_blank" rel="noopener">Redis源码学习——BIO</a></li>    </ul>  </li>  <li>debug<ul>      <li><a href="https://yq.aliyun.com/articles/33" target="_blank" rel="noopener">redis debug命令详解</a></li>    </ul>  </li>  <li>geo<ul>      <li><a href="https://www.cnblogs.com/wt645631686/p/8454497.html" target="_blank" rel="noopener">geohash</a></li>    </ul>  </li>  <li>hyperloglog<ul>      <li><a href="https://www.cnblogs.com/davidwang456/articles/9314699.html" target="_blank" rel="noopener">Redis 基数统计：HyperLogLog 小内存大用处</a></li>    </ul>  </li>  <li>latency<ul>      <li><a href="https://www.jianshu.com/p/95a9ce63ddb2" target="_blank" rel="noopener">聊聊redis的slowlog与latency monitor</a></li>    </ul>  </li>  <li>lazyfree<ul>      <li><a href="https://www.jianshu.com/p/e927e99e650d" target="_blank" rel="noopener">Redis4.0新特性(三)-Lazy Free</a></li>    </ul>  </li>  <li>listpack<ul>      <li><a href="https://yq.aliyun.com/articles/698601" target="_blank" rel="noopener">5.0新功能</a></li>    </ul>  </li>  <li>zipmap<ul>      <li><a href="https://www.jianshu.com/p/b2bb7ad3bcb2" target="_blank" rel="noopener">Redis zipmap</a></li>    </ul>  </li>  <li>module<ul>      <li><a href="https://blog.csdn.net/sdoyuxuan/article/details/82501680" target="_blank" rel="noopener">在Redis modules的实现自己的数据类型</a></li>    </ul>  </li>  <li>networking<ul>      <li><a href="https://www.jianshu.com/p/0ea536cda952?from=timeline&amp;isappinstalled=0" target="_blank" rel="noopener">Redis 源码阅读 ——— 网络模块</a></li>    </ul>  </li>  <li>notify<ul>      <li><a href="https://blog.csdn.net/asdfsadfasdfsa/article/details/88316785" target="_blank" rel="noopener">redis源码–notify</a></li>    </ul>  </li>  <li>quicklist<ul>      <li><a href="https://www.cnblogs.com/virgosnail/p/9542470.html" target="_blank" rel="noopener">Redis—quickList(快速列表)</a></li>    </ul>  </li>  <li>rax<ul>      <li><a href="https://blog.csdn.net/zhanglong_4444/article/details/88383464" target="_blank" rel="noopener">Redis 知识梳理 [ 基数树 ]</a></li>    </ul>  </li>  <li>rio<ul>      <li><a href="https://blog.csdn.net/andyhuabing/article/details/52585101" target="_blank" rel="noopener">Redis 之BIO与RIO</a></li>    </ul>  </li>  <li>sparkline<ul>      <li><a href="https://blog.csdn.net/u012658346/article/details/51332349" target="_blank" rel="noopener">redis学习笔记（8）—微线图sparkline</a></li>    </ul>  </li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;命令参考&quot;&gt;&lt;a href=&quot;#命令参考&quot; class=&quot;headerlink&quot; title=&quot;命令参考&quot;&gt;&lt;/a&gt;命令参考&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://redisdoc.com/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://redisdoc.com/index.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="reading" scheme="http://ipiao.top/categories/reading/"/>
    
    
      <category term="redis" scheme="http://ipiao.top/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>读&lt;&lt;Redis设计与实现&gt;&gt;与redis(5.0)源码__rdb</title>
    <link href="http://ipiao.top/2019/07/12/%E8%AF%BB-Redis%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0-%E4%B8%8Eredis-5-0-%E6%BA%90%E7%A0%81-rdb/"/>
    <id>http://ipiao.top/2019/07/12/读-Redis设计与实现-与redis-5-0-源码-rdb/</id>
    <published>2019-07-12T08:54:00.000Z</published>
    <updated>2019-07-12T10:07:12.118Z</updated>
    
    <content type="html"><![CDATA[<h4 id="db"><a href="#db" class="headerlink" title="db"></a>db</h4><figure class="highlight c">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">redisDb</span> &#123;</span></span><br><span class="line">    dict *dict;                 <span class="comment">/* The keyspace for this DB */</span></span><br><span class="line">    dict *expires;              <span class="comment">/* Timeout of keys with a timeout set */</span></span><br><span class="line">    dict *blocking_keys;        <span class="comment">/* Keys with clients waiting for data (BLPOP)*/</span></span><br><span class="line">    dict *ready_keys;           <span class="comment">/* Blocked keys that received a PUSH */</span></span><br><span class="line">    dict *watched_keys;         <span class="comment">/* WATCHED keys for MULTI/EXEC CAS */</span></span><br><span class="line">    <span class="keyword">int</span> id;                     <span class="comment">/* Database ID */</span></span><br><span class="line">    <span class="keyword">long</span> <span class="keyword">long</span> avg_ttl;          <span class="comment">/* Average TTL, just for stats */</span></span><br><span class="line">    <span class="built_in">list</span> *defrag_later;         <span class="comment">/* List of key names to attempt to defrag one by one, gradually. */</span></span><br><span class="line">&#125; redisDb;</span><br></pre>      </td>    </tr>  </table></figure><a id="more"></a><h4 id="save"><a href="#save" class="headerlink" title="save"></a>save</h4><blockquote>  <p>书上说save的时候过期键不保留,load的时候保留.<strong>但是5.0代码中,情况相反</strong>.可能是saveInfo的关系.即使是定时任务,因为在redis的过期处理规则可能会有部分过期键没有删除掉(serverCron-&gt;databasesCron-&gt;activeExpireCycle),所以这个不保留不是绝对的</p></blockquote><ol>  <li>    <p>rdbSave-&gt;rdbSaveRio</p>    <blockquote>      <p>REDIS|db_version(4)|saveInfo|database|EOF|check_sum</p>      <figure class="highlight c">        <table>          <tr>            <td class="gutter">              <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br></pre>            </td>            <td class="code">              <pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">rdbSaveRio</span><span class="params">(rio *rdb, <span class="keyword">int</span> *error, <span class="keyword">int</span> flags, rdbSaveInfo *rsi)</span> </span>&#123;</span><br><span class="line">    dictIterator *di = <span class="literal">NULL</span>;</span><br><span class="line">    dictEntry *de;</span><br><span class="line">    <span class="keyword">char</span> magic[<span class="number">10</span>];</span><br><span class="line">    <span class="keyword">int</span> j;</span><br><span class="line">    <span class="keyword">uint64_t</span> cksum;</span><br><span class="line">    <span class="keyword">size_t</span> processed = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (server.rdb_checksum)</span><br><span class="line">    <span class="comment">// 校验函数</span></span><br><span class="line">        rdb-&gt;update_cksum = rioGenericUpdateChecksum;</span><br><span class="line">    <span class="built_in">snprintf</span>(magic,<span class="keyword">sizeof</span>(magic),<span class="string">"REDIS%04d"</span>,RDB_VERSION);</span><br><span class="line">    <span class="comment">// 从这里开始,redis+4个字节的版本号</span></span><br><span class="line">    <span class="keyword">if</span> (rdbWriteRaw(rdb,magic,<span class="number">9</span>) == <span class="number">-1</span>) <span class="keyword">goto</span> werr;</span><br><span class="line">    <span class="comment">// 写入saveInfo,type是 RDB_OPCODE_AUX</span></span><br><span class="line">    <span class="keyword">if</span> (rdbSaveInfoAuxFields(rdb,flags,rsi) == <span class="number">-1</span>) <span class="keyword">goto</span> werr;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (j = <span class="number">0</span>; j &lt; server.dbnum; j++) &#123;</span><br><span class="line">        redisDb *db = server.db+j;</span><br><span class="line">        dict *d = db-&gt;dict;</span><br><span class="line">        <span class="keyword">if</span> (dictSize(d) == <span class="number">0</span>) <span class="keyword">continue</span>;</span><br><span class="line">        di = dictGetSafeIterator(d);</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* Write the SELECT DB opcode */</span></span><br><span class="line">        <span class="comment">// 写入SELECTDB = 254</span></span><br><span class="line">        <span class="keyword">if</span> (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == <span class="number">-1</span>) <span class="keyword">goto</span> werr;</span><br><span class="line">        <span class="comment">// 调用的是写入长度,其实是写入数字</span></span><br><span class="line">        <span class="keyword">if</span> (rdbSaveLen(rdb,j) == <span class="number">-1</span>) <span class="keyword">goto</span> werr;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* Write the RESIZE DB opcode. We trim the size to UINT32_MAX, which</span></span><br><span class="line"><span class="comment">         * is currently the largest type we are able to represent in RDB sizes.</span></span><br><span class="line"><span class="comment">         * However this does not limit the actual size of the DB to load since</span></span><br><span class="line"><span class="comment">         * these sizes are just hints to resize the hash tables. */</span></span><br><span class="line">        <span class="keyword">uint64_t</span> db_size, expires_size;</span><br><span class="line">        db_size = dictSize(db-&gt;dict);</span><br><span class="line">        expires_size = dictSize(db-&gt;expires);</span><br><span class="line">        <span class="comment">// 写入db的size,RDB_OPCODE_RESIZEDB = 251</span></span><br><span class="line">        <span class="keyword">if</span> (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == <span class="number">-1</span>) <span class="keyword">goto</span> werr;</span><br><span class="line">        <span class="keyword">if</span> (rdbSaveLen(rdb,db_size) == <span class="number">-1</span>) <span class="keyword">goto</span> werr;</span><br><span class="line">        <span class="comment">// 跟着过期字典的大小</span></span><br><span class="line">        <span class="keyword">if</span> (rdbSaveLen(rdb,expires_size) == <span class="number">-1</span>) <span class="keyword">goto</span> werr;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* Iterate this DB writing every entry */</span></span><br><span class="line">        <span class="keyword">while</span>((de = dictNext(di)) != <span class="literal">NULL</span>) &#123;</span><br><span class="line">            sds keystr = dictGetKey(de);</span><br><span class="line">            robj key, *o = dictGetVal(de);</span><br><span class="line">            <span class="keyword">long</span> <span class="keyword">long</span> expire;</span><br><span class="line"></span><br><span class="line">            initStaticStringObject(key,keystr);</span><br><span class="line">            expire = getExpire(db,&amp;key);</span><br><span class="line">            <span class="comment">// key,value保存</span></span><br><span class="line">            <span class="keyword">if</span> (rdbSaveKeyValuePair(rdb,&amp;key,o,expire) == <span class="number">-1</span>) <span class="keyword">goto</span> werr;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* When this RDB is produced as part of an AOF rewrite, move</span></span><br><span class="line"><span class="comment">             * accumulated diff from parent to child while rewriting in</span></span><br><span class="line"><span class="comment">             * order to have a smaller final write. */</span></span><br><span class="line">             <span class="comment">// 这个应该是AOF重写中被rdb阻塞了而造成延迟</span></span><br><span class="line">            <span class="keyword">if</span> (flags &amp; RDB_SAVE_AOF_PREAMBLE &amp;&amp;</span><br><span class="line">                rdb-&gt;processed_bytes &gt; processed+AOF_READ_DIFF_INTERVAL_BYTES)</span><br><span class="line">            &#123;</span><br><span class="line">                processed = rdb-&gt;processed_bytes;</span><br><span class="line">                aofReadDiffFromParent();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        dictReleaseIterator(di);</span><br><span class="line">        di = <span class="literal">NULL</span>; <span class="comment">/* So that we don't release it again on error. */</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* If we are storing the replication information on disk, persist</span></span><br><span class="line"><span class="comment">     * the script cache as well: on successful PSYNC after a restart, we need</span></span><br><span class="line"><span class="comment">     * to be able to process any EVALSHA inside the replication backlog the</span></span><br><span class="line"><span class="comment">     * master will send us. */</span></span><br><span class="line">    <span class="keyword">if</span> (rsi &amp;&amp; dictSize(server.lua_scripts)) &#123;</span><br><span class="line">        di = dictGetIterator(server.lua_scripts);</span><br><span class="line">        <span class="keyword">while</span>((de = dictNext(di)) != <span class="literal">NULL</span>) &#123;</span><br><span class="line">            robj *body = dictGetVal(de);</span><br><span class="line">            <span class="keyword">if</span> (rdbSaveAuxField(rdb,<span class="string">"lua"</span>,<span class="number">3</span>,body-&gt;ptr,sdslen(body-&gt;ptr)) == <span class="number">-1</span>)</span><br><span class="line">                <span class="keyword">goto</span> werr;</span><br><span class="line">        &#125;</span><br><span class="line">        dictReleaseIterator(di);</span><br><span class="line">        di = <span class="literal">NULL</span>; <span class="comment">/* So that we don't release it again on error. */</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* EOF opcode */</span></span><br><span class="line">    <span class="keyword">if</span> (rdbSaveType(rdb,RDB_OPCODE_EOF) == <span class="number">-1</span>) <span class="keyword">goto</span> werr;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* CRC64 checksum. It will be zero if checksum computation is disabled, the</span></span><br><span class="line"><span class="comment">     * loading code skips the check in this case. */</span></span><br><span class="line">    cksum = rdb-&gt;cksum;</span><br><span class="line">    memrev64ifbe(&amp;cksum);</span><br><span class="line">    <span class="keyword">if</span> (rioWrite(rdb,&amp;cksum,<span class="number">8</span>) == <span class="number">0</span>) <span class="keyword">goto</span> werr;</span><br><span class="line">    <span class="keyword">return</span> C_OK;</span><br><span class="line"></span><br><span class="line">werr:</span><br><span class="line">    <span class="keyword">if</span> (error) *error = errno;</span><br><span class="line">    <span class="keyword">if</span> (di) dictReleaseIterator(di);</span><br><span class="line">    <span class="keyword">return</span> C_ERR;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 各类型在rdb中的编码方式</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">rdbSaveKeyValuePair</span><span class="params">(rio *rdb, robj *key, robj *val, <span class="keyword">long</span> <span class="keyword">long</span> expiretime)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> savelru = server.maxmemory_policy &amp; MAXMEMORY_FLAG_LRU;</span><br><span class="line">    <span class="keyword">int</span> savelfu = server.maxmemory_policy &amp; MAXMEMORY_FLAG_LFU;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Save the expire time */</span></span><br><span class="line">    <span class="comment">//如果设置了过期时间,先写过期时间 RDB_OPCODE_EXPIRETIME_MS = 252</span></span><br><span class="line">    <span class="keyword">if</span> (expiretime != <span class="number">-1</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (rdbSaveType(rdb,RDB_OPCODE_EXPIRETIME_MS) == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        <span class="comment">// 8个字节长的时间</span></span><br><span class="line">        <span class="keyword">if</span> (rdbSaveMillisecondTime(rdb,expiretime) == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Save the LRU info. */</span></span><br><span class="line">    <span class="comment">// 如果设置了LRU策略,就要保存对象的lru信息,为了后面策略的执行</span></span><br><span class="line">    <span class="keyword">if</span> (savelru) &#123;</span><br><span class="line">    <span class="comment">// 因为lru是相对时间,所以要修饰一下</span></span><br><span class="line">        <span class="keyword">uint64_t</span> idletime = estimateObjectIdleTime(val);</span><br><span class="line">        idletime /= <span class="number">1000</span>; <span class="comment">/* Using seconds is enough and requires less space.*/</span></span><br><span class="line">        <span class="keyword">if</span> (rdbSaveType(rdb,RDB_OPCODE_IDLE) == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">if</span> (rdbSaveLen(rdb,idletime) == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Save the LFU info. */</span></span><br><span class="line">    <span class="comment">// 按频率的删除策略,要存储频率</span></span><br><span class="line">    <span class="keyword">if</span> (savelfu) &#123;</span><br><span class="line">        <span class="keyword">uint8_t</span> buf[<span class="number">1</span>];</span><br><span class="line">        buf[<span class="number">0</span>] = LFUDecrAndReturn(val);</span><br><span class="line">        <span class="comment">/* We can encode this in exactly two bytes: the opcode and an 8</span></span><br><span class="line"><span class="comment">         * bit counter, since the frequency is logarithmic with a 0-255 range.</span></span><br><span class="line"><span class="comment">         * Note that we do not store the halving time because to reset it</span></span><br><span class="line"><span class="comment">         * a single time when loading does not affect the frequency much. */</span></span><br><span class="line">        <span class="keyword">if</span> (rdbSaveType(rdb,RDB_OPCODE_FREQ) == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">if</span> (rdbWriteRaw(rdb,buf,<span class="number">1</span>) == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Save type, key, value */</span></span><br><span class="line">    <span class="comment">// 存储对象类型</span></span><br><span class="line">    <span class="keyword">if</span> (rdbSaveObjectType(rdb,val) == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    <span class="comment">// 存储key,一定是string对象类型</span></span><br><span class="line">    <span class="keyword">if</span> (rdbSaveStringObject(rdb,key) == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    <span class="comment">// 存储值,key在类型为OBJ_STREAM和OBJ_MODULE中有用到</span></span><br><span class="line">    <span class="comment">// 除了quicklist</span></span><br><span class="line">    <span class="keyword">if</span> (rdbSaveObject(rdb,val,key) == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre>            </td>          </tr>        </table>      </figure>    </blockquote>  </li>  <li>    <p>rdbSaveObject</p>  </li></ol><ul>  <li>String.会尝试存储成整形(编码为整数,或者长度小于11),长度大于20尝试LZF压缩</li>  <li>List.quickList,如果是压缩过的quickList(lzf),如果没有压缩过,保存string(sz)</li>  <li>SET,hash编码按hash,如果是OBJ_ENCODING_INTSET转string保存</li>  <li>其他类似</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;db&quot;&gt;&lt;a href=&quot;#db&quot; class=&quot;headerlink&quot; title=&quot;db&quot;&gt;&lt;/a&gt;db&lt;/h4&gt;
&lt;figure class=&quot;highlight c&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td class=&quot;gutter&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
      &lt;td class=&quot;code&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;redisDb&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    dict *dict;                 &lt;span class=&quot;comment&quot;&gt;/* The keyspace for this DB */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    dict *expires;              &lt;span class=&quot;comment&quot;&gt;/* Timeout of keys with a timeout set */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    dict *blocking_keys;        &lt;span class=&quot;comment&quot;&gt;/* Keys with clients waiting for data (BLPOP)*/&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    dict *ready_keys;           &lt;span class=&quot;comment&quot;&gt;/* Blocked keys that received a PUSH */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    dict *watched_keys;         &lt;span class=&quot;comment&quot;&gt;/* WATCHED keys for MULTI/EXEC CAS */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; id;                     &lt;span class=&quot;comment&quot;&gt;/* Database ID */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;long&lt;/span&gt; avg_ttl;          &lt;span class=&quot;comment&quot;&gt;/* Average TTL, just for stats */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;built_in&quot;&gt;list&lt;/span&gt; *defrag_later;         &lt;span class=&quot;comment&quot;&gt;/* List of key names to attempt to defrag one by one, gradually. */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125; redisDb;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/figure&gt;
    
    </summary>
    
      <category term="reading" scheme="http://ipiao.top/categories/reading/"/>
    
    
      <category term="redis" scheme="http://ipiao.top/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>读&lt;&lt;Redis设计与实现&gt;&gt;与redis(5.0)源码__对象</title>
    <link href="http://ipiao.top/2019/07/11/%E8%AF%BB-Redis%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0-%E4%B8%8Eredis-5-0-%E6%BA%90%E7%A0%81-%E5%AF%B9%E8%B1%A1/"/>
    <id>http://ipiao.top/2019/07/11/读-Redis设计与实现-与redis-5-0-源码-对象/</id>
    <published>2019-07-11T07:11:00.000Z</published>
    <updated>2019-07-12T02:12:27.246Z</updated>
    
    <content type="html"><![CDATA[<h4 id="redisObject"><a href="#redisObject" class="headerlink" title="redisObject"></a>redisObject</h4><figure class="highlight c">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">redisObject</span> &#123;</span></span><br><span class="line">    <span class="keyword">unsigned</span> type:<span class="number">4</span>; <span class="comment">// 类型,4位</span></span><br><span class="line">    <span class="keyword">unsigned</span> encoding:<span class="number">4</span>; <span class="comment">// 编码</span></span><br><span class="line">    <span class="comment">// 上次访问时间</span></span><br><span class="line">    <span class="comment">// #define LRU_BITS 24</span></span><br><span class="line">    <span class="keyword">unsigned</span> lru:LRU_BITS; <span class="comment">/* LRU time (relative to global lru_clock) or</span></span><br><span class="line"><span class="comment">                            * LFU data (least significant 8 bits frequency</span></span><br><span class="line"><span class="comment">                            * and most significant 16 bits access time). */</span></span><br><span class="line">    <span class="keyword">int</span> refcount; <span class="comment">// 引用次数</span></span><br><span class="line">    <span class="keyword">void</span> *ptr;    <span class="comment">// 底层地址</span></span><br><span class="line">&#125; robj;</span><br></pre>      </td>    </tr>  </table></figure><a id="more"></a><ul>  <li>    <p>type类型</p>    <figure class="highlight c">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_STRING 0    <span class="comment">/* String object. */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_LIST 1      <span class="comment">/* List object. */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_SET 2       <span class="comment">/* Set object. */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ZSET 3      <span class="comment">/* Sorted set object. */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_HASH 4      <span class="comment">/* Hash object. */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_MODULE 5    <span class="comment">/* Module object. */</span>,Redis module 直接管理的</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_STREAM 6    <span class="comment">/* Stream object. */</span></span></span><br></pre>          </td>        </tr>      </table>    </figure>  </li>  <li>    <p>encoding编码</p>    <figure class="highlight c">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_RAW 0     <span class="comment">/* Raw representation */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_INT 1     <span class="comment">/* Encoded as integer */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_HT 2      <span class="comment">/* Encoded as hash table */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_ZIPMAP 3  <span class="comment">/* Encoded as zipmap */</span> 主要在rdb里面使用</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_LINKEDLIST 4 <span class="comment">/* No longer used: old list encoding. */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_ZIPLIST 5 <span class="comment">/* Encoded as ziplist */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_INTSET 6  <span class="comment">/* Encoded as intset */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_SKIPLIST 7  <span class="comment">/* Encoded as skiplist */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_EMBSTR 8  <span class="comment">/* Embedded sds string encoding */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_QUICKLIST 9 <span class="comment">/* Encoded as linked list of ziplists */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OBJ_ENCODING_STREAM 10 <span class="comment">/* Encoded as a radix tree of listpacks */</span></span></span><br></pre>          </td>        </tr>      </table>    </figure>  </li></ul><h4 id="t-string"><a href="#t-string" class="headerlink" title="t_string"></a>t_string</h4><ol>  <li>有三种编码方式,OBJ_ENCODING_INT,OBJ_ENCODING_EMBSTR,OBJ_ENCODING_RAW</li>  <li>OBJ_ENCODING_EMBSTR_SIZE_LIMIT = 44,也就是小于等于44子长用embstr编码,大于就用raw.因为redisObject大小 = (4+4+24)/8+4+8 = 16,sdshdr8除去buf外的大小是3(len,alloc,flag),预留1byte的’\0’,供20byte.redis的内存分配方法按2^n分配,所以最接近的是64,剩下44byte.</li>  <li>tryObjectEncoding<figure class="highlight c">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre>          </td>          <td class="code">            <pre><span class="line"></span><br><span class="line"><span class="comment">/* Try to encode a string object in order to save space */</span></span><br><span class="line"><span class="function">robj *<span class="title">tryObjectEncoding</span><span class="params">(robj *o)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">long</span> value;</span><br><span class="line">    sds s = o-&gt;ptr;</span><br><span class="line">    <span class="keyword">size_t</span> len;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Make sure this is a string object, the only type we encode</span></span><br><span class="line"><span class="comment">     * in this function. Other types use encoded memory efficient</span></span><br><span class="line"><span class="comment">     * representations but are handled by the commands implementing</span></span><br><span class="line"><span class="comment">     * the type. */</span></span><br><span class="line">    serverAssertWithInfo(<span class="literal">NULL</span>,o,o-&gt;type == OBJ_STRING);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* We try some specialized encoding only for objects that are</span></span><br><span class="line"><span class="comment">     * RAW or EMBSTR encoded, in other words objects that are still</span></span><br><span class="line"><span class="comment">     * in represented by an actually array of chars. */</span></span><br><span class="line">    <span class="keyword">if</span> (!sdsEncodedObject(o)) <span class="keyword">return</span> o;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* It's not safe to encode shared objects: shared objects can be shared</span></span><br><span class="line"><span class="comment">     * everywhere in the "object space" of Redis and may end in places where</span></span><br><span class="line"><span class="comment">     * they are not handled. We handle them only as values in the keyspace. */</span></span><br><span class="line">     <span class="keyword">if</span> (o-&gt;refcount &gt; <span class="number">1</span>) <span class="keyword">return</span> o;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Check if we can represent this string as a long integer.</span></span><br><span class="line"><span class="comment">     * Note that we are sure that a string larger than 20 chars is not</span></span><br><span class="line"><span class="comment">     * representable as a 32 nor 64 bit integer. */</span></span><br><span class="line">    len = sdslen(s);</span><br><span class="line">    <span class="comment">// 20减去4个长度剩下16,2^64~=1.84E19(所以超过20位,减去符号19位,超过了int64表示的极限)</span></span><br><span class="line">    <span class="keyword">if</span> (len &lt;= <span class="number">20</span> &amp;&amp; string2l(s,len,&amp;value)) &#123; <span class="comment">// 是整数</span></span><br><span class="line">        <span class="comment">/* This object is encodable as a long. Try to use a shared object.</span></span><br><span class="line"><span class="comment">         * Note that we avoid using shared integers when maxmemory is used</span></span><br><span class="line"><span class="comment">         * because every object needs to have a private LRU field for the LRU</span></span><br><span class="line"><span class="comment">         * algorithm to work well. */</span></span><br><span class="line">         <span class="comment">// 如果设置了最大使用内存,说明要进行回收,不能有共享变量(要LRU)</span></span><br><span class="line">        <span class="keyword">if</span> ((server.maxmemory == <span class="number">0</span> ||</span><br><span class="line">            !(server.maxmemory_policy &amp; MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) &amp;&amp;</span><br><span class="line">            value &gt;= <span class="number">0</span> &amp;&amp;</span><br><span class="line">            value &lt; OBJ_SHARED_INTEGERS)</span><br><span class="line">        &#123;</span><br><span class="line">            decrRefCount(o);</span><br><span class="line">            incrRefCount(shared.integers[value]);</span><br><span class="line">            <span class="keyword">return</span> shared.integers[value];</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (o-&gt;encoding == OBJ_ENCODING_RAW) sdsfree(o-&gt;ptr);</span><br><span class="line">            o-&gt;encoding = OBJ_ENCODING_INT;</span><br><span class="line">            o-&gt;ptr = (<span class="keyword">void</span>*) value;</span><br><span class="line">            <span class="keyword">return</span> o;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* If the string is small and is still RAW encoded,</span></span><br><span class="line"><span class="comment">     * try the EMBSTR encoding which is more efficient.</span></span><br><span class="line"><span class="comment">     * In this representation the object and the SDS string are allocated</span></span><br><span class="line"><span class="comment">     * in the same chunk of memory to save space and cache misses. */</span></span><br><span class="line">     <span class="comment">// 如果小于等于44,改用EMBSTR</span></span><br><span class="line">     <span class="comment">// 说明默认是RAW编码</span></span><br><span class="line">    <span class="keyword">if</span> (len &lt;= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) &#123;</span><br><span class="line">        robj *emb;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (o-&gt;encoding == OBJ_ENCODING_EMBSTR) <span class="keyword">return</span> o;</span><br><span class="line">        emb = createEmbeddedStringObject(s,sdslen(s));</span><br><span class="line">        decrRefCount(o);</span><br><span class="line">        <span class="keyword">return</span> emb;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* We can't encode the object...</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * Do the last try, and at least optimize the SDS string inside</span></span><br><span class="line"><span class="comment">     * the string object to require little space, in case there</span></span><br><span class="line"><span class="comment">     * is more than 10% of free space at the end of the SDS string.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * We do that only for relatively large strings as this branch</span></span><br><span class="line"><span class="comment">     * is only entered if the length of the string is greater than</span></span><br><span class="line"><span class="comment">     * OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */</span></span><br><span class="line">    trimStringObjectIfNeeded(o);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Return the original object. */</span></span><br><span class="line">    <span class="keyword">return</span> o;</span><br><span class="line">&#125;</span><br></pre>          </td>        </tr>      </table>    </figure>  </li></ol><h4 id="t-list"><a href="#t-list" class="headerlink" title="t_list"></a>t_list</h4><ol>  <li>编码方式有OBJ_ENCODING_QUICKLIST(OBJ_ENCODING_LINKEDLIST被弃用)(,OBJ_ENCODING_ZIPLIST在list里没用了)</li>  <li>quicklist</li></ol><ul>  <li>将linkedlist和ziplist混合起来使用,每个节点是一个ziplist</li>  <li>list-max-ziplist-size设置ziplist的大小,正数表示长度,负数表示 -n = 2^(n+1)kb</li></ul><h4 id="t-hash"><a href="#t-hash" class="headerlink" title="t_hash"></a>t_hash</h4><ol>  <li>编码方式有OBJ_ENCODING_ZIPLIST,OBJ_ENCODING_HT</li>  <li>如果长度大于server.hash_max_ziplist_entries,转换成OBJ_ENCODING_HT</li>  <li>如果键值的长度大于 server.hash_max_ziplist_value,转换成OBJ_ENCODING_HT</li></ol><h4 id="t-set"><a href="#t-set" class="headerlink" title="t_set"></a>t_set</h4><ol>  <li>编码方式有OBJ_ENCODING_HT,OBJ_ENCODING_INTSET</li>  <li>如果长度大于server.set_max_intset_entries,或者有非整数,转换成OBJ_ENCODING_HT</li></ol><h4 id="t-zset"><a href="#t-zset" class="headerlink" title="t_zset"></a>t_zset</h4><ol>  <li>编码方式有OBJ_ENCODING_ZIPLIST,OBJ_ENCODING_SKIPLIST</li>  <li>如果长度大于server.zset_max_ziplist_entries,或者有非整数,转换成OBJ_ENCODING_SKIPLIST</li>  <li>如果值的长度大于 server.zset_max_ziplist_value,转换成OBJ_ENCODING_SKIPLIST</li></ol>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;redisObject&quot;&gt;&lt;a href=&quot;#redisObject&quot; class=&quot;headerlink&quot; title=&quot;redisObject&quot;&gt;&lt;/a&gt;redisObject&lt;/h4&gt;
&lt;figure class=&quot;highlight c&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td class=&quot;gutter&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
      &lt;td class=&quot;code&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;redisObject&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; type:&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// 类型,4位&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; encoding:&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// 编码&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// 上次访问时间&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// #define LRU_BITS 24&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; lru:LRU_BITS; &lt;span class=&quot;comment&quot;&gt;/* LRU time (relative to global lru_clock) or&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;                            * LFU data (least significant 8 bits frequency&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;                            * and most significant 16 bits access time). */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; refcount; &lt;span class=&quot;comment&quot;&gt;// 引用次数&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;void&lt;/span&gt; *ptr;    &lt;span class=&quot;comment&quot;&gt;// 底层地址&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125; robj;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/figure&gt;
    
    </summary>
    
      <category term="reading" scheme="http://ipiao.top/categories/reading/"/>
    
    
      <category term="redis" scheme="http://ipiao.top/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>读&lt;&lt;Redis设计与实现&gt;&gt;与redis(5.0)源码__ziplist</title>
    <link href="http://ipiao.top/2019/07/11/%E8%AF%BB-Redis%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0-%E4%B8%8Eredis-5-0-%E6%BA%90%E7%A0%81-ziplist/"/>
    <id>http://ipiao.top/2019/07/11/读-Redis设计与实现-与redis-5-0-源码-ziplist/</id>
    <published>2019-07-11T03:34:00.000Z</published>
    <updated>2019-07-11T07:16:08.161Z</updated>
    
    <content type="html"><![CDATA[<h4 id="ziplist构成"><a href="#ziplist构成" class="headerlink" title="ziplist构成"></a>ziplist构成</h4><blockquote>  <table>    <thead>      <tr>        <th>zlbytes-</th>        <th>ztail-</th>        <th>zllen-</th>        <th>entrys-…-</th>        <th>zlend</th>      </tr>    </thead>    <tbody>      <tr>        <td>uint32_t</td>        <td>uint32_t</td>        <td>uint16_t</td>        <td>zlentry</td>        <td>uint8_t</td>      </tr>    </tbody>  </table></blockquote><blockquote>  <p>因为zlend使用固定值<code>ZIP_END = 255</code>作为结束标志(0xFF作为uint8的最大值,与prevlen单字节长度0xFE进行区别,同时是最后面的两个数作为标志(这个懂的吧)).</p></blockquote><a id="more"></a><h4 id="zlentry"><a href="#zlentry" class="headerlink" title="zlentry"></a>zlentry</h4><blockquote>  <p>这个结构体只是用来接收存放信息的,只是为了方便操作,而并不是其在ziplist中的真正编码方式<br>实际是:<br>previous_entry_length-&gt; encoding -&gt; content<br>    <figure class="highlight c">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">zlentry</span> &#123;</span></span><br><span class="line"><span class="comment">// 前一个entry的字长</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">int</span> prevrawlensize; <span class="comment">/* Bytes used to encode the previous entry len*/</span></span><br><span class="line">    <span class="comment">// </span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">int</span> prevrawlen;     <span class="comment">/* Previous entry len. */</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">int</span> lensize;        <span class="comment">/* Bytes used to encode this entry type/len.</span></span><br><span class="line"><span class="comment">                                    For example strings have a 1, 2 or 5 bytes</span></span><br><span class="line"><span class="comment">                                    header. Integers always use a single byte.*/</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">int</span> len;            <span class="comment">/* Bytes used to represent the actual entry.</span></span><br><span class="line"><span class="comment">                                    For strings this is just the string length</span></span><br><span class="line"><span class="comment">                                    while for integers it is 1, 2, 3, 4, 8 or</span></span><br><span class="line"><span class="comment">                                    0 (for 4 bit immediate) depending on the</span></span><br><span class="line"><span class="comment">                                    number range. */</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">int</span> headersize;     <span class="comment">/* prevrawlensize + lensize. */</span></span><br><span class="line">    <span class="comment">// 编码方式</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">char</span> encoding;      <span class="comment">/* Set to ZIP_STR_* or ZIP_INT_* depending on</span></span><br><span class="line"><span class="comment">                                    the entry encoding. However for 4 bits</span></span><br><span class="line"><span class="comment">                                    immediate integers this can assume a range</span></span><br><span class="line"><span class="comment">                                    of values and must be range-checked. */</span></span><br><span class="line">    <span class="comment">// 值                           </span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">char</span> *p;            <span class="comment">/* Pointer to the very start of the entry, that</span></span><br><span class="line"><span class="comment">                                    is, this points to prev-entry-len field. */</span></span><br><span class="line">&#125; zlentry;</span><br></pre>          </td>        </tr>      </table>    </figure>  </p></blockquote><h4 id="previous-entry-length"><a href="#previous-entry-length" class="headerlink" title="previous_entry_length"></a>previous_entry_length</h4><blockquote>  <p>读取第一个字节是否为0xfe区分是1个字长还是5个字长(为什么是0xfe,因为0xff被用作结束标志)</p>  <ol>    <li>1字节长,小于0xfe(244)</li>    <li>5字节长,第一字节是 0xfe,后面四个字节是实际长度</li>  </ol></blockquote><h4 id="encoding"><a href="#encoding" class="headerlink" title="encoding"></a>encoding</h4><blockquote>  <p>根据第一字节前2位,判断是整数还是字符串</p>  <ol>    <li>前2位是00,01,10.这时候是字符串<ul>        <li>00bbbbbb,后面6位表示content长度,(&lt;=2^6-1 = 63)</li>        <li>01bbbbbb xxxxxxxx,用后6位以及后面一个字节表示content长度,共14位(&lt;=16383)</li>        <li>10______ aaaaaaaa bbbbbbbb cccccccc dddddddd,5字节长,后4个字节表示长度(&lt;=2^32-1)</li>      </ul>    </li>  </ol></blockquote><ol start="2">  <li>前2为是11.这时候是整数<ul>      <li>11 00 0000,content长2,表示int16_t类型</li>      <li>11 01 0000,int32_t</li>      <li>11 10 0000,int64_t</li>      <li>11 11 0000,24位有符号整数</li>      <li>11 11 1110,8位有符号整数</li>      <li>11 11 xxxx,介于0-12的整数值,直接存在xxxx中</li>    </ul>  </li></ol>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;ziplist构成&quot;&gt;&lt;a href=&quot;#ziplist构成&quot; class=&quot;headerlink&quot; title=&quot;ziplist构成&quot;&gt;&lt;/a&gt;ziplist构成&lt;/h4&gt;
&lt;blockquote&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;zlbytes-&lt;/th&gt;
        &lt;th&gt;ztail-&lt;/th&gt;
        &lt;th&gt;zllen-&lt;/th&gt;
        &lt;th&gt;entrys-…-&lt;/th&gt;
        &lt;th&gt;zlend&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;uint32_t&lt;/td&gt;
        &lt;td&gt;uint32_t&lt;/td&gt;
        &lt;td&gt;uint16_t&lt;/td&gt;
        &lt;td&gt;zlentry&lt;/td&gt;
        &lt;td&gt;uint8_t&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
  &lt;p&gt;因为zlend使用固定值&lt;code&gt;ZIP_END = 255&lt;/code&gt;作为结束标志(0xFF作为uint8的最大值,与prevlen单字节长度0xFE进行区别,同时是最后面的两个数作为标志(这个懂的吧)).&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="reading" scheme="http://ipiao.top/categories/reading/"/>
    
    
      <category term="redis" scheme="http://ipiao.top/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>vimium</title>
    <link href="http://ipiao.top/2019/07/10/vimium/"/>
    <id>http://ipiao.top/2019/07/10/vimium/</id>
    <published>2019-07-10T06:37:00.000Z</published>
    <updated>2019-07-11T02:20:19.421Z</updated>
    
    <content type="html"><![CDATA[<h4 id="chrome扩展vimium"><a href="#chrome扩展vimium" class="headerlink" title="chrome扩展vimium"></a>chrome扩展vimium</h4><ul>  <li>github: <a href="https://github.com/philc/vimium" target="_blank" rel="noopener">https://github.com/philc/vimium</a></li>  <li>安装访问: chrome://extensions</li>  <li>在无痕模式下是无效的</li></ul><h4 id="使用说明"><a href="#使用说明" class="headerlink" title="使用说明"></a>使用说明</h4><ul>  <li>    <p>前缀 c=ctrl, m=meta, a=alt,大写+shift</p>  </li>  <li>    <p>当前页的操作</p>    <ul>      <li>使用 ? 调出使用说明,快捷键列表</li>      <li>h,j,k,l 左下上右滚动</li>      <li>gg回到顶部</li>      <li>G到页面底部</li>      <li>d下滚半屏</li>      <li>u上滚半屏</li>      <li>f在当前tab打开链接</li>      <li>F在新tab打开链接</li>      <li>r刷新</li>      <li>gs查看源码</li>      <li>i 进入插入模式,其他键失效,知道ESC</li>      <li>yy复制当前url</li>      <li>yf复制链接url</li>      <li>gf进入下一个frame</li>      <li>gF回到上级frame        <a id="more"></a>      </li>    </ul>  </li>  <li>    <p>进入新页面的操作</p>    <ul>      <li>o 打开URL,书签或者历史记录(提供搜索框)</li>      <li>O 在新tab打开URL,书签或者历史记录(提供搜索框)</li>      <li>b 打开书签</li>      <li>B 在新tab打开标签</li>    </ul>  </li>  <li>    <p>使用查找功能</p>    <ul>      <li>/ 模式查找</li>      <li>n 向前搜索</li>      <li>N 向后搜索</li>    </ul>  </li>  <li>    <p>历史</p>    <ul>      <li>H 回退</li>      <li>L 前进</li>    </ul>  </li>  <li>    <p>tab操作</p>    <ul>      <li>J,gT 到上一个(左)tab</li>      <li>K,gt 到下一个tab</li>      <li>g0 到第一个tab</li>      <li>g$ 到最后一个tab</li>      <li>^ 回到上一个访问的tab</li>      <li>t 新建一个tab</li>      <li>yt 复制当前tab</li>      <li>x 关闭当前tab</li>      <li>X 恢复已关闭tab</li>      <li>T 在当前tab搜索</li>      <li>W 将当前tab移动到新窗口</li>      <li>        <a-p> 固定/取消固定当前tab</a-p>      </li>    </ul>  </li>  <li>    <p>使用标注</p>    <ul>      <li>ma[bcd], mA 设置标注</li>      <li>使用 `a[bcd]回到标注的地方,`A全局标注可以跨tab</li>      <li>`` 回到上一个jump之前的位置(包含gg,G,n,N,/,`a的跳转)</li>    </ul>  </li>  <li>    <p>其他的浏览优化命令</p>    <ul>      <li><code>]]</code>,<code>[[</code> 大概是在分页中定位到有<code>next</code>或者<code>&gt;</code>标签的地方,也就是上一页\下一页</li>      <li>        <a-f> 在新tab中打开多个链接</a-f>      </li>      <li>gi 聚焦到页面上第一个(第n个)文本输入框</li>      <li>gu 去url的上一级</li>      <li>gU 回到url的顶层(域名层)</li>      <li>ge 编辑当前url</li>      <li>gE 编辑当前url幷在新tab中打开</li>      <li>zH 左滑到底?(scroll all the way left)</li>      <li>zL 右滑到底?</li>      <li>v 视图模式,p/P 复制,y粘贴</li>      <li>V 视图行模式</li>    </ul>  </li></ul><h4 id="自定义键"><a href="#自定义键" class="headerlink" title="自定义键"></a>自定义键</h4><blockquote>  <p>前往github查看</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;chrome扩展vimium&quot;&gt;&lt;a href=&quot;#chrome扩展vimium&quot; class=&quot;headerlink&quot; title=&quot;chrome扩展vimium&quot;&gt;&lt;/a&gt;chrome扩展vimium&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;github: &lt;a href=&quot;https://github.com/philc/vimium&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/philc/vimium&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;安装访问: chrome://extensions&lt;/li&gt;
  &lt;li&gt;在无痕模式下是无效的&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;使用说明&quot;&gt;&lt;a href=&quot;#使用说明&quot; class=&quot;headerlink&quot; title=&quot;使用说明&quot;&gt;&lt;/a&gt;使用说明&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;前缀 c=ctrl, m=meta, a=alt,大写+shift&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;当前页的操作&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;使用 ? 调出使用说明,快捷键列表&lt;/li&gt;
      &lt;li&gt;h,j,k,l 左下上右滚动&lt;/li&gt;
      &lt;li&gt;gg回到顶部&lt;/li&gt;
      &lt;li&gt;G到页面底部&lt;/li&gt;
      &lt;li&gt;d下滚半屏&lt;/li&gt;
      &lt;li&gt;u上滚半屏&lt;/li&gt;
      &lt;li&gt;f在当前tab打开链接&lt;/li&gt;
      &lt;li&gt;F在新tab打开链接&lt;/li&gt;
      &lt;li&gt;r刷新&lt;/li&gt;
      &lt;li&gt;gs查看源码&lt;/li&gt;
      &lt;li&gt;i 进入插入模式,其他键失效,知道ESC&lt;/li&gt;
      &lt;li&gt;yy复制当前url&lt;/li&gt;
      &lt;li&gt;yf复制链接url&lt;/li&gt;
      &lt;li&gt;gf进入下一个frame&lt;/li&gt;
      &lt;li&gt;gF回到上级frame
    
    </summary>
    
      <category term="不务正业" scheme="http://ipiao.top/categories/%E4%B8%8D%E5%8A%A1%E6%AD%A3%E4%B8%9A/"/>
    
    
      <category term="其他" scheme="http://ipiao.top/tags/%E5%85%B6%E4%BB%96/"/>
    
  </entry>
  
  <entry>
    <title>读&lt;&lt;Redis设计与实现&gt;&gt;与redis(5.0)源码__skiplist</title>
    <link href="http://ipiao.top/2019/07/09/%E8%AF%BB-Redis%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0-%E4%B8%8Eredis-5-0-%E6%BA%90%E7%A0%81-skiplist/"/>
    <id>http://ipiao.top/2019/07/09/读-Redis设计与实现-与redis-5-0-源码-skiplist/</id>
    <published>2019-07-09T02:06:00.000Z</published>
    <updated>2019-07-10T09:51:33.500Z</updated>
    
    <content type="html"><![CDATA[<h4 id="结构差异"><a href="#结构差异" class="headerlink" title="结构差异"></a>结构差异</h4><blockquote>  <p>结构定义在<code>server.h</code>文件<br>方法实现在<code>t_zset.c</code>文件(ziplist是压缩)</p></blockquote><ol>  <li>    <p>zskiplistNode</p>    <figure class="highlight c">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">zskiplistNode</span> &#123;</span></span><br><span class="line">    sds ele; <span class="comment">// 原本是 robj *obj,表示保存的成员对象</span></span><br><span class="line">    <span class="keyword">double</span> score; <span class="comment">// 分值</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">zskiplistNode</span> *<span class="title">backward</span>;</span> <span class="comment">// 后退指针</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">zskiplistLevel</span> &#123;</span></span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">zskiplistNode</span> *<span class="title">forward</span>;</span> <span class="comment">// 前进指针</span></span><br><span class="line">        <span class="keyword">unsigned</span> <span class="keyword">long</span> span;            <span class="comment">// 跨度</span></span><br><span class="line">    &#125; level[];</span><br><span class="line">&#125; zskiplistNode;</span><br></pre>          </td>        </tr>      </table>    </figure>  </li>  <li>    <p>zskiplist</p>    <figure class="highlight c">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">zskiplist</span> &#123;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">zskiplistNode</span> *<span class="title">header</span>, *<span class="title">tail</span>;</span> <span class="comment">// 表头和表尾</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">long</span> length; <span class="comment">// 长度,即节点数()不含表头</span></span><br><span class="line">    <span class="keyword">int</span> level; <span class="comment">// 跳跃表内层数最大的节点的层数</span></span><br><span class="line">&#125; zskiplist;</span><br></pre>          </td>        </tr>      </table>    </figure>  </li></ol><a id="more"></a><h4 id="一些常量"><a href="#一些常量" class="headerlink" title="一些常量"></a>一些常量</h4><ul>  <li>ZSKIPLIST_P = 0.25,随机level的概率</li>  <li>ZSKIPLIST_MAXLEVEL = 64,最大level</li></ul><h4 id="level的幂次定律"><a href="#level的幂次定律" class="headerlink" title="level的幂次定律"></a>level的幂次定律</h4><blockquote>  <p>幂次定律: 越大的数出现的概率越小<br>level为 n的概率大概是 (3/4)^n<br>    <figure class="highlight c">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">zslRandomLevel</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> level = <span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 每次1/4的概率往上加</span></span><br><span class="line">    <span class="keyword">while</span> ((random()&amp;<span class="number">0xFFFF</span>) &lt; (ZSKIPLIST_P * <span class="number">0xFFFF</span>))</span><br><span class="line">        level += <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">return</span> (level&lt;ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;</span><br><span class="line">&#125;</span><br></pre>          </td>        </tr>      </table>    </figure>  </p></blockquote><h4 id="API"><a href="#API" class="headerlink" title="API"></a>API</h4><ol>  <li>    <p>zslCreate</p>    <figure class="highlight c">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="function">zskiplist *<span class="title">zslCreate</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> j;</span><br><span class="line">    zskiplist *zsl;</span><br><span class="line"></span><br><span class="line">    zsl = zmalloc(<span class="keyword">sizeof</span>(*zsl));</span><br><span class="line">    zsl-&gt;level = <span class="number">1</span>;</span><br><span class="line">    zsl-&gt;length = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//所以头节点是不计算长度的,而且初始化了最大的level但不计算level(算1)</span></span><br><span class="line">    zsl-&gt;header = zslCreateNode(ZSKIPLIST_MAXLEVEL,<span class="number">0</span>,<span class="literal">NULL</span>);</span><br><span class="line">    <span class="keyword">for</span> (j = <span class="number">0</span>; j &lt; ZSKIPLIST_MAXLEVEL; j++) &#123; </span><br><span class="line">        zsl-&gt;header-&gt;level[j].forward = <span class="literal">NULL</span>;</span><br><span class="line">        zsl-&gt;header-&gt;level[j].span = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    zsl-&gt;header-&gt;backward = <span class="literal">NULL</span>;</span><br><span class="line">    zsl-&gt;tail = <span class="literal">NULL</span>;</span><br><span class="line">    <span class="keyword">return</span> zsl;</span><br><span class="line">&#125;</span><br></pre>          </td>        </tr>      </table>    </figure>  </li>  <li>    <p>zslInsert</p>    <blockquote>      <p>前提保证了元素不存在<br>看起来有点复杂的</p>    </blockquote>  </li></ol><figure class="highlight c">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="function">zskiplistNode *<span class="title">zslInsert</span><span class="params">(zskiplist *zsl, <span class="keyword">double</span> score, sds ele)</span> </span>&#123;</span><br><span class="line"><span class="comment">// 更新数组,存储新node插入,各层需要修改的node,也就是新node在各层插入在哪个node之后</span></span><br><span class="line">    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;</span><br><span class="line">    <span class="comment">// 排序数组,存着各层从header到update里面的node的跨度和</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">int</span> rank[ZSKIPLIST_MAXLEVEL];</span><br><span class="line">    <span class="keyword">int</span> i, level;</span><br><span class="line"><span class="comment">// 非数字断言</span></span><br><span class="line">    serverAssert(!isnan(score));</span><br><span class="line">    x = zsl-&gt;header;</span><br><span class="line">    <span class="keyword">for</span> (i = zsl-&gt;level<span class="number">-1</span>; i &gt;= <span class="number">0</span>; i--) &#123; <span class="comment">// 进行最大level次的遍历</span></span><br><span class="line">        <span class="comment">/* store rank that is crossed to reach the insert position */</span></span><br><span class="line">        rank[i] = i == (zsl-&gt;level<span class="number">-1</span>) ? <span class="number">0</span> : rank[i+<span class="number">1</span>];</span><br><span class="line">        <span class="keyword">while</span> (x-&gt;level[i].forward &amp;&amp;</span><br><span class="line">                (x-&gt;level[i].forward-&gt;score &lt; score ||</span><br><span class="line">                    (x-&gt;level[i].forward-&gt;score == score &amp;&amp;</span><br><span class="line">                    sdscmp(x-&gt;level[i].forward-&gt;ele,ele) &lt; <span class="number">0</span>)))</span><br><span class="line">        &#123;</span><br><span class="line">            rank[i] += x-&gt;level[i].span;</span><br><span class="line">            x = x-&gt;level[i].forward;</span><br><span class="line">        &#125;</span><br><span class="line">        update[i] = x;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/* we assume the element is not already inside, since we allow duplicated</span></span><br><span class="line"><span class="comment">     * scores, reinserting the same element should never happen since the</span></span><br><span class="line"><span class="comment">     * caller of zslInsert() should test in the hash table if the element is</span></span><br><span class="line"><span class="comment">     * already inside or not. */</span></span><br><span class="line">    level = zslRandomLevel();</span><br><span class="line">    <span class="comment">// 如果随机出来的level更大,要补充update</span></span><br><span class="line">    <span class="comment">// update的值直接设置成header,跨度是length,因为是一步到达</span></span><br><span class="line">    <span class="keyword">if</span> (level &gt; zsl-&gt;level) &#123;</span><br><span class="line">        <span class="keyword">for</span> (i = zsl-&gt;level; i &lt; level; i++) &#123;</span><br><span class="line">            rank[i] = <span class="number">0</span>;</span><br><span class="line">            update[i] = zsl-&gt;header;</span><br><span class="line">            update[i]-&gt;level[i].span = zsl-&gt;length;</span><br><span class="line">        &#125;</span><br><span class="line">        zsl-&gt;level = level;</span><br><span class="line">    &#125;</span><br><span class="line">    x = zslCreateNode(level,score,ele);</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; level; i++) &#123;</span><br><span class="line">    <span class="comment">// 每一层</span></span><br><span class="line">    <span class="comment">// 把新node放在update[i]和它的forward之间</span></span><br><span class="line">        x-&gt;level[i].forward = update[i]-&gt;level[i].forward;</span><br><span class="line">        update[i]-&gt;level[i].forward = x;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* update span covered by update[i] as x is inserted here */</span></span><br><span class="line">        <span class="comment">// 因为之前的rank累积关系,新node和update之间的node跨度就是(rank[0] - rank[i])</span></span><br><span class="line">        <span class="comment">//  x-&gt;level[i].span而这个值很可能是1</span></span><br><span class="line">        <span class="comment">// 在新加层里面,因为指向的是Null,所以span值没有关系</span></span><br><span class="line">        x-&gt;level[i].span = update[i]-&gt;level[i].span - (rank[<span class="number">0</span>] - rank[i]);</span><br><span class="line">        update[i]-&gt;level[i].span = (rank[<span class="number">0</span>] - rank[i]) + <span class="number">1</span>; <span class="comment">// 因为新加了一个node,所以+1</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* increment span for untouched levels */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 新层因为新节点的出现,是forward到新节点的,sapn+1</span></span><br><span class="line">    <span class="keyword">for</span> (i = level; i &lt; zsl-&gt;level; i++) &#123;</span><br><span class="line">        update[i]-&gt;level[i].span++;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">// 第一层是顺序完全的</span></span><br><span class="line">    x-&gt;backward = (update[<span class="number">0</span>] == zsl-&gt;header) ? <span class="literal">NULL</span> : update[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">if</span> (x-&gt;level[<span class="number">0</span>].forward)</span><br><span class="line">        x-&gt;level[<span class="number">0</span>].forward-&gt;backward = x;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        zsl-&gt;tail = x;</span><br><span class="line">    zsl-&gt;length++;</span><br><span class="line">    <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><ol start="3">  <li>    <p>zslDelete</p>  </li>  <li>    <p>zslUpdateScore</p>    <blockquote>      <p>是先delete,在insert,不复用</p>    </blockquote>  </li></ol>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;结构差异&quot;&gt;&lt;a href=&quot;#结构差异&quot; class=&quot;headerlink&quot; title=&quot;结构差异&quot;&gt;&lt;/a&gt;结构差异&lt;/h4&gt;
&lt;blockquote&gt;
  &lt;p&gt;结构定义在&lt;code&gt;server.h&lt;/code&gt;文件&lt;br&gt;方法实现在&lt;code&gt;t_zset.c&lt;/code&gt;文件(ziplist是压缩)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;zskiplistNode&lt;/p&gt;
    &lt;figure class=&quot;highlight c&quot;&gt;
      &lt;table&gt;
        &lt;tr&gt;
          &lt;td class=&quot;gutter&quot;&gt;
            &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
          &lt;/td&gt;
          &lt;td class=&quot;code&quot;&gt;
            &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;zskiplistNode&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    sds ele; &lt;span class=&quot;comment&quot;&gt;// 原本是 robj *obj,表示保存的成员对象&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;double&lt;/span&gt; score; &lt;span class=&quot;comment&quot;&gt;// 分值&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;zskiplistNode&lt;/span&gt; *&lt;span class=&quot;title&quot;&gt;backward&lt;/span&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// 后退指针&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;zskiplistLevel&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;zskiplistNode&lt;/span&gt; *&lt;span class=&quot;title&quot;&gt;forward&lt;/span&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// 前进指针&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;long&lt;/span&gt; span;            &lt;span class=&quot;comment&quot;&gt;// 跨度&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; level[];&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125; zskiplistNode;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
      &lt;/table&gt;
    &lt;/figure&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;zskiplist&lt;/p&gt;
    &lt;figure class=&quot;highlight c&quot;&gt;
      &lt;table&gt;
        &lt;tr&gt;
          &lt;td class=&quot;gutter&quot;&gt;
            &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
          &lt;/td&gt;
          &lt;td class=&quot;code&quot;&gt;
            &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;zskiplist&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;zskiplistNode&lt;/span&gt; *&lt;span class=&quot;title&quot;&gt;header&lt;/span&gt;, *&lt;span class=&quot;title&quot;&gt;tail&lt;/span&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// 表头和表尾&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;long&lt;/span&gt; length; &lt;span class=&quot;comment&quot;&gt;// 长度,即节点数()不含表头&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; level; &lt;span class=&quot;comment&quot;&gt;// 跳跃表内层数最大的节点的层数&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125; zskiplist;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
      &lt;/table&gt;
    &lt;/figure&gt;
  &lt;/li&gt;
&lt;/ol&gt;
    
    </summary>
    
      <category term="reading" scheme="http://ipiao.top/categories/reading/"/>
    
    
      <category term="redis" scheme="http://ipiao.top/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>读&lt;&lt;Redis设计与实现&gt;&gt;与redis(5.0)源码__dict</title>
    <link href="http://ipiao.top/2019/07/04/%E8%AF%BB-Redis%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0-%E4%B8%8Eredis-5-0-%E6%BA%90%E7%A0%81-dict/"/>
    <id>http://ipiao.top/2019/07/04/读-Redis设计与实现-与redis-5-0-源码-dict/</id>
    <published>2019-07-04T08:39:00.000Z</published>
    <updated>2019-07-05T07:21:47.742Z</updated>
    
    <content type="html"><![CDATA[<h4 id="结构差异"><a href="#结构差异" class="headerlink" title="结构差异"></a>结构差异</h4><ol>  <li>dictEntry,该结构中<code>union</code>里面多了double类型d</li></ol><figure class="highlight c">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">dictEntry</span> &#123;</span></span><br><span class="line">    <span class="keyword">void</span> *key;</span><br><span class="line">    <span class="keyword">union</span> &#123;</span><br><span class="line">        <span class="keyword">void</span> *val;</span><br><span class="line">        <span class="keyword">uint64_t</span> u64;</span><br><span class="line">        <span class="keyword">int64_t</span> s64;</span><br><span class="line">        <span class="keyword">double</span> d;</span><br><span class="line">    &#125; v;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">dictEntry</span> *<span class="title">next</span>;</span></span><br><span class="line">&#125; dictEntry;</span><br></pre>      </td>    </tr>  </table></figure><ol start="2">  <li>dict,多了一个迭代标志</li></ol><figure class="highlight c">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">dict</span> &#123;</span></span><br><span class="line">    dictType *type;</span><br><span class="line">    <span class="keyword">void</span> *privdata;</span><br><span class="line">    dictht ht[<span class="number">2</span>];</span><br><span class="line">    <span class="keyword">long</span> rehashidx; <span class="comment">/* rehashing not in progress if rehashidx == -1 */</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">long</span> iterators; <span class="comment">/* number of iterators currently running */</span></span><br><span class="line">&#125; dict;</span><br></pre>      </td>    </tr>  </table></figure><ol start="3">  <li>不知道是不是多出来的迭代器(adlist也有)<blockquote>      <p>迭代器,大多用在rdb,aof,cluster场景中<br>有safe的差异</p>      <figure class="highlight c">        <table>          <tr>            <td class="gutter">              <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>            </td>            <td class="code">              <pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">dictIterator</span> &#123;</span></span><br><span class="line">    dict *d;</span><br><span class="line">    <span class="keyword">long</span> index;</span><br><span class="line">    <span class="keyword">int</span> table, safe;</span><br><span class="line">    dictEntry *entry, *nextEntry;</span><br><span class="line">    <span class="comment">/* unsafe iterator fingerprint for misuse detection. */</span></span><br><span class="line">    <span class="keyword">long</span> <span class="keyword">long</span> fingerprint; <span class="comment">// 在非安全模式模式下,迭代的时候生成一个指纹,在后续再次处理相应键的时候,对比指纹是否一致,判断键值是否被修改</span></span><br><span class="line">&#125; dictIterator;</span><br></pre>            </td>          </tr>        </table>      </figure>    </blockquote>  </li></ol><a id="more"></a><h4 id="dict一些参数"><a href="#dict一些参数" class="headerlink" title="dict一些参数"></a>dict一些参数</h4><ol>  <li>hash用的是siphash</li>  <li>强制负载因子 dict_force_resize_ratio = 5</li>  <li>最小的size是 DICT_HT_INITIAL_SIZE = 4(大小是2^n)</li>  <li>dict_can_resize,在bgsave等情况下会设置成0</li></ol><h4 id="dictExpand"><a href="#dictExpand" class="headerlink" title="dictExpand"></a>dictExpand</h4><blockquote>  <p>在rehash 过程中,dict是不会再次扩充的,那么有没有可能dict填满了,而还没有rehash完?<br>不会,dicths的table是二维链表,size只是外层bucket的数量,幷不限制每个bucket的存储数量</p></blockquote><figure class="highlight c">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">/* Expand or create the hash table */</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">dictExpand</span><span class="params">(dict *d, <span class="keyword">unsigned</span> <span class="keyword">long</span> size)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">/* the size is invalid if it is smaller than the number of</span></span><br><span class="line"><span class="comment">     * elements already inside the hash table */</span></span><br><span class="line">     </span><br><span class="line">    <span class="comment">// dictIsRehashing 返回 rehashidx &gt; -1</span></span><br><span class="line">    <span class="keyword">if</span> (dictIsRehashing(d) || d-&gt;ht[<span class="number">0</span>].used &gt; size)</span><br><span class="line">        <span class="keyword">return</span> DICT_ERR;</span><br><span class="line"></span><br><span class="line">    dictht n; <span class="comment">/* the new hash table */</span></span><br><span class="line">    <span class="comment">// 返回下一个2^n,使得它大于等于size(最小是 DICT_HT_INITIAL_SIZE = 4)</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">long</span> realsize = _dictNextPower(size);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Rehashing to the same table size is not useful. */</span></span><br><span class="line">    <span class="comment">// 如果当前hashtable的使用长度到realsize相同,相当于没有扩充,是没有意义的</span></span><br><span class="line">    <span class="keyword">if</span> (realsize == d-&gt;ht[<span class="number">0</span>].size) <span class="keyword">return</span> DICT_ERR;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Allocate the new hash table and initialize all pointers to NULL */</span></span><br><span class="line">    <span class="comment">// 分配新结构内存</span></span><br><span class="line">    n.size = realsize;</span><br><span class="line">    n.sizemask = realsize<span class="number">-1</span>;</span><br><span class="line">    n.table = zcalloc(realsize*<span class="keyword">sizeof</span>(dictEntry*));</span><br><span class="line">    n.used = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Is this the first initialization? If so it's not really a rehashing</span></span><br><span class="line"><span class="comment">     * we just set the first hash table so that it can accept keys. */</span></span><br><span class="line">    <span class="keyword">if</span> (d-&gt;ht[<span class="number">0</span>].table == <span class="literal">NULL</span>) &#123;</span><br><span class="line">        d-&gt;ht[<span class="number">0</span>] = n;</span><br><span class="line">        <span class="keyword">return</span> DICT_OK;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Prepare a second hash table for incremental rehashing */</span></span><br><span class="line">    d-&gt;ht[<span class="number">1</span>] = n;</span><br><span class="line">    d-&gt;rehashidx = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">return</span> DICT_OK;</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><h4 id="dictRehash"><a href="#dictRehash" class="headerlink" title="dictRehash"></a>dictRehash</h4><ol>  <li>渐进式rehash过程</li></ol><figure class="highlight c">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">// n是rehash的bucket数量(不包含空的)</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">dictRehash</span><span class="params">(dict *d, <span class="keyword">int</span> n)</span> </span>&#123;</span><br><span class="line"><span class="comment">// 最大访问空的bucket数量</span></span><br><span class="line">    <span class="keyword">int</span> empty_visits = n*<span class="number">10</span>; <span class="comment">/* Max number of empty buckets to visit. */</span></span><br><span class="line">    <span class="keyword">if</span> (!dictIsRehashing(d)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(n-- &amp;&amp; d-&gt;ht[<span class="number">0</span>].used != <span class="number">0</span>) &#123;</span><br><span class="line">        dictEntry *de, *nextde;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* Note that rehashidx can't overflow as we are sure there are more</span></span><br><span class="line"><span class="comment">         * elements because ht[0].used != 0 */</span></span><br><span class="line">        <span class="comment">// 因为是从ht[0]迁移到ht[1]的过程,所以rehashidx自然要小于ht[0]的size</span></span><br><span class="line">        assert(d-&gt;ht[<span class="number">0</span>].size &gt; (<span class="keyword">unsigned</span> <span class="keyword">long</span>)d-&gt;rehashidx);</span><br><span class="line">        <span class="comment">// 访问到空bucket</span></span><br><span class="line">        <span class="keyword">while</span>(d-&gt;ht[<span class="number">0</span>].table[d-&gt;rehashidx] == <span class="literal">NULL</span>) &#123;</span><br><span class="line">            d-&gt;rehashidx++;</span><br><span class="line">            <span class="keyword">if</span> (--empty_visits == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// de,非空bucket的(head)dictEntry</span></span><br><span class="line">        de = d-&gt;ht[<span class="number">0</span>].table[d-&gt;rehashidx];</span><br><span class="line">        <span class="comment">/* Move all the keys in this bucket from the old to the new hash HT */</span></span><br><span class="line">        <span class="keyword">while</span>(de) &#123;</span><br><span class="line">            <span class="keyword">uint64_t</span> h;</span><br><span class="line"></span><br><span class="line">            nextde = de-&gt;next;</span><br><span class="line">            <span class="comment">/* Get the index in the new hash table */</span></span><br><span class="line">            <span class="comment">// 获取新bucket的索引,值超过sizemask后不是取余而是取与</span></span><br><span class="line">            h = dictHashKey(d, de-&gt;key) &amp; d-&gt;ht[<span class="number">1</span>].sizemask;</span><br><span class="line">            <span class="comment">// 将当前 de放到新bucket的头,相当于对应的实现了一个'倒序'</span></span><br><span class="line">            de-&gt;next = d-&gt;ht[<span class="number">1</span>].table[h];</span><br><span class="line">            d-&gt;ht[<span class="number">1</span>].table[h] = de;</span><br><span class="line">            d-&gt;ht[<span class="number">0</span>].used--;</span><br><span class="line">            d-&gt;ht[<span class="number">1</span>].used++;</span><br><span class="line">            de = nextde;</span><br><span class="line">        &#125;</span><br><span class="line">        d-&gt;ht[<span class="number">0</span>].table[d-&gt;rehashidx] = <span class="literal">NULL</span>;</span><br><span class="line">        d-&gt;rehashidx++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Check if we already rehashed the whole table... */</span></span><br><span class="line">    <span class="comment">// 如果used直接是0了,说明直接可以结束rehash了</span></span><br><span class="line">    <span class="keyword">if</span> (d-&gt;ht[<span class="number">0</span>].used == <span class="number">0</span>) &#123;</span><br><span class="line">        zfree(d-&gt;ht[<span class="number">0</span>].table);</span><br><span class="line">        d-&gt;ht[<span class="number">0</span>] = d-&gt;ht[<span class="number">1</span>];</span><br><span class="line">        _dictReset(&amp;d-&gt;ht[<span class="number">1</span>]);</span><br><span class="line">        d-&gt;rehashidx = <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* More to rehash... */</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><ol start="2">  <li>    <p>rehash机制dictRehashMilliseconds</p>    <blockquote>      <p>这个返回的rehashes的bucket数量不一定准确,因为rehash完成了就不会把最后一次的具体数量加进去,然后这个毫秒时间差异也不是精确的</p>      <figure class="highlight c">        <table>          <tr>            <td class="gutter">              <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>            </td>            <td class="code">              <pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">dictRehashMilliseconds</span><span class="params">(dict *d, <span class="keyword">int</span> ms)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">long</span> <span class="keyword">long</span> start = timeInMilliseconds();</span><br><span class="line">    <span class="keyword">int</span> rehashes = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(dictRehash(d,<span class="number">100</span>)) &#123;</span><br><span class="line">        rehashes += <span class="number">100</span>;</span><br><span class="line">        <span class="keyword">if</span> (timeInMilliseconds()-start &gt; ms) <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> rehashes;</span><br><span class="line">&#125;</span><br></pre>            </td>          </tr>        </table>      </figure>    </blockquote>  </li>  <li>    <p>rehash机制_dictRehashStep</p>    <blockquote>      <p>这个就简单了,迭代一个一个bucket去rehash</p>      <figure class="highlight c">        <table>          <tr>            <td class="gutter">              <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>            </td>            <td class="code">              <pre><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> _dictRehashStep(dict *d) &#123;</span><br><span class="line">    <span class="keyword">if</span> (d-&gt;iterators == <span class="number">0</span>) dictRehash(d,<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre>            </td>          </tr>        </table>      </figure>    </blockquote>  </li></ol><h4 id="dict操作"><a href="#dict操作" class="headerlink" title="dict操作"></a>dict操作</h4><ol>  <li>    <p>比如dictAdd</p>    <figure class="highlight c">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre>          </td>          <td class="code">            <pre><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">dictAdd</span><span class="params">(dict *d, <span class="keyword">void</span> *key, <span class="keyword">void</span> *val)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    dictEntry *entry = dictAddRaw(d,key,<span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!entry) <span class="keyword">return</span> DICT_ERR;</span><br><span class="line">    dictSetVal(d, entry, val);</span><br><span class="line">    <span class="keyword">return</span> DICT_OK;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 查找和替换都通过这个实现</span></span><br><span class="line"><span class="function">dictEntry *<span class="title">dictAddRaw</span><span class="params">(dict *d, <span class="keyword">void</span> *key, dictEntry **existing)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">long</span> index;</span><br><span class="line">    dictEntry *entry;</span><br><span class="line">    dictht *ht;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 如果正在rehash,遇到add就执行一个bucket的rehash</span></span><br><span class="line">    <span class="keyword">if</span> (dictIsRehashing(d)) _dictRehashStep(d);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Get the index of the new element, or -1 if</span></span><br><span class="line"><span class="comment">     * the element already exists. */</span></span><br><span class="line">    <span class="comment">// 这里是获取index的过程</span></span><br><span class="line">    <span class="comment">// 如果existing不为nil,会将找到的值放进去然后直接返回</span></span><br><span class="line">    <span class="comment">// 在addOrFind 和replace的时候,existing不是nil</span></span><br><span class="line">    <span class="keyword">if</span> ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == <span class="number">-1</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Allocate the memory and store the new entry.</span></span><br><span class="line"><span class="comment">     * Insert the element in top, with the assumption that in a database</span></span><br><span class="line"><span class="comment">     * system it is more likely that recently added entries are accessed</span></span><br><span class="line"><span class="comment">     * more frequently. */</span></span><br><span class="line">    <span class="comment">// 如在正在rehash就直接加到新ht里去</span></span><br><span class="line">    ht = dictIsRehashing(d) ? &amp;d-&gt;ht[<span class="number">1</span>] : &amp;d-&gt;ht[<span class="number">0</span>];</span><br><span class="line">    entry = zmalloc(<span class="keyword">sizeof</span>(*entry));</span><br><span class="line">    entry-&gt;next = ht-&gt;table[index];</span><br><span class="line">    ht-&gt;table[index] = entry;</span><br><span class="line">    ht-&gt;used++;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Set the hash entry fields. */</span></span><br><span class="line">    <span class="comment">// 涉及dict的type和keyDup</span></span><br><span class="line">    dictSetKey(d, entry, key);</span><br><span class="line">    <span class="keyword">return</span> entry;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// existing如果有值,那么用existing存储找到的值,幷不再返回索引</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">long</span> _dictKeyIndex(dict *d, <span class="keyword">const</span> <span class="keyword">void</span> *key, <span class="keyword">uint64_t</span> hash, dictEntry **existing)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">long</span> idx, table;</span><br><span class="line">    dictEntry *he;</span><br><span class="line">    <span class="keyword">if</span> (existing) *existing = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Expand the hash table if needed */</span></span><br><span class="line">    <span class="comment">// 每次都会检查是否要expand</span></span><br><span class="line">    <span class="comment">// 如果正在rehash,返回OK</span></span><br><span class="line">    <span class="comment">// 如果还没初始化,调用dictExpand初始化</span></span><br><span class="line">    <span class="comment">// 在used&gt;size的情况下,如果dict_can_resize或者used/size&gt;dict_force_resize_ratio,进行resize</span></span><br><span class="line">    <span class="keyword">if</span> (_dictExpandIfNeeded(d) == DICT_ERR)</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    <span class="comment">// 这个过程,如果dict不在rehash,就遍历一次</span></span><br><span class="line">    <span class="comment">// 如果rehash了就要遍历两次</span></span><br><span class="line">    <span class="keyword">for</span> (table = <span class="number">0</span>; table &lt;= <span class="number">1</span>; table++) &#123;</span><br><span class="line">        idx = hash &amp; d-&gt;ht[table].sizemask;</span><br><span class="line">        <span class="comment">/* Search if this slot does not already contain the given key */</span></span><br><span class="line">        he = d-&gt;ht[table].table[idx];</span><br><span class="line">        <span class="keyword">while</span>(he) &#123;</span><br><span class="line">            <span class="keyword">if</span> (key==he-&gt;key || dictCompareKeys(d, key, he-&gt;key)) &#123;</span><br><span class="line">                <span class="keyword">if</span> (existing) *existing = he;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            he = he-&gt;next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!dictIsRehashing(d)) <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> idx;</span><br><span class="line">&#125;</span><br></pre>          </td>        </tr>      </table>    </figure>  </li>  <li>    <p>dictGenericDelete</p>    <blockquote>      <p>很简单,就是找到幷删除,然后在删除的时候区分是否释放内存<br>如果是dictDelete的实现,是要释放内存的<br>如果是dictUnlink,则不释放内存,在之后用dictFreeUnlinkedEntry释放内存,用在释放前,需要对它做其他操作而不影响其他增删改查.比如数据库server-&gt;db-&gt;dict对键dbAsyncDelete,zse里的del</p>      <figure class="highlight c">        <table>          <tr>            <td class="gutter">              <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre>            </td>            <td class="code">              <pre><span class="line"><span class="function"><span class="keyword">static</span> dictEntry *<span class="title">dictGenericDelete</span><span class="params">(dict *d, <span class="keyword">const</span> <span class="keyword">void</span> *key, <span class="keyword">int</span> nofree)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">uint64_t</span> h, idx;</span><br><span class="line">    dictEntry *he, *prevHe;</span><br><span class="line">    <span class="keyword">int</span> table;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (d-&gt;ht[<span class="number">0</span>].used == <span class="number">0</span> &amp;&amp; d-&gt;ht[<span class="number">1</span>].used == <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (dictIsRehashing(d)) _dictRehashStep(d);</span><br><span class="line">    h = dictHashKey(d, key);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (table = <span class="number">0</span>; table &lt;= <span class="number">1</span>; table++) &#123;</span><br><span class="line">        idx = h &amp; d-&gt;ht[table].sizemask;</span><br><span class="line">        he = d-&gt;ht[table].table[idx];</span><br><span class="line">        prevHe = <span class="literal">NULL</span>;</span><br><span class="line">        <span class="keyword">while</span>(he) &#123;</span><br><span class="line">            <span class="keyword">if</span> (key==he-&gt;key || dictCompareKeys(d, key, he-&gt;key)) &#123;</span><br><span class="line">                <span class="comment">/* Unlink the element from the list */</span></span><br><span class="line">                <span class="keyword">if</span> (prevHe)</span><br><span class="line">                    prevHe-&gt;next = he-&gt;next;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    d-&gt;ht[table].table[idx] = he-&gt;next;</span><br><span class="line">                <span class="keyword">if</span> (!nofree) &#123;</span><br><span class="line">                    dictFreeKey(d, he);</span><br><span class="line">                    dictFreeVal(d, he);</span><br><span class="line">                    zfree(he);</span><br><span class="line">                &#125;</span><br><span class="line">                d-&gt;ht[table].used--;</span><br><span class="line">                <span class="keyword">return</span> he;</span><br><span class="line">            &#125;</span><br><span class="line">            prevHe = he;</span><br><span class="line">            he = he-&gt;next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!dictIsRehashing(d)) <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>; <span class="comment">/* not found */</span></span><br><span class="line">&#125;</span><br></pre>            </td>          </tr>        </table>      </figure>    </blockquote>  </li></ol><h4 id="dict的type和privdata"><a href="#dict的type和privdata" class="headerlink" title="dict的type和privdata"></a>dict的type和privdata</h4><ol>  <li>type,类型特定的函数,差不多是类函数的意思,因为dict有多种使用场景,其中的复制,对比,销毁函数有不同的实现,有点接口的意思</li>  <li>privdata,保存了需要传递给dict三个函数所需要的参数.比如,在dictReplace,如果key存在,执行dictSetVal,就是将旧值取出来,然后在<code>privdata</code>找到相关参数直接赋值过去<figure class="highlight c">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> dictSetVal(d, entry, _val_) do &#123; \</span></span><br><span class="line">    <span class="keyword">if</span> ((d)-&gt;type-&gt;valDup) \</span><br><span class="line">        (entry)-&gt;v.val = (d)-&gt;type-&gt;valDup((d)-&gt;privdata, _val_); \</span><br><span class="line">    <span class="keyword">else</span> \</span><br><span class="line">        (entry)-&gt;v.val = (_val_); \</span><br><span class="line">&#125; <span class="keyword">while</span>(<span class="number">0</span>)</span><br></pre>          </td>        </tr>      </table>    </figure>  </li></ol><h4 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h4><ol>  <li>随机获取,公平随机()</li>  <li>**dictscan,实现了一个算法,最小消耗的动态迭代(可能有重复,单不会遗漏)</li></ol>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;结构差异&quot;&gt;&lt;a href=&quot;#结构差异&quot; class=&quot;headerlink&quot; title=&quot;结构差异&quot;&gt;&lt;/a&gt;结构差异&lt;/h4&gt;
&lt;ol&gt;
  &lt;li&gt;dictEntry,该结构中&lt;code&gt;union&lt;/code&gt;里面多了double类型d&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&quot;highlight c&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td class=&quot;gutter&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
      &lt;td class=&quot;code&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;dictEntry&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;void&lt;/span&gt; *key;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;union&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;void&lt;/span&gt; *val;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;uint64_t&lt;/span&gt; u64;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;int64_t&lt;/span&gt; s64;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;double&lt;/span&gt; d;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; v;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;dictEntry&lt;/span&gt; *&lt;span class=&quot;title&quot;&gt;next&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125; dictEntry;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/figure&gt;
&lt;ol start=&quot;2&quot;&gt;
  &lt;li&gt;dict,多了一个迭代标志&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&quot;highlight c&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td class=&quot;gutter&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
      &lt;td class=&quot;code&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;dict&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    dictType *type;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;void&lt;/span&gt; *privdata;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    dictht ht[&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;];&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;long&lt;/span&gt; rehashidx; &lt;span class=&quot;comment&quot;&gt;/* rehashing not in progress if rehashidx == -1 */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;long&lt;/span&gt; iterators; &lt;span class=&quot;comment&quot;&gt;/* number of iterators currently running */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125; dict;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/figure&gt;
&lt;ol start=&quot;3&quot;&gt;
  &lt;li&gt;不知道是不是多出来的迭代器(adlist也有)&lt;blockquote&gt;
      &lt;p&gt;迭代器,大多用在rdb,aof,cluster场景中&lt;br&gt;有safe的差异&lt;/p&gt;
      &lt;figure class=&quot;highlight c&quot;&gt;
        &lt;table&gt;
          &lt;tr&gt;
            &lt;td class=&quot;gutter&quot;&gt;
              &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
            &lt;/td&gt;
            &lt;td class=&quot;code&quot;&gt;
              &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;dictIterator&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    dict *d;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;long&lt;/span&gt; index;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; table, safe;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    dictEntry *entry, *nextEntry;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;/* unsafe iterator fingerprint for misuse detection. */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;long&lt;/span&gt; fingerprint; &lt;span class=&quot;comment&quot;&gt;// 在非安全模式模式下,迭代的时候生成一个指纹,在后续再次处理相应键的时候,对比指纹是否一致,判断键值是否被修改&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125; dictIterator;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
        &lt;/table&gt;
      &lt;/figure&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ol&gt;
    
    </summary>
    
      <category term="reading" scheme="http://ipiao.top/categories/reading/"/>
    
    
      <category term="redis" scheme="http://ipiao.top/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>读&lt;&lt;Redis设计与实现&gt;&gt;与redis(5.0)源码__sds</title>
    <link href="http://ipiao.top/2019/07/04/%E8%AF%BB-Redis%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0-%E4%B8%8Eredis-5-0-%E6%BA%90%E7%A0%81/"/>
    <id>http://ipiao.top/2019/07/04/读-Redis设计与实现-与redis-5-0-源码/</id>
    <published>2019-07-04T02:58:00.000Z</published>
    <updated>2019-07-04T08:43:53.198Z</updated>
    
    <content type="html"><![CDATA[<h4 id="主要是区别"><a href="#主要是区别" class="headerlink" title="主要是区别"></a>主要是区别</h4><ol>  <li>sdshdr<blockquote>      <p>不是单一的<code>sdshdr</code>结构,而是分了<code>sdshdr5</code>,<code>sdshdr8</code>,<code>sdshdr16</code>,<code>sdshdr32</code>,<code>sdshdr64</code>几种.鉴于对<code>__attribute__</code>的浅陋理解,是对某种结构属性的定义,也就是说可以当作同一个结构,然后在不同的场景,赋予了不同的结构属性.然后主要用flags的前三位(5种类型)来判断使用的是那种属性结构</p>    </blockquote>  </li></ol><figure class="highlight c">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">// 这个不存在使用场景,只是给出理论上的属性结构</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> __<span class="title">attribute__</span> ((__<span class="title">packed__</span>)) <span class="title">sdshdr5</span> &#123;</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">char</span> flags; <span class="comment">/* 3 lsb of type, and 5 msb of string length */</span></span><br><span class="line">    <span class="keyword">char</span> buf[];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 其他几种字段相同,但是len和alloc的类型结构跟着名称变</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> __<span class="title">attribute__</span> ((__<span class="title">packed__</span>)) <span class="title">sdshdr8</span> &#123;</span> </span><br><span class="line">    <span class="keyword">uint8_t</span> len; <span class="comment">/* used */</span></span><br><span class="line">    <span class="comment">// 这里是总分配,书中是`free`,这里`free`用`sdsavail`计算得到</span></span><br><span class="line">    <span class="keyword">uint8_t</span> alloc; <span class="comment">/* excluding the header and null terminator */</span></span><br><span class="line">    <span class="comment">// 多出来的类型标志</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">char</span> flags; <span class="comment">/* 3 lsb of type, 5 unused bits */</span></span><br><span class="line">    <span class="keyword">char</span> buf[];</span><br><span class="line">&#125;;</span><br></pre>      </td>    </tr>  </table></figure><a id="more"></a><ol start="2">  <li>    <p>#define SDS_MAX_PREALLOC (1024*1024)</p>    <blockquote>      <p>这是重分配的1M空间临界定义字段</p>    </blockquote>  </li>  <li>    <p>sdsReqType</p>    <blockquote>      <p>这个函数没什么特殊的</p>    </blockquote>  </li></ol><figure class="highlight c">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="function">tatic <span class="keyword">inline</span> <span class="keyword">char</span> <span class="title">sdsReqType</span><span class="params">(<span class="keyword">size_t</span> string_size)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (string_size &lt; <span class="number">1</span>&lt;&lt;<span class="number">5</span>)</span><br><span class="line">        <span class="keyword">return</span> SDS_TYPE_5;</span><br><span class="line">    <span class="keyword">if</span> (string_size &lt; <span class="number">1</span>&lt;&lt;<span class="number">8</span>)</span><br><span class="line">        <span class="keyword">return</span> SDS_TYPE_8;</span><br><span class="line">    <span class="keyword">if</span> (string_size &lt; <span class="number">1</span>&lt;&lt;<span class="number">16</span>)</span><br><span class="line">        <span class="keyword">return</span> SDS_TYPE_16;</span><br><span class="line"><span class="comment">// LONG_MAX大概是系统位数决定的最大数</span></span><br><span class="line"><span class="comment">// LLONG_MAX大概是固定的64位最大数</span></span><br><span class="line"><span class="comment">// 所以就是判断是32位还是64位的意思</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> (LONG_MAX == LLONG_MAX)</span></span><br><span class="line">    <span class="keyword">if</span> (string_size &lt; <span class="number">1l</span>l&lt;&lt;<span class="number">32</span>)</span><br><span class="line">        <span class="keyword">return</span> SDS_TYPE_32;</span><br><span class="line">    <span class="keyword">return</span> SDS_TYPE_64;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span> <span class="comment">// 所以如果是32位系统就没有使用sdshdr64一说了</span></span></span><br><span class="line">    <span class="keyword">return</span> SDS_TYPE_32;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><ol start="4">  <li>sdsnewlen<blockquote>      <p>这个函数是主要功能,其他的很多函数都是调用它实现的</p>    </blockquote>  </li></ol><figure class="highlight c">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="function">sds <span class="title">sdsnewlen</span><span class="params">(<span class="keyword">const</span> <span class="keyword">void</span> *init, <span class="keyword">size_t</span> initlen)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">void</span> *sh;</span><br><span class="line">    sds s;</span><br><span class="line">    <span class="keyword">char</span> type = sdsReqType(initlen);</span><br><span class="line">    <span class="comment">/* Empty strings are usually created in order to append. Use type 8</span></span><br><span class="line"><span class="comment">     * since type 5 is not good at this. */</span></span><br><span class="line">    <span class="comment">// sdshdr5没有被使用的原因所在 </span></span><br><span class="line">    <span class="keyword">if</span> (type == SDS_TYPE_5 &amp;&amp; initlen == <span class="number">0</span>) type = SDS_TYPE_8;</span><br><span class="line">    <span class="keyword">int</span> hdrlen = sdsHdrSize(type);</span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">char</span> *fp; <span class="comment">/* flags pointer. */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 结构长度+buf长度+1(空格),没问题</span></span><br><span class="line">    <span class="comment">// buf和结构的地址是连在一起的,一起分配</span></span><br><span class="line">    sh = s_malloc(hdrlen+initlen+<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span> (init==SDS_NOINIT)</span><br><span class="line">        init = <span class="literal">NULL</span>;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (!init)</span><br><span class="line">    <span class="comment">// 初始化,sh设置成hdrlen+initlen+1)个字节长的0</span></span><br><span class="line">        <span class="built_in">memset</span>(sh, <span class="number">0</span>, hdrlen+initlen+<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span> (sh == <span class="literal">NULL</span>) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    <span class="comment">// 这个是s要指向buf的意思</span></span><br><span class="line">    s = (<span class="keyword">char</span>*)sh+hdrlen;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// flag pointer</span></span><br><span class="line">    <span class="comment">// 指针前移1位,说明flags也只是占用一个字节</span></span><br><span class="line">    fp = ((<span class="keyword">unsigned</span> <span class="keyword">char</span>*)s)<span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">switch</span>(type) &#123;</span><br><span class="line">        <span class="keyword">case</span> SDS_TYPE_5: &#123;</span><br><span class="line">        <span class="comment">// flags记录长度的时候用的是高5位</span></span><br><span class="line">            *fp = type | (initlen &lt;&lt; SDS_TYPE_BITS);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> SDS_TYPE_8: &#123;</span><br><span class="line">            SDS_HDR_VAR(<span class="number">8</span>,s);  <span class="comment">// 这个大概就是指针指向sdshdr结构</span></span><br><span class="line">            sh-&gt;len = initlen;</span><br><span class="line">            sh-&gt;alloc = initlen;</span><br><span class="line">            *fp = type;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> SDS_TYPE_16: &#123;</span><br><span class="line">            SDS_HDR_VAR(<span class="number">16</span>,s);</span><br><span class="line">            sh-&gt;len = initlen;</span><br><span class="line">            sh-&gt;alloc = initlen;</span><br><span class="line">            *fp = type;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> SDS_TYPE_32: &#123;</span><br><span class="line">            SDS_HDR_VAR(<span class="number">32</span>,s);</span><br><span class="line">            sh-&gt;len = initlen;</span><br><span class="line">            sh-&gt;alloc = initlen;</span><br><span class="line">            *fp = type;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> SDS_TYPE_64: &#123;</span><br><span class="line">            SDS_HDR_VAR(<span class="number">64</span>,s);</span><br><span class="line">            sh-&gt;len = initlen;</span><br><span class="line">            sh-&gt;alloc = initlen;</span><br><span class="line">            *fp = type;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (initlen &amp;&amp; init)</span><br><span class="line">        <span class="built_in">memcpy</span>(s, init, initlen); <span class="comment">// 把init的值copy到s去</span></span><br><span class="line">    s[initlen] = <span class="string">'\0'</span>;           <span class="comment">// 结尾 \0</span></span><br><span class="line">    <span class="keyword">return</span> s;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="number">5.</span> sdsMakeRoomFor</span><br><span class="line"></span><br><span class="line">``` c</span><br><span class="line"><span class="function">sds <span class="title">sdsMakeRoomFor</span><span class="params">(sds s, <span class="keyword">size_t</span> addlen)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">void</span> *sh, *newsh;</span><br><span class="line">    <span class="keyword">size_t</span> avail = sdsavail(s);</span><br><span class="line">    <span class="keyword">size_t</span> len, newlen;</span><br><span class="line">    <span class="comment">// 反正这个s[-1]是取到了flags的值</span></span><br><span class="line">    <span class="keyword">char</span> type, oldtype = s[<span class="number">-1</span>] &amp; SDS_TYPE_MASK;</span><br><span class="line">    <span class="keyword">int</span> hdrlen;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Return ASAP if there is enough space left. */</span></span><br><span class="line">    <span class="comment">// 剩余空间足够</span></span><br><span class="line">    <span class="keyword">if</span> (avail &gt;= addlen) <span class="keyword">return</span> s;</span><br><span class="line"></span><br><span class="line">    len = sdslen(s);</span><br><span class="line">    <span class="comment">// sdshdr指针</span></span><br><span class="line">    sh = (<span class="keyword">char</span>*)s-sdsHdrSize(oldtype);</span><br><span class="line">    newlen = (len+addlen);</span><br><span class="line">    <span class="keyword">if</span> (newlen &lt; SDS_MAX_PREALLOC)</span><br><span class="line">        newlen *= <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        newlen += SDS_MAX_PREALLOC;</span><br><span class="line"><span class="comment">// 加长之后新类型</span></span><br><span class="line">    type = sdsReqType(newlen);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Don't use type 5: the user is appending to the string and type 5 is</span></span><br><span class="line"><span class="comment">     * not able to remember empty space, so sdsMakeRoomFor() must be called</span></span><br><span class="line"><span class="comment">     * at every appending operation. */</span></span><br><span class="line">     <span class="comment">// 避免使用sdshdr5</span></span><br><span class="line">    <span class="keyword">if</span> (type == SDS_TYPE_5) type = SDS_TYPE_8;</span><br><span class="line"></span><br><span class="line">    hdrlen = sdsHdrSize(type);</span><br><span class="line">    <span class="keyword">if</span> (oldtype==type) &#123;</span><br><span class="line">    <span class="comment">// 如果类型没有改变,直接给sh重新分配新空间</span></span><br><span class="line">        newsh = s_realloc(sh, hdrlen+newlen+<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">if</span> (newsh == <span class="literal">NULL</span>) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">        s = (<span class="keyword">char</span>*)newsh+hdrlen;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">/* Since the header size changes, need to move the string forward,</span></span><br><span class="line"><span class="comment">         * and can't use realloc */</span></span><br><span class="line">         <span class="comment">// 否则,新申请一个满足长度的空间</span></span><br><span class="line">        newsh = s_malloc(hdrlen+newlen+<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">if</span> (newsh == <span class="literal">NULL</span>) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">        <span class="comment">// 把数据复制到新内存</span></span><br><span class="line">        <span class="built_in">memcpy</span>((<span class="keyword">char</span>*)newsh+hdrlen, s, len+<span class="number">1</span>);</span><br><span class="line">        <span class="comment">// 释放旧内存</span></span><br><span class="line">        s_free(sh);</span><br><span class="line">        <span class="comment">// 重新赋值s</span></span><br><span class="line">        s = (<span class="keyword">char</span>*)newsh+hdrlen;</span><br><span class="line">        <span class="comment">// 重新定义s类型,即flags</span></span><br><span class="line">        s[<span class="number">-1</span>] = type;</span><br><span class="line">        <span class="comment">// 重新设置长度(会根据类型重新构建属性)</span></span><br><span class="line">        sdssetlen(s, len);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 重新设置已分配长度</span></span><br><span class="line">    sdssetalloc(s, newlen);</span><br><span class="line">    <span class="keyword">return</span> s;</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;主要是区别&quot;&gt;&lt;a href=&quot;#主要是区别&quot; class=&quot;headerlink&quot; title=&quot;主要是区别&quot;&gt;&lt;/a&gt;主要是区别&lt;/h4&gt;
&lt;ol&gt;
  &lt;li&gt;sdshdr&lt;blockquote&gt;
      &lt;p&gt;不是单一的&lt;code&gt;sdshdr&lt;/code&gt;结构,而是分了&lt;code&gt;sdshdr5&lt;/code&gt;,&lt;code&gt;sdshdr8&lt;/code&gt;,&lt;code&gt;sdshdr16&lt;/code&gt;,&lt;code&gt;sdshdr32&lt;/code&gt;,&lt;code&gt;sdshdr64&lt;/code&gt;几种.鉴于对&lt;code&gt;__attribute__&lt;/code&gt;的浅陋理解,是对某种结构属性的定义,也就是说可以当作同一个结构,然后在不同的场景,赋予了不同的结构属性.然后主要用flags的前三位(5种类型)来判断使用的是那种属性结构&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&quot;highlight c&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td class=&quot;gutter&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
      &lt;td class=&quot;code&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// 这个不存在使用场景,只是给出理论上的属性结构&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; __&lt;span class=&quot;title&quot;&gt;attribute__&lt;/span&gt; ((__&lt;span class=&quot;title&quot;&gt;packed__&lt;/span&gt;)) &lt;span class=&quot;title&quot;&gt;sdshdr5&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;char&lt;/span&gt; flags; &lt;span class=&quot;comment&quot;&gt;/* 3 lsb of type, and 5 msb of string length */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;char&lt;/span&gt; buf[];&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// 其他几种字段相同,但是len和alloc的类型结构跟着名称变&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; __&lt;span class=&quot;title&quot;&gt;attribute__&lt;/span&gt; ((__&lt;span class=&quot;title&quot;&gt;packed__&lt;/span&gt;)) &lt;span class=&quot;title&quot;&gt;sdshdr8&lt;/span&gt; &amp;#123;&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;uint8_t&lt;/span&gt; len; &lt;span class=&quot;comment&quot;&gt;/* used */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// 这里是总分配,书中是`free`,这里`free`用`sdsavail`计算得到&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;uint8_t&lt;/span&gt; alloc; &lt;span class=&quot;comment&quot;&gt;/* excluding the header and null terminator */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// 多出来的类型标志&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;char&lt;/span&gt; flags; &lt;span class=&quot;comment&quot;&gt;/* 3 lsb of type, 5 unused bits */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;char&lt;/span&gt; buf[];&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/figure&gt;
    
    </summary>
    
      <category term="reading" scheme="http://ipiao.top/categories/reading/"/>
    
    
      <category term="redis" scheme="http://ipiao.top/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>一次go内存泄漏调试</title>
    <link href="http://ipiao.top/2019/07/02/%E4%B8%80%E6%AC%A1go%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E8%B0%83%E8%AF%95/"/>
    <id>http://ipiao.top/2019/07/02/一次go内存泄漏调试/</id>
    <published>2019-07-02T09:37:00.000Z</published>
    <updated>2019-07-03T01:50:53.268Z</updated>
    
    <content type="html"><![CDATA[<h4 id="go性能调试工具"><a href="#go性能调试工具" class="headerlink" title="go性能调试工具"></a>go性能调试工具</h4><blockquote>  <p>虽然不一定都需要</p>  <ul>    <li>gv,go自带的pprof命令,实际上是<code>Graphviz</code>,搜索安装就可以了</li>    <li>web,同gv,不过是在浏览器中显示</li>    <li>go-torch,火焰图</li>  </ul></blockquote><a id="more"></a><h4 id="案例背景"><a href="#案例背景" class="headerlink" title="案例背景"></a>案例背景</h4><blockquote>  <p>源自于同事的socket连接服务,服务启动一段时间后,内存飙升,呈现累积不下的现象,初步判断为内存泄漏(top下单个服务占用29G内存).</p></blockquote><h4 id="问题定位"><a href="#问题定位" class="headerlink" title="问题定位"></a>问题定位</h4><ol>  <li>找到函数<figure class="highlight routeros">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br></pre>          </td>          <td class="code">            <pre><span class="line">go<span class="built_in"> tool </span>pprof --inuse_space &#123;&#123;host&#125;&#125;/debug/pprof/heap</span><br></pre>          </td>        </tr>      </table>    </figure>  </li></ol><p>结果如下:</p><p><img src="/images/pasted-8.png" alt="upload successful"></p><ol start="2">  <li>    <p>分析代码(由于代码变动,实际上137行移动到140行)<br><img src="/images/pasted-17.png" alt="upload successful"></p>    <ul>      <li>很明显是函数中<code>msgData</code>泄漏了.但是一开始在分析怎么泄漏的时候走了不少弯路</li>      <li>因为<code>conn</code>是socket连接,所以在一开始Read的时候,读出的数据包是要自定义解析的,初步判定<code>msgLen</code>值过大引起分配过大,至于为什么过大(是客户端传错了还是怎么的,暂不考虑,主要是也没打日志).</li>      <li>那么为什么分配过大会引起内存泄漏?因为<code>go</code>是自带<code>gc</code>的语言 </li>      <li>弯路: 怀疑<code>go</code>线程泄漏(因为想当然的认为接下来的错误没有问题),于是去分析<code>goroutine</code><br><img src="/images/pasted-10.png" alt="upload successful"></li>      <li>一顿分析之后还是觉得不是<code>goroutine</code>的泄漏,很简单,因为前面内存泄漏是指向这个函数的</li>      <li>查看该函数相关的阻塞,当然肯定不是<code>block</code>里面的阻塞(为什么呢?因为这个函数很简单,<code>msgData</code>泄漏的唯一可能是这个变量没有被使用,于是怀疑上是阻塞).重新打开<code>goroutine</code>的pprof界面,搜索该方法及文件名,很快发现一些<code>IO Wait</code>状态线程,于是确定是阻塞</li>    </ul>    <p><img src="/images/pasted-11.png" alt="upload successful"></p>    <ul>      <li>那么,唯一可能的阻塞,也正如<code>heap</code>文件指向的 140(137)行之后的<code>io.ReadFull</code>方法</li>      <li>一开始想到阻塞的时候甚至用byte.NewReader配合写了一个测试,然后否定了<br><img src="/images/pasted-16.png" alt="upload successful"></li>      <li>再次定位确认的时候,只好查阅官方文档.话外的意思就是说,一开开始读到了,<em>不够了,要遇到EOF</em>才返回.</li>    </ul>    <p><img src="/images/pasted-13.png" alt="upload successful"></p>    <ul>      <li>于是想到之前的测试用例,读完是有EOF的,本着严谨的态度确认一下去<code>bytes</code>包的文档,当然下面给出的是Buffer的注释(Reader下没有)</li>    </ul>    <p><img src="/images/pasted-14.png" alt="upload successful"></p>    <ul>      <li>在去看一下<code>net.Conn</code>下的Read方法的注释,于是确认了这里这样读是会阻塞的</li>    </ul>    <p><img src="/images/pasted-15.png" alt="upload successful"></p>  </li></ol><h4 id="解决问题"><a href="#解决问题" class="headerlink" title="解决问题"></a>解决问题</h4><ol>  <li>设置最大的buffer值.该同学设置过,但是使用默认的设置值过大,没有效果.这样可以避免内存阻塞过大,但是解决不了阻塞问题</li>  <li>使用SetReadDeadline读取数据,这样就要在143行读取之后,再次将该值设置成<code>0</code>或者比较大的一个值,比较麻烦.</li></ol>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;go性能调试工具&quot;&gt;&lt;a href=&quot;#go性能调试工具&quot; class=&quot;headerlink&quot; title=&quot;go性能调试工具&quot;&gt;&lt;/a&gt;go性能调试工具&lt;/h4&gt;
&lt;blockquote&gt;
  &lt;p&gt;虽然不一定都需要&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;gv,go自带的pprof命令,实际上是&lt;code&gt;Graphviz&lt;/code&gt;,搜索安装就可以了&lt;/li&gt;
    &lt;li&gt;web,同gv,不过是在浏览器中显示&lt;/li&gt;
    &lt;li&gt;go-torch,火焰图&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="golang大法好" scheme="http://ipiao.top/categories/golang%E5%A4%A7%E6%B3%95%E5%A5%BD/"/>
    
    
      <category term="go" scheme="http://ipiao.top/tags/go/"/>
    
  </entry>
  
  <entry>
    <title>go-runtime之HACKING.md</title>
    <link href="http://ipiao.top/2019/06/21/go-runtime%E4%B9%8BHACKING-md/"/>
    <id>http://ipiao.top/2019/06/21/go-runtime之HACKING-md/</id>
    <published>2019-06-21T07:44:00.000Z</published>
    <updated>2019-06-24T03:21:37.065Z</updated>
    
    <content type="html"><![CDATA[<h4 id="HACKING-md"><a href="#HACKING-md" class="headerlink" title="HACKING.md"></a>HACKING.md</h4><blockquote>  <p>这是作者对runtime设计的一个阐述,翻译内容是这样的</p></blockquote><p>这是一份原型文档,虽然在当下可能已经有点过时了.目的是讲述go程序运行原理,与我们写的go代码有什么不同.着重讲述一些大概的概念而不是详细的细节.</p><a id="more"></a><h1 id="调度器结构"><a href="#调度器结构" class="headerlink" title="调度器结构"></a>调度器结构</h1><p>调度器管理了三种贯穿整个运行环境的资源:Gs,Ms和Ps.即时不需要操作运行环境也是需要了解的.</p><h2 id="Gs-Ms-Ps"><a href="#Gs-Ms-Ps" class="headerlink" title="Gs,Ms,Ps"></a>Gs,Ms,Ps</h2><p>一个”G”就可以简单的代表一个goroutine,有类型<code>g</code>表示,当一个goroutine退出,<code>g</code>对象就会被放在空闲g列表(池),之后被其他goroutine复用.</p><p>一个”M”代表一个可以真正执行用户代码,运行时代码以,系统调用,也可以空闲.由结构体<code>m</code>表示.在某一时刻可能有任意多的<code>m</code>,因为同时间可能发生任意多的系统调用</p><p>最后,一个”P”代表执行用户代码所需要的资源,比如调度器和内存分配状态,由结构体<code>p</code>表示.数量可以由<code>GOMAXPROCS</code>决定.一个P就像操作系统的CPU,<code>p</code>的内容就像每个CPU的状态.为了调度效率,将共享状态放在<code>p</code>中比放在每个<code>m</code>中或者每个<code>g</code>里面要好.</p><p>调度的工作是匹配G(要执行的代码),M(执行时机)和P(执行的资源和环境).当M停止执行用户代码的时候(比如发生系统调用的时候),将P放回P池,在恢复执行之前,再到P池里面取出一个P.</p><p>所有的<code>g</code>,<code>m</code>,<code>p</code>都是在堆里分配的,并且是不释放的资源,所以他们是稳定存在的,不需要运行时通过write barrier.</p><h2 id="用户栈和系统栈"><a href="#用户栈和系统栈" class="headerlink" title="用户栈和系统栈"></a>用户栈和系统栈</h2><p>每个没有进入到死亡状态的G都有一个<em>用户栈</em>,是Go代码运行的地方.用户栈在开始分配的时候很小,在运行过程中动态增长或缩减</p><p>每个M都有一个<em>系统栈</em>(也就是M的”g0”栈,因为它是固定的),并且在Unix平台中有一个<em>信号栈</em>(gsignal).系统栈和信号栈不会增长,但是足以执行运行时代码和cgo代码(纯go二进制文件是8k;cgo由系统分配)</p><p>运行时经常会调用<code>systemstack</code>,<code>mcall</code>,或者<code>asmcgocall</code>临时地切换到系统栈取执行那些无法增长栈空间或者且切换用户goroutine的非抢占任务.在系统栈上运行的代码一定是非抢占式的,而且gc无法扫描系统栈.在系统栈上运行的时候,当前的用户栈也不是用来执行代码的.</p><h2 id="getg-和getg-m-curg"><a href="#getg-和getg-m-curg" class="headerlink" title="getg()和getg().m.curg"></a><code>getg()</code>和<code>getg().m.curg</code></h2><p>获取当前的用户<code>g</code>,用 <code>getg().m.curg</code>.<code>getg()</code>也返回当前的<code>g</code>,但是在系统栈或者信号栈执行代码的时候,这个方法返回的是当前<code>m</code>的”g0”或者”gsignal”.</p><p>所以可以用<code>getg() == getg().m.curg</code>判断当前是在用户栈上运行还是系统栈上运行</p><h1 id="错误处理反馈"><a href="#错误处理反馈" class="headerlink" title="错误处理反馈"></a>错误处理反馈</h1><p>一般情况下,用户可以捕捉到<code>panic</code>进行处理(recover).但是,有一些情况,比如在系统栈上调用,或者在<code>mallocgc</code>时候调用的<code>panic</code>是灾难性的,无法恢复.</p><p>大部分在运行时抛出的错误是无法<code>recover</code>的.比如,用<code>throw</code>会立即抛出错误路径幷终止进程.一般情况下,<code>throw</code>应该传递一个常量string参数,避免不安全的分配.最终,在<code>throw</code>之前会打印出以”runtime”开头的错误信息.</p><p>为了runtime错误进行debug,可以加参数 <code>GOTRACEBACK=system</code>或者<code>GOTRACEBACK=crash</code>.</p><h1 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h1><p>运行时有很多不同的同步机制.这与语境上有关,比如goroutine调度上,和系统调度.</p><p>最简单的是 <code>mutex</code>, 只要使用<code>lock</code>和<code>unlock</code>就行了.这个用在短时间内保护共享结构.在<code>mutex</code>的是阻塞会直接阻塞<code>m</code>,与Go调度没什么关系.这意味着它是安全的也是runtime中最低级别的,因为阻碍了相关的G和P进行重调度.<code>rwmutex</code>也是一样的.</p><p>在一次性的通知情形下,使用<code>note</code>,提供了 <code>notesleep</code>和<code>notewakeup</code>两个方法.与传统UNIX系统中<code>sleep</code>/<code>wakeup</code>不同.<code>note</code>是一种无静态的调用,在调用<code>notesleep</code>或<code>notewakeup</code>之后立刻返回.<code>note</code>可以在用户调用<code>noteclear</code>之后重置,这也决定了<code>note</code>在sleep或者wakeup的时候不能有竟态.<code>note</code>和<code>mutex</code>一样,阻塞<code>m</code>,但是它进入睡眠状态的方式是不一样的:<code>notesleep</code>也阻碍了相关G和P的重调度,但是<code>notetsleepg</code>就像系统调用的阻塞,不影响在P上运行另一个G.当然,这仍然比阻塞G更低效.</p><p>用<code>gopark</code>和<code>goready</code>.<code>gopark</code>直接操作goroutine,将当前G放到等待队列幷从M/P就绪队列中移除,然后运行另一个就绪G,<code>goready</code>将一个停下的G重新放入就绪队列等待运行</p><p>总结一下,阻塞关系就是:</p><tr>  <th></th>  <th colspan="3">Blocks</th></tr><br><tr>  <th>Interface</th>  <th>G</th>  <th>M</th>  <th>P</th></tr><br><tr>  <td>(rw)mutex</td>  <td>Y</td>  <td>Y</td>  <td>Y</td></tr><br><tr>  <td>note</td>  <td>Y</td>  <td>Y</td>  <td>Y/N</td></tr><br><tr>  <td>park</td>  <td>Y</td>  <td>N</td>  <td>N</td></tr><h1 id="Atomics"><a href="#Atomics" class="headerlink" title="Atomics"></a>Atomics</h1><p>运行时有自己的atomic包 <code>runtime/internal/atomic</code>,与<code>sync/atomic</code>对应,但是应为历史遗留问题,函数有不同的名称,还有一下附加的功能</p><p>一般而言,我们在运行时要慎重使用atomics,最好避免使用atomics操作.如果一个变量有可能被其他的同步机制锁保护,这个被保护的变量一般也不需要是原子的.这其中有个方面的理由:</p><ol>  <li>    <p>使用non-atomic或者atomic会使得相应代码self-documenting.因为atomic的介入往往意味着有会有线程问题.</p>  </li>  <li>    <p>non-atomic操作允许自动竟态检测,运行时虽然不会惊醒多线程静态检测,但是在不久的将来会.而atomic操作不允许竟态检测.而non-atomic允许你自定义的进行静态检测.</p>  </li>  <li>    <p>non-atomic可以提升性能</p>  </li></ol><p>当然,任何对共享变量的non-atomic操作都应该描述清楚如何进行保护</p><p>有些模式混用了atomic和non-atomic:</p><ul>  <li>    <p>Read-mostly 变量在更新的时候进行保护,而在锁定的时候,读操作不需要是原子的.在锁定区域外,读操作是原子的</p>  </li>  <li>    <p>在STW的读,因为在STW的时候不会发生写操作</p>  </li></ul><p>也就是说,就像 Go Memory Model的观点,”Don’t be [too] clever”.这是在runtime里的情况,在其他地方也一样.</p><h1 id="Unmanaged-memory-非托管内存"><a href="#Unmanaged-memory-非托管内存" class="headerlink" title="Unmanaged memory(非托管内存?)"></a>Unmanaged memory(非托管内存?)</h1><p>通常,runtime尝试使用正常的堆内存分配.但是,有时runtime必须在无法gc的地方(堆之外)进行分配,也就是<em>unmanaged memory</em>.这是必要的,如果对象是memory manager自己的或者调用方可能没有P.</p><p>有三种Unmanaged memory 分配机制</p><ul>  <li>    <p>sysAlloc直接从操作系统中获取内存.内存可能来自各个分页,但是通过sysFree可以全部释放</p>  </li>  <li>    <p>fragmentation从小页中获取内存,联合成sysAlloc一样的,这样避免造成太多的内存碎片,但是正如它的名字,这些内存没有办法释放</p>  </li>  <li>    <p>fixalloc是SLAB算法分配内存的,它分配固定大小的内存,可以释放,但是这些内存只能由同一个fixalloc池复用.所以这些内存只能被同一种类型数据复用.</p>  </li></ul><p>通常,使用以上方法分配内存的地方都会注释编译标记<code>//go:notinheap</code>.</p><p>在Unmanaged memory分配的对象,除了以下情况不许序包含堆指针.</p><ol>  <li>    <p>任何Unmanaged memory里包含的堆内指针必须明确加入gc根(源)<code>runtime.markroot</code></p>  </li>  <li>    <p>如果内存被复用,他们被标记为GC roots钱必须是零初始化的</p>  </li></ol><h1 id="Zero-initialization-versus-zeroing"><a href="#Zero-initialization-versus-zeroing" class="headerlink" title="Zero-initialization versus zeroing"></a>Zero-initialization versus zeroing</h1><p>在运行时有两种类型的零化,取决于内存是否已经初始化成类型安全的状态.</p><p>如果内存不是类型安全的,意味着它可能包含”garbage”,因为它刚被分配并且正在首次初始化,这种情况,必须使用<em>zero-initialized</em>,也就是<code>memclrNoHeapPointers</code>或者非指针写.这种情况下不会有写屏障write barriers.</p><p>如果内存已经是类型安全的并且简单的设置为零值了,那么必须使用<code>typedmemclr</code>或者<code>memclrHasPointers</code>,.这种方式有write barriers.</p><h1 id="Runtime-only-compiler-directives"><a href="#Runtime-only-compiler-directives" class="headerlink" title="Runtime-only compiler directives"></a>Runtime-only compiler directives</h1><p> 除了”//go:”除了用在文档之外,还可以直接对编译进行干预</p><h2 id="go-systemstack"><a href="#go-systemstack" class="headerlink" title=" go:systemstack"></a> go:systemstack</h2><p><code>go:systemstack</code>表示函数必须运行在系统栈,这会在函数的开始进行动态检查</p><h2 id="go-nowritebarrier"><a href="#go-nowritebarrier" class="headerlink" title="go:nowritebarrier"></a>go:nowritebarrier</h2><p><code>go:nowritebarrier</code>让函数在出现write barriers的时候报错(无法阻止write barriers的生成,只是简单的插入)</p><p>通常的使用场景是,最好不要write barriers,但是不是必要的<code>go:nowritebarrierrec</code>. <code>go:nowritebarrier</code> </p><h2 id="go-nowritebarrierrec-and-go-yeswritebarrierrec"><a href="#go-nowritebarrierrec-and-go-yeswritebarrierrec" class="headerlink" title="go:nowritebarrierrec and go:yeswritebarrierrec"></a>go:nowritebarrierrec and go:yeswritebarrierrec</h2><p><code>go:nowritebarrierrec</code> 让编译器在函数及其递归调用的的函数中在write barrier的时候抛出错误.相应的 <code>go:yeswritebarrierrec</code>保证编译器进行write barrier</p><p>逻辑上,编译器floods每个一<code>go:nowritebarrierrec</code> 开始的函数调用图,当遇到一个包含write barrier 的函数的时候.在<code>go:yeswritebarrierrec</code>是没有flood的</p><p><code>go:nowritebarrierrec</code>用在write barrier的实现上,防止循环初始化</p><p>两种方式都是直接在调度器使用,write barrier需要一个有效P(<code>getg().m.p != nil</code>)而调度器代码经常不在一个有效P上运行.在这种情况,<code>go:nowritebarrierrec</code>使用在P的释放函数上,或者在没有P的时候,<code>go:yeswritebarrierrec</code>运行在重新获取P的函数上.因为有函数层级概念,释放和获取P要分成2个函数</p><h2 id="go-notinheap"><a href="#go-notinheap" class="headerlink" title="go:notinheap"></a>go:notinheap</h2><p><code>go:notinheap</code>运用在类型声明,这意味着一个类型绝不会在GC’d(不GC?)堆里分配.所以,这种类型的指针总是会在<code>runtime.inheap</code>的检查中失败.这种类型可以是全局变量,栈变量或者非托管内存对象( <code>sysAlloc</code>, <code>persistentalloc</code>,<code>fixalloc</code>分配).</p><ol>  <li>    <p><code>new(T)</code>, <code>make([]T)</code>, <code>append([]T, ...)</code> 以及隐藏类型(泛型)分配是不允许的</p>  </li>  <li>    <p>正常类型的指针(不是<code>unsafe.Pointer</code>)不能被转换成一个<code>go:notinheap</code> 类型,即时它们有相同的底层</p>  </li>  <li>    <p>任何包含 <code>go:notinheap</code>类型的类型也是<code>go:notinheap</code>的,数组和结构体的元素是<code>go:notinheap</code>的,那么也是.map和channel不允许有<code>go:notinheap</code>.为了明确任何隐藏<code>go:notinheap</code>类型必须声明</p>  </li>  <li>    <p>在<code>go:notinheap</code>的指针上的Write barriers将被忽略</p>  </li></ol><p>最后,<code>go:notinheap</code>真正的用处.运行时在低层级的内部结构中使用,以避免调度器和内存分配器上的memory barriers ,因为在这里他们是无效的,这种方式的安全的,也不会影响可读性.</p>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;HACKING-md&quot;&gt;&lt;a href=&quot;#HACKING-md&quot; class=&quot;headerlink&quot; title=&quot;HACKING.md&quot;&gt;&lt;/a&gt;HACKING.md&lt;/h4&gt;
&lt;blockquote&gt;
  &lt;p&gt;这是作者对runtime设计的一个阐述,翻译内容是这样的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这是一份原型文档,虽然在当下可能已经有点过时了.目的是讲述go程序运行原理,与我们写的go代码有什么不同.着重讲述一些大概的概念而不是详细的细节.&lt;/p&gt;
    
    </summary>
    
      <category term="golang大法好" scheme="http://ipiao.top/categories/golang%E5%A4%A7%E6%B3%95%E5%A5%BD/"/>
    
    
      <category term="go" scheme="http://ipiao.top/tags/go/"/>
    
  </entry>
  
  <entry>
    <title>go-runtime之cpuprof.go</title>
    <link href="http://ipiao.top/2019/06/21/go-runtime%E4%B9%8Bcpuprof-go-1/"/>
    <id>http://ipiao.top/2019/06/21/go-runtime之cpuprof-go-1/</id>
    <published>2019-06-21T05:59:00.000Z</published>
    <updated>2019-06-21T07:57:21.402Z</updated>
    
    <content type="html"><![CDATA[<h4 id="cpuProfile"><a href="#cpuProfile" class="headerlink" title="cpuProfile"></a>cpuProfile</h4><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="keyword">type</span> cpuProfile <span class="keyword">struct</span> &#123;</span><br><span class="line">lock mutex</span><br><span class="line">on   <span class="keyword">bool</span>     <span class="comment">// profiling is on</span></span><br><span class="line">log  *profBuf <span class="comment">// profile events written here</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// extra holds extra stacks accumulated in addNonGo</span></span><br><span class="line"><span class="comment">// corresponding to profiling signals arriving on</span></span><br><span class="line"><span class="comment">// non-Go-created threads. Those stacks are written</span></span><br><span class="line"><span class="comment">// to log the next time a normal Go thread gets the</span></span><br><span class="line"><span class="comment">// signal handler.</span></span><br><span class="line"><span class="comment">// Assuming the stacks are 2 words each (we don't get</span></span><br><span class="line"><span class="comment">// a full traceback from those threads), plus one word</span></span><br><span class="line"><span class="comment">// size for framing, 100 Hz profiling would generate</span></span><br><span class="line"><span class="comment">// 300 words per second.</span></span><br><span class="line"><span class="comment">// Hopefully a normal Go thread will get the profiling</span></span><br><span class="line"><span class="comment">// signal at least once every few seconds.</span></span><br><span class="line">    <span class="comment">// extra累积着非Go创建的线程调用addNonGo获取的摘要标记,一旦有Go线程获得这些标记句柄,extra栈就会被写入日志文件</span></span><br><span class="line">extra     [<span class="number">1000</span>]<span class="keyword">uintptr</span></span><br><span class="line">numExtra  <span class="keyword">int</span></span><br><span class="line">lostExtra <span class="keyword">uint64</span> <span class="comment">// count of frames lost because extra is full</span></span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><a id="more"></a><h4 id="SetCPUProfileRate"><a href="#SetCPUProfileRate" class="headerlink" title="SetCPUProfileRate"></a>SetCPUProfileRate</h4><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre>      </td>      <td class="code">        <pre><span class="line"></span><br><span class="line"><span class="comment">// SetCPUProfileRate sets the CPU profiling rate to hz samples per second.</span></span><br><span class="line"><span class="comment">// If hz &lt;= 0, SetCPUProfileRate turns off profiling.</span></span><br><span class="line"><span class="comment">// If the profiler is on, the rate cannot be changed without first turning it off.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Most clients should use the runtime/pprof package or</span></span><br><span class="line"><span class="comment">// the testing package's -test.cpuprofile flag instead of calling</span></span><br><span class="line"><span class="comment">// SetCPUProfileRate directly.</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">SetCPUProfileRate</span><span class="params">(hz <span class="keyword">int</span>)</span></span> &#123;</span><br><span class="line"><span class="comment">// Clamp hz to something reasonable.</span></span><br><span class="line"><span class="keyword">if</span> hz &lt; <span class="number">0</span> &#123;</span><br><span class="line">hz = <span class="number">0</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> hz &gt; <span class="number">1000000</span> &#123;</span><br><span class="line">hz = <span class="number">1000000</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">lock(&amp;cpuprof.lock)</span><br><span class="line"><span class="keyword">if</span> hz &gt; <span class="number">0</span> &#123;</span><br><span class="line"><span class="keyword">if</span> cpuprof.on || cpuprof.log != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"runtime: cannot set cpu profile rate until previous profile has finished.\n"</span>)</span><br><span class="line">unlock(&amp;cpuprof.lock)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">cpuprof.on = <span class="literal">true</span></span><br><span class="line">        <span class="comment">// 设置cpufile 至少1个字头,32k数据,4k个tag</span></span><br><span class="line">cpuprof.log = newProfBuf(<span class="number">1</span>, <span class="number">1</span>&lt;&lt;<span class="number">17</span>, <span class="number">1</span>&lt;&lt;<span class="number">14</span>)</span><br><span class="line">hdr := [<span class="number">1</span>]<span class="keyword">uint64</span>&#123;<span class="keyword">uint64</span>(hz)&#125;</span><br><span class="line">cpuprof.log.write(<span class="literal">nil</span>, nanotime(), hdr[:], <span class="literal">nil</span>)</span><br><span class="line">setcpuprofilerate(<span class="keyword">int32</span>(hz))</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> cpuprof.on &#123;</span><br><span class="line">    <span class="comment">// 直接关闭了profile</span></span><br><span class="line">setcpuprofilerate(<span class="number">0</span>)</span><br><span class="line">cpuprof.on = <span class="literal">false</span></span><br><span class="line">cpuprof.addExtra()</span><br><span class="line">cpuprof.log.<span class="built_in">close</span>()</span><br><span class="line">&#125;</span><br><span class="line">unlock(&amp;cpuprof.lock)</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><h4 id="add"><a href="#add" class="headerlink" title="add"></a>add</h4><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p *cpuProfile)</span> <span class="title">add</span><span class="params">(gp *g, stk []<span class="keyword">uintptr</span>)</span></span> &#123;</span><br><span class="line"><span class="comment">// Simple cas-lock to coordinate with setcpuprofilerate.</span></span><br><span class="line">    <span class="comment">// 循环等待直到,获取信号锁</span></span><br><span class="line"><span class="keyword">for</span> !atomic.Cas(&amp;prof.signalLock, <span class="number">0</span>, <span class="number">1</span>) &#123;</span><br><span class="line">osyield()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> prof.hz != <span class="number">0</span> &#123; <span class="comment">// implies cpuprof.log != nil</span></span><br><span class="line"><span class="keyword">if</span> p.numExtra &gt; <span class="number">0</span> || p.lostExtra &gt; <span class="number">0</span> &#123;</span><br><span class="line">        <span class="comment">// 将extra信息写入文件</span></span><br><span class="line">p.addExtra()</span><br><span class="line">&#125;</span><br><span class="line">hdr := [<span class="number">1</span>]<span class="keyword">uint64</span>&#123;<span class="number">1</span>&#125;</span><br><span class="line"><span class="comment">// Note: write "knows" that the argument is &amp;gp.labels,</span></span><br><span class="line"><span class="comment">// because otherwise its write barrier behavior may not</span></span><br><span class="line"><span class="comment">// be correct. See the long comment there before</span></span><br><span class="line"><span class="comment">// changing the argument here.</span></span><br><span class="line">cpuprof.log.write(&amp;gp.labels, nanotime(), hdr[:], stk)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">atomic.Store(&amp;prof.signalLock, <span class="number">0</span>)</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;cpuProfile&quot;&gt;&lt;a href=&quot;#cpuProfile&quot; class=&quot;headerlink&quot; title=&quot;cpuProfile&quot;&gt;&lt;/a&gt;cpuProfile&lt;/h4&gt;
&lt;figure class=&quot;highlight go&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td class=&quot;gutter&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
      &lt;td class=&quot;code&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; cpuProfile &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	lock mutex&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	on   &lt;span class=&quot;keyword&quot;&gt;bool&lt;/span&gt;     &lt;span class=&quot;comment&quot;&gt;// profiling is on&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	log  *profBuf &lt;span class=&quot;comment&quot;&gt;// profile events written here&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// extra holds extra stacks accumulated in addNonGo&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// corresponding to profiling signals arriving on&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// non-Go-created threads. Those stacks are written&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// to log the next time a normal Go thread gets the&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// signal handler.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// Assuming the stacks are 2 words each (we don&#39;t get&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// a full traceback from those threads), plus one word&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// size for framing, 100 Hz profiling would generate&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// 300 words per second.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// Hopefully a normal Go thread will get the profiling&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&lt;span class=&quot;comment&quot;&gt;// signal at least once every few seconds.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// extra累积着非Go创建的线程调用addNonGo获取的摘要标记,一旦有Go线程获得这些标记句柄,extra栈就会被写入日志文件&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	extra     [&lt;span class=&quot;number&quot;&gt;1000&lt;/span&gt;]&lt;span class=&quot;keyword&quot;&gt;uintptr&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	numExtra  &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	lostExtra &lt;span class=&quot;keyword&quot;&gt;uint64&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// count of frames lost because extra is full&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/figure&gt;
    
    </summary>
    
      <category term="golang大法好" scheme="http://ipiao.top/categories/golang%E5%A4%A7%E6%B3%95%E5%A5%BD/"/>
    
    
      <category term="go" scheme="http://ipiao.top/tags/go/"/>
    
  </entry>
  
  <entry>
    <title>go-runtime之chan*.go</title>
    <link href="http://ipiao.top/2019/06/20/go-runtime%E4%B9%8Bchan-go/"/>
    <id>http://ipiao.top/2019/06/20/go-runtime之chan-go/</id>
    <published>2019-06-20T02:22:00.000Z</published>
    <updated>2019-06-21T03:15:21.282Z</updated>
    
    <content type="html"><![CDATA[<h4 id="文件注释"><a href="#文件注释" class="headerlink" title="文件注释"></a>文件注释</h4><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">// Invariants:</span></span><br><span class="line"><span class="comment">//  At least one of c.sendq and c.recvq is empty,</span></span><br><span class="line"><span class="comment">//  except for the case of an unbuffered channel with a single goroutine</span></span><br><span class="line"><span class="comment">//  blocked on it for both sending and receiving using a select statement,</span></span><br><span class="line"><span class="comment">//  in which case the length of c.sendq and c.recvq is limited only by the</span></span><br><span class="line"><span class="comment">//  size of the select statement.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// For buffered channels, also:</span></span><br><span class="line"><span class="comment">//  c.qcount &gt; 0 implies that c.recvq is empty.</span></span><br><span class="line"><span class="comment">//  c.qcount &lt; c.dataqsiz implies that c.sendq is empty.</span></span><br></pre>      </td>    </tr>  </table></figure><blockquote>  <p>翻译一下:<br>定式:<br> 在无缓冲通道,除非send和rece都在单线程下阻塞, c.sendq和c.recvq至少有一个是空的,而且c.sendq和c.recvq的长度只受select语法限制</p></blockquote><a id="more"></a><h4 id="hchanSize"><a href="#hchanSize" class="headerlink" title="hchanSize"></a>hchanSize</h4><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br></pre>      </td>      <td class="code">        <pre><span class="line">hchanSize = unsafe.Sizeof(hchan&#123;&#125;) + <span class="keyword">uintptr</span>(-<span class="keyword">int</span>(unsafe.Sizeof(hchan&#123;&#125;))&amp;(maxAlign<span class="number">-1</span>))</span><br></pre>      </td>    </tr>  </table></figure><blockquote>  <p>看结果是结构体hchan的实际大小,因为hchan的对齐值是8</p></blockquote><h4 id="func-makechan-t-chantype-size-int-hchan"><a href="#func-makechan-t-chantype-size-int-hchan" class="headerlink" title="func makechan(t chantype, size int) hchan"></a>func makechan(t <em>chantype, size int) </em>hchan</h4><ol>  <li>    <p>在<code>makechan64(t *chantype, size int64) *hchan</code>中,size不能越界</p>    <figure class="highlight go">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">makechan64</span><span class="params">(t *chantype, size <span class="keyword">int64</span>)</span> *<span class="title">hchan</span></span> &#123;</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">int64</span>(<span class="keyword">int</span>(size)) != size &#123;</span><br><span class="line"><span class="built_in">panic</span>(plainError(<span class="string">"makechan: size out of range"</span>))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> makechan(t, <span class="keyword">int</span>(size))</span><br><span class="line">&#125;</span><br></pre>          </td>        </tr>      </table>    </figure>  </li>  <li>    <p>元素类型大小不能超过64k</p>    <figure class="highlight go">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">makechan</span><span class="params">(t *chantype, size <span class="keyword">int</span>)</span> *<span class="title">hchan</span></span> &#123;</span><br><span class="line">elem := t.elem</span><br><span class="line"></span><br><span class="line"><span class="comment">// compiler checks this but be safe.</span></span><br><span class="line">    <span class="comment">// 意思大概是编译需要,实际上没有哪个的类型的大小会超过64k</span></span><br><span class="line">    <span class="comment">// 对应在sendDirect/recvDirect的typeBitsBulkBarrier里面的64k限制</span></span><br><span class="line"><span class="keyword">if</span> elem.size &gt;= <span class="number">1</span>&lt;&lt;<span class="number">16</span> &#123;</span><br><span class="line">throw(<span class="string">"makechan: invalid channel element type"</span>)</span><br><span class="line">&#125;</span><br><span class="line">    <span class="comment">// 基本上就是说elem的对齐了量不能超过</span></span><br><span class="line"><span class="keyword">if</span> hchanSize%maxAlign != <span class="number">0</span> || elem.align &gt; maxAlign &#123;</span><br><span class="line">throw(<span class="string">"makechan: bad alignment"</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 1.size不能小于0</span></span><br><span class="line">    <span class="comment">// 2.go内存内配限制,不允许分配大小超过限制</span></span><br><span class="line">    <span class="comment">// 3.和2差不多意思</span></span><br><span class="line"><span class="keyword">if</span> size &lt; <span class="number">0</span> || <span class="keyword">uintptr</span>(size) &gt; maxSliceCap(elem.size) || <span class="keyword">uintptr</span>(size)*elem.size &gt; maxAlloc-hchanSize &#123;</span><br><span class="line"><span class="built_in">panic</span>(plainError(<span class="string">"makechan: size out of range"</span>))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Hchan does not contain pointers interesting for GC when elements stored in buf do not contain pointers.</span></span><br><span class="line"><span class="comment">// buf points into the same allocation, elemtype is persistent.</span></span><br><span class="line"><span class="comment">// SudoG's are referenced from their owning thread so they can't be collected.</span></span><br><span class="line"><span class="comment">// TODO(dvyukov,rlh): Rethink when collector can move allocated objects.</span></span><br><span class="line">    <span class="comment">// Hchan存储元素与元素是否是指针值有关,具体体现在下面</span></span><br><span class="line"><span class="keyword">var</span> c *hchan</span><br><span class="line"><span class="keyword">switch</span> &#123;</span><br><span class="line"><span class="keyword">case</span> size == <span class="number">0</span> || elem.size == <span class="number">0</span>:</span><br><span class="line"><span class="comment">// 大概这就是无缓冲通道</span></span><br><span class="line">c = (*hchan)(mallocgc(hchanSize, <span class="literal">nil</span>, <span class="literal">true</span>))</span><br><span class="line"><span class="comment">// Race detector uses this location for synchronization.</span></span><br><span class="line">c.buf = unsafe.Pointer(c)</span><br><span class="line"><span class="keyword">case</span> elem.kind&amp;kindNoPointers != <span class="number">0</span>:</span><br><span class="line"><span class="comment">// Elements do not contain pointers.</span></span><br><span class="line"><span class="comment">// Allocate hchan and buf in one call.</span></span><br><span class="line">        <span class="comment">// 如果是无指针类型,buf和hchan是一起分配的</span></span><br><span class="line">c = (*hchan)(mallocgc(hchanSize+<span class="keyword">uintptr</span>(size)*elem.size, <span class="literal">nil</span>, <span class="literal">true</span>))</span><br><span class="line">c.buf = add(unsafe.Pointer(c), hchanSize)</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line"><span class="comment">// Elements contain pointers.</span></span><br><span class="line">        <span class="comment">// 这里hchan和buf是分开分配的</span></span><br><span class="line">c = <span class="built_in">new</span>(hchan)</span><br><span class="line">c.buf = mallocgc(<span class="keyword">uintptr</span>(size)*elem.size, elem, <span class="literal">true</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">c.elemsize = <span class="keyword">uint16</span>(elem.size)</span><br><span class="line">c.elemtype = elem</span><br><span class="line">c.dataqsiz = <span class="keyword">uint</span>(size)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> debugChan &#123;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"makechan: chan="</span>, c, <span class="string">"; elemsize="</span>, elem.size, <span class="string">"; elemalg="</span>, elem.alg, <span class="string">"; dataqsiz="</span>, size, <span class="string">"\n"</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> c</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> chantype <span class="keyword">struct</span> &#123;</span><br><span class="line">typ  _type</span><br><span class="line">elem *_type</span><br><span class="line">dir  <span class="keyword">uintptr</span></span><br><span class="line">&#125;</span><br></pre>          </td>        </tr>      </table>    </figure>  </li></ol><h4 id="func-chansend-c-hchan-ep-unsafe-Pointer-block-bool-callerpc-uintptr-bool"><a href="#func-chansend-c-hchan-ep-unsafe-Pointer-block-bool-callerpc-uintptr-bool" class="headerlink" title="func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool"></a>func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool</h4><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * generic single channel send/recv</span></span><br><span class="line"><span class="comment"> * If block is not nil,</span></span><br><span class="line"><span class="comment"> * then the protocol will not</span></span><br><span class="line"><span class="comment"> * sleep but return if it could</span></span><br><span class="line"><span class="comment"> * not complete.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * sleep can wake up with g.param == nil</span></span><br><span class="line"><span class="comment"> * when a channel involved in the sleep has</span></span><br><span class="line"><span class="comment"> * been closed.  it is easiest to loop and re-run</span></span><br><span class="line"><span class="comment"> * the operation; we'll see that it's now closed.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">chansend</span><span class="params">(c *hchan, ep unsafe.Pointer, block <span class="keyword">bool</span>, callerpc <span class="keyword">uintptr</span>)</span> <span class="title">bool</span></span> &#123;</span><br><span class="line"><span class="keyword">if</span> c == <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">if</span> !block &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">&#125;</span><br><span class="line">        <span class="comment">// 停止当前线程,幷设置好错误信息</span></span><br><span class="line">gopark(<span class="literal">nil</span>, <span class="literal">nil</span>, waitReasonChanSendNilChan, traceEvGoStop, <span class="number">2</span>)</span><br><span class="line">throw(<span class="string">"unreachable"</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> debugChan &#123;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"chansend: chan="</span>, c, <span class="string">"\n"</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 大概是竟态检测</span></span><br><span class="line"><span class="keyword">if</span> raceenabled &#123;</span><br><span class="line">racereadpc(unsafe.Pointer(c), callerpc, funcPC(chansend))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Fast path: check for failed non-blocking operation without acquiring the lock.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// After observing that the channel is not closed, we observe that the channel is</span></span><br><span class="line"><span class="comment">// not ready for sending. Each of these observations is a single word-sized read</span></span><br><span class="line"><span class="comment">// (first c.closed and second c.recvq.first or c.qcount depending on kind of channel).</span></span><br><span class="line"><span class="comment">// Because a closed channel cannot transition from 'ready for sending' to</span></span><br><span class="line"><span class="comment">// 'not ready for sending', even if the channel is closed between the two observations,</span></span><br><span class="line"><span class="comment">// they imply a moment between the two when the channel was both not yet closed</span></span><br><span class="line"><span class="comment">// and not ready for sending. We behave as if we observed the channel at that moment,</span></span><br><span class="line"><span class="comment">// and report that the send cannot proceed.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// It is okay if the reads are reordered here: if we observe that the channel is not</span></span><br><span class="line"><span class="comment">// ready for sending and then observe that it is not closed, that implies that the</span></span><br><span class="line"><span class="comment">// channel wasn't closed during the first observation.</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 无缓冲通道如果没有接收者,无法完成</span></span><br><span class="line"><span class="keyword">if</span> !block &amp;&amp; c.closed == <span class="number">0</span> &amp;&amp; ((c.dataqsiz == <span class="number">0</span> &amp;&amp; c.recvq.first == <span class="literal">nil</span>) ||</span><br><span class="line">(c.dataqsiz &gt; <span class="number">0</span> &amp;&amp; c.qcount == c.dataqsiz)) &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> t0 <span class="keyword">int64</span></span><br><span class="line"><span class="keyword">if</span> blockprofilerate &gt; <span class="number">0</span> &#123;</span><br><span class="line">t0 = cputicks()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">lock(&amp;c.lock)</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 已关闭通道,直接panic</span></span><br><span class="line"><span class="keyword">if</span> c.closed != <span class="number">0</span> &#123;</span><br><span class="line">unlock(&amp;c.lock)</span><br><span class="line"><span class="built_in">panic</span>(plainError(<span class="string">"send on closed channel"</span>))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// dequeue 会查找到一个sudog,如果有暂时从队列中删掉</span></span><br><span class="line"><span class="keyword">if</span> sg := c.recvq.dequeue(); sg != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="comment">// Found a waiting receiver. We pass the value we want to send</span></span><br><span class="line"><span class="comment">// directly to the receiver, bypassing the channel buffer (if any).</span></span><br><span class="line">        <span class="comment">// 如果存在(空闲的)sudog,直接将值send到接收方,而不管缓冲通道</span></span><br><span class="line">send(c, sg, ep, <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123; unlock(&amp;c.lock) &#125;, <span class="number">3</span>)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 否则,在缓冲区未满的时候,将值放入缓冲区</span></span><br><span class="line"><span class="keyword">if</span> c.qcount &lt; c.dataqsiz &#123;</span><br><span class="line"><span class="comment">// Space is available in the channel buffer. Enqueue the element to send.</span></span><br><span class="line">qp := chanbuf(c, c.sendx)</span><br><span class="line"><span class="keyword">if</span> raceenabled &#123;</span><br><span class="line">raceacquire(qp)</span><br><span class="line">racerelease(qp)</span><br><span class="line">&#125;</span><br><span class="line">        <span class="comment">// GO有write/read barrier</span></span><br><span class="line">        <span class="comment">// 涉及GC三色标记</span></span><br><span class="line">        <span class="comment">// 作用应该是重新指定不可gc内存</span></span><br><span class="line">typedmemmove(c.elemtype, qp, ep)</span><br><span class="line">        <span class="comment">// buf是循环引用的,标记可send指针</span></span><br><span class="line">c.sendx++ </span><br><span class="line"><span class="keyword">if</span> c.sendx == c.dataqsiz &#123;</span><br><span class="line">c.sendx = <span class="number">0</span></span><br><span class="line">&#125;</span><br><span class="line">c.qcount++</span><br><span class="line">unlock(&amp;c.lock)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 不是阻塞发是不用加锁的</span></span><br><span class="line"><span class="keyword">if</span> !block &#123;</span><br><span class="line">unlock(&amp;c.lock)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Block on the channel. Some receiver will complete our operation for us.</span></span><br><span class="line">    <span class="comment">// 如果无缓冲空间可用了,阻塞通道</span></span><br><span class="line">gp := getg() <span class="comment">// 获取当前g</span></span><br><span class="line">mysg := acquireSudog()</span><br><span class="line">mysg.releasetime = <span class="number">0</span></span><br><span class="line">    <span class="comment">// 收集block信息的情况下,将releasetime设置为-1,估计是当作一个标记</span></span><br><span class="line"><span class="keyword">if</span> t0 != <span class="number">0</span> &#123;</span><br><span class="line">mysg.releasetime = <span class="number">-1</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// No stack splits between assigning elem and enqueuing mysg</span></span><br><span class="line"><span class="comment">// on gp.waiting where copystack can find it.</span></span><br><span class="line">mysg.elem = ep</span><br><span class="line">mysg.waitlink = <span class="literal">nil</span></span><br><span class="line">mysg.g = gp</span><br><span class="line">mysg.isSelect = <span class="literal">false</span></span><br><span class="line">mysg.c = c</span><br><span class="line">gp.waiting = mysg</span><br><span class="line">gp.param = <span class="literal">nil</span></span><br><span class="line">    <span class="comment">// enqueue 重新排队到队末(注意这个sudog不是程序自己写的receiver)</span></span><br><span class="line">c.sendq.enqueue(mysg)</span><br><span class="line">    <span class="comment">// 将当前g设置为阻塞等待(waiting)状态,直到被唤醒</span></span><br><span class="line">goparkunlock(&amp;c.lock, waitReasonChanSend, traceEvGoBlockSend, <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// someone woke us up.</span></span><br><span class="line">    <span class="comment">// 当被唤醒的时候,mysg不是当前g所等的waiting状态,这就是大问题了(当前g信息在阻塞期间被操作过)</span></span><br><span class="line"><span class="keyword">if</span> mysg != gp.waiting &#123;</span><br><span class="line">throw(<span class="string">"G waiting list is corrupted"</span>)</span><br><span class="line">&#125;</span><br><span class="line">gp.waiting = <span class="literal">nil</span></span><br><span class="line"><span class="keyword">if</span> gp.param == <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">if</span> c.closed == <span class="number">0</span> &#123;</span><br><span class="line">throw(<span class="string">"chansend: spurious wakeup"</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">panic</span>(plainError(<span class="string">"send on closed channel"</span>))</span><br><span class="line">&#125;</span><br><span class="line">gp.param = <span class="literal">nil</span></span><br><span class="line">    <span class="comment">// 记录阻塞事件</span></span><br><span class="line"><span class="keyword">if</span> mysg.releasetime &gt; <span class="number">0</span> &#123; </span><br><span class="line">blockevent(mysg.releasetime-t0, <span class="number">2</span>)</span><br><span class="line">&#125;</span><br><span class="line">mysg.c = <span class="literal">nil</span></span><br><span class="line">    <span class="comment">// 主动释放这个从m缓冲池里取出来的sudog</span></span><br><span class="line">releaseSudog(mysg)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 这是acquireSudog的注释</span></span><br><span class="line"><span class="comment">//go:nosplit</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">acquireSudog</span><span class="params">()</span> *<span class="title">sudog</span></span> &#123;</span><br><span class="line"><span class="comment">// Delicate dance: the semaphore implementation calls</span></span><br><span class="line"><span class="comment">// acquireSudog, acquireSudog calls new(sudog),</span></span><br><span class="line"><span class="comment">// new calls malloc, malloc can call the garbage collector,</span></span><br><span class="line"><span class="comment">// and the garbage collector calls the semaphore implementation</span></span><br><span class="line"><span class="comment">// in stopTheWorld.</span></span><br><span class="line"><span class="comment">// Break the cycle by doing acquirem/releasem around new(sudog).</span></span><br><span class="line"><span class="comment">// The acquirem/releasem increments m.locks during new(sudog),</span></span><br><span class="line"><span class="comment">// which keeps the garbage collector from being invoked.</span></span><br><span class="line">    <span class="comment">// 大意是说semaphore(信号?调用者?)调用acquireSudog直接new(sudog)的话,</span></span><br><span class="line">    <span class="comment">// new方法调用了malloc,malloc会调用gc,gc会再次调用semaphore,从而引发循环.打破循环的</span></span><br><span class="line">    <span class="comment">// 方法是用acquirem/releasem,因为这个会屏蔽gc</span></span><br><span class="line">    <span class="comment">// 这里调用acquirem后获取m,幷从其缓存的sudog列表中取出最后一个</span></span><br></pre>      </td>    </tr>  </table></figure><ol start="2">  <li>closechan<figure class="highlight go">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">closechan</span><span class="params">(c *hchan)</span></span> &#123;</span><br><span class="line"><span class="keyword">if</span> c == <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="built_in">panic</span>(plainError(<span class="string">"close of nil channel"</span>))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">lock(&amp;c.lock)</span><br><span class="line"><span class="keyword">if</span> c.closed != <span class="number">0</span> &#123;</span><br><span class="line">unlock(&amp;c.lock)</span><br><span class="line"><span class="built_in">panic</span>(plainError(<span class="string">"close of closed channel"</span>))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> raceenabled &#123;</span><br><span class="line">callerpc := getcallerpc()</span><br><span class="line">racewritepc(unsafe.Pointer(c), callerpc, funcPC(closechan))</span><br><span class="line">racerelease(unsafe.Pointer(c))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">c.closed = <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 这个先读后写,按顺序去除一个g,然后把所有的g通过schedlink连接到一起</span></span><br><span class="line"><span class="keyword">var</span> glist *g</span><br><span class="line"></span><br><span class="line"><span class="comment">// release all readers</span></span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line">sg := c.recvq.dequeue()</span><br><span class="line"><span class="keyword">if</span> sg == <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> sg.elem != <span class="literal">nil</span> &#123;</span><br><span class="line">typedmemclr(c.elemtype, sg.elem)</span><br><span class="line">sg.elem = <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> sg.releasetime != <span class="number">0</span> &#123; <span class="comment">// 上面 releasetime = -1 的用处</span></span><br><span class="line">sg.releasetime = cputicks()</span><br><span class="line">&#125;</span><br><span class="line">gp := sg.g</span><br><span class="line">gp.param = <span class="literal">nil</span></span><br><span class="line"><span class="keyword">if</span> raceenabled &#123;</span><br><span class="line">raceacquireg(gp, unsafe.Pointer(c))</span><br><span class="line">&#125;</span><br><span class="line">gp.schedlink.set(glist)</span><br><span class="line">glist = gp</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// release all writers (they will panic)</span></span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line">sg := c.sendq.dequeue()</span><br><span class="line"><span class="keyword">if</span> sg == <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line">&#125;</span><br><span class="line">sg.elem = <span class="literal">nil</span></span><br><span class="line"><span class="keyword">if</span> sg.releasetime != <span class="number">0</span> &#123;</span><br><span class="line">sg.releasetime = cputicks()</span><br><span class="line">&#125;</span><br><span class="line">gp := sg.g</span><br><span class="line">gp.param = <span class="literal">nil</span></span><br><span class="line"><span class="keyword">if</span> raceenabled &#123;</span><br><span class="line">raceacquireg(gp, unsafe.Pointer(c))</span><br><span class="line">&#125;</span><br><span class="line">gp.schedlink.set(glist)</span><br><span class="line">glist = gp</span><br><span class="line">&#125;</span><br><span class="line">unlock(&amp;c.lock)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Ready all Gs now that we've dropped the channel lock.</span></span><br><span class="line"><span class="keyword">for</span> glist != <span class="literal">nil</span> &#123;</span><br><span class="line">gp := glist</span><br><span class="line">glist = glist.schedlink.ptr()</span><br><span class="line">gp.schedlink = <span class="number">0</span></span><br><span class="line">goready(gp, <span class="number">3</span>) <span class="comment">// goreadey解除程阻塞状态</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre>          </td>        </tr>      </table>    </figure>  </li></ol><h4 id="chanrecv"><a href="#chanrecv" class="headerlink" title="chanrecv"></a>chanrecv</h4><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br></pre>      </td>      <td class="code">        <pre><span class="line"></span><br><span class="line"><span class="comment">// chanrecv receives on channel c and writes the received data to ep.</span></span><br><span class="line"><span class="comment">// ep may be nil, in which case received data is ignored.</span></span><br><span class="line"><span class="comment">// If block == false and no elements are available, returns (false, false).</span></span><br><span class="line"><span class="comment">// Otherwise, if c is closed, zeros *ep and returns (true, false).</span></span><br><span class="line"><span class="comment">// Otherwise, fills in *ep with an element and returns (true, true).</span></span><br><span class="line"><span class="comment">// A non-nil ep must point to the heap or the caller's stack.</span></span><br><span class="line"><span class="comment">// ep 必须指向堆空间或者调用栈,主要是防gc</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">chanrecv</span><span class="params">(c *hchan, ep unsafe.Pointer, block <span class="keyword">bool</span>)</span> <span class="params">(selected, received <span class="keyword">bool</span>)</span></span> &#123;</span><br><span class="line"><span class="comment">// raceenabled: don't need to check ep, as it is always on the stack</span></span><br><span class="line"><span class="comment">// or is new memory allocated by reflect.</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> debugChan &#123;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"chanrecv: chan="</span>, c, <span class="string">"\n"</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> c == <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">if</span> !block &#123;</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line">gopark(<span class="literal">nil</span>, <span class="literal">nil</span>, waitReasonChanReceiveNilChan, traceEvGoStop, <span class="number">2</span>)</span><br><span class="line">throw(<span class="string">"unreachable"</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Fast path: check for failed non-blocking operation without acquiring the lock.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// After observing that the channel is not ready for receiving, we observe that the</span></span><br><span class="line"><span class="comment">// channel is not closed. Each of these observations is a single word-sized read</span></span><br><span class="line"><span class="comment">// (first c.sendq.first or c.qcount, and second c.closed).</span></span><br><span class="line"><span class="comment">// Because a channel cannot be reopened, the later observation of the channel</span></span><br><span class="line"><span class="comment">// being not closed implies that it was also not closed at the moment of the</span></span><br><span class="line"><span class="comment">// first observation. We behave as if we observed the channel at that moment</span></span><br><span class="line"><span class="comment">// and report that the receive cannot proceed.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// The order of operations is important here: reversing the operations can lead to</span></span><br><span class="line"><span class="comment">// incorrect behavior when racing with a close.</span></span><br><span class="line">    <span class="comment">// 1. 非阻塞无缓冲没有发送方</span></span><br><span class="line">    <span class="comment">// 2. 非阻塞有缓冲没有消息并且没有关闭(没有缓消息的另一种情况是数据直接发送到接收方而不是主动接收)</span></span><br><span class="line"><span class="keyword">if</span> !block &amp;&amp; (c.dataqsiz == <span class="number">0</span> &amp;&amp; c.sendq.first == <span class="literal">nil</span> ||</span><br><span class="line">c.dataqsiz &gt; <span class="number">0</span> &amp;&amp; atomic.Loaduint(&amp;c.qcount) == <span class="number">0</span>) &amp;&amp;</span><br><span class="line">atomic.Load(&amp;c.closed) == <span class="number">0</span> &#123;</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> t0 <span class="keyword">int64</span></span><br><span class="line"><span class="keyword">if</span> blockprofilerate &gt; <span class="number">0</span> &#123;</span><br><span class="line">t0 = cputicks()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">lock(&amp;c.lock)</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 没有关闭,没有消息</span></span><br><span class="line"><span class="keyword">if</span> c.closed != <span class="number">0</span> &amp;&amp; c.qcount == <span class="number">0</span> &#123;</span><br><span class="line"><span class="keyword">if</span> raceenabled &#123;</span><br><span class="line">raceacquire(unsafe.Pointer(c))</span><br><span class="line">&#125;</span><br><span class="line">unlock(&amp;c.lock)</span><br><span class="line"><span class="keyword">if</span> ep != <span class="literal">nil</span> &#123;</span><br><span class="line">        <span class="comment">//  clear标记白色,允许gc</span></span><br><span class="line">typedmemclr(c.elemtype, ep)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>, <span class="literal">false</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> sg := c.sendq.dequeue(); sg != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="comment">// Found a waiting sender. If buffer is size 0, receive value</span></span><br><span class="line"><span class="comment">// directly from sender. Otherwise, receive from head of queue</span></span><br><span class="line"><span class="comment">// and add sender's value to the tail of the queue (both map to</span></span><br><span class="line"><span class="comment">// the same buffer slot because the queue is full).</span></span><br><span class="line">        <span class="comment">// 发现发送者,基本上是阻塞(无缓冲/满缓冲)的发送者</span></span><br><span class="line">        <span class="comment">// recv 方法:如果无缓冲,直接接收</span></span><br><span class="line">        <span class="comment">// 如果有缓冲,肯定是满缓冲</span></span><br><span class="line">recv(c, sg, ep, <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123; unlock(&amp;c.lock) &#125;, <span class="number">3</span>)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>, <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> c.qcount &gt; <span class="number">0</span> &#123;</span><br><span class="line"><span class="comment">// Receive directly from queue</span></span><br><span class="line">qp := chanbuf(c, c.recvx)</span><br><span class="line"><span class="keyword">if</span> raceenabled &#123;</span><br><span class="line">raceacquire(qp)</span><br><span class="line">racerelease(qp)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> ep != <span class="literal">nil</span> &#123;</span><br><span class="line">        <span class="comment">// 赋值</span></span><br><span class="line">typedmemmove(c.elemtype, ep, qp)</span><br><span class="line">&#125;</span><br><span class="line">        <span class="comment">// 移除</span></span><br><span class="line">typedmemclr(c.elemtype, qp)</span><br><span class="line">c.recvx++</span><br><span class="line"><span class="keyword">if</span> c.recvx == c.dataqsiz &#123;</span><br><span class="line">c.recvx = <span class="number">0</span></span><br><span class="line">&#125;</span><br><span class="line">c.qcount--</span><br><span class="line">unlock(&amp;c.lock)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>, <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> !block &#123;</span><br><span class="line">unlock(&amp;c.lock)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>, <span class="literal">false</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// no sender available: block on this channel.</span></span><br><span class="line">    <span class="comment">// 没有发送方,也没有数量</span></span><br><span class="line">    <span class="comment">// 和chansend相似的操作</span></span><br><span class="line">gp := getg()</span><br><span class="line">mysg := acquireSudog()</span><br><span class="line">mysg.releasetime = <span class="number">0</span></span><br><span class="line"><span class="keyword">if</span> t0 != <span class="number">0</span> &#123;</span><br><span class="line">mysg.releasetime = <span class="number">-1</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// No stack splits between assigning elem and enqueuing mysg</span></span><br><span class="line"><span class="comment">// on gp.waiting where copystack can find it.</span></span><br><span class="line">mysg.elem = ep</span><br><span class="line">mysg.waitlink = <span class="literal">nil</span></span><br><span class="line">gp.waiting = mysg</span><br><span class="line">mysg.g = gp</span><br><span class="line">mysg.isSelect = <span class="literal">false</span></span><br><span class="line">mysg.c = c</span><br><span class="line">gp.param = <span class="literal">nil</span></span><br><span class="line">c.recvq.enqueue(mysg)</span><br><span class="line">goparkunlock(&amp;c.lock, waitReasonChanReceive, traceEvGoBlockRecv, <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// someone woke us up</span></span><br><span class="line"><span class="keyword">if</span> mysg != gp.waiting &#123;</span><br><span class="line">throw(<span class="string">"G waiting list is corrupted"</span>)</span><br><span class="line">&#125;</span><br><span class="line">gp.waiting = <span class="literal">nil</span></span><br><span class="line"><span class="keyword">if</span> mysg.releasetime &gt; <span class="number">0</span> &#123;</span><br><span class="line">blockevent(mysg.releasetime-t0, <span class="number">2</span>)</span><br><span class="line">&#125;</span><br><span class="line">closed := gp.param == <span class="literal">nil</span></span><br><span class="line">gp.param = <span class="literal">nil</span></span><br><span class="line">mysg.c = <span class="literal">nil</span></span><br><span class="line">releaseSudog(mysg)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>, !closed</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><h4 id="chanrecv和chansend的block为false的情况"><a href="#chanrecv和chansend的block为false的情况" class="headerlink" title="chanrecv和chansend的block为false的情况"></a>chanrecv和chansend的block为false的情况</h4><figure class="highlight go">  <table>    <tr>      <td class="gutter">        <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre>      </td>      <td class="code">        <pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestChan6</span><span class="params">(t *testing.T)</span></span> &#123;</span><br><span class="line"><span class="keyword">var</span> ch (<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line"><span class="keyword">select</span> &#123;</span><br><span class="line"><span class="keyword">case</span> c := &lt;-ch:</span><br><span class="line">t.Log(c)</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line">t.Log(<span class="string">"default"</span>)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestChan7</span><span class="params">(t *testing.T)</span></span> &#123;</span><br><span class="line"><span class="keyword">var</span> ch (<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line"><span class="keyword">select</span> &#123;</span><br><span class="line"><span class="keyword">case</span> ch &lt;- <span class="number">1</span>:</span><br><span class="line">t.Log(<span class="number">1</span>)</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line">t.Log(<span class="string">"default"</span>)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre>      </td>    </tr>  </table></figure><blockquote>  <p>default或其他条件的存在使得ch不是block读</p></blockquote><blockquote>  <p>补充:<br>    <figure class="highlight go">      <table>        <tr>          <td class="gutter">            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre>          </td>          <td class="code">            <pre><span class="line"><span class="comment">// compiler implements</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//select &#123;</span></span><br><span class="line"><span class="comment">//case c &lt;- v:</span></span><br><span class="line"><span class="comment">//... foo</span></span><br><span class="line"><span class="comment">//default:</span></span><br><span class="line"><span class="comment">//... bar</span></span><br><span class="line"><span class="comment">//&#125;</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// as</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//if selectnbsend(c, v) &#123;</span></span><br><span class="line"><span class="comment">//... foo</span></span><br><span class="line"><span class="comment">//&#125; else &#123;</span></span><br><span class="line"><span class="comment">//... bar</span></span><br><span class="line"><span class="comment">//&#125;</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">selectnbsend</span><span class="params">(c *hchan, elem unsafe.Pointer)</span> <span class="params">(selected <span class="keyword">bool</span>)</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> chansend(c, elem, <span class="literal">false</span>, getcallerpc())</span><br><span class="line">&#125;</span><br></pre>          </td>        </tr>      </table>    </figure>  </p></blockquote><blockquote>  <p>虽然文档说这样子是非阻塞调用,但是在debug的时候并没有见到该函数被调用!!</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;文件注释&quot;&gt;&lt;a href=&quot;#文件注释&quot; class=&quot;headerlink&quot; title=&quot;文件注释&quot;&gt;&lt;/a&gt;文件注释&lt;/h4&gt;
&lt;figure class=&quot;highlight go&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td class=&quot;gutter&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
      &lt;td class=&quot;code&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// Invariants:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;//  At least one of c.sendq and c.recvq is empty,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;//  except for the case of an unbuffered channel with a single goroutine&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;//  blocked on it for both sending and receiving using a select statement,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;//  in which case the length of c.sendq and c.recvq is limited only by the&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;//  size of the select statement.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;//&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// For buffered channels, also:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;//  c.qcount &amp;gt; 0 implies that c.recvq is empty.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;//  c.qcount &amp;lt; c.dataqsiz implies that c.sendq is empty.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/figure&gt;
&lt;blockquote&gt;
  &lt;p&gt;翻译一下:&lt;br&gt;定式:&lt;br&gt; 在无缓冲通道,除非send和rece都在单线程下阻塞, c.sendq和c.recvq至少有一个是空的,而且c.sendq和c.recvq的长度只受select语法限制&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="golang大法好" scheme="http://ipiao.top/categories/golang%E5%A4%A7%E6%B3%95%E5%A5%BD/"/>
    
    
      <category term="go" scheme="http://ipiao.top/tags/go/"/>
    
  </entry>
  
</feed>
