<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>代码笔记</title>
  
  
  <link href="https://freelifeblog.top/atom.xml" rel="self"/>
  
  <link href="https://freelifeblog.top/"/>
  <updated>2024-11-08T07:05:26.046Z</updated>
  <id>https://freelifeblog.top/</id>
  
  <author>
    <name>OnlyPiglet</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>golang101之如何实现一个http合规的客户端ip校验</title>
    <link href="https://freelifeblog.top/2024/11/05/golang101%E4%B9%8B%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AAhttp%E5%90%88%E8%A7%84%E7%9A%84%E5%AE%A2%E6%88%B7%E7%AB%AFip%E6%A0%A1%E9%AA%8C/"/>
    <id>https://freelifeblog.top/2024/11/05/golang101%E4%B9%8B%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AAhttp%E5%90%88%E8%A7%84%E7%9A%84%E5%AE%A2%E6%88%B7%E7%AB%AFip%E6%A0%A1%E9%AA%8C/</id>
    <published>2024-11-05T17:15:09.000Z</published>
    <updated>2024-11-08T07:05:26.046Z</updated>
    
    <content type="html"><![CDATA[<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p><a href="https://github.com/sebest/xff">XFF</a> 处理 X-Forwarded-For 头的中间件，可以在7层过滤出内网ip以外的相对来说真实的客户端ip。</p><h2 id="基础背景知识"><a href="#基础背景知识" class="headerlink" title="基础背景知识"></a>基础背景知识</h2><h3 id="cidr的网络地址表示"><a href="#cidr的网络地址表示" class="headerlink" title="cidr的网络地址表示"></a>cidr的网络地址表示</h3><p><code>192.168.0.0/16</code> 网络地址中的 <code>/16</code>表示网络掩码，说明<code>192.168.0.0</code> 前16位为网络位，后16位网络内的主机位，网络内可以容纳后16位共计 (2^16-2) ⚠️需要 去除 <code>192.168.0.0 </code> 网络地址 与 <code>192.168.255.255</code> 广播地址</p><h3 id="X-Forwarded-For说明"><a href="#X-Forwarded-For说明" class="headerlink" title="X-Forwarded-For说明"></a>X-Forwarded-For说明</h3><p>X-Forwarded-For 在 <code>rfc 7329 Forwarded HTTP Extension </code>中有过说明</p><h3 id="A-B-C-D-E-网络分类"><a href="#A-B-C-D-E-网络分类" class="headerlink" title="A&#x2F;B&#x2F;C&#x2F;D&#x2F;E 网络分类"></a>A&#x2F;B&#x2F;C&#x2F;D&#x2F;E 网络分类</h3><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/image-20241107154126651.png" alt="subnet"></p><h3 id="默认局域网保留网络地址"><a href="#默认局域网保留网络地址" class="headerlink" title="默认局域网保留网络地址"></a>默认局域网保留网络地址</h3><p>局域网地址的分配说明可以参考<code>rfc1918</code></p><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/image-20241107154007741.png" alt="private subnet"></p><h2 id="快速图解"><a href="#快速图解" class="headerlink" title="快速图解"></a>快速图解</h2><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/xff.drawio.png" alt="xff.drawio"></p><h2 id="代码分析"><a href="#代码分析" class="headerlink" title="代码分析"></a>代码分析</h2><h3 id="创建XFF对象"><a href="#创建XFF对象" class="headerlink" title="创建XFF对象"></a>创建XFF对象</h3><p>根据选项参数，创建一个<code>XFF</code>对象指针，选项参数是否开启debug模式(会增加一些打印)，设置信任的 <code>L7 EdgeProxy</code>的网段，支持多个网段设置，并提供了默认创建方法 <code> Default</code></p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// Options is a configuration container to setup the XFF middleware.</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Options</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// AllowedSubnets is a list of Subnets from which we will accept the</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// X-Forwarded-For header.</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// If this list is empty we will accept every Subnets (default).</span></span><span class="line"><span style="color: #24292E">AllowedSubnets []</span><span style="color: #D73A49">string</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// Debugging flag adds additional output to debug server side XFF issues.</span></span><span class="line"><span style="color: #24292E">Debug </span><span style="color: #D73A49">bool</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// New creates a new XFF handler with the provided options.</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">New</span><span style="color: #24292E">(options Options) (</span><span style="color: #D73A49">*</span><span style="color: #24292E">XFF, </span><span style="color: #D73A49">error</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">    allowedMasks, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">toMasks</span><span style="color: #24292E">(options.AllowedSubnets)</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">       </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E">, err</span></span><span class="line"><span style="color: #24292E">    }</span></span><span class="line"><span style="color: #24292E">    xff </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">XFF{</span></span><span class="line"><span style="color: #24292E">       allowAll:     </span><span style="color: #005CC5">len</span><span style="color: #24292E">(options.AllowedSubnets) </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">0</span><span style="color: #24292E">,</span></span><span class="line"><span style="color: #24292E">       allowedMasks: allowedMasks,</span></span><span class="line"><span style="color: #24292E">    }</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">if</span><span style="color: #24292E"> options.Debug {</span></span><span class="line"><span style="color: #24292E">       xff.Log </span><span style="color: #D73A49">=</span><span style="color: #24292E"> log.</span><span style="color: #005CC5">New</span><span style="color: #24292E">(os.Stdout, </span><span style="color: #032F62">&quot;[xff] &quot;</span><span style="color: #24292E">, log.LstdFlags)</span></span><span class="line"><span style="color: #24292E">    }</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">return</span><span style="color: #24292E"> xff, </span><span style="color: #005CC5">nil</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #6A737D">// XFF http handler</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">XFF</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// Debug logger</span></span><span class="line"><span style="color: #24292E">Log </span><span style="color: #D73A49">*</span><span style="color: #24292E">log.Logger</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// Set to true if all IPs or Subnets are allowed.</span></span><span class="line"><span style="color: #24292E">allowAll </span><span style="color: #D73A49">bool</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// List of IP subnets that are allowed.</span></span><span class="line"><span style="color: #24292E">allowedMasks []net.IPNet</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// New creates a new XFF handler with the provided options.</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">New</span><span style="color: #24292E">(options Options) (</span><span style="color: #D73A49">*</span><span style="color: #24292E">XFF, </span><span style="color: #D73A49">error</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">allowedMasks, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">toMasks</span><span style="color: #24292E">(options.AllowedSubnets)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E">, err</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">xff </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">XFF{</span></span><span class="line"><span style="color: #24292E">allowAll:     </span><span style="color: #005CC5">len</span><span style="color: #24292E">(options.AllowedSubnets) </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">0</span><span style="color: #24292E">,</span></span><span class="line"><span style="color: #24292E">allowedMasks: allowedMasks,</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> options.Debug {</span></span><span class="line"><span style="color: #24292E">xff.Log </span><span style="color: #D73A49">=</span><span style="color: #24292E"> log.</span><span style="color: #005CC5">New</span><span style="color: #24292E">(os.Stdout, </span><span style="color: #032F62">&quot;[xff] &quot;</span><span style="color: #24292E">, log.LstdFlags)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> xff, </span><span style="color: #005CC5">nil</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Default creates a new XFF handler with default options.</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Default</span><span style="color: #24292E">() (</span><span style="color: #D73A49">*</span><span style="color: #24292E">XFF, </span><span style="color: #D73A49">error</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">New</span><span style="color: #24292E">(Options{})</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h3 id="将客户端真实IP设置进RemoteAddress"><a href="#将客户端真实IP设置进RemoteAddress" class="headerlink" title="将客户端真实IP设置进RemoteAddress"></a>将客户端真实IP设置进RemoteAddress</h3><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// HandlerFunc provides Martini compatible handler</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (xff </span><span style="color: #D73A49">*</span><span style="color: #24292E">XFF) </span><span style="color: #6F42C1">HandlerFunc</span><span style="color: #24292E">(w http.ResponseWriter, r </span><span style="color: #D73A49">*</span><span style="color: #24292E">http.Request) {</span></span><span class="line"><span style="color: #24292E">r.RemoteAddr </span><span style="color: #D73A49">=</span><span style="color: #24292E"> </span><span style="color: #005CC5">GetRemoteAddrIfAllowed</span><span style="color: #24292E">(r, xff.allowed)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Handler updates RemoteAdd from X-Fowarded-For Headers.</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (xff </span><span style="color: #D73A49">*</span><span style="color: #24292E">XFF) </span><span style="color: #6F42C1">Handler</span><span style="color: #24292E">(h http.Handler) http.Handler {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> http.</span><span style="color: #005CC5">HandlerFunc</span><span style="color: #24292E">(</span><span style="color: #D73A49">func</span><span style="color: #24292E">(w http.ResponseWriter, r </span><span style="color: #D73A49">*</span><span style="color: #24292E">http.Request) {</span></span><span class="line"><span style="color: #24292E">r.RemoteAddr </span><span style="color: #D73A49">=</span><span style="color: #24292E"> </span><span style="color: #005CC5">GetRemoteAddrIfAllowed</span><span style="color: #24292E">(r, xff.allowed)</span></span><span class="line"><span style="color: #24292E">h.</span><span style="color: #005CC5">ServeHTTP</span><span style="color: #24292E">(w, r)</span></span><span class="line"><span style="color: #24292E">})</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Negroni compatible interface</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (xff </span><span style="color: #D73A49">*</span><span style="color: #24292E">XFF) </span><span style="color: #6F42C1">ServeHTTP</span><span style="color: #24292E">(w http.ResponseWriter, r </span><span style="color: #D73A49">*</span><span style="color: #24292E">http.Request, next http.HandlerFunc) {</span></span><span class="line"><span style="color: #24292E">r.RemoteAddr </span><span style="color: #D73A49">=</span><span style="color: #24292E"> </span><span style="color: #005CC5">GetRemoteAddrIfAllowed</span><span style="color: #24292E">(r, xff.allowed)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">next</span><span style="color: #24292E">(w, r)</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h3 id="解析X-Forwarded-For"><a href="#解析X-Forwarded-For" class="headerlink" title="解析X-Forwarded-For"></a>解析X-Forwarded-For</h3><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// Parse parses the value of the X-Forwarded-For Header and returns the IP address.</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Parse</span><span style="color: #24292E">(ipList </span><span style="color: #D73A49">string</span><span style="color: #24292E">) </span><span style="color: #D73A49">string</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> _, ip </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">range</span><span style="color: #24292E"> strings.</span><span style="color: #005CC5">Split</span><span style="color: #24292E">(ipList, </span><span style="color: #032F62">&quot;,&quot;</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">ip </span><span style="color: #D73A49">=</span><span style="color: #24292E"> strings.</span><span style="color: #005CC5">TrimSpace</span><span style="color: #24292E">(ip)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> IP </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> net.</span><span style="color: #005CC5">ParseIP</span><span style="color: #24292E">(ip); IP </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;&amp;</span><span style="color: #24292E"> </span><span style="color: #005CC5">IsPublicIP</span><span style="color: #24292E">(IP) {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> ip</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #032F62">&quot;&quot;</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><p>对<code>X-Forwarded-For</code>进行处理，按照<code>,</code> 进行切割，遍历次IP数组，当ip不为空，且IP为合法的公网IP，则将当前IP返回，并将此ip赋值&#96;&#96;request.RemoteAddress&#96;</p><p>在看下核心的<code>IsPublicIp</code>的实现，<code>IsGlobalUnicast</code>用于判断IP是否是一个单播地址，在配合<code>privateMasks</code>是否为局域网保留地址，其实这里不用自己写这个判断，现在是有<code>IsPrivate</code> 的相同功能的函数</p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// list of private subnets</span></span><span class="line"><span style="color: #D73A49">var</span><span style="color: #24292E"> privateMasks, _ </span><span style="color: #D73A49">=</span><span style="color: #24292E"> </span><span style="color: #005CC5">toMasks</span><span style="color: #24292E">([]</span><span style="color: #D73A49">string</span><span style="color: #24292E">{</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;127.0.0.0/8&quot;</span><span style="color: #24292E">,</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;10.0.0.0/8&quot;</span><span style="color: #24292E">,</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;172.16.0.0/12&quot;</span><span style="color: #24292E">,</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;192.168.0.0/16&quot;</span><span style="color: #24292E">,</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;fc00::/7&quot;</span><span style="color: #24292E">,</span></span><span class="line"><span style="color: #24292E">})</span></span><span class="line"></span><span class="line"></span><span class="line"><span style="color: #6A737D">// IsPublicIP returns true if the given IP can be routed on the Internet.</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">IsPublicIP</span><span style="color: #24292E">(ip net.IP) </span><span style="color: #D73A49">bool</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> </span><span style="color: #D73A49">!</span><span style="color: #24292E">ip.</span><span style="color: #005CC5">IsGlobalUnicast</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">!</span><span style="color: #005CC5">ipInMasks</span><span style="color: #24292E">(ip, privateMasks)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"></span><span class="line"></span><span class="line"><span style="color: #6A737D">// IsGlobalUnicast reports whether ip is a global unicast</span></span><span class="line"><span style="color: #6A737D">// address.</span></span><span class="line"><span style="color: #6A737D">//</span></span><span class="line"><span style="color: #6A737D">// The identification of global unicast addresses uses address type</span></span><span class="line"><span style="color: #6A737D">// identification as defined in RFC 1122, RFC 4632 and RFC 4291 with</span></span><span class="line"><span style="color: #6A737D">// the exception of IPv4 directed broadcast addresses.</span></span><span class="line"><span style="color: #6A737D">// It returns true even if ip is in IPv4 private address space or</span></span><span class="line"><span style="color: #6A737D">// local IPv6 unicast address space.</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (ip IP) </span><span style="color: #6F42C1">IsGlobalUnicast</span><span style="color: #24292E">() </span><span style="color: #D73A49">bool</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> (</span><span style="color: #005CC5">len</span><span style="color: #24292E">(ip) </span><span style="color: #D73A49">==</span><span style="color: #24292E"> IPv4len </span><span style="color: #D73A49">||</span><span style="color: #24292E"> </span><span style="color: #005CC5">len</span><span style="color: #24292E">(ip) </span><span style="color: #D73A49">==</span><span style="color: #24292E"> IPv6len) </span><span style="color: #D73A49">&amp;&amp;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">!</span><span style="color: #24292E">ip.</span><span style="color: #005CC5">Equal</span><span style="color: #24292E">(IPv4bcast) </span><span style="color: #D73A49">&amp;&amp;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">!</span><span style="color: #24292E">ip.</span><span style="color: #005CC5">IsUnspecified</span><span style="color: #24292E">() </span><span style="color: #D73A49">&amp;&amp;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">!</span><span style="color: #24292E">ip.</span><span style="color: #005CC5">IsLoopback</span><span style="color: #24292E">() </span><span style="color: #D73A49">&amp;&amp;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">!</span><span style="color: #24292E">ip.</span><span style="color: #005CC5">IsMulticast</span><span style="color: #24292E">() </span><span style="color: #D73A49">&amp;&amp;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">!</span><span style="color: #24292E">ip.</span><span style="color: #005CC5">IsLinkLocalUnicast</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><p>官方提供判断<code>ipv4/ipv6</code> 是否为局域网函数，<code>xff</code> 的<a href="https://github.com/sebest/xff/issues/15">issue</a>中提到了一个更加全面的判断是否为局域网保留ip的判断<a href="https://github.com/letsencrypt/boulder/blob/30a516737c9daa4c88c8c47070c25a5e7033cdcf/bdns/dns.go#L31-L145%EF%BC%8C%E8%BF%98%E6%9C%89%E4%B8%80%E7%AF%87%E5%85%B3%E4%BA%8E%60%60X-Forwarded-For%60%60%E5%A6%82%E4%BD%95%E8%A7%A3%E6%9E%90%E7%9A%84%E5%A5%BD%E6%96%87%E7%AB%A0https://github.com/ajvondrak/remote_ip/issues/29">https://github.com/letsencrypt/boulder/blob/30a516737c9daa4c88c8c47070c25a5e7033cdcf/bdns/dns.go#L31-L145，还有一篇关于``X-Forwarded-For``如何解析的好文章https://github.com/ajvondrak/remote_ip/issues/29</a></p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// IsPrivate reports whether ip is a private address, according to</span></span><span class="line"><span style="color: #6A737D">// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses).</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (ip IP) </span><span style="color: #6F42C1">IsPrivate</span><span style="color: #24292E">() </span><span style="color: #D73A49">bool</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> ip4 </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> ip.</span><span style="color: #005CC5">To4</span><span style="color: #24292E">(); ip4 </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// Following RFC 1918, Section 3. Private Address Space which says:</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">//   The Internet Assigned Numbers Authority (IANA) has reserved the</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">//   following three blocks of the IP address space for private internets:</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">//     10.0.0.0        -   10.255.255.255  (10/8 prefix)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">//     172.16.0.0      -   172.31.255.255  (172.16/12 prefix)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">//     192.168.0.0     -   192.168.255.255 (192.168/16 prefix)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> ip4[</span><span style="color: #005CC5">0</span><span style="color: #24292E">] </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">10</span><span style="color: #24292E"> </span><span style="color: #D73A49">||</span></span><span class="line"><span style="color: #24292E">(ip4[</span><span style="color: #005CC5">0</span><span style="color: #24292E">] </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">172</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;&amp;</span><span style="color: #24292E"> ip4[</span><span style="color: #005CC5">1</span><span style="color: #24292E">]</span><span style="color: #D73A49">&amp;0x</span><span style="color: #005CC5">f0</span><span style="color: #24292E"> </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">16</span><span style="color: #24292E">) </span><span style="color: #D73A49">||</span></span><span class="line"><span style="color: #24292E">(ip4[</span><span style="color: #005CC5">0</span><span style="color: #24292E">] </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">192</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;&amp;</span><span style="color: #24292E"> ip4[</span><span style="color: #005CC5">1</span><span style="color: #24292E">] </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">168</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// Following RFC 4193, Section 8. IANA Considerations which says:</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">//   The IANA has assigned the FC00::/7 prefix to &quot;Unique Local Unicast&quot;.</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">len</span><span style="color: #24292E">(ip) </span><span style="color: #D73A49">==</span><span style="color: #24292E"> IPv6len </span><span style="color: #D73A49">&amp;&amp;</span><span style="color: #24292E"> ip[</span><span style="color: #005CC5">0</span><span style="color: #24292E">]</span><span style="color: #D73A49">&amp;0x</span><span style="color: #005CC5">fe</span><span style="color: #24292E"> </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #D73A49">0x</span><span style="color: #005CC5">fc</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ol><li>在获取客户端ip前判断当前的<code>RemoteAddress</code>是否为我方的代理IP</li><li>不能直接拿X-Forwarded-For 的第一个IP地址作为客户端ip，需要判断IP是否为单播地址且不为局域网保留地址</li></ol>]]></content>
    
    
    <summary type="html">how to get a true client-ip of http</summary>
    
    
    
    <category term="web" scheme="https://freelifeblog.top/categories/web/"/>
    
    
    <category term="codeview101" scheme="https://freelifeblog.top/tags/codeview101/"/>
    
    <category term="go" scheme="https://freelifeblog.top/tags/go/"/>
    
    <category term="web" scheme="https://freelifeblog.top/tags/web/"/>
    
    <category term="client-ip" scheme="https://freelifeblog.top/tags/client-ip/"/>
    
  </entry>
  
  <entry>
    <title>微服务接口重试实现</title>
    <link href="https://freelifeblog.top/2024/08/28/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%8E%A5%E5%8F%A3%E9%87%8D%E8%AF%95%E5%AE%9E%E7%8E%B0/"/>
    <id>https://freelifeblog.top/2024/08/28/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%8E%A5%E5%8F%A3%E9%87%8D%E8%AF%95%E5%AE%9E%E7%8E%B0/</id>
    <published>2024-08-28T03:41:03.000Z</published>
    <updated>2024-08-30T02:08:13.913Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p><a href="https://github.com:duke-git/lancet">https://github.com:duke-git/lancet</a></p></blockquote><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><h3 id="lancet"><a href="#lancet" class="headerlink" title="lancet"></a><a href="https://github.com:duke-git/lancet">lancet</a></h3><p>lancet（柳叶刀）是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。本篇仅针对其中的retry功能进行解读。</p><h2 id="快速图解"><a href="#快速图解" class="headerlink" title="快速图解"></a>快速图解</h2><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/microservice-retry.png" alt="microservice-retry"></p><h2 id="功能解读"><a href="#功能解读" class="headerlink" title="功能解读"></a>功能解读</h2><p>重试在微服务调用的过程中可以有效的应对上游接口不稳定，网络状态差等问题，为下游提供稳定可靠的服务。也算是一种降级处理的方式</p><h3 id="Lancet-Retry"><a href="#Lancet-Retry" class="headerlink" title="Lancet Retry"></a>Lancet Retry</h3><p>Lancet 中的 retry 包中关于 重试的逻辑进行的抽象，提供了不同的重试策略， 线性避退 或 指数避退 ，并在每次重试间隔加上一定的抖动间隔，确保不会触发上游接口的限流策略。</p><h2 id="代码解析"><a href="#代码解析" class="headerlink" title="代码解析"></a>代码解析</h2><h3 id="Lancet-Retry-1"><a href="#Lancet-Retry-1" class="headerlink" title="Lancet Retry"></a>Lancet Retry</h3><h4 id="重试策略配置"><a href="#重试策略配置" class="headerlink" title="重试策略配置"></a>重试策略配置</h4><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">RetryConfig</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 外部取消重试动作的上下文</span></span><span class="line"><span style="color: #24292E">context         context.Context</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 最大重试次数</span></span><span class="line"><span style="color: #24292E">retryTimes      </span><span style="color: #D73A49">uint</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 重试间隔策略</span></span><span class="line"><span style="color: #24292E">backoffStrategy BackoffStrategy</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h4 id="重试间隔接口"><a href="#重试间隔接口" class="headerlink" title="重试间隔接口"></a>重试间隔接口</h4><p>对比不同的重试间隔策略，其实就是每次重试间隔不同，所以只需要提供一个<strong>下次重试间隔</strong>的接口就可以了</p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// BackoffStrategy is an interface that defines a method for calculating backoff intervals.</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">BackoffStrategy</span><span style="color: #24292E"> </span><span style="color: #D73A49">interface</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// CalculateInterval returns the time.Duration after which the next retry attempt should be made.</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">CalculateInterval</span><span style="color: #24292E">() time.Duration</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h4 id="重试功能"><a href="#重试功能" class="headerlink" title="重试功能"></a>重试功能</h4><p>RetryFunc 为重试的具体实现</p><p>Option 是选项设置模式，用于灵活的生成对象，具体的可以看<a href="#Option%E6%A8%A1%E5%BC%8F%E5%A5%BD%E5%A4%84">话唠区&#x2F;Option模式好处</a></p><p>其他的部分就比较简单了，直接根据执行的结果的成功失败，失败的话则根据 <code>backoffStrategy.CalculateInterval</code>计算出来的时间间隔进行避退，并根据<code>ctx.Done()</code>及时取消重试。</p><p>还有一段比较有意思的函数段是用于获取 函数指针指向的具体函数的名称：</p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #24292E"> </span><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">RetryFunc</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() </span><span style="color: #D73A49">error</span></span><span class="line"></span><span class="line"><span style="color: #24292E"> funcPath </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> runtime.</span><span style="color: #005CC5">FuncForPC</span><span style="color: #24292E">(reflect.</span><span style="color: #005CC5">ValueOf</span><span style="color: #24292E">(retryFunc).</span><span style="color: #005CC5">Pointer</span><span style="color: #24292E">()).</span><span style="color: #005CC5">Name</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">lastSlash </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> strings.</span><span style="color: #005CC5">LastIndex</span><span style="color: #24292E">(funcPath, </span><span style="color: #032F62">&quot;/&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">funcName </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> funcPath[lastSlash</span><span style="color: #D73A49">+</span><span style="color: #005CC5">1</span><span style="color: #24292E">:]</span></span></code></pre></div></div></figure><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// RetryFunc is function that retry executes</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">RetryFunc</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() </span><span style="color: #D73A49">error</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Option is for adding retry config</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Option</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(</span><span style="color: #D73A49">*</span><span style="color: #24292E">RetryConfig)</span></span><span class="line"></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Retry</span><span style="color: #24292E">(retryFunc RetryFunc, opts </span><span style="color: #D73A49">...</span><span style="color: #24292E">Option) </span><span style="color: #D73A49">error</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">config </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">RetryConfig{</span></span><span class="line"><span style="color: #24292E">retryTimes: DefaultRetryTimes,</span></span><span class="line"><span style="color: #24292E">context:    context.</span><span style="color: #005CC5">TODO</span><span style="color: #24292E">(),</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> _, opt </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">range</span><span style="color: #24292E"> opts {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">opt</span><span style="color: #24292E">(config)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> config.backoffStrategy </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">config.backoffStrategy </span><span style="color: #D73A49">=</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">linear{</span></span><span class="line"><span style="color: #24292E">interval: DefaultRetryLinearInterval,</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">var</span><span style="color: #24292E"> i </span><span style="color: #D73A49">uint</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> i </span><span style="color: #D73A49">&lt;</span><span style="color: #24292E"> config.retryTimes {</span></span><span class="line"><span style="color: #24292E">err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">retryFunc</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">select</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">case</span><span style="color: #24292E"> </span><span style="color: #D73A49">&lt;-</span><span style="color: #24292E">time.</span><span style="color: #005CC5">After</span><span style="color: #24292E">(config.backoffStrategy.</span><span style="color: #005CC5">CalculateInterval</span><span style="color: #24292E">()):</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">case</span><span style="color: #24292E"> </span><span style="color: #D73A49">&lt;-</span><span style="color: #24292E">config.context.</span><span style="color: #005CC5">Done</span><span style="color: #24292E">():</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> errors.</span><span style="color: #005CC5">New</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;retry is cancelled&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">} </span><span style="color: #D73A49">else</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">i</span><span style="color: #D73A49">++</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #24292E">funcPath </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> runtime.</span><span style="color: #005CC5">FuncForPC</span><span style="color: #24292E">(reflect.</span><span style="color: #005CC5">ValueOf</span><span style="color: #24292E">(retryFunc).</span><span style="color: #005CC5">Pointer</span><span style="color: #24292E">()).</span><span style="color: #005CC5">Name</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">lastSlash </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> strings.</span><span style="color: #005CC5">LastIndex</span><span style="color: #24292E">(funcPath, </span><span style="color: #032F62">&quot;/&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">funcName </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> funcPath[lastSlash</span><span style="color: #D73A49">+</span><span style="color: #005CC5">1</span><span style="color: #24292E">:]</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> fmt.</span><span style="color: #005CC5">Errorf</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;function </span><span style="color: #005CC5">%s</span><span style="color: #032F62"> run failed after </span><span style="color: #005CC5">%d</span><span style="color: #032F62"> times retry&quot;</span><span style="color: #24292E">, funcName, i)</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h4 id="重试间隔实现"><a href="#重试间隔实现" class="headerlink" title="重试间隔实现"></a>重试间隔实现</h4><h5 id="线性重试"><a href="#线性重试" class="headerlink" title="线性重试"></a>线性重试</h5><p>比较简单，每次返回的重试间隔是一样的</p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// linear is a struct that implements the BackoffStrategy interface using a linear backoff strategy.</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">linear</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">// interval specifies the fixed duration to wait between retry attempts.</span></span><span class="line"><span style="color: #24292E">    interval time.Duration</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// CalculateInterval calculates the next interval returns a constant.</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (l </span><span style="color: #D73A49">*</span><span style="color: #24292E">linear) </span><span style="color: #6F42C1">CalculateInterval</span><span style="color: #24292E">() time.Duration {</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">return</span><span style="color: #24292E"> l.interval</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h5 id="指数重试"><a href="#指数重试" class="headerlink" title="指数重试"></a>指数重试</h5><p>可以看到每次都是以<code>base</code> 为底数，指数每次重试迭代+1 最大为 重试次数 ，并扩大 <code>interval</code> 倍 的重试间隔</p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// exponentialWithJitter is a struct that implements the BackoffStrategy interface using a exponential backoff strategy.</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">exponentialWithJitter</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">    base      time.Duration </span><span style="color: #6A737D">// base is the multiplier for the exponential backoff.</span></span><span class="line"><span style="color: #24292E">    interval  time.Duration </span><span style="color: #6A737D">// interval is the current backoff interval, which will be adjusted over time.</span></span><span class="line"><span style="color: #24292E">    maxJitter time.Duration </span><span style="color: #6A737D">// maxJitter is the maximum amount of jitter to apply to the backoff interval.</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// CalculateInterval calculates the next backoff interval with jitter and updates the interval.</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (e </span><span style="color: #D73A49">*</span><span style="color: #24292E">exponentialWithJitter) </span><span style="color: #6F42C1">CalculateInterval</span><span style="color: #24292E">() time.Duration {</span></span><span class="line"><span style="color: #24292E">    current </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> e.interval</span></span><span class="line"><span style="color: #24292E">    e.interval </span><span style="color: #D73A49">=</span><span style="color: #24292E"> e.interval </span><span style="color: #D73A49">*</span><span style="color: #24292E"> e.base</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">return</span><span style="color: #24292E"> current </span><span style="color: #D73A49">+</span><span style="color: #24292E"> </span><span style="color: #005CC5">jitter</span><span style="color: #24292E">(e.maxJitter)</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h5 id="位移间隔重试"><a href="#位移间隔重试" class="headerlink" title="位移间隔重试"></a>位移间隔重试</h5><p>这个间隔的避加以<code>interval</code>为操作数，左移操作<code>shifter</code>位</p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// shiftExponentialWithJitter is a struct that implements the BackoffStrategy interface using a exponential backoff strategy.</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">shiftExponentialWithJitter</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">interval  time.Duration </span><span style="color: #6A737D">// interval is the current backoff interval, which will be adjusted over time.</span></span><span class="line"><span style="color: #24292E">maxJitter time.Duration </span><span style="color: #6A737D">// maxJitter is the maximum amount of jitter to apply to the backoff interval.</span></span><span class="line"><span style="color: #24292E">shifter   </span><span style="color: #D73A49">uint64</span><span style="color: #24292E">        </span><span style="color: #6A737D">// shift by n faster than multiplication</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// CalculateInterval calculates the next backoff interval with jitter and updates the interval.</span></span><span class="line"><span style="color: #6A737D">// Uses shift instead of multiplication</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (e </span><span style="color: #D73A49">*</span><span style="color: #24292E">shiftExponentialWithJitter) </span><span style="color: #6F42C1">CalculateInterval</span><span style="color: #24292E">() time.Duration {</span></span><span class="line"><span style="color: #24292E">current </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> e.interval</span></span><span class="line"><span style="color: #24292E">e.interval </span><span style="color: #D73A49">=</span><span style="color: #24292E"> e.interval </span><span style="color: #D73A49">&lt;&lt;</span><span style="color: #24292E"> e.shifter</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> current </span><span style="color: #D73A49">+</span><span style="color: #24292E"> </span><span style="color: #005CC5">jitter</span><span style="color: #24292E">(e.maxJitter)</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h2 id="话唠区"><a href="#话唠区" class="headerlink" title="话唠区"></a>话唠区</h2><h3 id="Option模式好处"><a href="#Option模式好处" class="headerlink" title="Option模式好处"></a>Option模式好处</h3><p>当创建对象需要新增一个<strong>可选配置选项时-潜台词就是如果不设置就会有默认值</strong>，只需要创建新参数对应的的Option就可以了。</p><p>可以简单对比下使用和不使用Option的情况下，对于调用方的影响</p><h5 id="Bad-Case"><a href="#Bad-Case" class="headerlink" title="Bad Case"></a>Bad Case</h5><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">//</span></span><span class="line"><span style="color: #6A737D">//Old</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">NewCacheConfig</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">  MaxSize </span><span style="color: #D73A49">int64</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">NewCacheConfig</span><span style="color: #24292E">(maxSize </span><span style="color: #D73A49">int64</span><span style="color: #24292E">)</span><span style="color: #D73A49">*</span><span style="color: #24292E">NewCacheConfig{</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">NewCacheConfig{</span></span><span class="line"><span style="color: #24292E">    MaxSize: maxSize,</span></span><span class="line"><span style="color: #24292E">  }</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #005CC5">NewCacheConfig</span><span style="color: #24292E">(</span><span style="color: #005CC5">20</span><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">//New</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">NewCacheConfig</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">  MaxSize </span><span style="color: #D73A49">int64</span></span><span class="line"><span style="color: #24292E">  MaxLifeTime time.Duration</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">NewCacheConfig</span><span style="color: #24292E">(maxSize </span><span style="color: #D73A49">int64</span><span style="color: #24292E">,maxLifeTime time.Duration)</span><span style="color: #D73A49">*</span><span style="color: #24292E">NewCacheConfig{</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">NewCacheConfig{</span></span><span class="line"><span style="color: #24292E">    MaxSize: maxSize,</span></span><span class="line"><span style="color: #24292E">    MaxLifeTime: maxLifeTime,</span></span><span class="line"><span style="color: #24292E">  }</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">//caller need to be changed</span></span><span class="line"><span style="color: #005CC5">NewCacheConfig</span><span style="color: #24292E">(</span><span style="color: #005CC5">20</span><span style="color: #24292E">,</span><span style="color: #005CC5">2</span><span style="color: #D73A49">*</span><span style="color: #24292E">time.Second)</span></span></code></pre></div></div></figure><h5 id="Good-Case"><a href="#Good-Case" class="headerlink" title="Good Case"></a>Good Case</h5><p>可以发现调用方在调用创建新版本对象的时候，可以不用改动，也可以使用<code>WithMaxLifeTime</code>进行设置，这也符合<strong>可选配置选项</strong>的含义</p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">//Old</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Option</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(</span><span style="color: #D73A49">*</span><span style="color: #24292E">CacheConfig)</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">CacheConfig</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">  MaxSize </span><span style="color: #D73A49">int64</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">NewCacheConfig</span><span style="color: #24292E">(maxSize </span><span style="color: #D73A49">int64</span><span style="color: #24292E">,opts </span><span style="color: #D73A49">...</span><span style="color: #24292E">Option)</span><span style="color: #D73A49">*</span><span style="color: #24292E">CacheConfig{</span></span><span class="line"><span style="color: #24292E">  </span></span><span class="line"><span style="color: #24292E">  cc </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">CacheConfig{</span></span><span class="line"><span style="color: #24292E">    MaxSize: maxSize,</span></span><span class="line"><span style="color: #24292E">  }</span></span><span class="line"><span style="color: #24292E">  </span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">for</span><span style="color: #24292E"> _,opt </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">range</span><span style="color: #24292E"> opts {</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #005CC5">opt</span><span style="color: #24292E">(cc)</span></span><span class="line"><span style="color: #24292E">  }</span></span><span class="line"><span style="color: #24292E">  </span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">return</span><span style="color: #24292E"> cc</span></span><span class="line"><span style="color: #24292E">  </span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #005CC5">NewCacheConfig</span><span style="color: #24292E">(</span><span style="color: #005CC5">20</span><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">//New</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Cacher</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">  MaxSize </span><span style="color: #D73A49">int64</span></span><span class="line"><span style="color: #24292E">  MaxLifeTime time.Duration</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">WithMaxLifeTime</span><span style="color: #24292E">(maxLifeTime time.Duration)Option{</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(cc </span><span style="color: #D73A49">*</span><span style="color: #24292E">CacheConfig){</span></span><span class="line"><span style="color: #24292E">    cc.MaxLifeTime </span><span style="color: #D73A49">=</span><span style="color: #24292E"> maxLifeTime</span></span><span class="line"><span style="color: #24292E">  }</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">NewCacheConfig</span><span style="color: #24292E">(maxSize </span><span style="color: #D73A49">int64</span><span style="color: #24292E">,maxLifeTime time.Duration)</span><span style="color: #D73A49">*</span><span style="color: #24292E">CacheConfig{</span></span><span class="line"><span style="color: #24292E">  cc </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">CacheConfig{</span></span><span class="line"><span style="color: #24292E">    MaxSize: maxSize,</span></span><span class="line"><span style="color: #24292E">    MaxLifeTime: </span><span style="color: #005CC5">3</span><span style="color: #D73A49">*</span><span style="color: #24292E">time.Second,</span></span><span class="line"><span style="color: #24292E">  }</span></span><span class="line"><span style="color: #24292E">  </span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">for</span><span style="color: #24292E"> _,opt </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">range</span><span style="color: #24292E"> opts {</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #005CC5">opt</span><span style="color: #24292E">(cc)</span></span><span class="line"><span style="color: #24292E">  }</span></span><span class="line"><span style="color: #24292E">  </span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">return</span><span style="color: #24292E"> cc</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">//caller no need to be changed</span></span><span class="line"><span style="color: #005CC5">NewCacheConfig</span><span style="color: #24292E">(</span><span style="color: #005CC5">20</span><span style="color: #24292E">) </span><span style="color: #6A737D">//output &amp;{20 3s}</span></span><span class="line"></span><span class="line"><span style="color: #005CC5">NewCacheConfig</span><span style="color: #24292E">(</span><span style="color: #005CC5">20</span><span style="color: #24292E">,</span><span style="color: #005CC5">WithMaxLifeTime</span><span style="color: #24292E">(</span><span style="color: #005CC5">1</span><span style="color: #D73A49">*</span><span style="color: #24292E">time.Second)) </span><span style="color: #6A737D">//output &amp;{20 1s}</span></span></code></pre></div></div></figure>]]></content>
    
    
    <summary type="html">golang;micro service;retry strategy;codeview</summary>
    
    
    
    <category term="IT" scheme="https://freelifeblog.top/categories/IT/"/>
    
    
    <category term="codeview101" scheme="https://freelifeblog.top/tags/codeview101/"/>
    
    <category term="go" scheme="https://freelifeblog.top/tags/go/"/>
    
    <category term="retry strategy" scheme="https://freelifeblog.top/tags/retry-strategy/"/>
    
    <category term="micro service" scheme="https://freelifeblog.top/tags/micro-service/"/>
    
  </entry>
  
  <entry>
    <title>golang常见陷阱</title>
    <link href="https://freelifeblog.top/2024/08/23/golang%E5%B8%B8%E8%A7%81%E9%99%B7%E9%98%B1/"/>
    <id>https://freelifeblog.top/2024/08/23/golang%E5%B8%B8%E8%A7%81%E9%99%B7%E9%98%B1/</id>
    <published>2024-08-23T20:42:46.000Z</published>
    <updated>2024-11-08T07:05:26.046Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>编程思想架构如果是内功的话，那对一门语言的掌握与精通就是外功招式。光有内功，外功不扎实，那就是有劲使不出；同样的光有外功，内功不深厚那就是外强中干。所以在此记录下遇到的关于golang中使用的一些问题，属于外功招式了。希望大家都能从中受益，一直会持续更新下去。</p></blockquote><h2 id="Interface篇"><a href="#Interface篇" class="headerlink" title="Interface篇"></a>Interface篇</h2><h3 id="接口判空nil"><a href="#接口判空nil" class="headerlink" title="接口判空nil"></a>接口判空<code>nil</code></h3><h4 id="接口是由类型和值组成，只要其中任意一个部分不为nil，则接口判定则不会nil"><a href="#接口是由类型和值组成，只要其中任意一个部分不为nil，则接口判定则不会nil" class="headerlink" title="接口是由类型和值组成，只要其中任意一个部分不为nil，则接口判定则不会nil"></a>接口是由类型和值组成，只要其中任意一个部分不为<code>nil</code>，则接口判定则不会<code>nil</code></h4><p>下述代码执行结果是<code>not nil</code>，<code>因为返回的是 RoundFace 具体的类型，所以 Face 就不为nil，但是如果15行改成var rf Face</code> 那么判断就会是<code>nil</code></p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Face</span><span style="color: #24292E"> </span><span style="color: #D73A49">interface</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">FaceShape</span><span style="color: #24292E">() </span><span style="color: #D73A49">string</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">RoundFace</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (rf RoundFace) </span><span style="color: #6F42C1">FaceShape</span><span style="color: #24292E">() </span><span style="color: #D73A49">string</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #032F62">&quot;round&quot;</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">GetFace</span><span style="color: #24292E">() Face {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">var</span><span style="color: #24292E"> rf RoundFace</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> rf</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span><span style="color: #24292E">() {</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> </span><span style="color: #005CC5">GetFace</span><span style="color: #24292E">() </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">println</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;nil&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">} </span><span style="color: #D73A49">else</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">println</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;not nil&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h2 id="Chan篇"><a href="#Chan篇" class="headerlink" title="Chan篇"></a>Chan篇</h2><h3 id="chan关闭陷阱"><a href="#chan关闭陷阱" class="headerlink" title="chan关闭陷阱"></a>chan关闭陷阱</h3><h4 id="chan只可以被关闭一次"><a href="#chan只可以被关闭一次" class="headerlink" title="chan只可以被关闭一次"></a>chan只可以被关闭一次</h4><p>这个估计是被说的做多的，chan多次关闭的话就会直接panic 这个倒是没啥说的，一般都会配合once保证channel只会被close一次。</p><h5 id="正确示例"><a href="#正确示例" class="headerlink" title="正确示例"></a>正确示例</h5><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">sync</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">var</span><span style="color: #24292E"> once sync.Once</span></span><span class="line"></span><span class="line"><span style="color: #24292E">ch </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">make</span><span style="color: #24292E">(</span><span style="color: #D73A49">chan</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E">{}, </span><span style="color: #005CC5">1</span><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">ch </span><span style="color: #D73A49">&lt;-</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E">{}{}</span></span><span class="line"></span><span class="line"><span style="color: #24292E">once.</span><span style="color: #005CC5">Do</span><span style="color: #24292E">(</span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">close</span><span style="color: #24292E">(ch)</span></span><span class="line"><span style="color: #24292E">})</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h4 id="chan关闭后，receive-接收零值，导致for-select泄露"><a href="#chan关闭后，receive-接收零值，导致for-select泄露" class="headerlink" title="chan关闭后，receive 接收零值，导致for - select泄露"></a>chan关闭后，receive 接收零值，导致for - select泄露</h4><p>for-select是一段很常用的代码，可以发现如果使用 select 进行接收，即使关闭了channel之后，receive还是会一直收到零值，从而导致不会退出</p><h5 id="正确示例-1"><a href="#正确示例-1" class="headerlink" title="正确示例"></a>正确示例</h5><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">fmt</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">time</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span><span style="color: #24292E">() {</span></span><span class="line"></span><span class="line"><span style="color: #24292E">ch </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">make</span><span style="color: #24292E">(</span><span style="color: #D73A49">chan</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E">{}, </span><span style="color: #005CC5">1</span><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">ch </span><span style="color: #D73A49">&lt;-</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E">{}{}</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">close</span><span style="color: #24292E">(ch)</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">select</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">case</span><span style="color: #24292E"> zero, open </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">&lt;-</span><span style="color: #24292E">ch:</span></span><span class="line"><span style="color: #24292E">        fmt.</span><span style="color: #005CC5">Println</span><span style="color: #24292E">(open)</span></span><span class="line"><span style="color: #24292E">        </span><span style="color: #6A737D">// 当channel被关闭后，open 会变成false</span></span><span class="line"><span style="color: #24292E">        </span><span style="color: #D73A49">if</span><span style="color: #24292E"> </span><span style="color: #D73A49">!</span><span style="color: #24292E">closed {</span></span><span class="line"><span style="color: #24292E">          </span><span style="color: #D73A49">return</span></span><span class="line"><span style="color: #24292E">        }</span></span><span class="line"><span style="color: #24292E">fmt.</span><span style="color: #005CC5">Println</span><span style="color: #24292E">(zero)</span></span><span class="line"><span style="color: #24292E">time.</span><span style="color: #005CC5">Sleep</span><span style="color: #24292E">(time.Second)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h5 id="错误示例"><a href="#错误示例" class="headerlink" title="错误示例"></a>错误示例</h5><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">fmt</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">time</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span><span style="color: #24292E">() {</span></span><span class="line"></span><span class="line"><span style="color: #24292E">ch </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">make</span><span style="color: #24292E">(</span><span style="color: #D73A49">chan</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E">{}, </span><span style="color: #005CC5">1</span><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">ch </span><span style="color: #D73A49">&lt;-</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E">{}{}</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">close</span><span style="color: #24292E">(ch)</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">select</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">case</span><span style="color: #24292E"> zero, open </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">&lt;-</span><span style="color: #24292E">ch:</span></span><span class="line"><span style="color: #24292E">fmt.</span><span style="color: #005CC5">Println</span><span style="color: #24292E">(zero)</span></span><span class="line"><span style="color: #24292E">fmt.</span><span style="color: #005CC5">Println</span><span style="color: #24292E">(open)</span></span><span class="line"><span style="color: #24292E">time.</span><span style="color: #005CC5">Sleep</span><span style="color: #24292E">(time.Second)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><p>输出如下</p><figure class="shiki shell"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #24292E">{} </span><span style="color: #6A737D">#塞入的数值</span></span><span class="line"><span style="color: #005CC5">true</span><span style="color: #24292E"> </span><span style="color: #6A737D">#当前通道未关闭</span></span><span class="line"><span style="color: #24292E">{} </span><span style="color: #6A737D">#关闭后返回的零值</span></span><span class="line"><span style="color: #005CC5">false</span><span style="color: #24292E"> </span><span style="color: #6A737D">#当前通道已关闭</span></span><span class="line"><span style="color: #24292E">{}</span></span><span class="line"><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">{}</span></span><span class="line"><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">{}</span></span><span class="line"><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">{}</span></span><span class="line"><span style="color: #005CC5">false</span></span></code></pre></div></div></figure><h2 id="http-篇"><a href="#http-篇" class="headerlink" title="http 篇"></a>http 篇</h2><h3 id="http-同域名TCP连接复用"><a href="#http-同域名TCP连接复用" class="headerlink" title="http 同域名TCP连接复用"></a>http 同域名TCP连接复用</h3><p>响应一定需要 读取处理，及时不需要，也需要读取下<code>io.Copy(io.Discard, resp.Body)</code>，不然及时服务端支持<code>keep_alive</code>，go http client 也不会复用TCP连接</p><p><strong>错误代码</strong></p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">net/http</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">time</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span><span style="color: #24292E">() {</span></span><span class="line"></span><span class="line"><span style="color: #24292E">client </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> http.DefaultClient</span></span><span class="line"></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 创建两个tcp连接</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> </span><span style="color: #005CC5">request</span><span style="color: #24292E">(client)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> </span><span style="color: #005CC5">request</span><span style="color: #24292E">(client)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 间隔5s让前面两个http请求处理完毕</span></span><span class="line"><span style="color: #24292E">time.</span><span style="color: #005CC5">Sleep</span><span style="color: #24292E">(</span><span style="color: #005CC5">5</span><span style="color: #24292E"> </span><span style="color: #D73A49">*</span><span style="color: #24292E"> time.Second)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 正常来说下面的两个http请求会复用上面的tcp链接</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> </span><span style="color: #005CC5">request</span><span style="color: #24292E">(client)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> </span><span style="color: #005CC5">request</span><span style="color: #24292E">(client)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">time.</span><span style="color: #005CC5">Sleep</span><span style="color: #24292E">(</span><span style="color: #005CC5">6</span><span style="color: #24292E"> </span><span style="color: #D73A49">*</span><span style="color: #24292E"> time.Second)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">request</span><span style="color: #24292E">(client </span><span style="color: #D73A49">*</span><span style="color: #24292E">http.Client) {</span></span><span class="line"><span style="color: #24292E">resp, _ </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> client.</span><span style="color: #005CC5">Get</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;https://www.baidu.com&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> resp.Body.</span><span style="color: #005CC5">Close</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// 未读取response.Body</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">//io.Copy(io.Discard, resp.Body)</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><p><strong>错误现象</strong></p><p>可以看到在首次tcp连接创建完后，间隔5s还是会重新创建新的tcp连接</p><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/image-20240902101238139.png" alt="http_tcp_keep_alive_resue_error_case"></p><p><strong>正确代码</strong></p><p>可以发现只有在32行有不一样的地方读取处理了响应</p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">io</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">net/http</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">time</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span><span style="color: #24292E">() {</span></span><span class="line"></span><span class="line"><span style="color: #24292E">client </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> http.DefaultClient</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// 创建两个tcp连接</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> </span><span style="color: #005CC5">request</span><span style="color: #24292E">(client)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> </span><span style="color: #005CC5">request</span><span style="color: #24292E">(client)</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// 间隔5s让前面两个http请求处理完毕</span></span><span class="line"><span style="color: #24292E">time.</span><span style="color: #005CC5">Sleep</span><span style="color: #24292E">(</span><span style="color: #005CC5">5</span><span style="color: #24292E"> </span><span style="color: #D73A49">*</span><span style="color: #24292E"> time.Second)</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// 正常来说下面的两个http请求会复用上面的tcp链接</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> </span><span style="color: #005CC5">request</span><span style="color: #24292E">(client)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> </span><span style="color: #005CC5">request</span><span style="color: #24292E">(client)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">time.</span><span style="color: #005CC5">Sleep</span><span style="color: #24292E">(</span><span style="color: #005CC5">6</span><span style="color: #24292E"> </span><span style="color: #D73A49">*</span><span style="color: #24292E"> time.Second)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">request</span><span style="color: #24292E">(client </span><span style="color: #D73A49">*</span><span style="color: #24292E">http.Client) {</span></span><span class="line"><span style="color: #24292E">resp, _ </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> client.</span><span style="color: #005CC5">Get</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;https://www.baidu.com&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> resp.Body.</span><span style="color: #005CC5">Close</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// 读取response.Body</span></span><span class="line"><span style="color: #24292E">io.</span><span style="color: #005CC5">Copy</span><span style="color: #24292E">(io.Discard, resp.Body)</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><p><strong>正确现象</strong></p><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/image-20240902101605819.png" alt="http_tcp_keep_alive_resue_right_case"></p><h2 id="map篇"><a href="#map篇" class="headerlink" title="map篇"></a>map篇</h2><p>在使用未知map之前，需要先判断下是否为nil指针，预防空指针</p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">if</span><span style="color: #24292E"> cr.Annotations </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> v, ok </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> cr.Annotations[</span><span style="color: #032F62">&quot;key&quot;</span><span style="color: #24292E">]; ok </span><span style="color: #D73A49">&amp;&amp;</span><span style="color: #24292E"> v </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #032F62">&quot;false&quot;</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h2 id="slice篇"><a href="#slice篇" class="headerlink" title="slice篇"></a>slice篇</h2><h3 id="reflect-DeepEqual-slice判断是否相同，注意顺序问题"><a href="#reflect-DeepEqual-slice判断是否相同，注意顺序问题" class="headerlink" title="reflect.DeepEqual slice判断是否相同，注意顺序问题"></a><code>reflect.DeepEqual</code> slice判断是否相同，注意顺序问题</h3><p>对于<code>slice</code>元素内容一样，但是顺序不同，<code>reflect.DeepEqual</code> 返回的是<code>false</code>，可以使用 <code>slices.Sort</code> 先整体排序完在进行比较</p> <figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">var</span><span style="color: #24292E"> s1 </span><span style="color: #D73A49">=</span><span style="color: #24292E"> []</span><span style="color: #D73A49">int</span><span style="color: #24292E">{</span><span style="color: #005CC5">1</span><span style="color: #24292E">, </span><span style="color: #005CC5">2</span><span style="color: #24292E">, </span><span style="color: #005CC5">3</span><span style="color: #24292E">}</span></span><span class="line"><span style="color: #D73A49">var</span><span style="color: #24292E"> s2 </span><span style="color: #D73A49">=</span><span style="color: #24292E"> []</span><span style="color: #D73A49">int</span><span style="color: #24292E">{</span><span style="color: #005CC5">2</span><span style="color: #24292E">, </span><span style="color: #005CC5">1</span><span style="color: #24292E">, </span><span style="color: #005CC5">3</span><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">TestDeepEqual</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E">log.</span><span style="color: #005CC5">Println</span><span style="color: #24292E">(reflect.</span><span style="color: #005CC5">DeepEqual</span><span style="color: #24292E">(s1, s2)) </span><span style="color: #6A737D">// false</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">TestDeepEqualWithSort</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E">slices.</span><span style="color: #005CC5">Sort</span><span style="color: #24292E">(s1)</span></span><span class="line"><span style="color: #24292E">slices.</span><span style="color: #005CC5">Sort</span><span style="color: #24292E">(s2)</span></span><span class="line"><span style="color: #24292E">log.</span><span style="color: #005CC5">Println</span><span style="color: #24292E">(reflect.</span><span style="color: #005CC5">DeepEqual</span><span style="color: #24292E">(s1, s2)) </span><span style="color: #6A737D">// true</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure>]]></content>
    
    
    <summary type="html">go,golang使用过程中常见陷阱,channel,http client</summary>
    
    
    
    <category term="IT" scheme="https://freelifeblog.top/categories/IT/"/>
    
    
    <category term="go" scheme="https://freelifeblog.top/tags/go/"/>
    
    <category term="golang101" scheme="https://freelifeblog.top/tags/golang101/"/>
    
  </entry>
  
  <entry>
    <title>rst-not-fin-a-tcp-connection</title>
    <link href="https://freelifeblog.top/2024/08/21/rst-not-fin-a-tcp-connection/"/>
    <id>https://freelifeblog.top/2024/08/21/rst-not-fin-a-tcp-connection/</id>
    <published>2024-08-21T02:12:47.000Z</published>
    <updated>2024-08-23T15:32:50.021Z</updated>
    
    <content type="html"><![CDATA[<h2 id="为什么会有这篇文章"><a href="#为什么会有这篇文章" class="headerlink" title="为什么会有这篇文章"></a>为什么会有这篇文章</h2><p>在 plantegg 大佬的知识星球里看到了 <a href="https://wx.zsxq.com/dweb2/index/topic_detail/1522858812224852">应用断开连接的时候如何让 OS 走 RST 流程？</a>提问，想着写前一篇<a href="https://freelifeblog.top/2024/08/05/balancer/">balancer</a>刚好看到了 kubernetes 关于 tcp 探测的优化，所以顺便实验下巩固下，顺便推荐下 <a href="https://t.zsxq.com/VzBcY">plantegg 大佬的星球</a>性能优化的干货很多。</p><h2 id="SO-LINGER-参数说明"><a href="#SO-LINGER-参数说明" class="headerlink" title="SO_LINGER 参数说明"></a>SO_LINGER 参数说明</h2><p><a href="https://man7.org/linux/man-pages/man7/socket.7.html">https://man7.org/linux/man-pages/man7/socket.7.html</a></p><figure class="shiki c"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #24292E">Sets or gets the SO_LINGER option.  The argument is a linger structure.</span></span><span class="line"><span style="color: #D73A49">struct</span><span style="color: #24292E"> linger {</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">int</span><span style="color: #24292E"> l_onoff;</span><span style="color: #6A737D">    /* linger active */</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">int</span><span style="color: #24292E"> l_linger;</span><span style="color: #6A737D">   /* how many seconds to linger for */</span></span><span class="line"><span style="color: #24292E">};</span></span><span class="line"></span><span class="line"><span style="color: #24292E">When enabled, a </span><span style="color: #6F42C1">close</span><span style="color: #24292E">(</span><span style="color: #005CC5">2</span><span style="color: #24292E">) or </span><span style="color: #6F42C1">shutdown</span><span style="color: #24292E">(</span><span style="color: #005CC5">2</span><span style="color: #24292E">) will not </span><span style="color: #D73A49">return</span><span style="color: #24292E"> until all queued messages </span><span style="color: #D73A49">for</span><span style="color: #24292E"> the socket have been successfully sent or the linger timeout has been reached. Otherwise, the call returns immediately and the closing is done in the background.  When the socket is closed as part of </span><span style="color: #6F42C1">exit</span><span style="color: #24292E">(</span><span style="color: #005CC5">2</span><span style="color: #24292E">), it always lingers in the background</span></span></code></pre></div></div></figure><p>这里说明的很详细了，如果设置了 <code>linger</code> 的 <code>l_onoff</code> 为 1，在设置 <code>l_linger</code> 单位秒，在调用close或者shutdown的时候，在 l_linger 规定的时间内 发送&#x2F;接收 完数据，或者达到了 <code>l_linger</code> 规定的时间，会直接发送 rst 结束请求，所以这个<code>l_linger</code>大小的设置需要谨慎，不然会出现数据还没发送或者接收完就关闭链接了，不过一般场景不会直接这么用rst请求。</p><h2 id="实验"><a href="#实验" class="headerlink" title="实验"></a>实验</h2><ol><li>本地随便启动个 8080 端口的服务</li><li>运行如下程序 <a href="https://github.com/OnlyPiglet/blog_code_pratice/blob/main/network/direct_rst_connect.go">https://github.com/OnlyPiglet/blog_code_pratice/blob/main/network/direct_rst_connect.go</a></li></ol><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">net</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">syscall</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">NewRstDialer</span><span style="color: #24292E">() </span><span style="color: #D73A49">*</span><span style="color: #24292E">net.Dialer {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">net.Dialer{</span></span><span class="line"><span style="color: #24292E">Control: </span><span style="color: #D73A49">func</span><span style="color: #24292E">(network, address </span><span style="color: #D73A49">string</span><span style="color: #24292E">, c syscall.RawConn) </span><span style="color: #D73A49">error</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> c.</span><span style="color: #005CC5">Control</span><span style="color: #24292E">(</span><span style="color: #D73A49">func</span><span style="color: #24292E">(fd </span><span style="color: #D73A49">uintptr</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">        </span><span style="color: #6A737D">// 此处激活so_linger，同时设置超时时间为0</span></span><span class="line"><span style="color: #24292E">syscall.</span><span style="color: #005CC5">SetsockoptLinger</span><span style="color: #24292E">(</span><span style="color: #005CC5">int</span><span style="color: #24292E">(fd), syscall.SOL_SOCKET, syscall.SO_LINGER, </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">syscall.Linger{Onoff: </span><span style="color: #005CC5">1</span><span style="color: #24292E">, Linger: </span><span style="color: #005CC5">0</span><span style="color: #24292E">})</span></span><span class="line"><span style="color: #24292E">})</span></span><span class="line"><span style="color: #24292E">},</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span><span style="color: #24292E">() {</span></span><span class="line"></span><span class="line"><span style="color: #24292E">rstDialer </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">NewRstDialer</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">rstConn, _ </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> rstDialer.</span><span style="color: #005CC5">Dial</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;tcp&quot;</span><span style="color: #24292E">, </span><span style="color: #032F62">&quot;127.0.0.1:8080&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">_ </span><span style="color: #D73A49">=</span><span style="color: #24292E"> rstConn.</span><span style="color: #005CC5">Close</span><span style="color: #24292E">()</span></span><span class="line"></span><span class="line"><span style="color: #24292E">finDialer </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> net.Dialer{}</span></span><span class="line"><span style="color: #24292E">finConn, _ </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> finDialer.</span><span style="color: #005CC5">Dial</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;tcp&quot;</span><span style="color: #24292E">, </span><span style="color: #032F62">&quot;127.0.0.1:8080&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">_ </span><span style="color: #D73A49">=</span><span style="color: #24292E"> finConn.</span><span style="color: #005CC5">Close</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h2 id="效果展示"><a href="#效果展示" class="headerlink" title="效果展示"></a>效果展示</h2><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/image-20240821104835962.png" alt="image-20240821104835962">可以看到第一次在ack完之后调用close直接 rst 掉了链接，第二次调用close是正常的4次挥手流程。</p><h2 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h2><p>在k8s中存在一个tcp探针偶发失败的问题，原因就是由于k8s管理的pod数量过多时，短时间内会进行大量的tcp探测，如果走默认的timewait默认60s关闭，会导致单台kubelet 机器上的 很多资源比如  socket, ephemeral port, conntrack entry 等等会被占用 ，导致后续探测失败，具体的issue可以参考</p><p><a href="https://github.com/kubernetes/kubernetes/pull/115143">https://github.com/kubernetes/kubernetes/pull/115143</a></p>]]></content>
    
    
    <summary type="html">程序中如何rst掉一个链接，而不是fin一个链接</summary>
    
    
    
    <category term="IT" scheme="https://freelifeblog.top/categories/IT/"/>
    
    
    <category term="network" scheme="https://freelifeblog.top/tags/network/"/>
    
    <category term="tcp" scheme="https://freelifeblog.top/tags/tcp/"/>
    
  </entry>
  
  <entry>
    <title>balancer</title>
    <link href="https://freelifeblog.top/2024/08/05/balancer/"/>
    <id>https://freelifeblog.top/2024/08/05/balancer/</id>
    <published>2024-08-05T15:57:00.000Z</published>
    <updated>2024-11-08T07:05:26.046Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p><a href="https://github.com/zehuamama/balancer.git">https://github.com/zehuamama/balancer.git</a></p></blockquote><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>balancer是一个支持http和https的7层负载均衡器，也是一个实现负载均衡算法的go库。</p><p>目前支持的负载均衡算法有</p><ul><li><code>round-robin</code></li><li><code>random</code></li><li><code>power of 2 random choice</code></li><li><code>consistent hash</code></li><li><code>consistent hash with bounded</code></li><li><code>ip-hash</code></li><li><code>least-load</code></li></ul><h2 id="快速图解"><a href="#快速图解" class="headerlink" title="快速图解"></a>快速图解</h2><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/balancer_health_checker.png" alt="balancer_health_checker"></p><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/socket_linger_option.png" alt="socket_linger_option"></p><p>（偷下懒借用张师傅的图片🤣，顺带推荐下 张师傅的tcp小册，讲解的通俗易通，知识点丰富，链接 <a href="https://s.juejin.cn/ds/irJbCpd2/%EF%BC%89">https://s.juejin.cn/ds/irJbCpd2/）</a></p><h2 id="功能分解"><a href="#功能分解" class="headerlink" title="功能分解"></a>功能分解</h2><h3 id="健康检查"><a href="#健康检查" class="headerlink" title="健康检查"></a>健康检查</h3><p>负责根据健康检查规则对后端服务进行检查，剔除不健康的节点，加入健康节点</p><h3 id="负载均衡"><a href="#负载均衡" class="headerlink" title="负载均衡"></a>负载均衡</h3><p>从一组负载均衡节点中根据负载均衡策略选择节点实例</p><h2 id="代码赏析"><a href="#代码赏析" class="headerlink" title="代码赏析"></a>代码赏析</h2><h3 id="HTTP-Proxy-反向代理核心结构"><a href="#HTTP-Proxy-反向代理核心结构" class="headerlink" title="HTTP Proxy 反向代理核心结构"></a>HTTP Proxy 反向代理核心结构</h3><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">HTTPProxy</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">// ReverseProxy为原始net.httputil封装的 http/https 代理对象</span></span><span class="line"><span style="color: #24292E">    hostMap </span><span style="color: #D73A49">map</span><span style="color: #24292E">[</span><span style="color: #D73A49">string</span><span style="color: #24292E">]</span><span style="color: #D73A49">*</span><span style="color: #24292E">httputil.ReverseProxy</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">// 负载均衡对象</span></span><span class="line"><span style="color: #24292E">    lb      balancer.Balancer</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">//用于 保证 alive map 读写安全，因为针对每个单独的节点都会起一个协程 用来读写alive map</span></span><span class="line"><span style="color: #24292E">    sync.RWMutex </span><span style="color: #6A737D">// protect alive</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">//用于保存 后端节点健康状态</span></span><span class="line"><span style="color: #24292E">    alive        </span><span style="color: #D73A49">map</span><span style="color: #24292E">[</span><span style="color: #D73A49">string</span><span style="color: #24292E">]</span><span style="color: #D73A49">bool</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h3 id="路由匹配"><a href="#路由匹配" class="headerlink" title="路由匹配"></a>路由匹配</h3><p>因为原生的 HTTP 没有办法动态设置 路由，该项目使用 mux 项目设置路由匹配，当然mux的性能 相较于前缀树差了很多，可以参考下 <a href="https://yushuanhsieh.github.io/post/2020-01-21-golang-router">https://yushuanhsieh.github.io/post/2020-01-21-golang-router</a> ，能够相差几十倍</p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">log</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">net/http</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">strconv</span><span style="color: #032F62">&quot;</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">github.com/gorilla/mux</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">github.com/zehuamama/balancer/proxy</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"></span><span class="line"><span style="color: #24292E">router </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> mux.</span><span style="color: #005CC5">NewRouter</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> _, l </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">range</span><span style="color: #24292E"> config.Location {</span></span><span class="line"><span style="color: #24292E">httpProxy, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> proxy.</span><span style="color: #005CC5">NewHTTPProxy</span><span style="color: #24292E">(l.ProxyPass, l.BalanceMode)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">log.</span><span style="color: #005CC5">Fatalf</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;create proxy error: </span><span style="color: #005CC5">%s</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">, err)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// start health check</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> config.HealthCheck {</span></span><span class="line"><span style="color: #24292E">httpProxy.</span><span style="color: #005CC5">HealthCheck</span><span style="color: #24292E">(config.HealthCheckInterval)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">router.</span><span style="color: #005CC5">Handle</span><span style="color: #24292E">(l.Pattern, httpProxy)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> config.MaxAllowed </span><span style="color: #D73A49">&gt;</span><span style="color: #24292E"> </span><span style="color: #005CC5">0</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">router.</span><span style="color: #005CC5">Use</span><span style="color: #24292E">(</span><span style="color: #005CC5">maxAllowedMiddleware</span><span style="color: #24292E">(config.MaxAllowed))</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h3 id="负载均衡策略"><a href="#负载均衡策略" class="headerlink" title="负载均衡策略"></a>负载均衡策略</h3><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">//负载均衡策略接口</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Balancer</span><span style="color: #24292E"> </span><span style="color: #D73A49">interface</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 新增节点</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">Add</span><span style="color: #24292E">(</span><span style="color: #D73A49">string</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 删除节点</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">Remove</span><span style="color: #24292E">(</span><span style="color: #D73A49">string</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 选择节点</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">Balance</span><span style="color: #24292E">(</span><span style="color: #D73A49">string</span><span style="color: #24292E">) (</span><span style="color: #D73A49">string</span><span style="color: #24292E">, </span><span style="color: #D73A49">error</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 节点新增连接</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">Inc</span><span style="color: #24292E">(</span><span style="color: #D73A49">string</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 节点结束连接</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">Done</span><span style="color: #24292E">(</span><span style="color: #D73A49">string</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// 定义了默认的 基础负载均衡策略，没有实现任何负载均衡算法，直接单纯实现 balance 接口，具体的算法实现不一定需要实现所有的接口，所以由 BaseBalancer 负责实现默认/空实现接口的功能函数，其他具体的实现只需要引用BaseBalancer即可</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">BaseBalancer</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">    sync.RWMutex</span></span><span class="line"><span style="color: #24292E">    hosts []</span><span style="color: #D73A49">string</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Add new host to the balancer</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (b </span><span style="color: #D73A49">*</span><span style="color: #24292E">BaseBalancer) </span><span style="color: #6F42C1">Add</span><span style="color: #24292E">(host </span><span style="color: #D73A49">string</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">b.</span><span style="color: #005CC5">Lock</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> b.</span><span style="color: #005CC5">Unlock</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> _, h </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">range</span><span style="color: #24292E"> b.hosts {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> h </span><span style="color: #D73A49">==</span><span style="color: #24292E"> host {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">b.hosts </span><span style="color: #D73A49">=</span><span style="color: #24292E"> </span><span style="color: #005CC5">append</span><span style="color: #24292E">(b.hosts, host)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Remove new host from the balancer</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (b </span><span style="color: #D73A49">*</span><span style="color: #24292E">BaseBalancer) </span><span style="color: #6F42C1">Remove</span><span style="color: #24292E">(host </span><span style="color: #D73A49">string</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">b.</span><span style="color: #005CC5">Lock</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> b.</span><span style="color: #005CC5">Unlock</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> i, h </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">range</span><span style="color: #24292E"> b.hosts {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> h </span><span style="color: #D73A49">==</span><span style="color: #24292E"> host {</span></span><span class="line"><span style="color: #24292E">b.hosts </span><span style="color: #D73A49">=</span><span style="color: #24292E"> </span><span style="color: #005CC5">append</span><span style="color: #24292E">(b.hosts[:i], b.hosts[i</span><span style="color: #D73A49">+</span><span style="color: #005CC5">1</span><span style="color: #24292E">:]</span><span style="color: #D73A49">...</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Balance selects a suitable host according</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (b </span><span style="color: #D73A49">*</span><span style="color: #24292E">BaseBalancer) </span><span style="color: #6F42C1">Balance</span><span style="color: #24292E">(key </span><span style="color: #D73A49">string</span><span style="color: #24292E">) (</span><span style="color: #D73A49">string</span><span style="color: #24292E">, </span><span style="color: #D73A49">error</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #032F62">&quot;&quot;</span><span style="color: #24292E">, </span><span style="color: #005CC5">nil</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Inc .</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (b </span><span style="color: #D73A49">*</span><span style="color: #24292E">BaseBalancer) </span><span style="color: #6F42C1">Inc</span><span style="color: #24292E">(_ </span><span style="color: #D73A49">string</span><span style="color: #24292E">) {}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Done .</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (b </span><span style="color: #D73A49">*</span><span style="color: #24292E">BaseBalancer) </span><span style="color: #6F42C1">Done</span><span style="color: #24292E">(_ </span><span style="color: #D73A49">string</span><span style="color: #24292E">) {}</span></span></code></pre></div></div></figure><p>接下来来看几个比较有代表性的负载均衡算法策略</p><h4 id="random"><a href="#random" class="headerlink" title="random"></a>random</h4><h5 id="策略描述"><a href="#策略描述" class="headerlink" title="策略描述"></a>策略描述</h5><h5 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h5><p>每次新连接，随机选择一个节点进行处理</p><h5 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h5><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// Balance selects a suitable host according</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (r </span><span style="color: #D73A49">*</span><span style="color: #24292E">Random) </span><span style="color: #6F42C1">Balance</span><span style="color: #24292E">(_ </span><span style="color: #D73A49">string</span><span style="color: #24292E">) (</span><span style="color: #D73A49">string</span><span style="color: #24292E">, </span><span style="color: #D73A49">error</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">r.</span><span style="color: #005CC5">RLock</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> r.</span><span style="color: #005CC5">RUnlock</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> </span><span style="color: #005CC5">len</span><span style="color: #24292E">(r.hosts) </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">0</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #032F62">&quot;&quot;</span><span style="color: #24292E">, NoHostError</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> r.hosts[r.rnd.</span><span style="color: #005CC5">Intn</span><span style="color: #24292E">(</span><span style="color: #005CC5">len</span><span style="color: #24292E">(r.hosts))], </span><span style="color: #005CC5">nil</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h4 id="ip-hash"><a href="#ip-hash" class="headerlink" title="ip-hash"></a>ip-hash</h4><h5 id="策略描述-1"><a href="#策略描述-1" class="headerlink" title="策略描述"></a>策略描述</h5><p>根据原ip选择对应的节点</p><h5 id="特点-1"><a href="#特点-1" class="headerlink" title="特点"></a>特点</h5><p>同一客户ip发起的连接在健康检查都ok的情况下，会被分配至同一节点</p><h5 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h5><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// Balance selects a suitable host according</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (r </span><span style="color: #D73A49">*</span><span style="color: #24292E">IPHash) </span><span style="color: #6F42C1">Balance</span><span style="color: #24292E">(key </span><span style="color: #D73A49">string</span><span style="color: #24292E">) (</span><span style="color: #D73A49">string</span><span style="color: #24292E">, </span><span style="color: #D73A49">error</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">    r.</span><span style="color: #005CC5">RLock</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">defer</span><span style="color: #24292E"> r.</span><span style="color: #005CC5">RUnlock</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">if</span><span style="color: #24292E"> </span><span style="color: #005CC5">len</span><span style="color: #24292E">(r.hosts) </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">0</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">       </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #032F62">&quot;&quot;</span><span style="color: #24292E">, NoHostError</span></span><span class="line"><span style="color: #24292E">    }</span></span><span class="line"><span style="color: #24292E">    value </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> crc32.</span><span style="color: #005CC5">ChecksumIEEE</span><span style="color: #24292E">([]</span><span style="color: #005CC5">byte</span><span style="color: #24292E">(key)) </span><span style="color: #D73A49">%</span><span style="color: #24292E"> </span><span style="color: #005CC5">uint32</span><span style="color: #24292E">(</span><span style="color: #005CC5">len</span><span style="color: #24292E">(r.hosts))</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">return</span><span style="color: #24292E"> r.hosts[value], </span><span style="color: #005CC5">nil</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h4 id="least-load"><a href="#least-load" class="headerlink" title="least load"></a>least load</h4><h5 id="策略描述-2"><a href="#策略描述-2" class="headerlink" title="策略描述"></a>策略描述</h5><p>选择当前处理连接最少的节点</p><h5 id="特点-2"><a href="#特点-2" class="headerlink" title="特点"></a>特点</h5><p>可以根据后端节点的连接处理数量，合理的将新连接配置至后端节点</p><h5 id="代码-2"><a href="#代码-2" class="headerlink" title="代码"></a>代码</h5><p>使用了 FibonacciHeap  （斐波那契堆）可以快速排序，获取当前连接数最少的节点，开源库 <a href="https://github.com/starwander/GoFibonacciHeap">https://github.com/starwander/GoFibonacciHeap</a></p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"></span><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">sync</span><span style="color: #032F62">&quot;</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #6F42C1">fibHeap</span><span style="color: #24292E"> </span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">github.com/starwander/GoFibonacciHeap</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Tag .</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (h </span><span style="color: #D73A49">*</span><span style="color: #24292E">host) </span><span style="color: #6F42C1">Tag</span><span style="color: #24292E">() </span><span style="color: #D73A49">interface</span><span style="color: #24292E">{} { </span><span style="color: #D73A49">return</span><span style="color: #24292E"> h.name }</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Key .</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (h </span><span style="color: #D73A49">*</span><span style="color: #24292E">host) </span><span style="color: #6F42C1">Key</span><span style="color: #24292E">() </span><span style="color: #D73A49">float64</span><span style="color: #24292E"> { </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">float64</span><span style="color: #24292E">(h.load) }</span></span></code></pre></div></div></figure><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// Done refers to the number of connections to the server `-1`</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (l </span><span style="color: #D73A49">*</span><span style="color: #24292E">LeastLoad) </span><span style="color: #6F42C1">Done</span><span style="color: #24292E">(hostName </span><span style="color: #D73A49">string</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">    l.</span><span style="color: #005CC5">Lock</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">defer</span><span style="color: #24292E"> l.</span><span style="color: #005CC5">Unlock</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">if</span><span style="color: #24292E"> ok </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> l.heap.</span><span style="color: #005CC5">GetValue</span><span style="color: #24292E">(hostName); ok </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">       </span><span style="color: #D73A49">return</span></span><span class="line"><span style="color: #24292E">    }</span></span><span class="line"><span style="color: #24292E">    h </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> l.heap.</span><span style="color: #005CC5">GetValue</span><span style="color: #24292E">(hostName)</span></span><span class="line"><span style="color: #24292E">    h.(</span><span style="color: #D73A49">*</span><span style="color: #24292E">host).load</span><span style="color: #D73A49">--</span></span><span class="line"><span style="color: #24292E">    _ </span><span style="color: #D73A49">=</span><span style="color: #24292E"> l.heap.</span><span style="color: #005CC5">DecreaseKeyValue</span><span style="color: #24292E">(h)</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h3 id="健康检查代码"><a href="#健康检查代码" class="headerlink" title="健康检查代码"></a>健康检查代码</h3><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (h </span><span style="color: #D73A49">*</span><span style="color: #24292E">HTTPProxy) </span><span style="color: #6F42C1">healthCheck</span><span style="color: #24292E">(host </span><span style="color: #D73A49">string</span><span style="color: #24292E">, interval </span><span style="color: #D73A49">uint</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">ticker </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> time.</span><span style="color: #005CC5">NewTicker</span><span style="color: #24292E">(time.</span><span style="color: #005CC5">Duration</span><span style="color: #24292E">(interval) </span><span style="color: #D73A49">*</span><span style="color: #24292E"> time.Second)</span></span><span class="line"><span style="color: #24292E"></span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">/*</span></span><span class="line"><span style="color: #6A737D">  当然这里不需要写这个，但是养成良好习惯，及时回收资源，防止泄露</span></span><span class="line"><span style="color: #6A737D">  defer func(){</span></span><span class="line"><span style="color: #6A737D">    ticker.Stop()  </span></span><span class="line"><span style="color: #6A737D">  }()</span></span><span class="line"><span style="color: #6A737D">  */</span></span><span class="line"><span style="color: #24292E">  </span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">//定时执行健康检查探活逻辑</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> </span><span style="color: #D73A49">range</span><span style="color: #24292E"> ticker.C {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> </span><span style="color: #D73A49">!</span><span style="color: #005CC5">IsBackendAlive</span><span style="color: #24292E">(host) </span><span style="color: #D73A49">&amp;&amp;</span><span style="color: #24292E"> h.</span><span style="color: #005CC5">ReadAlive</span><span style="color: #24292E">(host) {</span></span><span class="line"><span style="color: #24292E">log.</span><span style="color: #005CC5">Printf</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;Site unreachable, remove </span><span style="color: #005CC5">%s</span><span style="color: #032F62"> from load balancer.&quot;</span><span style="color: #24292E">, host)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">h.</span><span style="color: #005CC5">SetAlive</span><span style="color: #24292E">(host, </span><span style="color: #005CC5">false</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">h.lb.</span><span style="color: #005CC5">Remove</span><span style="color: #24292E">(host)</span></span><span class="line"><span style="color: #24292E">} </span><span style="color: #D73A49">else</span><span style="color: #24292E"> </span><span style="color: #D73A49">if</span><span style="color: #24292E"> </span><span style="color: #005CC5">IsBackendAlive</span><span style="color: #24292E">(host) </span><span style="color: #D73A49">&amp;&amp;</span><span style="color: #24292E"> </span><span style="color: #D73A49">!</span><span style="color: #24292E">h.</span><span style="color: #005CC5">ReadAlive</span><span style="color: #24292E">(host) {</span></span><span class="line"><span style="color: #24292E">log.</span><span style="color: #005CC5">Printf</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;Site reachable, add </span><span style="color: #005CC5">%s</span><span style="color: #032F62"> to load balancer.&quot;</span><span style="color: #24292E">, host)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">h.</span><span style="color: #005CC5">SetAlive</span><span style="color: #24292E">(host, </span><span style="color: #005CC5">true</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">h.lb.</span><span style="color: #005CC5">Add</span><span style="color: #24292E">(host)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><p>可以看到就是针对节点的端口进行tcp探活</p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// IsBackendAlive Attempt to establish a tcp connection to determine whether the site is alive</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">IsBackendAlive</span><span style="color: #24292E">(host </span><span style="color: #D73A49">string</span><span style="color: #24292E">) </span><span style="color: #D73A49">bool</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">addr, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> net.</span><span style="color: #005CC5">ResolveTCPAddr</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;tcp&quot;</span><span style="color: #24292E">, host)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">resolveAddr </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> fmt.</span><span style="color: #005CC5">Sprintf</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;</span><span style="color: #005CC5">%s</span><span style="color: #032F62">:</span><span style="color: #005CC5">%d</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">, addr.IP, addr.Port)</span></span><span class="line"><span style="color: #24292E">conn, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> net.</span><span style="color: #005CC5">DialTimeout</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;tcp&quot;</span><span style="color: #24292E">, resolveAddr, ConnectionTimeout)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">_ </span><span style="color: #D73A49">=</span><span style="color: #24292E"> conn.</span><span style="color: #005CC5">Close</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">true</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><p>可以对比下kubernetes的TCP探活代码对比下</p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">//kubernetes tcp端口探活</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">probe</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">net</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">syscall</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// ProbeDialer returns a dialer optimized for probes to avoid lingering sockets on TIME-WAIT state.</span></span><span class="line"><span style="color: #6A737D">// The dialer reduces the TIME-WAIT period to 1 seconds instead of the OS default of 60 seconds.</span></span><span class="line"><span style="color: #6A737D">// Using 1 second instead of 0 because SO_LINGER socket option to 0 causes pending data to be</span></span><span class="line"><span style="color: #6A737D">// discarded and the connection to be aborted with an RST rather than for the pending data to be</span></span><span class="line"><span style="color: #6A737D">// transmitted and the connection closed cleanly with a FIN.</span></span><span class="line"><span style="color: #6A737D">// Ref: https://issues.k8s.io/89898</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">ProbeDialer</span><span style="color: #24292E">() </span><span style="color: #D73A49">*</span><span style="color: #24292E">net.Dialer {</span></span><span class="line"><span style="color: #24292E">dialer </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">net.Dialer{</span></span><span class="line"><span style="color: #24292E">Control: </span><span style="color: #D73A49">func</span><span style="color: #24292E">(network, address </span><span style="color: #D73A49">string</span><span style="color: #24292E">, c syscall.RawConn) </span><span style="color: #D73A49">error</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> c.</span><span style="color: #005CC5">Control</span><span style="color: #24292E">(</span><span style="color: #D73A49">func</span><span style="color: #24292E">(fd </span><span style="color: #D73A49">uintptr</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">syscall.</span><span style="color: #005CC5">SetsockoptLinger</span><span style="color: #24292E">(</span><span style="color: #005CC5">int</span><span style="color: #24292E">(fd), syscall.SOL_SOCKET, syscall.SO_LINGER, </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">syscall.Linger{Onoff: </span><span style="color: #005CC5">1</span><span style="color: #24292E">, Linger: </span><span style="color: #005CC5">1</span><span style="color: #24292E">})</span></span><span class="line"><span style="color: #24292E">})</span></span><span class="line"><span style="color: #24292E">},</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> dialer</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><p>可以看到 kubernetes 对于http&#x2F; tcp 的健康检查进行了特殊处理，开启了 Linger 特性，并设置超时为 1s，在超时后会直接reset连接，相较于默认的timewait是60s，当节点上的kubelet会频繁进行健康检查，很可能会导致很多资源  socket, ephemeral port, conntrack entry 等被占用，这样的话，会导致影响其他请求，相关issue 可以参考</p><p><a href="https://github.com/kubernetes/kubernetes/pull/115143">https://github.com/kubernetes/kubernetes/pull/115143</a></p><p><em><strong>再次感概下 细节决定成败</strong></em></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ol><li>一个基本的负载均衡由 负载均衡算法 和 健康检查组成</li><li>常见的负载均衡算法有 轮询，iphash，least connect 以及如何编写</li><li>一个常见的tcp健康检查探针如何编写，已经如何优化http&#x2F;tcp探测</li><li>linger socket选项 开启后，可以设置 socket.close 的超时时间，如果超过时间，不会进入timewait阶段，会直接reset掉连接</li><li>斐波那契堆如何在项目中使用</li><li>常见的路由框架与性能比较</li></ol>]]></content>
    
    
    <summary type="html">如何编写一个最简http/http负载均衡</summary>
    
    
    
    <category term="IT" scheme="https://freelifeblog.top/categories/IT/"/>
    
    
    <category term="codeview" scheme="https://freelifeblog.top/tags/codeview/"/>
    
    <category term="codeview101" scheme="https://freelifeblog.top/tags/codeview101/"/>
    
    <category term="go" scheme="https://freelifeblog.top/tags/go/"/>
    
  </entry>
  
  <entry>
    <title>kratos-middleware</title>
    <link href="https://freelifeblog.top/2024/07/31/kratos-middleware/"/>
    <id>https://freelifeblog.top/2024/07/31/kratos-middleware/</id>
    <published>2024-07-31T02:03:22.000Z</published>
    <updated>2024-08-15T01:33:36.233Z</updated>
    
    <content type="html"><![CDATA[<h2 id="快速图解"><a href="#快速图解" class="headerlink" title="快速图解"></a>快速图解</h2><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/kratos_middleware.png?token=ALGHJOXVHT5SIGHGVLLATOTGVH3HC" alt="kratos_middleware"></p><h2 id="简化版代码"><a href="#简化版代码" class="headerlink" title="简化版代码"></a>简化版代码</h2><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Handler</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">()</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">var</span><span style="color: #24292E"> handler Handler </span><span style="color: #D73A49">=</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">println</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;ok&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Middleware</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(Handler) Handler</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Chain</span><span style="color: #24292E">(m </span><span style="color: #D73A49">...</span><span style="color: #24292E">Middleware) Middleware {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(next Handler) Handler {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> i </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">len</span><span style="color: #24292E">(m) </span><span style="color: #D73A49">-</span><span style="color: #24292E"> </span><span style="color: #005CC5">1</span><span style="color: #24292E">; i </span><span style="color: #D73A49">&gt;=</span><span style="color: #24292E"> </span><span style="color: #005CC5">0</span><span style="color: #24292E">; i</span><span style="color: #D73A49">--</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">next </span><span style="color: #D73A49">=</span><span style="color: #24292E"> m[i](next)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> next</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">middleware1</span><span style="color: #24292E">(next Handler) Handler {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">println</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;middleware1&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">println</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;middleware1f&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">next</span><span style="color: #24292E">()</span></span><span class="line"></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">middleware2</span><span style="color: #24292E">(next Handler) Handler {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">println</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;middleware2&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">println</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;middleware2f&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">next</span><span style="color: #24292E">()</span></span><span class="line"></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">middleware3</span><span style="color: #24292E">(next Handler) Handler {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">println</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;middleware3&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">println</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;middleware3f&quot;</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">next</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E">ms </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> []Middleware{middleware1, middleware2, middleware3}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #005CC5">Chain</span><span style="color: #24292E">(ms</span><span style="color: #D73A49">...</span><span style="color: #24292E">)(handler)()</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">/*输出</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">Chain(ms...)(handler)() 其实对应的形式就是 ms[0](ms[1](ms[2](handler)))() </span></span><span class="line"><span style="color: #6A737D">这样是不是更好理解了</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">middleware1</span></span><span class="line"><span style="color: #6A737D">middleware2</span></span><span class="line"><span style="color: #6A737D">middleware3</span></span><span class="line"><span style="color: #6A737D">ok</span></span><span class="line"><span style="color: #6A737D">middleware3f</span></span><span class="line"><span style="color: #6A737D">middleware2f</span></span><span class="line"><span style="color: #6A737D">middleware1f</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">*/</span></span></code></pre></div></div></figure><h2 id="代码赏析"><a href="#代码赏析" class="headerlink" title="代码赏析"></a>代码赏析</h2><h3 id="Middleware-处理接口定义"><a href="#Middleware-处理接口定义" class="headerlink" title="Middleware 处理接口定义"></a>Middleware 处理接口定义</h3><p>看完上面在看kratos中如何设计的 Middleware 使用 <code>type Handler func(ctx context.Context, req interface&#123;&#125;) (interface&#123;&#125;, error)</code> 用于简化复杂签名</p><p>Middleware 的定义是 <code>type func(Handler) Handler</code> 为什么传入一个Handler 在将 Handler 返回呢，其实看到真实的 middleware 的实例就会清楚，传入的 Handler 可以说是 后置的Handler，返回的Handler 是包含了 后置 Handler  与当前 Handler 逻辑新的Handler。</p><p>Chain 函数主要用于Handler循环迭代调用，返回最终 Middleware，并将最终调用业务逻辑的handler 传入返回的 Middleware</p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">middleware</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">context</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Handler defines the handler invoked by Middleware.</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Handler</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(ctx context.Context, req </span><span style="color: #D73A49">interface</span><span style="color: #24292E">{}) (</span><span style="color: #D73A49">interface</span><span style="color: #24292E">{}, </span><span style="color: #D73A49">error</span><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Middleware is HTTP/gRPC transport middleware.</span></span><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Middleware</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(Handler) Handler</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Chain returns a Middleware that specifies the chained handler for endpoint.</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Chain</span><span style="color: #24292E">(m </span><span style="color: #D73A49">...</span><span style="color: #24292E">Middleware) Middleware {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(next Handler) Handler {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> i </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">len</span><span style="color: #24292E">(m) </span><span style="color: #D73A49">-</span><span style="color: #24292E"> </span><span style="color: #005CC5">1</span><span style="color: #24292E">; i </span><span style="color: #D73A49">&gt;=</span><span style="color: #24292E"> </span><span style="color: #005CC5">0</span><span style="color: #24292E">; i</span><span style="color: #D73A49">--</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">next </span><span style="color: #D73A49">=</span><span style="color: #24292E"> m[i](next)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> next</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h3 id="Middleware-调用"><a href="#Middleware-调用" class="headerlink" title="Middleware 调用"></a>Middleware 调用</h3><p>kratos 中 middleware 的调用在 transport&#x2F;http&#x2F;client.go 处</p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (client </span><span style="color: #D73A49">*</span><span style="color: #24292E">Client) </span><span style="color: #6F42C1">invoke</span><span style="color: #24292E">(ctx context.Context, req </span><span style="color: #D73A49">*</span><span style="color: #24292E">http.Request, args </span><span style="color: #D73A49">interface</span><span style="color: #24292E">{}, reply </span><span style="color: #D73A49">interface</span><span style="color: #24292E">{}, c callInfo, opts </span><span style="color: #D73A49">...</span><span style="color: #24292E">CallOption) </span><span style="color: #D73A49">error</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">//kratos 定义好的调用业务逻辑的handler</span></span><span class="line"><span style="color: #24292E">h </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(ctx context.Context, in </span><span style="color: #D73A49">interface</span><span style="color: #24292E">{}) (</span><span style="color: #D73A49">interface</span><span style="color: #24292E">{}, </span><span style="color: #D73A49">error</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">res, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> client.</span><span style="color: #005CC5">do</span><span style="color: #24292E">(req.</span><span style="color: #005CC5">WithContext</span><span style="color: #24292E">(ctx))</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> res </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">cs </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> csAttempt{res: res}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> _, o </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">range</span><span style="color: #24292E"> opts {</span></span><span class="line"><span style="color: #24292E">o.</span><span style="color: #005CC5">after</span><span style="color: #24292E">(</span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">c, </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">cs)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E">, err</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> res.Body.</span><span style="color: #005CC5">Close</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> client.opts.</span><span style="color: #005CC5">decoder</span><span style="color: #24292E">(ctx, res, reply); err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E">, err</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> reply, </span><span style="color: #005CC5">nil</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">var</span><span style="color: #24292E"> p selector.Peer</span></span><span class="line"><span style="color: #24292E">ctx </span><span style="color: #D73A49">=</span><span style="color: #24292E"> selector.</span><span style="color: #005CC5">NewPeerContext</span><span style="color: #24292E">(ctx, </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">p)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> </span><span style="color: #005CC5">len</span><span style="color: #24292E">(client.opts.middleware) </span><span style="color: #D73A49">&gt;</span><span style="color: #24292E"> </span><span style="color: #005CC5">0</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">// kratos 定义好的 调用业务逻辑的handler 传入，迭代调用 后的middleware</span></span><span class="line"><span style="color: #24292E">h </span><span style="color: #D73A49">=</span><span style="color: #24292E"> middleware.</span><span style="color: #005CC5">Chain</span><span style="color: #24292E">(client.opts.middleware</span><span style="color: #D73A49">...</span><span style="color: #24292E">)(h)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">// 真实调用 包含了kratos定义好的调用业务逻辑的handler 迭代封装后的 middleware</span></span><span class="line"><span style="color: #24292E">_, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">h</span><span style="color: #24292E">(ctx, args)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> err</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h3 id="Middleware-实例"><a href="#Middleware-实例" class="headerlink" title="Middleware 实例"></a>Middleware 实例</h3><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">context</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">github.com/go-kratos/kratos/v2/middleware</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">github.com/go-kratos/kratos/v2/transport</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Middleware1</span><span style="color: #24292E">() middleware.Middleware {</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(handler middleware.Handler) middleware.Handler {</span></span><span class="line"><span style="color: #24292E">        </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">(ctx context.Context, req </span><span style="color: #D73A49">interface</span><span style="color: #24292E">{}) (reply </span><span style="color: #D73A49">interface</span><span style="color: #24292E">{}, err </span><span style="color: #D73A49">error</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">            </span><span style="color: #D73A49">if</span><span style="color: #24292E"> tr, ok </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> transport.</span><span style="color: #005CC5">FromServerContext</span><span style="color: #24292E">(ctx); ok {</span></span><span class="line"><span style="color: #24292E">                </span><span style="color: #6A737D">// Do something on entering</span></span><span class="line"><span style="color: #24292E">                </span><span style="color: #D73A49">defer</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E">                </span><span style="color: #6A737D">// Do something on exiting</span></span><span class="line"><span style="color: #24292E">                 }()</span></span><span class="line"><span style="color: #24292E">            }</span></span><span class="line"><span style="color: #24292E">            </span><span style="color: #6A737D">// 在最后需要手动调用 传入的 handler 形成链式调用</span></span><span class="line"><span style="color: #24292E">            </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">handler</span><span style="color: #24292E">(ctx, req)</span></span><span class="line"><span style="color: #24292E">        }</span></span><span class="line"><span style="color: #24292E">    }</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>现在在回过来看middreware 的设计，其实是需要构造一个 链式迭代处理的功能函数，同时配合 golang 的 context 将 context 传给 内层调用函数，保证对context 做的修改可以一直迭代持续下去，最终经过层层middleware处理的 context，传给 Request 的 context ，给业务处理逻辑使用。</p>]]></content>
    
    
    <summary type="html">kratos middleware原理</summary>
    
    
    
    <category term="IT" scheme="https://freelifeblog.top/categories/IT/"/>
    
    
    <category term="codeview" scheme="https://freelifeblog.top/tags/codeview/"/>
    
    <category term="go" scheme="https://freelifeblog.top/tags/go/"/>
    
    <category term="kratos" scheme="https://freelifeblog.top/tags/kratos/"/>
    
  </entry>
  
  <entry>
    <title>git-plus工具推荐</title>
    <link href="https://freelifeblog.top/2024/07/01/git-plus%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90/"/>
    <id>https://freelifeblog.top/2024/07/01/git-plus%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90/</id>
    <published>2024-07-01T14:33:55.000Z</published>
    <updated>2024-08-15T01:33:07.529Z</updated>
    
    <content type="html"><![CDATA[<h2 id="快速图解"><a href="#快速图解" class="headerlink" title="快速图解"></a>快速图解</h2><p><img src="https://ooo.0x0.ooo/2024/07/01/OPyA1P.png" alt="git-plus"></p><h2 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h2><p>当你需要提交代码github，需要使用自己的私人账号，同时在公司git仓库域下又需要使用工号和公司邮箱提交代码，正常做法是是将项目clone下来后，执行 <code>git config --local user.name foo</code> <code>git config --local user.email bar@example.com</code>，当项目多的时候，每个项目都需要单独设置，比较繁琐，git-plus就可以很好的解决这个问题</p><h2 id="安装方法"><a href="#安装方法" class="headerlink" title="安装方法"></a>安装方法</h2><p>本地安装go</p><p>执行</p><p><code>go install github.com/OnlyPiglet/git-plus@latest</code>，在go的安装目录的bin目录中就会有git-plus二进制文件，加入到 path 环境变量即可使用</p><h2 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h2><p>如希望github.com 使用 foo 和 <a href="mailto:&#102;&#111;&#x6f;&#x40;&#103;&#x69;&#116;&#x68;&#117;&#98;&#x2e;&#x63;&#111;&#x6d;">&#102;&#111;&#x6f;&#x40;&#103;&#x69;&#116;&#x68;&#117;&#98;&#x2e;&#x63;&#111;&#x6d;</a> email进行提交，gitlab.com 希望使用 bar 和 <a href="mailto:&#98;&#x61;&#114;&#64;&#103;&#x69;&#116;&#108;&#x61;&#98;&#x2e;&#99;&#111;&#x6d;">&#98;&#x61;&#114;&#64;&#103;&#x69;&#116;&#108;&#x61;&#98;&#x2e;&#99;&#111;&#x6d;</a> 进行使用，则可使用git-plus 进行初始设置</p><figure class="shiki shell"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6F42C1">git-plus</span><span style="color: #24292E"> </span><span style="color: #032F62">adduser</span><span style="color: #24292E"> </span><span style="color: #032F62">github.com</span><span style="color: #24292E"> </span><span style="color: #032F62">foo</span><span style="color: #24292E"> </span><span style="color: #032F62">foo@github.com</span></span><span class="line"><span style="color: #6F42C1">git-plus</span><span style="color: #24292E"> </span><span style="color: #032F62">adduser</span><span style="color: #24292E"> </span><span style="color: #032F62">gitlab.com</span><span style="color: #24292E"> </span><span style="color: #032F62">bar</span><span style="color: #24292E"> </span><span style="color: #032F62">bar@gitlab.com</span></span></code></pre></div></div></figure><p>执行 git-plus listuser github.com 可以看到 github.com 域下配置的用户，使用git-plus clone 项目地址与git使用方法一样，即可按照域名自动设置对应的 用户名 和 邮箱，省去手动设置的麻烦</p><h2 id="使用效果"><a href="#使用效果" class="headerlink" title="使用效果"></a>使用效果</h2><p><img src="https://ooo.0x0.ooo/2024/07/01/OPyew6.png" alt="效果"></p>]]></content>
    
    
    <summary type="html">如何git-plus自动切换git commit账号</summary>
    
    
    
    <category term="IT" scheme="https://freelifeblog.top/categories/IT/"/>
    
    
    <category term="tool" scheme="https://freelifeblog.top/tags/tool/"/>
    
  </entry>
  
  <entry>
    <title>tproxy源码</title>
    <link href="https://freelifeblog.top/2024/06/21/tproxy%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/"/>
    <id>https://freelifeblog.top/2024/06/21/tproxy%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/</id>
    <published>2024-06-21T13:23:49.000Z</published>
    <updated>2024-11-08T07:05:26.046Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p><a href="https://github.com/kevwan/tproxy">https://github.com/kevwan/tproxy</a></p></blockquote><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>tproxy 是一款代理支持多种协议的代理程序，并监控查看协议内容，收集常规的通信指标如tcp连接数等，支持常见的 http2，mqtt，mongo，grpc，redis</p><h2 id="快速图解"><a href="#快速图解" class="headerlink" title="快速图解"></a>快速图解</h2><p><img src="https://raw.githubusercontent.com/OnlyPiglet/blog_pict/main/tproxy.drawio.png" alt="tproxy.drawio"></p><h2 id="代码分析"><a href="#代码分析" class="headerlink" title="代码分析"></a>代码分析</h2><h3 id="启动"><a href="#启动" class="headerlink" title="启动"></a>启动</h3><p>直接从主入口函数main开始看起</p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">package</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">import</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">flag</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">fmt</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">os</span><span style="color: #032F62">&quot;</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #032F62">&quot;</span><span style="color: #6F42C1">github.com/fatih/color</span><span style="color: #032F62">&quot;</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">var</span><span style="color: #24292E"> settings Settings</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">main</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">//解析命令行参数，并校验参数</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">//启动服务</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">startListener</span><span style="color: #24292E">(); err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">fmt.</span><span style="color: #005CC5">Fprintln</span><span style="color: #24292E">(os.Stderr, color.</span><span style="color: #005CC5">HiRedString</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;[x] Failed to start listener: </span><span style="color: #005CC5">%v</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">, err))</span></span><span class="line"><span style="color: #24292E">os.</span><span style="color: #005CC5">Exit</span><span style="color: #24292E">(</span><span style="color: #005CC5">1</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><p>这其中关于flag包较为完整的用法还是值得借鉴的</p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">var</span><span style="color: #24292E"> (</span></span><span class="line"><span style="color: #24292E"> </span><span style="color: #6A737D">//定义 flag 变量</span></span><span class="line"><span style="color: #24292E">)</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">//当输入flag数量不正确时将默认的启动参数与用法进行打印输出，直接返回，提醒用户缺少参数输入</span></span><span class="line"><span style="color: #D73A49">if</span><span style="color: #24292E"> </span><span style="color: #005CC5">len</span><span style="color: #24292E">(os.Args) </span><span style="color: #D73A49">&lt;</span><span style="color: #24292E"> </span><span style="color: #005CC5">1</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">  flag.</span><span style="color: #005CC5">Usage</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">return</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #24292E">flag.</span><span style="color: #005CC5">Parse</span><span style="color: #24292E">()</span></span><span class="line"></span><span class="line"><span style="color: #D73A49">if</span><span style="color: #24292E"> </span><span style="color: #005CC5">len</span><span style="color: #24292E">(settings.Remote) </span><span style="color: #D73A49">==</span><span style="color: #24292E"> </span><span style="color: #005CC5">0</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">  fmt.</span><span style="color: #005CC5">Fprintln</span><span style="color: #24292E">(os.Stderr, color.</span><span style="color: #005CC5">HiRedString</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;[x] Remote target required&quot;</span><span style="color: #24292E">))</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">//在出错的时候将默认的启动参数与用法进行打印输出，并错误退出，提示用户虽然提供了参数，但是内容不符合要求</span></span><span class="line"><span style="color: #24292E">  flag.</span><span style="color: #005CC5">PrintDefaults</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">  os.</span><span style="color: #005CC5">Exit</span><span style="color: #24292E">(</span><span style="color: #005CC5">1</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h2 id="startListener-建立连接对，并启动连接监视器"><a href="#startListener-建立连接对，并启动连接监视器" class="headerlink" title="startListener 建立连接对，并启动连接监视器"></a>startListener 建立连接对，并启动连接监视器</h2><h3 id="监视器"><a href="#监视器" class="headerlink" title="监视器"></a>监视器</h3><p>主要记录tcp连接信息如下</p><ol><li>总计处理过的连接数</li><li>当前并发连接数</li><li>历史最大并发连接数</li><li>连接开始创建的时间</li><li>连接存活最长的时间</li></ol><h3 id="处理客户端请求"><a href="#处理客户端请求" class="headerlink" title="处理客户端请求"></a>处理客户端请求</h3><p>接收客户端链接，建立 客户端&lt;-&gt;tproxy&lt;-&gt;服务端 的连接对，并设置 MultiWriter，MultiReader，将客户端数据与服务端数据发送至io.Pipe 的内存管道，提供给Dump对象，进行打印，同时对 客户端连接 与 服务端连接包装上限流和延迟发送的功能</p><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">startListener</span><span style="color: #24292E">() </span><span style="color: #D73A49">error</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">//创建并启动tcp连接计数器</span></span><span class="line"><span style="color: #24292E">stat </span><span style="color: #D73A49">=</span><span style="color: #24292E"> </span><span style="color: #005CC5">NewStater</span><span style="color: #24292E">(</span><span style="color: #005CC5">NewConnCounter</span><span style="color: #24292E">(), </span><span style="color: #005CC5">NewStatPrinter</span><span style="color: #24292E">(statInterval))</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> stat.</span><span style="color: #005CC5">Start</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">  </span></span><span class="line"><span style="color: #24292E">conn, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> net.</span><span style="color: #005CC5">Listen</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;tcp&quot;</span><span style="color: #24292E">, fmt.</span><span style="color: #005CC5">Sprintf</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;</span><span style="color: #005CC5">%s</span><span style="color: #032F62">:</span><span style="color: #005CC5">%d</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">, settings.LocalHost, settings.LocalPort))</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> fmt.</span><span style="color: #005CC5">Errorf</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;failed to start listener: </span><span style="color: #005CC5">%w</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">, err)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">//defer 紧跟在conn成功创建之后，很好的习惯</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> conn.</span><span style="color: #005CC5">Close</span><span style="color: #24292E">()</span></span><span class="line"></span><span class="line"><span style="color: #24292E">display.</span><span style="color: #005CC5">PrintfWithTime</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;Listening on </span><span style="color: #005CC5">%s</span><span style="color: #032F62">...</span><span style="color: #005CC5">\n</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">, conn.</span><span style="color: #005CC5">Addr</span><span style="color: #24292E">().</span><span style="color: #005CC5">String</span><span style="color: #24292E">())</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">var</span><span style="color: #24292E"> connIndex </span><span style="color: #D73A49">int</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">// accept 客户端连接</span></span><span class="line"><span style="color: #24292E">cliConn, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> conn.</span><span style="color: #005CC5">Accept</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">return</span><span style="color: #24292E"> fmt.</span><span style="color: #005CC5">Errorf</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;server: accept: </span><span style="color: #005CC5">%w</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">, err)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #24292E">connIndex</span><span style="color: #D73A49">++</span></span><span class="line"><span style="color: #24292E">display.</span><span style="color: #005CC5">PrintlnWithTime</span><span style="color: #24292E">(color.</span><span style="color: #005CC5">HiGreenString</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;[</span><span style="color: #005CC5">%d</span><span style="color: #032F62">] Accepted from: </span><span style="color: #005CC5">%s</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">,</span></span><span class="line"><span style="color: #24292E">connIndex, cliConn.</span><span style="color: #005CC5">RemoteAddr</span><span style="color: #24292E">()))</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">//创建连接对，并使用协程处理连接数据    </span></span><span class="line"><span style="color: #24292E">pconn </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">NewPairedConnection</span><span style="color: #24292E">(connIndex, cliConn)</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> pconn.</span><span style="color: #005CC5">process</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h3 id="处理Crtl-C信号的代码"><a href="#处理Crtl-C信号的代码" class="headerlink" title="处理Crtl+C信号的代码"></a>处理Crtl+C信号的代码</h3><p>接收 Crtl+C 信号，优雅的停止信号 挺有意思的</p><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">go</span><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">//设置接收系统 signal Ctrl+C / kill -15 信号</span></span><span class="line"><span style="color: #24292E">c </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #005CC5">make</span><span style="color: #24292E">(</span><span style="color: #D73A49">chan</span><span style="color: #24292E"> os.Signal, </span><span style="color: #005CC5">1</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">signal.</span><span style="color: #005CC5">Notify</span><span style="color: #24292E">(c, os.Interrupt, syscall.SIGTERM)</span></span><span class="line"></span><span class="line"><span style="color: #24292E">        </span><span style="color: #6A737D">// 使用 range chan 阻塞等待 信号进入 ，个人感觉 直接 sig &lt;- c 可能更好</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">for</span><span style="color: #24292E"> sig </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> </span><span style="color: #D73A49">range</span><span style="color: #24292E"> c {</span></span><span class="line"><span style="color: #24292E">            </span><span style="color: #6A737D">// 停止接收 signal 信号</span></span><span class="line"><span style="color: #24292E">signal.</span><span style="color: #005CC5">Stop</span><span style="color: #24292E">(c)</span></span><span class="line"><span style="color: #24292E">          </span></span><span class="line"><span style="color: #24292E">            </span><span style="color: #6A737D">//执行程序停止前的处理</span></span><span class="line"><span style="color: #24292E">stat.</span><span style="color: #005CC5">Stop</span><span style="color: #24292E">()</span></span><span class="line"></span><span class="line"><span style="color: #24292E">            </span><span style="color: #6A737D">//找到当前进程</span></span><span class="line"><span style="color: #24292E">p, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> os.</span><span style="color: #005CC5">FindProcess</span><span style="color: #24292E">(syscall.</span><span style="color: #005CC5">Getpid</span><span style="color: #24292E">())</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">fmt.</span><span style="color: #005CC5">Println</span><span style="color: #24292E">(err)</span></span><span class="line"><span style="color: #24292E">os.</span><span style="color: #005CC5">Exit</span><span style="color: #24292E">(</span><span style="color: #005CC5">0</span><span style="color: #24292E">)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"></span><span class="line"><span style="color: #24292E">          </span><span style="color: #6A737D">//将signal信号 重新发送给当前进程 用以关闭当前程序，不用担心 再次接收到 signal信号，毕竟已经使用 signal.Stop(c) 停止接收signal信号了</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> p.</span><span style="color: #005CC5">Signal</span><span style="color: #24292E">(sig); err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">fmt.</span><span style="color: #005CC5">Println</span><span style="color: #24292E">(err)</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}</span></span><span class="line"><span style="color: #24292E">}()</span></span></code></pre></div></div></figure><h2 id="连接对"><a href="#连接对" class="headerlink" title="连接对"></a>连接对</h2><p>接下来看项目中最重要的代理功能的实现</p><h4 id="连接对结构"><a href="#连接对结构" class="headerlink" title="连接对结构"></a>连接对结构</h4><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">type</span><span style="color: #24292E"> </span><span style="color: #6F42C1">PairedConnection</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">     </span><span style="color: #6A737D">//connection id</span></span><span class="line"><span style="color: #24292E">      id </span><span style="color: #D73A49">int</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #6A737D">// 下游服务连接</span></span><span class="line"><span style="color: #24292E">      cliConn net.Conn</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #6A737D">// 上游服务连接</span></span><span class="line"><span style="color: #24292E">      svrConn net.Conn</span></span><span class="line"><span style="color: #24292E">      once    sync.Once</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #6A737D">//关闭 chan</span></span><span class="line"><span style="color: #24292E">      stopChan </span><span style="color: #D73A49">chan</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E">{}</span></span><span class="line"><span style="color: #24292E">  }</span></span></code></pre></div></div></figure><h4 id="连接对创建"><a href="#连接对创建" class="headerlink" title="连接对创建"></a>连接对创建</h4><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D">// NewPairedConnection 生成 , 设置 客户端连接 ID，客户端连接，还有 停止通道</span></span><span class="line"></span><span class="line"><span style="color: #24292E"> </span><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #005CC5">NewPairedConnection</span><span style="color: #24292E">(id </span><span style="color: #D73A49">int</span><span style="color: #24292E">, cliConn net.Conn) </span><span style="color: #D73A49">*</span><span style="color: #24292E">PairedConnection {</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;</span><span style="color: #24292E">PairedConnection{</span></span><span class="line"><span style="color: #24292E">          id:       id,</span></span><span class="line"><span style="color: #24292E">          cliConn:  cliConn,</span></span><span class="line"><span style="color: #24292E">          stopChan: </span><span style="color: #005CC5">make</span><span style="color: #24292E">(</span><span style="color: #D73A49">chan</span><span style="color: #24292E"> </span><span style="color: #D73A49">struct</span><span style="color: #24292E">{}),</span></span><span class="line"><span style="color: #24292E">      }</span></span><span class="line"><span style="color: #24292E"> }</span></span><span class="line"></span><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">func</span><span style="color: #24292E"> (c </span><span style="color: #D73A49">*</span><span style="color: #24292E">PairedConnection) </span><span style="color: #005CC5">process</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #D73A49">defer</span><span style="color: #24292E"> c.</span><span style="color: #005CC5">stop</span><span style="color: #24292E">()</span></span><span class="line"></span><span class="line"><span style="color: #24292E">      </span><span style="color: #6A737D">// 连接远端服务</span></span><span class="line"><span style="color: #24292E">      conn, err </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> net.</span><span style="color: #005CC5">Dial</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;tcp&quot;</span><span style="color: #24292E">, settings.Remote)</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #D73A49">if</span><span style="color: #24292E"> err </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">          display.</span><span style="color: #005CC5">PrintlnWithTime</span><span style="color: #24292E">(color.</span><span style="color: #005CC5">HiRedString</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;[x][</span><span style="color: #005CC5">%d</span><span style="color: #032F62">] Couldn&#39;t connect to server: </span><span style="color: #005CC5">%v</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">, c.id, err))</span></span><span class="line"><span style="color: #24292E">          </span><span style="color: #D73A49">return</span></span><span class="line"><span style="color: #24292E">      }</span></span><span class="line"></span><span class="line"><span style="color: #24292E">      display.</span><span style="color: #005CC5">PrintlnWithTime</span><span style="color: #24292E">(color.</span><span style="color: #005CC5">HiGreenString</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;[</span><span style="color: #005CC5">%d</span><span style="color: #032F62">] Connected to server: </span><span style="color: #005CC5">%s</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">, c.id, conn.</span><span style="color: #005CC5">RemoteAddr</span><span style="color: #24292E">()))</span></span><span class="line"></span><span class="line"><span style="color: #24292E">     </span><span style="color: #6A737D">//调用统计 新增连接方法</span></span><span class="line"><span style="color: #24292E">      stat.</span><span style="color: #005CC5">AddConn</span><span style="color: #24292E">(strconv.</span><span style="color: #005CC5">Itoa</span><span style="color: #24292E">(c.id), conn.(</span><span style="color: #D73A49">*</span><span style="color: #24292E">net.TCPConn))</span></span><span class="line"><span style="color: #24292E">    </span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #6A737D">// 保存 服务端 连接</span></span><span class="line"><span style="color: #24292E">      c.svrConn </span><span style="color: #D73A49">=</span><span style="color: #24292E"> conn</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #6A737D">// 处理 服务端 数据</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #D73A49">go</span><span style="color: #24292E"> c.</span><span style="color: #005CC5">handleServerMessage</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #6A737D">// 处理 客户端 数据</span></span><span class="line"><span style="color: #24292E">      c.</span><span style="color: #005CC5">handleClientMessage</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">  }</span></span></code></pre></div></div></figure><h4 id="连接对数据流处理"><a href="#连接对数据流处理" class="headerlink" title="连接对数据流处理"></a>连接对数据流处理</h4><h5 id="服务端数据处理"><a href="#服务端数据处理" class="headerlink" title="服务端数据处理"></a>服务端数据处理</h5><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (c </span><span style="color: #D73A49">*</span><span style="color: #24292E">PairedConnection) </span><span style="color: #6F42C1">handleServerMessage</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E">     </span><span style="color: #6A737D">// server closed also trigger client close.</span></span><span class="line"><span style="color: #24292E">     </span><span style="color: #D73A49">defer</span><span style="color: #24292E"> c.</span><span style="color: #005CC5">stop</span><span style="color: #24292E">()</span></span><span class="line"></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">//建立内存读写通道</span></span><span class="line"><span style="color: #24292E">     r, w </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> io.</span><span style="color: #005CC5">Pipe</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">//构造 客户端连接 与 内存通道 的 多写对象</span></span><span class="line"><span style="color: #24292E">     tee </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> io.</span><span style="color: #005CC5">MultiWriter</span><span style="color: #24292E">(</span><span style="color: #005CC5">newDelayedWriter</span><span style="color: #24292E">(c.cliConn, settings.Delay, c.stopChan), w)</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">//根据设置的数据协议，使用对应协议解析 内存读通道中 服务端写入的响应数据</span></span><span class="line"><span style="color: #24292E">     </span><span style="color: #D73A49">go</span><span style="color: #24292E"> protocol.</span><span style="color: #005CC5">CreateInterop</span><span style="color: #24292E">(settings.Protocol).</span><span style="color: #005CC5">Dump</span><span style="color: #24292E">(r, protocol.ServerSide, c.id, settings.Quiet)</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">// 将 服务端上游 返回数据 输出到 多写对象</span></span><span class="line"><span style="color: #24292E">     c.</span><span style="color: #005CC5">copyDataWithRateLimit</span><span style="color: #24292E">(tee, c.svrConn, protocol.ServerSide, settings.DownLimit)</span></span><span class="line"><span style="color: #24292E"> }</span></span></code></pre></div></div></figure><h5 id="客户端数据处理"><a href="#客户端数据处理" class="headerlink" title="客户端数据处理"></a>客户端数据处理</h5><figure class="shiki go"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (c </span><span style="color: #D73A49">*</span><span style="color: #24292E">PairedConnection) </span><span style="color: #6F42C1">handleClientMessage</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #6A737D">// client closed also trigger server close.</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">defer</span><span style="color: #24292E"> c.</span><span style="color: #005CC5">stop</span><span style="color: #24292E">()</span></span><span class="line"></span><span class="line"><span style="color: #24292E">r, w </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> io.</span><span style="color: #005CC5">Pipe</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">tee </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> io.</span><span style="color: #005CC5">MultiWriter</span><span style="color: #24292E">(c.svrConn, w)</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D">//根据设置的数据协议，使用对应协议解析 内存读通道中 客户端写入的请求数据</span></span><span class="line"><span style="color: #24292E"></span><span style="color: #D73A49">go</span><span style="color: #24292E"> protocol.</span><span style="color: #005CC5">CreateInterop</span><span style="color: #24292E">(settings.Protocol).</span><span style="color: #005CC5">Dump</span><span style="color: #24292E">(r, protocol.ClientSide, c.id, settings.Quiet)</span></span><span class="line"><span style="color: #24292E">c.</span><span style="color: #005CC5">copyDataWithRateLimit</span><span style="color: #24292E">(tee, c.cliConn, protocol.ClientSide, settings.UpLimit)</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h4 id="连接对关闭"><a href="#连接对关闭" class="headerlink" title="连接对关闭"></a>连接对关闭</h4><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> (c </span><span style="color: #D73A49">*</span><span style="color: #24292E">PairedConnection) </span><span style="color: #6F42C1">stop</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">// 使用 once 保证 stop 资源回收只会执行一次</span></span><span class="line"><span style="color: #24292E">    c.once.</span><span style="color: #005CC5">Do</span><span style="color: #24292E">(</span><span style="color: #D73A49">func</span><span style="color: #24292E">() {</span></span><span class="line"><span style="color: #24292E">        </span><span style="color: #005CC5">close</span><span style="color: #24292E">(c.stopChan)</span></span><span class="line"><span style="color: #24292E">        stat.</span><span style="color: #005CC5">DelConn</span><span style="color: #24292E">(strconv.</span><span style="color: #005CC5">Itoa</span><span style="color: #24292E">(c.id))</span></span><span class="line"></span><span class="line"><span style="color: #24292E">        </span><span style="color: #D73A49">if</span><span style="color: #24292E"> c.cliConn </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">            display.</span><span style="color: #005CC5">PrintlnWithTime</span><span style="color: #24292E">(color.</span><span style="color: #005CC5">HiBlueString</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;[</span><span style="color: #005CC5">%d</span><span style="color: #032F62">] Client connection closed&quot;</span><span style="color: #24292E">, c.id))</span></span><span class="line"><span style="color: #24292E">            c.cliConn.</span><span style="color: #005CC5">Close</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">        }</span></span><span class="line"><span style="color: #24292E">        </span><span style="color: #D73A49">if</span><span style="color: #24292E"> c.svrConn </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> {</span></span><span class="line"><span style="color: #24292E">            display.</span><span style="color: #005CC5">PrintlnWithTime</span><span style="color: #24292E">(color.</span><span style="color: #005CC5">HiBlueString</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;[</span><span style="color: #005CC5">%d</span><span style="color: #032F62">] Server connection closed&quot;</span><span style="color: #24292E">, c.id))</span></span><span class="line"><span style="color: #24292E">            c.svrConn.</span><span style="color: #005CC5">Close</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">        }</span></span><span class="line"><span style="color: #24292E">    })</span></span><span class="line"><span style="color: #24292E">}</span></span></code></pre></div></div></figure><h4 id="代码细节"><a href="#代码细节" class="headerlink" title="代码细节"></a>代码细节</h4><figure class="shiki go"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #24292E">  </span><span style="color: #D73A49">func</span><span style="color: #24292E"> (c </span><span style="color: #D73A49">*</span><span style="color: #24292E">PairedConnection) </span><span style="color: #005CC5">copyData</span><span style="color: #24292E">(dst io.Writer, src io.Reader, tag </span><span style="color: #D73A49">string</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">      _, e </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> io.</span><span style="color: #005CC5">Copy</span><span style="color: #24292E">(dst, src)</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #6A737D">//但是此处的判断 e != io.EOF 个人感觉是不需要的，因为 io.Copy 函数明确说明不会返回io.EOF 的错误，在判断 错误类型是非为 net操作类错误，如非 net 操作类错误，不做处理（这个合理吗）我去每个没吃透的点都是以后埋的坑，此处对 io.Copy 中如 io.Writer 或 io.Reader 被手动或其他原因意外关闭，则进行忽略，结束当前 Copy 即可</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">//https://golang-nuts.narkive.com/ae58tX8v/use-of-closed-network-connection-error-from-tcpconn-read</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #6A737D">// useOfClosedConn 代表了 当前操作的 io.Writer 或 io.Reader 被关闭</span></span><span class="line"><span style="color: #24292E">      </span><span style="color: #D73A49">if</span><span style="color: #24292E"> e </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> </span><span style="color: #005CC5">nil</span><span style="color: #24292E"> </span><span style="color: #D73A49">&amp;&amp;</span><span style="color: #24292E"> e </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> io.EOF {</span></span><span class="line"><span style="color: #24292E">          netOpError, ok </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> e.(</span><span style="color: #D73A49">*</span><span style="color: #24292E">net.OpError)</span></span><span class="line"><span style="color: #24292E">          </span><span style="color: #D73A49">if</span><span style="color: #24292E"> ok </span><span style="color: #D73A49">&amp;&amp;</span><span style="color: #24292E"> netOpError.Err.</span><span style="color: #005CC5">Error</span><span style="color: #24292E">() </span><span style="color: #D73A49">!=</span><span style="color: #24292E"> useOfClosedConn {</span></span><span class="line"><span style="color: #24292E">            </span><span style="color: #6A737D">//error 库的 使用 ?</span></span><span class="line"><span style="color: #24292E">              reason </span><span style="color: #D73A49">:=</span><span style="color: #24292E"> netOpError.</span><span style="color: #005CC5">Unwrap</span><span style="color: #24292E">().</span><span style="color: #005CC5">Error</span><span style="color: #24292E">()</span></span><span class="line"><span style="color: #24292E">              display.</span><span style="color: #005CC5">PrintlnWithTime</span><span style="color: #24292E">(color.</span><span style="color: #005CC5">HiRedString</span><span style="color: #24292E">(</span><span style="color: #032F62">&quot;[</span><span style="color: #005CC5">%d</span><span style="color: #032F62">] </span><span style="color: #005CC5">%s</span><span style="color: #032F62"> error, </span><span style="color: #005CC5">%s</span><span style="color: #032F62">&quot;</span><span style="color: #24292E">, c.id, tag, reason))</span></span><span class="line"><span style="color: #24292E">          }</span></span><span class="line"><span style="color: #24292E">      }</span></span><span class="line"><span style="color: #24292E">  }</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">//ErrFileClosing is returned when a file descriptor is used after it has been closed.</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// go io.Copy的api说明</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">// Copy copies from src to dst until either EOF is reached</span></span><span class="line"><span style="color: #6A737D">// on src or an error occurs. It returns the number of bytes</span></span><span class="line"><span style="color: #6A737D">// copied and the first error encountered while copying, if any.</span></span><span class="line"><span style="color: #6A737D">//</span></span><span class="line"><span style="color: #6A737D">// A successful Copy returns err == nil, not err == EOF.</span></span><span class="line"><span style="color: #6A737D">// Because Copy is defined to read from src until EOF, it does</span></span><span class="line"><span style="color: #6A737D">// not treat an EOF from Read as an error to be reported.</span></span><span class="line"><span style="color: #6A737D">//</span></span><span class="line"><span style="color: #6A737D">// If src implements [WriterTo],</span></span><span class="line"><span style="color: #6A737D">// the copy is implemented by calling src.WriteTo(dst).</span></span><span class="line"><span style="color: #6A737D">// Otherwise, if dst implements [ReaderFrom],</span></span><span class="line"><span style="color: #6A737D">// the copy is implemented by calling dst.ReadFrom(src).</span></span><span class="line"><span style="color: #D73A49">func</span><span style="color: #24292E"> </span><span style="color: #6F42C1">Copy</span><span style="color: #24292E">(dst Writer, src Reader) (written </span><span style="color: #D73A49">int64</span><span style="color: #24292E">, err </span><span style="color: #D73A49">error</span><span style="color: #24292E">) {</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #D73A49">return</span><span style="color: #24292E"> </span><span style="color: #005CC5">copyBuffer</span><span style="color: #24292E">(dst, src, </span><span style="color: #005CC5">nil</span><span style="color: #24292E">)</span></span></code></pre></div></div></figure><h2 id="贡献说明"><a href="#贡献说明" class="headerlink" title="贡献说明"></a>贡献说明</h2><p>在代码细节中说的 可以不用判断 io.Copy 的 io.EOF 的判断被项目作者merge了，😄</p><p><a href="https://github.com/kevwan/tproxy/pull/86">https://github.com/kevwan/tproxy/pull/86</a></p>]]></content>
    
    
    <summary type="html">tproxy codeview 源码分析</summary>
    
    
    
    <category term="IT" scheme="https://freelifeblog.top/categories/IT/"/>
    
    
    <category term="codeview" scheme="https://freelifeblog.top/tags/codeview/"/>
    
    <category term="go" scheme="https://freelifeblog.top/tags/go/"/>
    
    <category term="network" scheme="https://freelifeblog.top/tags/network/"/>
    
  </entry>
  
  <entry>
    <title>如何使用hexo构建一个自己的博客</title>
    <link href="https://freelifeblog.top/2024/06/21/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8hexo%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AA%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/"/>
    <id>https://freelifeblog.top/2024/06/21/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8hexo%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AA%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/</id>
    <published>2024-06-21T01:16:06.000Z</published>
    <updated>2024-08-15T01:31:42.185Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p><a href="https://hexo.io/zh-tw/">https://hexo.io/zh-tw/</a></p><p><a href="https://hexo.io/themes/">https://hexo.io/themes/</a></p></blockquote><h1 id="前期准备"><a href="#前期准备" class="headerlink" title="前期准备"></a>前期准备</h1><h2 id="安装hexo"><a href="#安装hexo" class="headerlink" title="安装hexo"></a>安装hexo</h2><figure class="shiki shell"><div class='codeblock'><div 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></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6F42C1">npm</span><span style="color: #24292E"> </span><span style="color: #032F62">install</span><span style="color: #24292E"> </span><span style="color: #032F62">hexo-cli</span><span style="color: #24292E"> </span><span style="color: #005CC5">-g</span></span><span class="line"><span style="color: #6F42C1">hexo</span><span style="color: #24292E"> </span><span style="color: #032F62">init</span><span style="color: #24292E"> </span><span style="color: #032F62">blog</span></span><span class="line"><span style="color: #005CC5">cd</span><span style="color: #24292E"> </span><span style="color: #032F62">blog</span></span><span class="line"><span style="color: #6F42C1">npm</span><span style="color: #24292E"> </span><span style="color: #032F62">install</span></span><span class="line"><span style="color: #6F42C1">hexo</span><span style="color: #24292E"> </span><span style="color: #032F62">server</span></span></code></pre></div></div></figure><h2 id="准备图床"><a href="#准备图床" class="headerlink" title="准备图床"></a>准备图床</h2><p>注册一个可用的图床，我用的是<a href="https://img.tg/">https://img.tg/</a>  可免费上传单张5MB以内的图片。</p><h2 id="注册Github账号"><a href="#注册Github账号" class="headerlink" title="注册Github账号"></a>注册Github账号</h2><p>登入 <a href="https://github.com/">https://github.com</a> 注册账号，创建私有或公开仓库。</p><h2 id="注册vercel账号"><a href="#注册vercel账号" class="headerlink" title="注册vercel账号"></a>注册vercel账号</h2><p>登入 <a href="https://vercel.com/">https://vercel.com</a> 注册账号，推荐使用 github 账号注册。</p><h1 id="网站部署"><a href="#网站部署" class="headerlink" title="网站部署"></a>网站部署</h1><h2 id="更换主题"><a href="#更换主题" class="headerlink" title="更换主题"></a>更换主题</h2><ol><li><p>进入 <a href="https://hexo.io/themes/">https://hexo.io/themes/</a> 选择自己喜欢的主题，将主题仓库克隆下载至hexo项目根目录的themes文件夹下，下载完注意删掉 主题仓库中的 .git 与 .github 目录，根据自己的喜好设置主题</p></li><li><p>设置好后需要修改根目录_config.yml 中的theme字段，并拷贝主题目录下的_config.yml 至根目录_config.{theme_name}.yml</p></li></ol><h2 id="上传github"><a href="#上传github" class="headerlink" title="上传github"></a>上传github</h2><p>将hexo项目 上传至 github 仓库中</p><h2 id="部署项目"><a href="#部署项目" class="headerlink" title="部署项目"></a>部署项目</h2><ol><li>在 vercel 创建应用，选择 对应的 github 仓库。</li><li>修改vercel项目设置 general，InstallCommand 为 <code>npm install hexo-renderer-pug hexo-renderer-stylus  hexo-generator-feed hexo-pagecounter  </code> ，否则某些插件的却是会导致功能缺失。</li></ol><h2 id="网站域名配置"><a href="#网站域名配置" class="headerlink" title="网站域名配置"></a>网站域名配置</h2><ol><li>国内可在 阿里云 或 腾讯云 购买域名，方便备案</li><li>在 vercel 应用中选择 Add Domain，添加购买的域名，vercel 会提示在 域名云解析商出需要添加的解析记录</li><li>在阿里云 或 腾讯云 中添加域名解析规则，选择解析路线为默认路线。</li><li>等待 vercel 域名验证成功，证书添加成功，即可通过域名访问网站。</li></ol><h2 id="新增rss订阅"><a href="#新增rss订阅" class="headerlink" title="新增rss订阅"></a>新增rss订阅</h2><ol><li>在项目根目录下安装插件 <strong>npm install hexo-generator-feed</strong></li><li>修改项目根目录_config.yml</li></ol><figure class="shiki yaml  "><div class='codeblock'><div 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></div><div class="code"><pre><code>plugins:- hexo-generator-feedfeed:  type: atom  path: /atom.xml  limit: 10  encoding: utf-8</code></pre></div></div></figure><ol start="3"><li>在主题目录下_config.yaml 设置rss 配置</li></ol><figure class="shiki yaml"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"></span><span class="line"><span style="color: #6A737D">#rss 路径配置</span></span><span class="line"><span style="color: #22863A">rss</span><span style="color: #24292E">: </span><span style="color: #032F62">/atom.xml</span></span></code></pre></div></div></figure><ol start="4"><li><p>同样修改完主题目录下的_config.yml文件记得要拷贝至根目录下_config.{theme_name}.yml</p></li><li><p>站点&#x2F;atom.xml路径为rss订阅地址</p></li></ol><h2 id="新增网页浏览量"><a href="#新增网页浏览量" class="headerlink" title="新增网页浏览量"></a>新增网页浏览量</h2><p>使用 hexo-pagecounter 插件，并修改主题中的 layout&#x2F;_partial&#x2F;footer.ejs 内容，新增 <code>👀 &lt;b&gt;访问量: &lt;/b&gt;&lt;span class=&quot;s3_pv&quot;&gt;&lt;/span&gt;</code></p><h1 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h1><p>我用的 flexblock 主题，配置如下</p><figure class="shiki yaml"><div class='codeblock'><div 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></pre></div><div class="code"><pre class="shiki github-light"><code><span class="line"><span style="color: #6A737D"># Header</span></span><span class="line"><span style="color: #22863A">menu</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">首页</span><span style="color: #24292E">: </span><span style="color: #032F62">/</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">订阅</span><span style="color: #24292E">: </span><span style="color: #032F62">/atom.xml</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># 主页banner图</span></span><span class="line"><span style="color: #22863A">banner</span><span style="color: #24292E">: </span><span style="color: #032F62">https://qiniu.sukoshi.xyz/src/images/68686407_p0.jpg</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># 站点通知信息</span></span><span class="line"><span style="color: #22863A">notice</span><span style="color: #24292E">: </span><span style="color: #032F62">因为你们的支持让我意识到写文章的价值</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># 作者信息</span></span><span class="line"><span style="color: #22863A">author</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">name</span><span style="color: #24292E">: </span><span style="color: #032F62">OnlyPiglet</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">avatar</span><span style="color: #24292E">: </span><span style="color: #032F62">https://ooo.0x0.ooo/2024/06/20/OPb4Zs.jpg</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">description</span><span style="color: #24292E">: </span><span style="color: #032F62">大音希声,大象无形</span></span><span class="line"></span><span class="line"><span style="color: #22863A">toc</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">max_depth</span><span style="color: #24292E">: </span><span style="color: #005CC5">3</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 生成 TOC 的最大深度</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">min_depth</span><span style="color: #24292E">: </span><span style="color: #005CC5">2</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 生成 TOC 的最小深度</span></span><span class="line"></span><span class="line"><span style="color: #22863A">comments</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">enable</span><span style="color: #24292E">: </span><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">use</span><span style="color: #24292E">: </span><span style="color: #032F62">&#39;valine&#39;</span><span style="color: #24292E"> </span><span style="color: #6A737D"># valine || disqus</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D"># valine 评论</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D"># docs: https://valine.js.org</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D"># You can get your appid and appkey from https://leancloud.cn</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D"># 这里appId和appKey一定一定要改成自己的，没有的话去 https://leancloud.cn 这个网站注册创建一个</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">valine</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">appId</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">appKey</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">placeholder</span><span style="color: #24292E">: </span><span style="color: #032F62">随便说点什么叭～</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 评论框占位提示符</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">avatar</span><span style="color: #24292E">: </span><span style="color: #032F62">mp</span><span style="color: #24292E"> </span><span style="color: #6A737D"># Gravatar 头像展示方式</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">pageSize</span><span style="color: #24292E">: </span><span style="color: #005CC5">10</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 评论列表分页，每页条数</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">lang</span><span style="color: #24292E">: </span><span style="color: #032F62">zh-cn</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 多语言支持</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">visitor</span><span style="color: #24292E">: </span><span style="color: #005CC5">true</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 文章访问量统计</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">recordIP</span><span style="color: #24292E">: </span><span style="color: #005CC5">true</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 是否记录评论者IP</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">enableQQ</span><span style="color: #24292E">: </span><span style="color: #005CC5">false</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 是否启用昵称框自动获取QQ昵称和QQ头像，开启后是在昵称框输入QQ号码进行获取</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">meta</span><span style="color: #24292E">: </span><span style="color: #032F62">&quot;[&#39;nick&#39;, &#39;mail&#39;, &#39;link&#39;]&quot;</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 评论者相关属性，可选值：[&#39;nick&#39;]，[&#39;nick&#39;,&#39;mail&#39;]，[&#39;nick&#39;, &#39;mail&#39;, &#39;link&#39;]</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">requiredFields</span><span style="color: #24292E">: </span><span style="color: #032F62">&quot;[&#39;nick&#39;]&quot;</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 设置必填项，默认匿名</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">bilibiliEmoji</span><span style="color: #24292E">: </span><span style="color: #005CC5">true</span><span style="color: #24292E"> </span><span style="color: #6A737D"># 是否启用Bilibili表情</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D"># disqus 评论</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #6A737D"># docs: https://disqus.com/</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">disqus</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">website</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">error</span><span style="color: #24292E">: </span><span style="color: #032F62">如果你看不到评论，那么就真的看不到评论 w(゜Д゜)w</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># dplayer 视频播放</span></span><span class="line"><span style="color: #6A737D"># docs: http://dplayer.js.org/#/zh-Hans/</span></span><span class="line"><span style="color: #22863A">dplayer</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">enable</span><span style="color: #24292E">: </span><span style="color: #005CC5">true</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">theme</span><span style="color: #24292E">: </span><span style="color: #032F62">&quot;#b7daff&quot;</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">autoplay</span><span style="color: #24292E">: </span><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">loop</span><span style="color: #24292E">: </span><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">mutex</span><span style="color: #24292E">: </span><span style="color: #005CC5">true</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># aplayer 音频播放</span></span><span class="line"><span style="color: #6A737D"># docs: https://aplayer.js.org/#/zh-Hans/</span></span><span class="line"><span style="color: #22863A">aplayer</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">enable</span><span style="color: #24292E">: </span><span style="color: #005CC5">true</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">theme</span><span style="color: #24292E">: </span><span style="color: #032F62">&quot;#b7daff&quot;</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">autoplay</span><span style="color: #24292E">: </span><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">loop</span><span style="color: #24292E">: </span><span style="color: #005CC5">false</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">mutex</span><span style="color: #24292E">: </span><span style="color: #005CC5">true</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">lrcType</span><span style="color: #24292E">: </span><span style="color: #005CC5">3</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># mathjax LaTeX公式支持</span></span><span class="line"><span style="color: #6A737D"># docs: http://docs.mathjax.org/en/latest/</span></span><span class="line"><span style="color: #22863A">mathjax</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">enable</span><span style="color: #24292E">: </span><span style="color: #005CC5">true</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># polyfill 提供老版浏览器对新语法的支持</span></span><span class="line"><span style="color: #6A737D"># website: https://polyfill.io/v3/</span></span><span class="line"><span style="color: #22863A">polyfill</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">enable</span><span style="color: #24292E">: </span><span style="color: #005CC5">false</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># jsdelivr镜像域名</span></span><span class="line"><span style="color: #6A737D"># e.g. cdn.example.com</span></span><span class="line"><span style="color: #22863A">jsdelivr_mirror</span><span style="color: #24292E">:</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># 国内备案信息</span></span><span class="line"><span style="color: #22863A">beian</span><span style="color: #24292E">:</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># 谷歌分析</span></span><span class="line"><span style="color: #22863A">google_analytics</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #6A737D"># gauges分析</span></span><span class="line"><span style="color: #22863A">gauges_analytics</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #6A737D"># 百度分析</span></span><span class="line"><span style="color: #22863A">baidu_analytics</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #6A737D"># 腾讯分析</span></span><span class="line"><span style="color: #22863A">tencent_analytics</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #6A737D"># 站点ico</span></span><span class="line"><span style="color: #22863A">favicon</span><span style="color: #24292E">: </span><span style="color: #032F62">/favicon.ico</span></span><span class="line"></span><span class="line"><span style="color: #6A737D">#rss 路径配置</span></span><span class="line"><span style="color: #22863A">rss</span><span style="color: #24292E">: </span><span style="color: #032F62">/atom.xml</span></span><span class="line"></span><span class="line"><span style="color: #6A737D"># Social Icon</span></span><span class="line"><span style="color: #22863A">social_icon</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">enable</span><span style="color: #24292E">: </span><span style="color: #005CC5">true</span></span><span class="line"><span style="color: #24292E">  </span><span style="color: #22863A">icons</span><span style="color: #24292E">:</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">ins</span><span style="color: #24292E">     :</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">zhihu</span><span style="color: #24292E">   :</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">weibo</span><span style="color: #24292E">   :</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">github</span><span style="color: #24292E">  : </span><span style="color: #032F62">https://github.com/OnlyPiglet</span></span><span class="line"><span style="color: #24292E">    </span><span style="color: #22863A">twitter</span><span style="color: #24292E"> : </span><span style="color: #032F62">https://x.com/wswch19941012</span></span></code></pre></div></div></figure>]]></content>
    
    
    <summary type="html">如何使用hexo构建一个自己的博客</summary>
    
    
    
    <category term="IT" scheme="https://freelifeblog.top/categories/IT/"/>
    
    
    <category term="实用技能" scheme="https://freelifeblog.top/tags/%E5%AE%9E%E7%94%A8%E6%8A%80%E8%83%BD/"/>
    
    <category term="建站" scheme="https://freelifeblog.top/tags/%E5%BB%BA%E7%AB%99/"/>
    
  </entry>
  
</feed>
