<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Go on </title>
    <link>https://note.lican.site/tags/go/</link>
    <description>Recent content in Go on </description>
    <generator>Hugo</generator>
    <language>en</language>
    <copyright>© lican.asia All rights reserved</copyright>
    <lastBuildDate>Sat, 05 Feb 2022 16:03:31 +0800</lastBuildDate>
    <atom:link href="https://note.lican.site/tags/go/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Go1.18 新特性：新增好用的 Cut 方法</title>
      <link>https://note.lican.site/posts/posts/go/118-cut/</link>
      <pubDate>Sat, 05 Feb 2022 16:03:31 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/118-cut/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在各种写业务代码的时候，大家会常常要处理字符串的内容。常见的像是用邮箱登陆账号，如果是：eddycjy@gmail.com，那就得根据 @ 来切割，分别取出前和后，来识别用户名和邮箱地址。&lt;/p&gt;&#xA;&lt;p&gt;这种需求，在 Go 里写起来方便吗？今天就由煎鱼带大家了解。&lt;/p&gt;&#xA;&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;&#xA;&lt;h3 id=&#34;重复代码&#34;&gt;重复代码&lt;/h3&gt;&#xA;&lt;p&gt;无独有偶，Ainar Garipov 在许多项目中遇到了前面我们所提的切割需求。&lt;/p&gt;&#xA;&lt;p&gt;例如：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Index&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt;[:&lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;又或是：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;LastIndex&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;address&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;host&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;address&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;host&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;address&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;经常要反复写一些繁琐的代码，提案提出者表示不愉快。&lt;/p&gt;&#xA;&lt;h2 id=&#34;新提案&#34;&gt;新提案&lt;/h2&gt;&#xA;&lt;h3 id=&#34;实施内容&#34;&gt;实施内容&lt;/h3&gt;&#xA;&lt;p&gt;建议新增 Cut 方法到 strings 标准库：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Cut&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;sep&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;before&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;after&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;found&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Index&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;sep&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;[:&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;], &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;len(&lt;span style=&#34;color:#a6e22e&#34;&gt;sep&lt;/span&gt;):], &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同步也要在 bytes 标准库：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Cut&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;sep&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;before&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;after&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;found&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样一来，就可以从原本的：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IndexByte&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;rec&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;=&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ErrHeader&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;rec&lt;/span&gt;[:&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt;], &lt;span style=&#34;color:#a6e22e&#34;&gt;rec&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;变成：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.18 新特性：编译后的二进制文件，将包含更多信息</title>
      <link>https://note.lican.site/posts/posts/go/118-build-info/</link>
      <pubDate>Sat, 05 Feb 2022 16:02:45 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/118-build-info/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;我有一个朋友，，开开心心入职，想着施展拳脚，第一个任务就是对老旧的二进制文件进行研究。&lt;/p&gt;&#xA;&lt;p&gt;他一看，这文件，不知道是编译器用什么参数怎么打出来的，环境不知道是什么，更不知道来自什么代码分支？&lt;/p&gt;&#xA;&lt;p&gt;这除了是项目流程上的问题外，Go 在这块也有类似的小问题，处理起来比较麻烦。&lt;/p&gt;&#xA;&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;&#xA;&lt;p&gt;日常中很难从 Go 二进制文件中检索元信息，要么是信息完全缺失，要么提取需要对二进制文件进行大量解析。&lt;/p&gt;&#xA;&lt;p&gt;包含的元信息如下：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;元信息&lt;/th&gt;&#xA;          &lt;th&gt;提取处&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Go 构建版本&lt;/td&gt;&#xA;          &lt;td&gt;符号表，通过全局变量 &lt;code&gt;runtime.buildVersion&lt;/code&gt; 来获取&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;构建信息，例如：模块和版本&lt;/td&gt;&#xA;          &lt;td&gt;符号表，通过全局变量 &lt;code&gt;runtime/debug.modinfo&lt;/code&gt; 来获取&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;编译器选项，例如：构建模式、编译器、gcflags、ldflags 等&lt;/td&gt;&#xA;          &lt;td&gt;无法获取&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;用户定义的自定义数据，例如：应用程序版本等&lt;/td&gt;&#xA;          &lt;td&gt;需在编译时设置全局字符串变量，才可以获取&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;关注到编译器选项，也就是参数等都是无法得知的，也就是会提高获取如何编译出来的难度。&lt;/p&gt;&#xA;&lt;h2 id=&#34;新提案&#34;&gt;新提案&lt;/h2&gt;&#xA;&lt;p&gt;Michael Obermüller 提出了一个新的提案《&lt;a href=&#34;https://github.com/golang/go/issues/35667&#34;&gt;cmd/go: add compiler flags, relevant env vars to &amp;lsquo;go version -m&amp;rsquo; output&lt;/a&gt;》用于解决上述问题。&lt;/p&gt;&#xA;&lt;p&gt;在提案中想要的是 JSON 格式的结构输出：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;go1.13.4&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;compileropts&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;compiler&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gc&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pie&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;linux&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;buildinfo&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HelloWorld&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(devel)&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;deps&amp;#34;&lt;/span&gt;: []&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;customkey&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;customval&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Russ Cox 表示由于编译信息已有既有格式，并且默认使用 JSON 只会让二进制文件变得更大。好处少，没必要，改为了选项化的支持。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.18 新特性：多 Module 工作区模式</title>
      <link>https://note.lican.site/posts/posts/go/118-module/</link>
      <pubDate>Sat, 05 Feb 2022 16:00:00 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/118-module/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;Go 的依赖管理，也就是 Go Module。从推出到现在，也已经有了一定的年头了，吐槽一直很多，官方也不断地在进行完善。&lt;/p&gt;&#xA;&lt;p&gt;Go1.18 将会推出一个新特性：Multi-Module Workspaces，用于支持 Module 多工作区，能解决以往的一系列问题。&lt;/p&gt;&#xA;&lt;p&gt;今天将由煎鱼带大家一起深入学习。&lt;/p&gt;&#xA;&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;&#xA;&lt;p&gt;在日常使用 Go 工程时，总会遇到 2 个经典问题，特别的折腾人。&lt;/p&gt;&#xA;&lt;p&gt;如下：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;依赖本地 replace module。&lt;/li&gt;&#xA;&lt;li&gt;依赖本地未发布的 module。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;replace-module&#34;&gt;replace module&lt;/h3&gt;&#xA;&lt;p&gt;第一个场景：像是平时在 Go 工程中，我们为了解决一些本地依赖，或是定制化代码。会在 go.mod 文件中使用 replace 做替换。&lt;/p&gt;&#xA;&lt;p&gt;如下代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;golang&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;org&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;net&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Users&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;eddycjy&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;awesomeProject&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样就可以实现本地开发联调时的准确性。&lt;/p&gt;&#xA;&lt;p&gt;问题就在这里：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;本地路径：所设定的 replace 本质上转换的是本地的路径，也就是每个人都不一样。&lt;/li&gt;&#xA;&lt;li&gt;仓库依赖：文件修改是会上传到 Git 仓库的，不小心传上去了，影响到其他开发同学，又或是每次上传都得重新改回去。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;用户体验非常差，很折腾人。&lt;/p&gt;&#xA;&lt;h3 id=&#34;未发布的-module&#34;&gt;未发布的 module&lt;/h3&gt;&#xA;&lt;p&gt;第二个场景：在做本地的 Go 项目开发时，可能会在本地同时开发多个库（项目库、工具库、第三方库）等。&lt;/p&gt;&#xA;&lt;p&gt;如下代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/eddycjy/pkgutil&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;pkgutil&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PrintFish&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果这个时候运行 &lt;code&gt;go run&lt;/code&gt; 或是 &lt;code&gt;go mod tidy&lt;/code&gt;，都不行，会运行失败。&lt;/p&gt;&#xA;&lt;p&gt;报如下类似错误：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fatal: repository &amp;#39;https://github.com/eddycjy/pkgutil/&amp;#39; not found&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这个问题报错是因为 &lt;code&gt;github.com/eddycjy/pkgutil&lt;/code&gt; 这个库，在 GitHub 是没有的，自然也就拉取不到。&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么 Go 有两种声明变量的方式，有什么区别，哪种好？</title>
      <link>https://note.lican.site/posts/posts/go/var/</link>
      <pubDate>Sat, 05 Feb 2022 15:56:48 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/var/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;有一个读者刚入门 Go ，提了一个很有意思的问题：Go 有几种种声明变量的方式，作为初学者，到底用哪种，有什么区别，又为什么要有多种声明方式呢？&lt;/p&gt;&#xA;&lt;p&gt;为此，煎鱼将和大家一起探索这个问题。&lt;/p&gt;&#xA;&lt;h2 id=&#34;变量声明&#34;&gt;变量声明&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 中，一共有 2 种变量声明的方式，各有不同的使用场景。&lt;/p&gt;&#xA;&lt;p&gt;分别是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;标准变量声明（Variable declarations）。&lt;/li&gt;&#xA;&lt;li&gt;简短变量声明（Short variable declarations）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;标准声明&#34;&gt;标准声明&lt;/h3&gt;&#xA;&lt;p&gt;变量声明创建了一个或多个变量，为它们绑定了相应的标识符，并给每个变量一个类型和初始值。&lt;/p&gt;&#xA;&lt;p&gt;使用语法：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;VarDecl&lt;/span&gt;     = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;var&amp;#34;&lt;/span&gt; ( &lt;span style=&#34;color:#a6e22e&#34;&gt;VarSpec&lt;/span&gt; | &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(&amp;#34;&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;VarSpec&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;;&amp;#34;&lt;/span&gt; } &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;)&amp;#34;&lt;/span&gt; ) .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;VarSpec&lt;/span&gt;     = &lt;span style=&#34;color:#a6e22e&#34;&gt;IdentifierList&lt;/span&gt; ( &lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt; [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;=&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ExpressionList&lt;/span&gt; ] | &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;=&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ExpressionList&lt;/span&gt; ) .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;案例代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;U&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;W&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float32&lt;/span&gt; = &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;u&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2.0&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;简短声明&#34;&gt;简短声明&lt;/h3&gt;&#xA;&lt;p&gt;一个短变量声明。使用语法：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ShortVarDecl = IdentifierList &amp;#34;:=&amp;#34; ExpressionList .&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;案例代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼进脑子了&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pipe&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;coord&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;网友疑惑&#34;&gt;网友疑惑&lt;/h2&gt;&#xA;&lt;p&gt;在我们群里的 Go 读者提了这问题后，我也搜了搜相关资料。发现在 stackoverflow 上也有人提出了类似的疑惑：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 为什么不在语言层面支持 map 并发？</title>
      <link>https://note.lican.site/posts/posts/go/map-con/</link>
      <pubDate>Sat, 05 Feb 2022 15:55:22 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/map-con/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;很多小伙伴学习 Go 语言的语法时，可能只是轻轻地看到过这个问题，结果一旦上手，多多少少一个组内总会碰到过几次。&lt;/p&gt;&#xA;&lt;p&gt;甚至会发现有一定年限的程序员也会遇到。有小伙伴疑惑了，这么折腾，为什么 Go 不直接在语言层面就支持 map 并发，那得有多香？&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么原生不支持&#34;&gt;为什么原生不支持&lt;/h2&gt;&#xA;&lt;p&gt;凭什么 Go 官方还不支持，难不成太复杂了，性能太差了，到底是为什么？&lt;/p&gt;&#xA;&lt;p&gt;官方答复原因如下（via @go faq）：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;典型使用场景：map 的典型使用场景是不需要从多个 goroutine 中进行安全访问。&lt;/li&gt;&#xA;&lt;li&gt;非典型场景（需要原子操作）：map 可能是一些更大的数据结构或已经同步的计算的一部分。&lt;/li&gt;&#xA;&lt;li&gt;性能场景考虑：若是只是为少数程序增加安全性，导致 map 所有的操作都要处理 mutex，将会降低大多数程序的性能。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;核心来讲就是：Go 团队在经过了长时间的讨论后，认为原生 map 更应适配典型使用场景。&lt;/p&gt;&#xA;&lt;p&gt;如果为了小部分情况，将&lt;strong&gt;会导致大部分程序付出性能代价&lt;/strong&gt;，决定了不支持原生的并发 map 读写。且在 Go1.6 起，&lt;strong&gt;增加了检测机制&lt;/strong&gt;，并发的话会导致异常。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么要崩溃&#34;&gt;为什么要崩溃&lt;/h2&gt;&#xA;&lt;p&gt;前面有提到一点，在 Go1.6 起会进行原生 map 的并发检测，这是一些人的 “噩梦”。&lt;/p&gt;&#xA;&lt;p&gt;在此有人吐槽到：“明明给我抛个错就好了，凭什么要让我的 Go 进程直接崩溃掉，分分钟给我背个 P0”。&lt;/p&gt;&#xA;&lt;h3 id=&#34;场景枚举&#34;&gt;场景枚举&lt;/h3&gt;&#xA;&lt;p&gt;这里我们假设一下，如果并发读写 map 是以下两种场景：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;产生 panic：程序 panic -&amp;gt; 默认走进 recover -&amp;gt; 没有对并发 map 进行处理 -&amp;gt; map 存在脏数据 -&amp;gt; 程序使用脏数据 -&amp;gt; 产生**未知((影响。&lt;/li&gt;&#xA;&lt;li&gt;产生 crash：程序 crash -&amp;gt; 直接崩溃 -&amp;gt; 保全数据（数据正常）-&amp;gt; 产生**明确((风险。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;你会选择哪一种方案呢？Go 官方在两者的风险衡量中选择了第二种。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 泛型的 3 个核心设计，你学会了吗？</title>
      <link>https://note.lican.site/posts/posts/go/generics-design/</link>
      <pubDate>Sat, 05 Feb 2022 15:52:46 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/generics-design/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;Go1.18 的泛型是闹得沸沸扬扬，虽然之前写过很多篇针对泛型的一些设计和思考。但因为泛型的提案之前一直还没定型，所以就没有写完整介绍。&lt;/p&gt;&#xA;&lt;p&gt;如今已经基本成型，就由煎鱼带大家一起摸透 Go 泛型。本文内容主要涉及泛型的 3 大概念，非常值得大家深入了解。&lt;/p&gt;&#xA;&lt;p&gt;如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;类型参数。&lt;/li&gt;&#xA;&lt;li&gt;类型约束。&lt;/li&gt;&#xA;&lt;li&gt;类型推导。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;类型参数&#34;&gt;类型参数&lt;/h2&gt;&#xA;&lt;p&gt;类型参数，这个名词。不熟悉的小伙伴咋一看就懵逼了。&lt;/p&gt;&#xA;&lt;p&gt;泛型代码是使用抽象的数据类型编写的，我们将其称之为类型参数。当程序运行通用代码时，类型参数就会被类型参数所取代。也就是&lt;strong&gt;类型参数是泛型的抽象数据类型&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;简单的泛型例子：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Print&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;代码有一个 &lt;code&gt;Print&lt;/code&gt; 函数，它打印出一个片断的每个元素，其中片断的元素类型，这里称为 T，是未知的。&lt;/p&gt;&#xA;&lt;p&gt;这里引出了一个要做泛型语法设计的点，那就是：T 的&lt;strong&gt;泛型类型参数，应该如何定义&lt;/strong&gt;？&lt;/p&gt;&#xA;&lt;p&gt;在现有的设计中，分为两个部分：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;类型参数列表：&lt;strong&gt;类型参数列表将会出现在常规参数的前面&lt;/strong&gt;。为了区分类型参数列表和常规参数列表，类型参数列表&lt;strong&gt;使用方括号&lt;/strong&gt;而不是小括号。&lt;/li&gt;&#xA;&lt;li&gt;类型参数约束：如同常规参数有类型一样，类型参数也有元类型，被称为约束（后面会进一步介绍）。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;结合完整的例子如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Print 可以打印任何片断的元素。&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Print 有一个类型参数 T，并有一个单一的（非类型）的 s，它是该类型参数的一个片断。&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Print&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#75715e&#34;&gt;// do something...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，我们声明了一个函数 &lt;code&gt;Print&lt;/code&gt;，其有一个类型参数 T，类型约束为 &lt;code&gt;any&lt;/code&gt;，表示为任意的类型，作用与 &lt;code&gt;interface{}&lt;/code&gt; 一样。他的入参变量 &lt;code&gt;s&lt;/code&gt; 是类型 T 的切片。&lt;/p&gt;&#xA;&lt;p&gt;函数声明完了，在函数调用时，我们需要指定类型参数的类型。如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Print&lt;/span&gt;[&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;]([]&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，我们指定了传入的类型参数为 int，并传入了 &lt;code&gt;[]int{1, 2, 3}&lt;/code&gt; 作为参数。&lt;/p&gt;&#xA;&lt;p&gt;其他类型，例如 float64:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 读者提问：值为 nil 也能调用函数，太神奇了吧？</title>
      <link>https://note.lican.site/posts/posts/go/nil-func/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:27 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/nil-func/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近在我们 Go 的技术交流群里，有一个小伙伴提了一个程序方面的问题，还挺有意思的，分享给大家。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/0774f643-81a2-4458-b557-05c81a9572ef.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;示例&#34;&gt;示例&lt;/h2&gt;&#xA;&lt;p&gt;示例程序如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Hello&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼进脑子了&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Hello&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这段程序的运行结果是什么？&lt;/p&gt;&#xA;&lt;p&gt;从程序的分析来看，变量 &lt;code&gt;t&lt;/code&gt; 并没有初始化，只是声明了类型。然后就直接调用了 &lt;code&gt;Hello&lt;/code&gt; 方法，像是 nil 调用函数，理论上应该出现恐慌（panic）。&lt;/p&gt;&#xA;&lt;p&gt;运行结果是：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;panic: runtime error: invalid memory address or nil pointer dereference&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;对不对呢？&lt;/p&gt;&#xA;&lt;p&gt;显然，真正的运行结果是：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;脑子进煎鱼了&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;请你思考一下，想想这是为什么？&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么&#34;&gt;为什么&lt;/h2&gt;&#xA;&lt;p&gt;问题的原因是：很多小伙伴认为变量 &lt;code&gt;t&lt;/code&gt; 的值都是 nil 了，不应该还能调用到才对。&lt;/p&gt;&#xA;&lt;p&gt;更抽象化来讲，就是 ”程序是如何检查对象指针来寻找和调度所需函数“。&lt;/p&gt;&#xA;&lt;p&gt;实际上，在 Go 中，表达式 &lt;code&gt;Expression.Name&lt;/code&gt; 的语法，所调用的函数完全由 &lt;code&gt;Expression&lt;/code&gt; 的类型决定。&lt;/p&gt;&#xA;&lt;p&gt;其调用函数的指向不是由该表达式的特定运行时值来决定，包括我们前面所提到的 nil。&lt;/p&gt;&#xA;&lt;p&gt;具体如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Sometype&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Somemethod&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;firstArg&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本质上是：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 有哪些无法恢复的致命场景？</title>
      <link>https://note.lican.site/posts/posts/go/throw/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:26 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/throw/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;有一次事故现场，在紧急恢复后，他正在排查代码，查了好一会。我回头一看，这错误提醒很明显就是致命错误，较好定位。&lt;/p&gt;&#xA;&lt;p&gt;但此时，他竟然在查 panic-recover 是不是哪里漏了，我表示大受震惊&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;今天就由煎鱼给大家分享一下错误类型有哪几种，又在什么场景下会触发。&lt;/p&gt;&#xA;&lt;h2 id=&#34;错误类型&#34;&gt;错误类型&lt;/h2&gt;&#xA;&lt;h3 id=&#34;error&#34;&gt;error&lt;/h3&gt;&#xA;&lt;p&gt;第一种是 Go 中最标准的 error 错误，其真身是一个 interface{}。&lt;/p&gt;&#xA;&lt;p&gt;如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Error&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在日常工程中，我们只需要创建任意结构体，实现了 Error 方法，就可以认为是 error 错误类型。&lt;/p&gt;&#xA;&lt;p&gt;如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;errorString&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;errorString&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Error&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在外部调用标准库 API，一般如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Open&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;filename.ext&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// do something with the open *File f&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们会约定最后一个参数为 error 类型，一般常见于第二个参数，可以有个约定俗成的习惯。&lt;/p&gt;&#xA;&lt;h3 id=&#34;panic&#34;&gt;panic&lt;/h3&gt;&#xA;&lt;p&gt;第二种是 Go 中的异常处理 panic，能够产生异常错误，结合 panic+recover 可以扭转程序的运行状态。&lt;/p&gt;&#xA;&lt;p&gt;如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    panic(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;a problem&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Create&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tmp/file&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        panic(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;</description>
    </item>
    <item>
      <title>长达 12 年，Go 才引入泛型，是政治，还是技术问题？</title>
      <link>https://note.lican.site/posts/posts/go/import-generics/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:25 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/import-generics/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前两天 Go1.18 beta1 已经发布，距离正式发布 Go1.18 的生产可用还有 2 个月，也就是泛型即将正式面世。&lt;/p&gt;&#xA;&lt;p&gt;最近正在收集泛型的一些资料，看到在 2015 年有人在 Hacker News 上的《&lt;a href=&#34;https://news.ycombinator.com/item?id=9622417&#34; title=&#34;Go 1.5 max procs default&#34;&gt;Go 1.5 max procs default&lt;/a&gt;》吐槽 Go 不支持泛型是 “政治” 原因&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;看了还是有些意义的，&lt;strong&gt;与现在的矛盾点基本一致&lt;/strong&gt;，为此分享给大家。&lt;/p&gt;&#xA;&lt;h2 id=&#34;网友吐槽&#34;&gt;网友吐槽&lt;/h2&gt;&#xA;&lt;p&gt;网友 @aikah 认为 Go 团队不太可能在语言中加入泛型，这显然是一个政治问题而不是技术问题。错误处理也是如此。&lt;/p&gt;&#xA;&lt;p&gt;和许多人一样，该网友&lt;strong&gt;认为 Go 在极简主义和功能之间没有取得正确的平衡&lt;/strong&gt;。反对泛型的人赞成用编译时类型检查（总是安全的）换取运行时类型断言（可能失败）。&lt;/p&gt;&#xA;&lt;p&gt;他们拒绝承认这一事实。这就是他们反对泛型的论点，并将最终损害语言的任何潜在增长。他们基本上是在违背自己的利益。&lt;/p&gt;&#xA;&lt;h2 id=&#34;官方回复&#34;&gt;官方回复&lt;/h2&gt;&#xA;&lt;p&gt;Russ Cox 做了正式的回复：很抱歉，但不是：&lt;strong&gt;泛型是一个技术问题&lt;/strong&gt;，不是一个政治问题。&lt;/p&gt;&#xA;&lt;p&gt;Go 团队并&lt;strong&gt;不反对泛型本身&lt;/strong&gt;，只是反对做那些没有被很好理解或不能很好地与 Go 配合的事情。&lt;/p&gt;&#xA;&lt;p&gt;这就是核心观点和矛盾点，也从 2009 年，延续到了现在。&lt;/p&gt;&#xA;&lt;h3 id=&#34;会遇到的问题&#34;&gt;会遇到的问题&lt;/h3&gt;&#xA;&lt;p&gt;Go 团队认为要将泛型的概念融入 Go，并与系统的其他部分很好地配合，必须解决一些深层次的技术问题，而我们并没有解决这些问题的办法。&lt;/p&gt;&#xA;&lt;p&gt;关于这些问题，在几年前就在博客上写过一篇《&lt;a href=&#34;https://research.swtch.com/generic&#34; title=&#34;The Generic Dilemma&#34;&gt;The Generic Dilemma&lt;/a&gt;》：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/8ed3923a-d5fc-482f-bb6d-6fff7a19fcaa.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;即使克服了那一页上的问题，也有其他问题，接下来你会遇到的问题是：”如何让程序员以一种有用的、易于解释的方式省略类型注释“。&lt;/p&gt;&#xA;&lt;p&gt;也就是如何更人性、更易于的表达泛型的类型参数。&lt;/p&gt;&#xA;&lt;h4 id=&#34;泛型例子&#34;&gt;泛型例子&lt;/h4&gt;&#xA;&lt;p&gt;举个例子，C++ 允许你写 &lt;code&gt;make_pair(1, &amp;quot;foo&amp;quot;)&lt;/code&gt;，而不是 &lt;code&gt;make_pair&amp;lt;int, string&amp;gt;(1, &amp;quot;foo&amp;quot;)&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;为了达到这种效果，推断注释背后的逻辑需要几页几页的规范，这并不是一个特别容易理解的编程模型，当事情出错时，编译器也不能轻易解释。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 为什么不支持可重入锁？</title>
      <link>https://note.lican.site/posts/posts/go/again-mutex/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:24 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/again-mutex/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;程序里的锁，是很多小伙伴在写分布式应用时用的最多的一个利器之一。&lt;/p&gt;&#xA;&lt;p&gt;使用 Go 的同学里，绝大部分都有其他语言的经验，就会对其中一点有疑惑，那就是 &lt;strong&gt;Go 里的锁，竟然不支持可重入&lt;/strong&gt;？&lt;/p&gt;&#xA;&lt;p&gt;为此，今天煎鱼带大家一起来了解这里的设计考量，看看为什么。&lt;/p&gt;&#xA;&lt;h2 id=&#34;可重入锁&#34;&gt;可重入锁&lt;/h2&gt;&#xA;&lt;p&gt;如果对已经上锁的普通互斥锁进行 “加锁” 操作，其结果要么失败，要么会阻塞至解锁。&lt;/p&gt;&#xA;&lt;p&gt;锁的场景如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在加锁上：如果是可重入互斥锁，当前尝试加锁的线程如果就是持有该锁的线程时，加锁操作就会成功。&lt;/li&gt;&#xA;&lt;li&gt;在解锁上：可重入互斥锁一般都会记录被加锁的次数，只有执行相同次数的解锁操作才会真正解锁。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;简单来讲，可重入互斥锁是互斥锁的一种，同一线程对其多次加锁不会产生死锁，又或是导致阻塞。&lt;/p&gt;&#xA;&lt;p&gt;不同语言间实现可能或多或少有些区别，但大体意思差不多。&lt;/p&gt;&#xA;&lt;p&gt;请你想一下，Go 是怎么样的呢？&lt;/p&gt;&#xA;&lt;h2 id=&#34;go-支持情况&#34;&gt;Go 支持情况&lt;/h2&gt;&#xA;&lt;p&gt;我们看到以下这个 Go 互斥锁例子：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mu&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sync&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Mutex&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;mu&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Lock&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;mu&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Lock&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这段 Go 程序会阻塞吗？不会，会报以下错误：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fatal error: all goroutines are asleep - deadlock!&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Go 显然是不支持可重入互斥锁的。&lt;/p&gt;&#xA;&lt;h2 id=&#34;官方回复&#34;&gt;官方回复&lt;/h2&gt;&#xA;&lt;h3 id=&#34;go-设计原则&#34;&gt;Go 设计原则&lt;/h3&gt;&#xA;&lt;p&gt;在工程中使用互斥的根本原因是：为了保护不变量，也可以用于保护内、外部的不变量。&lt;/p&gt;&#xA;&lt;p&gt;基于此，Go 在互斥锁设计上会遵守这几个原则。如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在调用 &lt;code&gt;mutex.Lock&lt;/code&gt; 方法时，要保证这些变量的不变性保持，不会在后续的过程中被破坏。&lt;/li&gt;&#xA;&lt;li&gt;在调用 &lt;code&gt;mu.Unlock&lt;/code&gt; 方法时，要保证：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;程序不再需要依赖那些不变量。&lt;/li&gt;&#xA;&lt;li&gt;如果程序在互斥锁加锁期间破坏了它们，则需要确保已经恢复了它们。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;不支持的原因&#34;&gt;不支持的原因&lt;/h3&gt;&#xA;&lt;p&gt;讲了 Go 自己的设计原则后，那为什么不支持可重入呢？&lt;/p&gt;&#xA;&lt;p&gt;其实 Russ Cox 于 2010 年在《&lt;a href=&#34;https://groups.google.com/g/golang-nuts/c/XqW1qcuZgKg/m/Ui3nQkeLV80J&#34; title=&#34;Experimenting with GO&#34;&gt;Experimenting with GO&lt;/a&gt;》就给出了答复，认为递归（又称：重入）互斥是个坏主意，这个设计并不好。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 泛型玩出花，详解新提案 switch type！</title>
      <link>https://note.lican.site/posts/posts/go/switch-type/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:23 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/switch-type/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前面写过好几篇 Go 泛型的语法、案例介绍，新的一手 Go 消息。实际上，随着一些提案被接受，新的提案也逐渐冒出。&lt;/p&gt;&#xA;&lt;p&gt;这不，我发现有了泛型后，大家可以更进一步玩出花来了。看到了一个 ”新“ 提案《&lt;a href=&#34;https://github.com/golang/go/issues/45380&#34; title=&#34;proposal: spec: generics: type switch on parametric types&#34;&gt;proposal: spec: generics: type switch on parametric types&lt;/a&gt;》，讲的就是增加泛型后的参数类型上的类型开关诉求。&lt;/p&gt;&#xA;&lt;p&gt;跟着煎鱼一起掌握新的 Go 知识吧！&lt;/p&gt;&#xA;&lt;h2 id=&#34;新提案&#34;&gt;新提案&lt;/h2&gt;&#xA;&lt;p&gt;新的提案内容是希望增加一个新的变种语句，允许在 switch 语句中使用泛型时，能够进一步便捷的约束其类型参数。&lt;/p&gt;&#xA;&lt;p&gt;例如：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;A1&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;A2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;A3&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是 switch-type 语句的 &lt;strong&gt;T 类型可以是一个泛型的类型参&lt;/strong&gt;，case 所对应的的类型可以是任何类型，包括泛型的约束类型。&lt;/p&gt;&#xA;&lt;p&gt;假设类型 T 的类型有可能是以下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;C&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;A&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以借助泛型的近似元素来约束：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;C&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;A1&lt;/span&gt; | &lt;span style=&#34;color:#a6e22e&#34;&gt;A2&lt;/span&gt; | &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt; | &lt;span style=&#34;color:#a6e22e&#34;&gt;An&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;甚至还可以在 case 上有新的写法：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; {~&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;}:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在支持泛型后，&lt;strong&gt;switch 在 type 和 case 上会存在很多种可能性&lt;/strong&gt;，需要进行具体的特性支持，这个提案就是为此出现。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 为什么不支持类和继承？</title>
      <link>https://note.lican.site/posts/posts/go/class-extends/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:22 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/class-extends/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;大家在早期学习 Go 时，一旦跨过语法的阶段后。马上就会进入到一个新的纠结点，Go 不支持面向对象吗？&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/a299d98d-e46c-4a6d-8362-02f957e86b10.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;这门编程语言里没有类（class）、继承（extends），&lt;del&gt;没法一把搜了，面试问啥面向对象（OOP）&lt;/del&gt;？&lt;/p&gt;&#xA;&lt;p&gt;今天煎鱼就带大家一起来了解这之中的思考，Go 真的不支持吗？&lt;/p&gt;&#xA;&lt;h2 id=&#34;类和继承&#34;&gt;类和继承&lt;/h2&gt;&#xA;&lt;h3 id=&#34;类是什么&#34;&gt;类是什么&lt;/h3&gt;&#xA;&lt;p&gt;类（class）在面向对象编程中是一种面向对象计算机编程语言的构造，是创建对象的蓝图，描述了所创建的对象共同的特性和方法（via @维基百科）。&lt;/p&gt;&#xA;&lt;p&gt;例子如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SimpleClass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 声明属性&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; $var &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;脑子进煎鱼了&amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 声明方法&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;displayVar&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; $this&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个类的定义都以关键字 class 开头，后面跟着类名，后面跟着一对花括号，里面包含有类的属性与方法的定义。&lt;/p&gt;&#xA;&lt;h3 id=&#34;继承是什么&#34;&gt;继承是什么&lt;/h3&gt;&#xA;&lt;p&gt;继承是面向对象软件技术当中的一个概念，如果一个类别 B “继承自”另一个类别 A，就把这个 B 称为 “A的子类”，而把 A 称为 “B的父类别” 也可以称 “A 是 B 的超类”（via @维基百科）。&lt;/p&gt;&#xA;&lt;p&gt;例子如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 父类&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Foo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;printItem&lt;/span&gt;($string)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;煎鱼1: &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; $string &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PHP_EOL&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;printPHP&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;PHP is great.&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PHP_EOL&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 子类&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Bar&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Foo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;printItem&lt;/span&gt;($string)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;煎鱼2: &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; $string &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PHP_EOL&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;继承有如下两个特性：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 新关键字 any，interface 会成历史吗？</title>
      <link>https://note.lican.site/posts/posts/go/any/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:21 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/any/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;大家在看 Go1.18 泛型的代码时，不知道是否有留意到一个新的关键字 any。&lt;/p&gt;&#xA;&lt;p&gt;例子如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Print&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;之前没有专门提过，但有没有小伙伴以为这个关键字，是泛型代码专属的？&lt;/p&gt;&#xA;&lt;p&gt;其实不是&amp;hellip;在这次新的 Go1.18 更新中，any 是作为一个新的关键字出现，&lt;strong&gt;any 有一个真身，本质上是 interface{} 的别名&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt; = &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是，在常规代码中，也可以直接使用：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.(&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;进脑子煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼进脑子了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3.1415&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼好！&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从使用层面来讲，新的关键字 any 会比 interface{} 方便不少，毕竟少打了好多个词，更快了，其实也是参照现有 rune 类型的做法。&lt;/p&gt;&#xA;&lt;p&gt;增加新关键字后的对比如下：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;长声明&lt;/th&gt;&#xA;          &lt;th&gt;短声明&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;func f[T interface{}](s []T) []T&lt;/td&gt;&#xA;          &lt;td&gt;func f[T any](s []T) []T&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;func f(a interface{})&lt;/td&gt;&#xA;          &lt;td&gt;func f(a any)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;var a interface{}&lt;/td&gt;&#xA;          &lt;td&gt;var a any&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;我们在了解他的便利性后，再从代码一致性和可读性来讲，是有些问题的，会造成一定的疑惑。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Rust 内讧，PHP 主力淡出？Go：好好放假</title>
      <link>https://note.lican.site/posts/posts/go/rust-php/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:20 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/rust-php/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;现在已经是 2021 年的 Q4 季度了，许多职场人都忙的飞起，被 PPT 各种轰炸。&lt;/p&gt;&#xA;&lt;p&gt;在上周，看到几门语言的社区都发生了一些大事，煎鱼表示大受震撼，来说几句我的看法。&lt;/p&gt;&#xA;&lt;h2 id=&#34;php-主力淡出&#34;&gt;PHP 主力淡出&lt;/h2&gt;&#xA;&lt;p&gt;在 11 月 23 日，看到 PHP 的主力开发 &lt;a href=&#34;https://twitter.com/nikita_ppv&#34; title=&#34;Nikita Popov&#34;&gt;Nikita Popov&lt;/a&gt; 在论坛上发文宣布将&lt;strong&gt;不再以专业身份从事 PHP 工作&lt;/strong&gt;，投入到 PHP 开发中的时间将会大幅度减少。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/82ba0eaf-fa69-4bee-9cbc-ec385d3b9793.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;根据 Jetbrains 分享的消息来看，可得知 Nikita Popov 在高中（2011 年）时就开始参与 PHP 开发，截止现在已有 10 年经验了。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/93d9d4c9-9863-42fe-9d2c-f5ada62210c2.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;他离开的原因，我看了一遍帖子，众说纷纭，业内猜测有以下两个观点：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;迫于生活压力，过多精力投入维护开源项目收入不高。&lt;/li&gt;&#xA;&lt;li&gt;PHP 新版本特性受阻等原因，把精力从 PHP 转到 LLVM。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;也因此，PHP 社区加速宣布成立 PHP 基金会《&lt;a href=&#34;https://blog.jetbrains.com/phpstorm/2021/11/the-php-foundation/&#34; title=&#34;The New Life of PHP – The PHP Foundation&#34;&gt;The New Life of PHP – The PHP Foundation&lt;/a&gt;》。基金会所募集的资金，将会用于资助开发者在 PHP 上工作。&lt;/p&gt;&#xA;&lt;p&gt;基金会的宣发上，讲的很清楚，为的就是&lt;strong&gt;避免再发生失去 PHP 的主要贡献者&lt;/strong&gt;的事情发生，这影响是非常之巨大的。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 新语法挺丑？最新的泛型类型约束介绍</title>
      <link>https://note.lican.site/posts/posts/go/118-constraints/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:19 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/118-constraints/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;近期我们在分享《&lt;a href=&#34;https://mp.weixin.qq.com/s/22TeQOUjf_glPX3QLPX8yw&#34;&gt;3 件与 Go 开发者有关的大小事&lt;/a&gt;》时，里面有一部分有提到 Go 泛型的约束类型语法又又又变了。&lt;/p&gt;&#xA;&lt;p&gt;在评论区里看到不少的读者朋友大呼泛型的新类型约束语法挺丑，不如原本的好&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;如下：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/8e734a8f-612c-41c3-a0cd-8a5fe6eea483.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;为此，今天煎鱼就带大家来看看，为什么会发生泛型的新语法会这种改变？&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题背景&#34;&gt;问题背景&lt;/h2&gt;&#xA;&lt;p&gt;原本 @Ian Lance Taylor 设计的的泛型类型关键字如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;int8&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;int16&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;int32&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;int64&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;uint&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;uintptr&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;float32&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;complex64&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;complex128&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看起来好像非常 “顺眼”。但在《&lt;a href=&#34;https://github.com/golang/go/issues/41716&#34; title=&#34;proposal: Go 2: sum types using interface type lists&#34;&gt;proposal: Go 2: sum types using interface type lists&lt;/a&gt;》中社区进行了热烈的讨论。&lt;/p&gt;&#xA;&lt;p&gt;认为该类型约束的关键字，过于 “模棱两可”。像是 @Damien Neil 所提出的以下两个例子。&lt;/p&gt;&#xA;&lt;p&gt;结构体的例子：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mustIncludeDefaultCase&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MySum&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;mustIncludeDefaultCase&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不明确的点之一，如果类型列表包含一个未导出的类型，那又应该是如何处理呢？&lt;/p&gt;&#xA;&lt;p&gt;接口的例子：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; { &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int16&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;int32&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;.(&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int16&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int32&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你认为程序会跑进哪个 switch-case 的代码块里呢，是 int16，还是 int32？&lt;/p&gt;</description>
    </item>
    <item>
      <title>泛型是双刃剑？Go1.18 编译会慢近 20%</title>
      <link>https://note.lican.site/posts/posts/go/118-build/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:18 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/118-build/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;目前 Go 的泛型已经在稳定推进的过程，在 Go1.18 将会释出正式的第一版。不过前两天我看到 @danscales 提出的《cmd/compile: Go 1.18 compile time may be about 18% slower than Go.17 (largely from changes due to generics)》。&lt;/p&gt;&#xA;&lt;p&gt;作者表示在 Go1.18 有了泛型后，编译速度将会变慢，虽然不意外，说明副作用还是有的，升级需谨慎。&lt;/p&gt;&#xA;&lt;p&gt;以下为修整后概括的原文信息。&lt;/p&gt;&#xA;&lt;h2 id=&#34;性能分析&#34;&gt;性能分析&lt;/h2&gt;&#xA;&lt;p&gt;这个测试主要是测试 Go 泛型对 Go 编译器带来的影响，并没有输入大量的测试用例，是最简单的比较，仅代表大部分的差异。&lt;/p&gt;&#xA;&lt;p&gt;比较的内容是 Go 泛型的 -G=0 和 -G=3 模式下的编译时间。&lt;/p&gt;&#xA;&lt;p&gt;分别代表以下含义：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;-G=0 模式：默认不打开泛型的模式。&lt;/li&gt;&#xA;&lt;li&gt;-G=3 模式：打开泛型的模式。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Go 1.18 中的 -G=0 模式和 Go 1.17 模式的比较显示，由于非泛型的变化，编译器的速度可能降低了~1%（因为 -G=0 模式不支持泛型）。&lt;/p&gt;&#xA;&lt;p&gt;Go 1.18 的编译时间可能比 Go 1.17 慢 15-18%，这主要是由于实现泛型所带来的变化，也就是 Go1.18 开启泛型下，编译时间会变慢。&lt;/p&gt;&#xA;&lt;h2 id=&#34;差异在哪&#34;&gt;差异在哪&lt;/h2&gt;&#xA;&lt;p&gt;大部分的差异是由于新的编译器前端处理，因为 SSA 后端对于泛型完全没有变化。&lt;/p&gt;</description>
    </item>
    <item>
      <title>分享 Go 最近的几件周边大小事</title>
      <link>https://note.lican.site/posts/posts/go/news115/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:17 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/news115/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近可能是因为 Q4 了，又恰逢 Go1.18 快要发布，各路 Go 语言的新消息层出不穷。&lt;/p&gt;&#xA;&lt;p&gt;今天煎鱼带大家一起来了解下最近社区发生的几件大小事，当然，我只讲一些核心的内容。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/d6f8bfde-141c-405b-8ec5-779d31b0283d.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;go-诞生-12-年&#34;&gt;Go 诞生 12 年&lt;/h2&gt;&#xA;&lt;p&gt;在 2021 年 11 月 10 日，是 Go 语言开源版本的 12 岁生日。Go 官方博客发表《&lt;a href=&#34;https://go.dev/blog/12years&#34; title=&#34;Twelve Years of Go&#34;&gt;Twelve Years of Go&lt;/a&gt;》。&lt;/p&gt;&#xA;&lt;p&gt;主体内容分为三块介绍：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;回顾过去一年的核心变更。&lt;/li&gt;&#xA;&lt;li&gt;展望明年的特性计划。&lt;/li&gt;&#xA;&lt;li&gt;介绍今年做的 Go 相关分享。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;回顾过去&#34;&gt;回顾过去&lt;/h3&gt;&#xA;&lt;p&gt;是对过去一年的版本更新进行说明：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Go1.16：默认启用 Go modules，增加 MacOS ARM64 的支持，新支持文件系统接口和嵌入文件的特性。&lt;/li&gt;&#xA;&lt;li&gt;Go1.17：Go 函数改为基于寄存器的调用规范（提高了 5~15% 的性能），增加 Windows ARM64 支持，还引入了模块裁剪等功能。&lt;/li&gt;&#xA;&lt;li&gt;Go1.18：预计支持模糊测试（Go fuzzing）、泛型等强大的新特性。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;核心总结：今年大力推动 Go modules，提高了 Go 函数的性能，增加了更多的计算机架构支持，以及若干改进和优化（例如：TLS）。&lt;/p&gt;&#xA;&lt;p&gt;也为了推广 Go 语言，做出了更多的努力和资料培训（Gin 也因此更上了一层楼）。&lt;/p&gt;&#xA;&lt;h3 id=&#34;后续安排&#34;&gt;后续安排&lt;/h3&gt;&#xA;&lt;p&gt;我们核心关注泛型方面的消息，泛型将会是 2022 年的核心重点之一。规划如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Go 1.18 中的初始版本只是泛型的开始，将会在此版本使用泛型并学习哪些有效、哪些无效。&lt;/li&gt;&#xA;&lt;li&gt;在确定泛型的 “实践” 后，会输出 “最佳实践”，并决定何时追加泛型实现到标准库和第三方库中。&lt;/li&gt;&#xA;&lt;li&gt;期待在 &lt;strong&gt;Go1.19&lt;/strong&gt;（也就是 2022.08）及更高版本将&lt;strong&gt;进一步完善泛型&lt;/strong&gt;的设计和实现，并将它们进一步整合到整体的 Go 体验中（也就是工具链等）。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;核心总结：明年要继续大力推进泛型，先尝鲜，再出最佳实践，进而融合进 Go 体系中，路还比较远。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 为什么不支持函数重载和参数默认值？</title>
      <link>https://note.lican.site/posts/posts/go/func-reload/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:16 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/func-reload/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;大家在初学习 Go 语言时，带着其他语言的习惯，总是会有些不习惯，感觉非常不能理解，直打问号。&lt;/p&gt;&#xA;&lt;p&gt;其中一点就是 Go 语言不支持函数重载和参数默认值，觉得使用起来很不方便。&lt;/p&gt;&#xA;&lt;p&gt;为此，在这篇文章中煎鱼就和大家一起来了解为什么，有又会怎么样。&lt;/p&gt;&#xA;&lt;h2 id=&#34;函数重载&#34;&gt;函数重载&lt;/h2&gt;&#xA;&lt;p&gt;函数重载（function overloading），也叫方法重载。是某些编程语言（如 C++、C#、Java、Swift、Kotlin 等）具有的一项特性。&lt;/p&gt;&#xA;&lt;p&gt;该特性&lt;strong&gt;允许创建多个具有不同实现的同名函数&lt;/strong&gt;，对重载函数的调用会运行其适用于调用上下文的具体实现。&lt;/p&gt;&#xA;&lt;p&gt;从功能上来讲，就是允许一个函数调用根据上下文执行不同的方法，达到调用同一个函数名，执行不同的方法。&lt;/p&gt;&#xA;&lt;p&gt;一个简单的例子：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c++&#34; data-lang=&#34;c++&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Volume&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; s) {  &lt;span style=&#34;color:#75715e&#34;&gt;// 立方体的体积。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; s &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; s &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; s;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Volume&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt; r, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; h) {  &lt;span style=&#34;color:#75715e&#34;&gt;// 圆柱体的体积。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3.1415926&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; r &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; r &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static_cast&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;(h);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Volume&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; l, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; h) {  &lt;span style=&#34;color:#75715e&#34;&gt;// 长方体的体积。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; l &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; h;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; Volume(&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; Volume(&lt;span style=&#34;color:#ae81ff&#34;&gt;2.5&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; Volume(&lt;span style=&#34;color:#ae81ff&#34;&gt;100l&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;75&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述例子中，实现了 3 个同名的 &lt;code&gt;Volume&lt;/code&gt; 函数，但是 3 个函数的入参个数、类型均不一样，也代表了不同的实现目的。&lt;/p&gt;&#xA;&lt;p&gt;在主函数 &lt;code&gt;main&lt;/code&gt; 中，传入了不同的入参，编译器或运行时再进行内部处理，从程序上来看达到了调用不同函数的目的。&lt;/p&gt;</description>
    </item>
    <item>
      <title>回归现实：Go Leader 对 1.18 泛型的期望</title>
      <link>https://note.lican.site/posts/posts/go/118-leader-generics/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:16 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/118-leader-generics/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前段时间根据 Go 泛型的最新动态，我写了一篇《&lt;a href=&#34;https://mp.weixin.qq.com/s/yWEM2EAwv80ZUFjbKGtpNA&#34;&gt;出泛型后 API 怎么办？Go 开发者要注意了&lt;/a&gt;》文章，引发了不少小伙伴的热议。&lt;/p&gt;&#xA;&lt;p&gt;Go 核心开发团队的现任 Leader&#xA;@Russ Cox 在 golang-dev 中正式发表《&lt;a href=&#34;https://groups.google.com/g/golang-dev/c/iuB22_G9Kbo&#34;&gt;expectations for generics in Go 1.18&lt;/a&gt;》对 Go 泛型给出了 “期待”，可以认为是后续的计划了。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/e4693268-eff4-4dd3-ab6b-e4c41d8728cc.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;如果不出现严重的问题，Go 1.18 将会包括对泛型的支持，并且这次泛型的支持将会是有史以来最大的一次语言变化，对以下几点有顾虑：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;最佳实践.&lt;/li&gt;&#xA;&lt;li&gt;生产经验&lt;/li&gt;&#xA;&lt;li&gt;兼容性承诺。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;接下来，煎鱼带大家一起了解 Russ Cox 发表的 Go 泛型进程，知悉官方一手消息。&lt;/p&gt;&#xA;&lt;h2 id=&#34;最佳实践&#34;&gt;最佳实践&lt;/h2&gt;&#xA;&lt;p&gt;Go 团队表示不知道使用泛型的最佳实践是什么，所以给出的官方文档将无法就何时使用泛型和何时不使用泛型给出精确、明确的答案，只可以给出粗略的指导。&lt;/p&gt;&#xA;&lt;p&gt;此处可以参考《Effective Go》的最初版本，是在不间断地写了一整年的 Go 代码后，才正式输出的。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/034ab5db-f125-450d-bc97-9ea42ba94022.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;按照现有的计划，官方只会提供关于如何使用泛型的文档，暂时无法提供任何关于风格、最佳实践的规定性文档。&lt;/p&gt;&#xA;&lt;p&gt;在提供的标准库上，先是已经通过提案的 maps 和 slices库会先放到 golang.org/x/exp 中作为实验，不会保证向后兼容。待成熟后，再推广到标准库中。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/b1d967a2-2ac7-4077-be2e-4d1c4d3565cb.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;可以明确，Go 泛型出来后，社区就会陆续开始百花齐放，接着有官方输出推荐方法了，历史是如此的相似。&lt;/p&gt;&#xA;&lt;h2 id=&#34;生产经验&#34;&gt;生产经验&lt;/h2&gt;&#xA;&lt;p&gt;目前 Go 团队没有关于泛型的生产经验，因此会在文档中给出明确提示，让大家在生产中使用泛型的时候应该适当谨慎。&lt;/p&gt;&#xA;&lt;p&gt;泛型出来后，会陆续涉及到大量的重写工作，但是由于现在处于中间阶段。正在重写的 Go 1.18 工具链去同时适配泛型、非泛型代码是需要时间和经验的，有一定的风险。&lt;/p&gt;&#xA;&lt;p&gt;因此泛型出来后，可能会出现一些意想不到的问题，仅在生产发现（教训）。&lt;/p&gt;&#xA;&lt;h2 id=&#34;兼容性承诺&#34;&gt;兼容性承诺&lt;/h2&gt;&#xA;&lt;p&gt;Go1.18 会和其他 Go1.x 版本一样，保证向后兼容的承诺：不会破坏用 Go 1.18 构建的代码，包括使用泛型的代码。&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么 Go 不支持循环引用？</title>
      <link>https://note.lican.site/posts/posts/go/import-cyc/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:15 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/import-cyc/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;近年来开始学习 Go 语言的开发者越来越多了。很多小伙伴在使用时，就会遇到种种不理解的问题。&lt;/p&gt;&#xA;&lt;p&gt;其中一点就是循环引入的报错：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;package command-line-arguments&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;imports github.com/eddycjy/awesome-project/a&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;imports github.com/eddycjy/awesome-project/b&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;imports github.com/eddycjy/awesome-project/a: import cycle not allowed&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为什么 Go 不支持循环引用呢，这就很不解了，难道还影响性能了？&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/4596a6d1-9286-40b2-8074-cc43f9bbd9f4.png&#34; alt=&#34;图来自网络&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;今天煎鱼将和大家一起了解背后的原因。&lt;/p&gt;&#xA;&lt;h2 id=&#34;案例演示&#34;&gt;案例演示&lt;/h2&gt;&#xA;&lt;p&gt;做一个基本的案例 Demo，便于没接触过的同学建立初步认知。我们的程序分别有 2 个 package。&lt;/p&gt;&#xA;&lt;p&gt;package a 的代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/eddycjy/awesome-project/b&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Hello&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Print&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;package b 的代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/eddycjy/awesome-project/a&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Hello&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Hello&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Print&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;再在 main.go 的文件中调用 &lt;code&gt;a.Hello(&amp;quot;脑子进煎鱼了&amp;quot;)&lt;/code&gt; 方法。&lt;/p&gt;&#xA;&lt;p&gt;一运行，就会出现如下错误提示：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;package command-line-arguments&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;imports github.com/eddycjy/awesome-project/a&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;imports github.com/eddycjy/awesome-project/b&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;imports github.com/eddycjy/awesome-project/a: import cycle not allowed&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;错误的本质原因是 package a 引用了 package b，而 package b 又引用了 package a，造成了循环引用。&lt;/p&gt;</description>
    </item>
    <item>
      <title>出泛型后 API 怎么办？Go 开发者要注意了</title>
      <link>https://note.lican.site/posts/posts/go/generics-apis/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:14 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/generics-apis/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前段时间社区里一下子就爆了，主要是各大媒体引用了 Go 语言之父&#xA;Rob Pike 所提的《go: don&amp;rsquo;t change the libraries in 1.18》。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/08e47a87-cf63-4dec-b0b5-fc178a494c4c.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;很多社交媒体都做了跟进，认为 Rob Pike 是硬性的反对 Go 泛型的 API 改造！&lt;/p&gt;&#xA;&lt;p&gt;如果读者只看了标题，有可能会产生一些误解实际上其表达的意思和近期 Go 社区讨论的事项是有关联的，要一起综合来看。&lt;/p&gt;&#xA;&lt;p&gt;为此，今天煎鱼就和大家一起来理一理，看看 Go 泛型 API 的改造工程，是个怎么一回事？&lt;/p&gt;&#xA;&lt;h2 id=&#34;现状&#34;&gt;现状&lt;/h2&gt;&#xA;&lt;p&gt;马上就是 2021.11 月，连深圳都变冷了&amp;hellip;根据 Go 语言的发布周期，Go1.18 版本的发布，那就是 2022.02 月左右。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/f849c76d-e84d-43d6-96c5-b30df77f1dd9.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;现在给到 Ian Lance Taylor、&#xA;Robert Griesemer 等大佬仅剩 3 个月的时间给大家讨论泛型细节，进一步完善实现，达到生产可用。&lt;/p&gt;&#xA;&lt;p&gt;抛出 Go 泛型的实现进度不说，现在遇到了一个比较大的问题。那就是&lt;strong&gt;实现泛型后 ”如何更新泛型的 API“&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;这之中包含好几个方面，分别是：既有标准库、开源库，新标准库等。不同库之间是不同的人在维护。&lt;/p&gt;&#xA;&lt;p&gt;但这里存在一个大问题，如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/033083ef-d0ba-40f7-93d4-006e65310cf9.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Russ Cox 在 9 月就提出了 ”how to update APIs for generics“ 的疑惑，当时显然这一块还没有共识。在 11 月的现在，从讨论的记录来看，&lt;strong&gt;怎么做还没有达成一个最终的明确共识&lt;/strong&gt;（初步已有，未正式答复）。&lt;/p&gt;</description>
    </item>
    <item>
      <title>上帝视角：Go 语言设计失误，缺乏远见？</title>
      <link>https://note.lican.site/posts/posts/go/go-design/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:13 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-design/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前段时间我有一个朋友在某乎上摸鱼时，给我甩来一个主题为《golang 设计者是如何偿还技术债的》链接。&lt;/p&gt;&#xA;&lt;p&gt;说是让我学习、围观一下社区观点，早日好修成正果，本鱼表示满脸问号。&lt;/p&gt;&#xA;&lt;p&gt;原回答如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/4bbdfe91-b98e-4ae1-bcc6-aa5fc50cd184.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;主要是以极短的话语表述 Go 语言的 “泛型、异常、channel、annotation、模块依赖” 的设计是失误的。&lt;/p&gt;&#xA;&lt;p&gt;说是没有向各种编程语言的 “最佳实践” 各取所需。&lt;/p&gt;&#xA;&lt;h2 id=&#34;那些故事&#34;&gt;那些故事&lt;/h2&gt;&#xA;&lt;p&gt;刚好煎鱼也入门 Go 没几天，偶尔翻过 issues 和 proposal，看了一点点历史事件。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/3213b564-1a53-4dcf-9b69-4f4359bd50db.png&#34; alt=&#34;图来自 Introduction to Golang&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;也从我的观点来围观一下 Go 官方这些年为特性挣扎过的那些事。&lt;/p&gt;&#xA;&lt;p&gt;涉及：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;泛型。&lt;/li&gt;&#xA;&lt;li&gt;错误处理。&lt;/li&gt;&#xA;&lt;li&gt;依赖管理。&lt;/li&gt;&#xA;&lt;li&gt;注解。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;泛型&#34;&gt;泛型&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;为什么 Go 语言这么久都没有泛型&lt;/strong&gt;，是不是 Go 官方不够 “聪明”，抄作业都不会抄。这显然是不对的。&lt;/p&gt;&#xA;&lt;p&gt;有如下几点原因：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;泛型本质上并不是绝对的必需品。&lt;/li&gt;&#xA;&lt;li&gt;泛型不是 Go 语言的早期目标。&lt;/li&gt;&#xA;&lt;li&gt;其他 feature 更重要，把精力放在这些上面，Go 团队人力很有限的。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h4 id=&#34;历史尝试&#34;&gt;历史尝试&lt;/h4&gt;&#xA;&lt;p&gt;在以往的尝试中，Go 团队有人进行过不少的泛型 proposal 试验。基本时间线（via @changkun）如下：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;简述&lt;/th&gt;&#xA;          &lt;th&gt;时间&lt;/th&gt;&#xA;          &lt;th&gt;作者&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Type Functions&lt;/td&gt;&#xA;          &lt;td&gt;2010年&lt;/td&gt;&#xA;          &lt;td&gt;Ian Lance Taylor&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Generalized Types&lt;/td&gt;&#xA;          &lt;td&gt;2011年&lt;/td&gt;&#xA;          &lt;td&gt;Ian Lance Taylor&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Generalized Types v2&lt;/td&gt;&#xA;          &lt;td&gt;2013年&lt;/td&gt;&#xA;          &lt;td&gt;Ian Lance Taylor&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Type Parameters&lt;/td&gt;&#xA;          &lt;td&gt;2013年&lt;/td&gt;&#xA;          &lt;td&gt;Ian Lance Taylor&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;go:generate&lt;/td&gt;&#xA;          &lt;td&gt;2014年&lt;/td&gt;&#xA;          &lt;td&gt;Rob Pike&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;First Class Types&lt;/td&gt;&#xA;          &lt;td&gt;2015年&lt;/td&gt;&#xA;          &lt;td&gt;Bryan C.Mills&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Contracts&lt;/td&gt;&#xA;          &lt;td&gt;2018年&lt;/td&gt;&#xA;          &lt;td&gt;Ian Lance Taylor, Robert Griesemer&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Contracts&lt;/td&gt;&#xA;          &lt;td&gt;2019年&lt;/td&gt;&#xA;          &lt;td&gt;Ian Lance Taylor, Robert Griesemer&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Redundancy in Contracts(2019)&amp;rsquo;s Design&lt;/td&gt;&#xA;          &lt;td&gt;2019年&lt;/td&gt;&#xA;          &lt;td&gt;Ian Lance Taylor, Robert Griesemer&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Constrained Type Parameters(2020, v1)&lt;/td&gt;&#xA;          &lt;td&gt;2020年&lt;/td&gt;&#xA;          &lt;td&gt;Ian Lance Taylor, Robert Griesemer&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Constrained Type Parameters(2020, v2)&lt;/td&gt;&#xA;          &lt;td&gt;2020年&lt;/td&gt;&#xA;          &lt;td&gt;Ian Lance Taylor, Robert Griesemer&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Constrained Type Parameters(2020, v3)&lt;/td&gt;&#xA;          &lt;td&gt;2020年&lt;/td&gt;&#xA;          &lt;td&gt;Ian Lance Taylor, Robert Griesemer&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;我们观察一下，10 年过去了，Ian Lance Taylor 依然在开展泛型提案，持续地在思考着 Go 泛型。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 和 Golang 有什么关系？</title>
      <link>https://note.lican.site/posts/posts/go/go-golang/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:12 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-golang/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近天气也冷了，掐指一算是招聘季了，无论是校招、社招、HR、面试官们都蠢蠢欲动。这不，我有一个朋友的 HR 朋友都有起名困难了，一看 Go 语言的工作说明（Job Description），发现各有不同。&lt;/p&gt;&#xA;&lt;p&gt;如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/629d4aaa-bb01-4e2b-9a43-d671200da099.png&#34; alt=&#34;来自某招聘网站&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;仔细一看，有叫 Go 的，也有叫 Golang，还有叫 GO 的。好家伙，Go 语言有这么多个别名，甚至某乎都讨论了起来。到底叫什么是正确的？&lt;/p&gt;&#xA;&lt;p&gt;为此，今天就由煎鱼带大家理一理，了解这背后的关系。&lt;/p&gt;&#xA;&lt;h2 id=&#34;go-官方定义&#34;&gt;Go 官方定义&lt;/h2&gt;&#xA;&lt;p&gt;从网上的资料来看，大家对 Go 的名字还是比较关注的，对于 Go 团队来讲，仿佛经常被问。例如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;“Go 和 Golang 的关系是什么？”&lt;/li&gt;&#xA;&lt;li&gt;“Go、Golang、GO 哪个对？”&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;甚至在之前探讨 Go2 草案时，也有人开始起 Go2 的名字了，纠结是要叫 “golang2”，还是 “go2lang”：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/7608f5a1-9e77-45b2-a552-a7b7c32ad100.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;其实这是错误的。在 Go FAQ 中有明确的回答这个问题：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/bdb43d48-f5c1-41bb-8495-7bbfc2fc60a6.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;这一门语言称为 “Go”，不叫 “Golang”，也不叫 “GO”。“golang” 只是网站的地址，而不是语言的名称。&lt;/p&gt;&#xA;&lt;p&gt;同时 “GO” 的语言名称叫法也是错误的，虽然官方上的 Logo 是 “GO”：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/67b022a9-49de-4349-861e-77dd25367ac6.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;但这显然只是设计师层面的美观考量，并不是这一门语言的标准定义。&lt;/p&gt;&#xA;&lt;p&gt;因此&lt;strong&gt;这一门语言叫做 “Go” 语言&lt;/strong&gt;，这是正确的，也得到官方认证的，也不曾改变过。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么会有-golang&#34;&gt;为什么会有 Golang&lt;/h2&gt;&#xA;&lt;p&gt;但可能又有小伙伴疑惑了，那为什么 “Golang” 这个别名，如此之火。到底是为什么？&lt;/p&gt;&#xA;&lt;p&gt;这里一共有三点原因，分别是：站点地址（Go FAQ 提到）、搜索引擎、社区和论坛、语言重名。&lt;/p&gt;&#xA;&lt;h3 id=&#34;go-站点地址&#34;&gt;Go 站点地址&lt;/h3&gt;&#xA;&lt;p&gt;Go 团队所期望的 &lt;a href=&#34;https://go.org&#34;&gt;https://go.org&lt;/a&gt; 早就被注册，从网站的底部标识来看，2008 年起建站：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go：我有注解，Java：不，你没有！</title>
      <link>https://note.lican.site/posts/posts/go/annotation/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:11 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/annotation/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;作为一位 Go 程序员，你会发现身边的同事大多都拥有其他语言的编写经验。那势必就会遇到一点，要把新学到的知识和以前的知识建立连接。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/050a9802-1ca2-4634-859d-325a09d418c5.png&#34; alt=&#34;图来自网络&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;特殊在于，Go 有些特性是其他语言有，他没有的。最经典的就是 N 位 Java 同学寻找 Go 语言的注解在哪里，总要解释。&lt;/p&gt;&#xA;&lt;p&gt;为此，今天煎鱼就带大家了解一下 Go 语言的注解的使用和情况。&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是注解&#34;&gt;什么是注解&lt;/h2&gt;&#xA;&lt;h3 id=&#34;了解历史&#34;&gt;了解历史&lt;/h3&gt;&#xA;&lt;p&gt;注解（Annotation）最早出现自何处，翻了一圈并没有找到。但可以明确，在注解的使用中，Java 注解最为经典，为了便于理解，因此我们基于 Java 做初步的注解理解。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/3c9e2434-c5f4-4d7a-bd2d-a8b582b570c8.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;在 2002 年，JSR-175 提出了 《&lt;a href=&#34;https://jcp.org/en/jsr/detail?id=175&#34;&gt;A Metadata Facility for the Java Programming Language&lt;/a&gt;》，也就是为 Java 编程语言提供元数据工具。&lt;/p&gt;&#xA;&lt;p&gt;这就是现在使用最广泛地注解（Annotation）的来源。&#xA;示例如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @annotation1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @annotation2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Hello&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在格式上均以 “@” 作为注解标识来使用。&lt;/p&gt;&#xA;&lt;h3 id=&#34;注解例子&#34;&gt;注解例子&lt;/h3&gt;&#xA;&lt;p&gt;摘抄自 @wikipedia 的一个注解例子：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;//等同于 @Edible(value = true)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;@Edible&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Item item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Carrot();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;@interface&lt;/span&gt; Edible {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;@Author&lt;/span&gt;(first &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Oompah&amp;#34;&lt;/span&gt;, last &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Loompah&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Book book &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Book();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;@interface&lt;/span&gt; Author {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    String &lt;span style=&#34;color:#a6e22e&#34;&gt;first&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    String &lt;span style=&#34;color:#a6e22e&#34;&gt;last&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 该标注可以在运行时通过反射访问。&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;@Retention&lt;/span&gt;(RetentionPolicy.&lt;span style=&#34;color:#a6e22e&#34;&gt;RUNTIME&lt;/span&gt;) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 该标注只用于类内方法。&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;@Target&lt;/span&gt;({ElementType.&lt;span style=&#34;color:#a6e22e&#34;&gt;METHOD&lt;/span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;@interface&lt;/span&gt; Tweezable {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述例子中，通过注解去做了一系列的定义、声明、赋值等。若是对语言既有注解不熟，或是做的比较复杂的注解，就会有一定的理解成本。&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么 Go 语言把类型放在后面？</title>
      <link>https://note.lican.site/posts/posts/go/type-after/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:10 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/type-after/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前段时间看到大家在吵一个话题，那就是 Go 语言的类型声明，抠知识抠的非常细了，就是为什么他要放在后面。&lt;/p&gt;&#xA;&lt;p&gt;示例代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其实在早年 Go 官方估计已经被问烦了，写过一篇《&lt;a href=&#34;https://go.dev/blog/declaration-syntax&#34; title=&#34;Go&#39;s Declaration Syntax&#34;&gt;Go&amp;rsquo;s Declaration Syntax&lt;/a&gt;》来具体介绍和说明情况。&lt;/p&gt;&#xA;&lt;p&gt;为此煎鱼将参考并结合这篇官方资料，带大家一起了解为什么 Go 如此的 “与众不同” ，为什么要把类型放在后面。&lt;/p&gt;&#xA;&lt;h2 id=&#34;类型前置&#34;&gt;类型前置&lt;/h2&gt;&#xA;&lt;p&gt;在业内目前有不少知名语言，也采取的是在声明变量类型时，把类型定义在名字前面。像是 C、C++、C#、Java 等：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; x;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; x &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;基本的格式定义：&amp;lt;data_type&amp;gt; &amp;lt;variable_list&amp;gt;;。&lt;/p&gt;&#xA;&lt;p&gt;上面的声明是一个简单的例子，如果更复杂一些，Go 官方还给出了著名的函数指针的例子：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;fp)(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;更进一步，如果返回值也是个函数指针类型，就会变成：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;fp)(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;), &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;))(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这已经很难看出来是个 fp 的声明了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;类型后置&#34;&gt;类型后置&lt;/h2&gt;&#xA;&lt;p&gt;前面所举例的类型前置的编程语言，很多都是 C 系列中的一者。类型后置的代表，分别有：Go、Rust、Scala、Kotlin 等。&lt;/p&gt;&#xA;&lt;p&gt;其实在很多类型后置的编程语言种，会采取变量名+冒号+类型的方式出现。就像 Rust 一样：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; x: &lt;span style=&#34;color:#66d9ef&#34;&gt;i32&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;基本的格式定义：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;x: int&#xA;p: pointer to int&#xA;a: array[3] of int&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Go 官方参照了这类类型后置的设计，并且为了简洁，进一步去掉了冒号和一些关键字，变成：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们再看回前面 fp 的声明的例子：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 切片导致内存泄露，被坑两次了！</title>
      <link>https://note.lican.site/posts/posts/go/slice-leak/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:09 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/slice-leak/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前段时间在我的 Go 读者群里，有小伙伴们在纠结切片（slice）的问题，我写了这篇《&lt;a href=&#34;https://mp.weixin.qq.com/s/kEQI74ge6VhvNEr1d3JW-Q&#34;&gt;Go 切片这道题，吵了一个下午！&lt;/a&gt;》，引起了一拨各种讨论，还是比较欣慰的。&lt;/p&gt;&#xA;&lt;p&gt;这不，有小伙伴给我提出了新的题材：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/a97e5e6d-b58d-44f2-b858-b9ca12780180.png&#34; alt=&#34;来自读者微信提问&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;提出的是 Go 中很容易踩坑的切片内存泄露问题。作为宠粉的煎鱼肯定不会放过，争取让大家都避开这个 “坑”。&lt;/p&gt;&#xA;&lt;p&gt;今天这篇文章，就由煎鱼带大家来了解这个问题：Go 切片可能可以怎么泄露法？&lt;/p&gt;&#xA;&lt;h2 id=&#34;切片泄露的可能&#34;&gt;切片泄露的可能&lt;/h2&gt;&#xA;&lt;p&gt;在业务代码的编写上，我们经常会接受来自外部的接口数据，再把他插入到对应的数据结构中去，再进行下一步的业务聚合、裁剪、封装、处理。&lt;/p&gt;&#xA;&lt;p&gt;像在 PHP 语言，常常会放到数组（array）中。在 Go 语言，会放到切片（slice）中。因此在 Go 的切片处理逻辑中，常常会涉及到如下类似的动作。&lt;/p&gt;&#xA;&lt;p&gt;示例代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[:&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;仔细想想，&lt;strong&gt;这段程序有没有问题&lt;/strong&gt;，是否存在内存泄露的风险？&lt;/p&gt;&#xA;&lt;p&gt;答案是：有的。有明确的切片内存泄露的可能性和风险。&lt;/p&gt;&#xA;&lt;h2 id=&#34;切片底层结构&#34;&gt;切片底层结构&lt;/h2&gt;&#xA;&lt;p&gt;可能有些小伙伴会疑惑，怎么就有问题了，是哪里有问题？&lt;/p&gt;&#xA;&lt;p&gt;这里就得复习一下切片的底层基本数据结构了，切片在运行时的表现是 SliceHeader 结构体，定义如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SliceHeader&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Data&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uintptr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Len&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Cap&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Data：指向具体的底层数组。&lt;/li&gt;&#xA;&lt;li&gt;Len：代表切片的长度。&lt;/li&gt;&#xA;&lt;li&gt;Cap：代表切片的容量。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;要点是：切片真正存储数据的地方，是一个数组。切片的 Data 属性中&lt;strong&gt;存储的是指向所引用的数组指针地址&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;背后的原因&#34;&gt;背后的原因&lt;/h2&gt;&#xA;&lt;p&gt;在上述案例中，我们有一个包全局变量 a，共有 2 个切片 a 和 b，截取了 b 的一部分赋值给了 a，两者存在着关联。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 什么时候会触发 GC？</title>
      <link>https://note.lican.site/posts/posts/go/when-gc/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:08 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/when-gc/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;Go 语言作为一门新语言，在早期经常遭到唾弃的就是在垃圾回收（下称：GC）机制中 STW（Stop-The-World）的时间过长。&lt;/p&gt;&#xA;&lt;p&gt;那么这个时候，我们又会好奇一点，作为 STW 的起始，Go 语言中什么时候才会触发 GC 呢？&lt;/p&gt;&#xA;&lt;p&gt;今天就由煎鱼带大家一起来学习研讨一轮。&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是-gc&#34;&gt;什么是 GC&lt;/h2&gt;&#xA;&lt;p&gt;在计算机科学中，垃圾回收（GC）是一种自动管理内存的机制，垃圾回收器会去尝试回收程序不再使用的对象及其占用的内存。&lt;/p&gt;&#xA;&lt;p&gt;最早 John McCarthy 在 1959 年左右发明了垃圾回收，以简化 Lisp 中的手动内存管理的机制（来自 @wikipedia）。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/09425faf-f521-43a9-a7fa-1db3c5163914.png&#34; alt=&#34;图来自网络&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么要-gc&#34;&gt;为什么要 GC&lt;/h2&gt;&#xA;&lt;p&gt;手动管理内存挺麻烦，管错或者管漏内存也很糟糕，将会直接导致程序不稳定（持续泄露）甚至直接崩溃。&lt;/p&gt;&#xA;&lt;h2 id=&#34;gc-触发场景&#34;&gt;GC 触发场景&lt;/h2&gt;&#xA;&lt;p&gt;GC 触发的场景主要分为两大类，分别是：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;系统触发：运行时自行根据内置的条件，检查、发现到，则进行 GC 处理，维护整个应用程序的可用性。&lt;/li&gt;&#xA;&lt;li&gt;手动触发：开发者在业务代码中自行调用 &lt;code&gt; runtime.GC&lt;/code&gt; 方法来触发 GC 行为。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;系统触发&#34;&gt;系统触发&lt;/h3&gt;&#xA;&lt;p&gt;在系统触发的场景中，Go 源码的 &lt;code&gt;src/runtime/mgc.go&lt;/code&gt; 文件，明确标识了 GC 系统触发的三种场景，分别如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gcTriggerHeap&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;gcTriggerKind&lt;/span&gt; = &lt;span style=&#34;color:#66d9ef&#34;&gt;iota&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gcTriggerTime&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gcTriggerCycle&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;gcTriggerHeap：当所分配的堆大小达到阈值（由控制器计算的触发堆的大小）时，将会触发。&lt;/li&gt;&#xA;&lt;li&gt;gcTriggerTime：当距离上一个 GC 周期的时间超过一定时间时，将会触发。&#xA;-时间周期以 &lt;code&gt;runtime.forcegcperiod&lt;/code&gt; 变量为准，默认 2 分钟。&lt;/li&gt;&#xA;&lt;li&gt;gcTriggerCycle：如果没有开启 GC，则启动 GC。&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在手动触发的 &lt;code&gt;runtime.GC&lt;/code&gt; 方法中涉及。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;手动触发&#34;&gt;手动触发&lt;/h3&gt;&#xA;&lt;p&gt;在手动触发的场景下，Go 语言中仅有 &lt;code&gt;runtime.GC&lt;/code&gt; 方法可以触发，也就没什么额外的分类的。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go map 如何缩容？</title>
      <link>https://note.lican.site/posts/posts/go/map-reset/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:07 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/map-reset/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天看到 Go 圈子的著名股神（不是我&amp;hellip;），在归类中简单的提到了 Go 语言中 map 的缩容的描述，这让我对其产生了兴趣，想要来一探究竟。&lt;/p&gt;&#xA;&lt;p&gt;我们常常喊扩缩容，扩缩容，但社区里都是清一色分析扩容机制，Go 面试官也都是卷 Go 语言 map 的扩容机制&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;在 &lt;strong&gt;Go 语言中的 map 缩容机制是怎么做的&lt;/strong&gt;呢，今天就由煎鱼带大家一起研讨围观一轮。&lt;/p&gt;&#xA;&lt;h2 id=&#34;基本分析&#34;&gt;基本分析&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 底层源码 src/runtime/map.go 中，扩缩容的处理方法是 grow 为前缀的方法来处理的。&lt;/p&gt;&#xA;&lt;p&gt;其中扩缩容涉及到的是插入元素的操作，对应 mapassign 方法：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mapassign&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;maptype&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;hmap&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; !&lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;growing&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;overLoadFactor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tooManyOverflowBuckets&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;noverflow&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;)) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;hashGrow&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;again&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;hmap&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;growing&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;oldbuckets&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;overLoadFactor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bucketCnt&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; uintptr(&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;) &amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;loadFactorNum&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;bucketShift&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;loadFactorDen&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tooManyOverflowBuckets&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;noverflow&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;noverflow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; uint16(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;核心看到针对扩缩容的判断逻辑：&lt;/p&gt;</description>
    </item>
    <item>
      <title>面试官：为什么 Go 的负载因子是 6.5？</title>
      <link>https://note.lican.site/posts/posts/go/map-65/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:07 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/map-65/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近我有一个朋友，在网上看到一个有趣的段子，引发了我一些兴趣。&lt;/p&gt;&#xA;&lt;p&gt;如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/e81547d8-8de7-4310-a62f-e59ce4c0def2.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;听说是在最后的闲聊、吹水、聊人生、乱扯环节了，不是在技术环节了，所以大家也不用太在意什么技术评估法则（别杠）。&lt;/p&gt;&#xA;&lt;p&gt;煎鱼作为一名技术号主，看到这里的 6.5，就想给大家挖一挖，这到底是何物，和大家一同学习和增长知识！&lt;/p&gt;&#xA;&lt;h2 id=&#34;65-是什么&#34;&gt;6.5 是什么&lt;/h2&gt;&#xA;&lt;p&gt;实际上在 Go 语言中，就存在 6.5 这一概念，与 map 存在直接关系，因此我们需要先了解 map 的基本数据结构，再介绍 6.5 的背景和由来。&lt;/p&gt;&#xA;&lt;p&gt;开始学习 6.5 吧！&lt;/p&gt;&#xA;&lt;h3 id=&#34;了解-map-底层&#34;&gt;了解 map 底层&lt;/h3&gt;&#xA;&lt;p&gt;我以前在写《&lt;a href=&#34;https://eddycjy.com/posts/go/map/2019-03-05-map-access/&#34;&gt;深入理解 Go map：初始化和访问元素&lt;/a&gt;》时有介绍过 map 的基础数据结构。&lt;/p&gt;&#xA;&lt;p&gt;基本结构如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/8679a84f-0d21-485d-b55f-dc9d70d5ddd1.png&#34; alt=&#34;map 基本数据结构&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;其中重要的一个基本单位是 hmap：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hmap&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;flags&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;         &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;noverflow&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;hash0&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;buckets&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldbuckets&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nevacuate&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uintptr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mapextra&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mapextra&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;overflow&lt;/span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldoverflow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nextOverflow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;count：map 的大小，也就是 len() 的值，代指 map 中的键值对个数。&lt;/li&gt;&#xA;&lt;li&gt;flags：状态标识，主要是 goroutine 写入和扩容机制的相关状态控制。并发读写的判断条件之一就是该值。&lt;/li&gt;&#xA;&lt;li&gt;B：&lt;strong&gt;桶，最大可容纳的元素数量，值为 负载因子（默认 6.5） * 2 ^ B，是 2 的指数&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;li&gt;noverflow：溢出桶的数量。&lt;/li&gt;&#xA;&lt;li&gt;hash0：哈希因子。&lt;/li&gt;&#xA;&lt;li&gt;buckets：保存当前桶数据的指针地址（指向一段连续的内存地址，主要存储键值对数据）。&lt;/li&gt;&#xA;&lt;li&gt;oldbuckets，保存旧桶的指针地址。&lt;/li&gt;&#xA;&lt;li&gt;nevacuate：迁移进度。&lt;/li&gt;&#xA;&lt;li&gt;extra：原有 buckets 满载后，会发生扩容动作，在 Go 的机制中使用了增量扩容，如下为细项：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;overflow 为 hmap.buckets （当前）溢出桶的指针地址。&lt;/li&gt;&#xA;&lt;li&gt;oldoverflow 为 hmap.oldbuckets （旧）溢出桶的指针地址。&lt;/li&gt;&#xA;&lt;li&gt;nextOverflow 为空闲溢出桶的指针地址。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;我们关注到 hmap 的 B 字段，其值就是 6.5，他就是我们在苦苦寻找的 6.5，但他又是什么呢？&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 切片这道题，吵了一个下午！</title>
      <link>https://note.lican.site/posts/posts/go/slice-discuss/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:06 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/slice-discuss/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天听到咱 Go 读者交流群里的小伙伴私聊我，表示他们在群里一直在讨论一个问题 slice 相关的问题，众说纷纭。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/f2cedcdf-8f6e-42f9-9009-6f1ee0f74f91.png&#34; alt=&#34;来自煎鱼的聊天记录&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;今天和各位小伙伴们一起来研究一下，避免后续又踩一遍坑，共同进步！&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题代码&#34;&gt;问题代码&lt;/h2&gt;&#xA;&lt;p&gt;引起群内大范围讨论的代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;sl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make([]&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;appenFunc&lt;/span&gt; = &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; = append(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sl&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;appenFunc&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sl&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sl&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sl&lt;/span&gt;[:&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你认为程序的输出结果是什么？&lt;/p&gt;&#xA;&lt;p&gt;是如下的答案：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[]&#xA;[10 20 30]&#xA;[]&#xA;[]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;对吗？&lt;/p&gt;&#xA;&lt;p&gt;看上去很有道理，但错了。正确的结果是：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[]&#xA;[10 20 30]&#xA;[]&#xA;[10 20 30 0 0 0 0 0 0 0]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这下可把大家整懵了，为什么输出 &lt;code&gt;sl&lt;/code&gt; 和 &lt;code&gt;sl[:10]&lt;/code&gt; 的结果差别这么大，这与预期的输出结果不一致。&lt;/p&gt;&#xA;&lt;p&gt;群内小伙伴的问题更明确了，疑惑点是：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sl&lt;/span&gt;)     &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sl&lt;/span&gt;[:&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;]) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上述代码中，&lt;strong&gt;为什么第一个 &lt;code&gt;sl&lt;/code&gt; 打印结果是空的，第二个 &lt;code&gt;sl&lt;/code&gt; 给索引位置就能打印出来&lt;/strong&gt;？&lt;/p&gt;&#xA;&lt;p&gt;也有小伙伴不断在尝试 &lt;code&gt;sl[:10]&lt;/code&gt; 以外的输出，有没有因为一些边界值改变而导致不行。&lt;/p&gt;&#xA;&lt;p&gt;例如：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sl&lt;/span&gt;[:])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你认为这个对应的输出结果是什么？&lt;/p&gt;</description>
    </item>
    <item>
      <title>会诱发 Goroutine 挂起的 27 个原因</title>
      <link>https://note.lican.site/posts/posts/go/goroutine-27/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:06 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/goroutine-27/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;上个月面向读者的提问，我们针对 goroutine 泄露中都会看到的大头 runtime.gopark 函数进行了学习和了解，输出了 《&lt;a href=&#34;https://mp.weixin.qq.com/s/x6Kzn7VA1wUz7g8txcBX7A&#34;&gt;Goroutine 一泄露就看到他，这是个什么？&lt;/a&gt;》。&lt;/p&gt;&#xA;&lt;p&gt;有小伙伴提到，虽然我们知道了 runtime.gopark 函数的缘起和内在，但其实没有提到 &lt;strong&gt;runtime.gopark 的诱发因素，这是我们日常编码中需要关注的&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;今天这篇文章就和大家一起围观 gopark 的 27 个诱发场景。为了方便阅读，我们会根据分类进行说明。&lt;/p&gt;&#xA;&lt;h3 id=&#34;第一部分&#34;&gt;第一部分&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;标识&lt;/th&gt;&#xA;          &lt;th&gt;含义&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonZero&lt;/td&gt;&#xA;          &lt;td&gt;无&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonGCAssistMarking&lt;/td&gt;&#xA;          &lt;td&gt;GC assist marking&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonIOWait&lt;/td&gt;&#xA;          &lt;td&gt;IO wait&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;waitReasonZero：无正式解释，从使用情况来看。主要在 sleep 和 lock 的 2 个场景中使用。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonGCAssistMarking：GC 辅助标记阶段会使得阻塞等待。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonIOWait：IO 阻塞等待时，例如：网络请求等。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;第二部分&#34;&gt;第二部分&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;标识&lt;/th&gt;&#xA;          &lt;th&gt;含义&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonChanReceiveNilChan&lt;/td&gt;&#xA;          &lt;td&gt;chan receive (nil chan)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonChanSendNilChan&lt;/td&gt;&#xA;          &lt;td&gt;chan send (nil chan)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;waitReasonChanReceiveNilChan：对未初始化的 channel 进行读操作。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonChanSendNilChan：对未初始化的 channel 进行写操作。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;第三部分&#34;&gt;第三部分&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;标识&lt;/th&gt;&#xA;          &lt;th&gt;含义&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonDumpingHeap&lt;/td&gt;&#xA;          &lt;td&gt;dumping heap&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonGarbageCollection&lt;/td&gt;&#xA;          &lt;td&gt;garbage collection&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonGarbageCollectionScan&lt;/td&gt;&#xA;          &lt;td&gt;garbage collection scan&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;waitReasonDumpingHeap：对 Go Heap 堆 dump 时，这个的使用场景仅在 runtime.debug 时，也就是常见的 pprof 这一类采集时阻塞。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonGarbageCollection：在垃圾回收时，主要场景是 GC 标记终止（GC Mark Termination）阶段时触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonGarbageCollectionScan：在垃圾回收扫描时，主要场景是 GC 标记（GC Mark）扫描 Root 阶段时触发。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;第四部分&#34;&gt;第四部分&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;标识&lt;/th&gt;&#xA;          &lt;th&gt;含义&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonPanicWait&lt;/td&gt;&#xA;          &lt;td&gt;panicwait&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonSelect&lt;/td&gt;&#xA;          &lt;td&gt;select&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonSelectNoCases&lt;/td&gt;&#xA;          &lt;td&gt;select (no cases)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;waitReasonPanicWait：在 main goroutine 发生 panic 时，会触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonSelect：在调用关键字 select 时会触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonSelectNoCases：在调用关键字 select 时，若一个 case 都没有，会直接触发。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;第五部分&#34;&gt;第五部分&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;标识&lt;/th&gt;&#xA;          &lt;th&gt;含义&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonGCAssistWait&lt;/td&gt;&#xA;          &lt;td&gt;GC assist wait&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonGCSweepWait&lt;/td&gt;&#xA;          &lt;td&gt;GC sweep wait&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonGCScavengeWait&lt;/td&gt;&#xA;          &lt;td&gt;GC scavenge wait&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;waitReasonGCAssistWait：GC 辅助标记阶段中的结束行为，会触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonGCSweepWait：GC 清扫阶段中的结束行为，会触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonGCScavengeWait：GC scavenge 阶段的结束行为，会触发。GC Scavenge 主要是新空间的垃圾回收，是一种经常运行、快速的 GC，负责从新空间中清理较小的对象。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;第六部分&#34;&gt;第六部分&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;标识&lt;/th&gt;&#xA;          &lt;th&gt;含义&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonChanReceive&lt;/td&gt;&#xA;          &lt;td&gt;chan receive&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonChanSend&lt;/td&gt;&#xA;          &lt;td&gt;chan send&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonFinalizerWait&lt;/td&gt;&#xA;          &lt;td&gt;finalizer wait&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;waitReasonChanReceive：在 channel 进行读操作，会触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonChanSend：在 channel 进行写操作，会触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonFinalizerWait：在 finalizer 结束的阶段，会触发。在 Go 程序中，可以通过调用 &lt;code&gt;runtime.SetFinalizer&lt;/code&gt; 函数来为一个对象设置一个终结者函数。这个行为对应着结束阶段造成的回收。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;第七部分&#34;&gt;第七部分&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;标识&lt;/th&gt;&#xA;          &lt;th&gt;含义&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonForceGCIdle&lt;/td&gt;&#xA;          &lt;td&gt;force gc (idle)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonSemacquire&lt;/td&gt;&#xA;          &lt;td&gt;semacquire&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonSleep&lt;/td&gt;&#xA;          &lt;td&gt;sleep&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;waitReasonForceGCIdle：强制 GC（空闲时间）结束时，会触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonSemacquire：信号量处理结束时，会触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonSleep：经典的 sleep 行为，会触发。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;第八部分&#34;&gt;第八部分&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;标识&lt;/th&gt;&#xA;          &lt;th&gt;含义&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonSyncCondWait&lt;/td&gt;&#xA;          &lt;td&gt;sync.Cond.Wait&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonTimerGoroutineIdle&lt;/td&gt;&#xA;          &lt;td&gt;timer goroutine (idle)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonTraceReaderBlocked&lt;/td&gt;&#xA;          &lt;td&gt;trace reader (blocked)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;waitReasonSyncCondWait：结合 &lt;code&gt;sync.Cond&lt;/code&gt; 用法能知道，是在调用 &lt;code&gt;sync.Wait&lt;/code&gt; 方法时所触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonTimerGoroutineIdle：与 Timer 相关，在没有定时器需要执行任务时，会触发。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonTraceReaderBlocked：与 Trace 相关，ReadTrace会返回二进制跟踪数据，将会阻塞直到数据可用。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;第九部分&#34;&gt;第九部分&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;标识&lt;/th&gt;&#xA;          &lt;th&gt;含义&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonWaitForGCCycle&lt;/td&gt;&#xA;          &lt;td&gt;wait for GC cycle&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonGCWorkerIdle&lt;/td&gt;&#xA;          &lt;td&gt;GC worker (idle)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonPreempted&lt;/td&gt;&#xA;          &lt;td&gt;preempted&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;waitReasonDebugCall&lt;/td&gt;&#xA;          &lt;td&gt;debug call&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;waitReasonWaitForGCCycle：等待 GC 周期，会休眠造成阻塞。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonGCWorkerIdle：GC Worker 空闲时，会休眠造成阻塞。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonPreempted：发生循环调用抢占时，会会休眠等待调度。&lt;/li&gt;&#xA;&lt;li&gt;waitReasonDebugCall：调用 GODEBUG 时，会触发。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;&#xA;&lt;p&gt;今天这篇文章是对开头 runtime.gopark 函数的详解文章的一个补充，我们能够对此了解到其诱发的因素。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.17 新特性，优化抛出的错误堆栈</title>
      <link>https://note.lican.site/posts/posts/go/117-errorstack/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:05 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/117-errorstack/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;平时在日常工程中，我们常常会用到异常恐慌（panic）的记录和追踪。最常见的就是，线上 panic 了之后，我们总想从中找到一些蛛丝马迹。&lt;/p&gt;&#xA;&lt;p&gt;我们很多人是看 panic 是看他的调用堆栈。然后就开始猜，看代码。猜测是不是哪里写的有问题，就想知道 panic 是由什么参数引起的？&lt;/p&gt;&#xA;&lt;p&gt;因为知道了诱发的参数，排查问题就非常方便了。为此&lt;strong&gt;在 Go1.17，官方对这块的调用堆栈信息展示进行了优化&lt;/strong&gt;，使其可读性更友好。&lt;/p&gt;&#xA;&lt;h2 id=&#34;案例&#34;&gt;案例&lt;/h2&gt;&#xA;&lt;p&gt;结合我们平时所使用的 panic 案例。如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;example&lt;/span&gt;(make([]&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//go:noinline&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;example&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;panic(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;运行结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go run main.go&#xA;panic: 脑子进煎鱼了&#xA;&#xA;goroutine 1 [running]:&#xA;main.example(0xc000032758, 0x1, 0x2, 0x1073d11, 0x6, 0x3, 0xc000102058, 0x1013201)&#xA;&#x9;/Users/eddycjy/go-application/awesomeProject/main.go:9 +0x39&#xA;main.main()&#xA;&#x9;/Users/eddycjy/go-application/awesomeProject/main.go:4 +0x68&#xA;exit status 2&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我们函数的入参是：&lt;code&gt;[]string、string、int&lt;/code&gt;，核心关注到 &lt;code&gt;main.example&lt;/code&gt; 方法的调用堆栈信息：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;example&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ae81ff&#34;&gt;0xc000032758&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ae81ff&#34;&gt;0x1073d11&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ae81ff&#34;&gt;0x6&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ae81ff&#34;&gt;0x3&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ae81ff&#34;&gt;0xc000102058&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ae81ff&#34;&gt;0x1013201&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;明明只是函数三个参数，却输出了一堆，对应起来非常的不清晰。&lt;/p&gt;&#xA;&lt;p&gt;其实际对应是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;slice：0xc000032758、0x1、0x2。&lt;/li&gt;&#xA;&lt;li&gt;string：0x1073d11、0x6。&lt;/li&gt;&#xA;&lt;li&gt;int：0x3。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这里存在的问题是，看调用堆栈的人，还得必须了解基本数据结构（例如：slice、string、int 等），他才知道每个函数入参他对应拥有几个字段，才能知道其内存布局的结构，有一点麻烦。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.17 新特性，凭什么提速 5~10%？</title>
      <link>https://note.lican.site/posts/posts/go/117-performance/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:04 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/117-performance/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在 Go1.17 发布后，我们惊喜的发现 Go 语言他又又又优化了，编译器改进后产生了约 5% 的性能提升，也没有什么破坏性修改，保证了向前兼容。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/c39ffb33-4afd-4537-a172-4919be7975a4.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;他做了些什么呢，好像没怎么看到有人提起。为此今天煎鱼带大家来解读两新提案：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;《&lt;a href=&#34;https://go.googlesource.com/proposal/+/master/design/40724-register-calling.md&#34; title=&#34;Proposal: Register-based Go calling convention&#34;&gt;Proposal: Register-based Go calling convention&lt;/a&gt;》&lt;/li&gt;&#xA;&lt;li&gt;《&lt;a href=&#34;https://go.googlesource.com/proposal/+/master/design/27539-internal-abi.md&#34; title=&#34;Proposal: Create an undefined internal calling convention&#34;&gt;Proposal: Create an undefined internal calling convention&lt;/a&gt;》&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;本文会基于提案讲解和拆解，毕竟分享新知识肯定要从官方资料作为事实基准出发。&lt;/p&gt;&#xA;&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;&#xA;&lt;p&gt;在以往的 Go 版本中，Go 的调用约定简单且几乎跨平台通用，其原因在于选用了基于 Plan9 ABI 的堆栈调用约定，也就是&lt;strong&gt;函数的参数和返回值都是通过堆栈上来进行传递&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;这里我们一共提到了 Plan9 和 ABI，这是两个很关键的理念：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Plan9：Go 语言所使用的汇编器，Rob Pike 是贝尔实验室的猛人。&lt;/li&gt;&#xA;&lt;li&gt;ABI：Application Binary Interface（应用程序二进制接口），ABI 包含了应用程序在操作系统下运行时必须遵守的编程约定（例如：二进制接口）。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;该方案的优缺点如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;优点：实现简单，简化了实现成本。&lt;/li&gt;&#xA;&lt;li&gt;缺点：性能方面付出了不少的代价。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;按我理解，在 Go 语言初创时期，采取先简单实现，跑起来再说。也合理，性能倒不是一个 TOP1 需求。&lt;/p&gt;&#xA;&lt;h2 id=&#34;go117-优化&#34;&gt;Go1.17 优化&lt;/h2&gt;&#xA;&lt;h3 id=&#34;什么是调用惯例&#34;&gt;什么是调用惯例&lt;/h3&gt;&#xA;&lt;p&gt;在新版本的优化中，提到了调用惯例（calling convention）的概念，指的是&lt;strong&gt;调用方和被调用方对函数调用的共识约定&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;这些共识包含：函数的参数、返回值、参数传递顺序、传递方式等。&lt;/p&gt;&#xA;&lt;p&gt;双方都必须遵循这个约定时，程序的函数才能正常的运行起来。如果不遵循，那么该函数是没法运行起来的。&lt;/p&gt;&#xA;&lt;h3 id=&#34;优化是什么&#34;&gt;优化是什么&lt;/h3&gt;&#xA;&lt;p&gt;在 Go1.17 起，正式将把 Go 内部 ABI 规范（在 Go 函数之间使用）从基于堆栈的函数参数和结果传递的方式&lt;strong&gt;改为基于寄存器的函数参数和结果传递&lt;/strong&gt;。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.17 新特性：对 Go 依赖管理的一把大剪刀</title>
      <link>https://note.lican.site/posts/posts/go/117-module-pruning-lazy/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:03 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/117-module-pruning-lazy/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;不得不说。我可是个经历过 Go 依赖管理群魔乱舞，Go modules 迁移一堆 BUG 的人儿，难顶&amp;hellip;&#xA;为此当年我写了不少技术文章，希望给大家避坑。&lt;/p&gt;&#xA;&lt;p&gt;如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s/6gJkSyGAFR0v6kow2uVklA&#34;&gt;Go Modules 终极入门&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s/uUNTH06_s6yzy5urtjPMsg&#34;&gt;干货满满的 Go Modules 知识分享&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s/0g89yj9sc1oIz9kS9ZIAEA&#34;&gt;Go1.16 新特性：Go mod 的后悔药，仅需这一招&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在近期 Go1.17 发布后，Go modules 带来了两大更新，煎鱼摩拳擦掌，他们分别是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;模块依赖图裁剪（module graph pruning）&lt;/li&gt;&#xA;&lt;li&gt;延时模块加载（lazy module loading）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;今天带大家一起来了解这两块内容，争取了解其为何物，背景又是什么。&lt;/p&gt;&#xA;&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;&#xA;&lt;p&gt;在日常的 Go 工程开发中，不知道你有没有遇到过 Go modules 的一个奇怪的点。大家没说，就以为是正确的，默认就接受了。&lt;/p&gt;&#xA;&lt;p&gt;引用官方的 &lt;a href=&#34;https://github.com/golang/go/blob/4012fea822763ef3aa66dd949fa95b9f8d89450a/src/cmd/go/testdata/script/mod_lazy_new_import.txt&#34; title=&#34;mod_lazy_new_import.txt&#34;&gt;mod_lazy_new_import.txt&lt;/a&gt; 的案例来说，就是假设我们在代码中：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;main module 是 lazy。其导入了 module A 的 package x，package x 又导入了 module B。&lt;/li&gt;&#xA;&lt;li&gt;main module lazy 也相当于同时导入了 module A 的 package y。&lt;/li&gt;&#xA;&lt;li&gt;module A 的 package y 又导入了 module C 的 package z。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;关联如下图所示：&lt;/p&gt;</description>
    </item>
    <item>
      <title>分久必合，golang.org 将成为历史！</title>
      <link>https://note.lican.site/posts/posts/go/site-history/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:03 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/site-history/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;这两天看到官方博客的《&lt;a href=&#34;https://go.dev/blog/tidy-web&#34; title=&#34;Tidying up the Go web experience&#34;&gt;Tidying up the Go web experience&lt;/a&gt;》，已经明确了优化 Go 站点的计划和安排了，为此今天和大家分享这一个好消息。&lt;/p&gt;&#xA;&lt;p&gt;在之前 Go 官方推出了新的站点 go.dev，一个新的 Go 开发者中心：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/c70f1330-64da-4b65-8988-f9dbe6b1df05.png&#34; alt=&#34;go.dev&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;以及提供给开发者查询 Go 包（package）和模块（module）信息的配套网站 pkg.go.dev：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/41df22df-78e2-4b15-83d7-c8e38fcaba54.png&#34; alt=&#34;pkg.go.dev&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;看上去似乎美好，但其实原有的 golang.org 在继续提供 Go 发行版下载、文档和标准库的软件包的资料。&lt;/p&gt;&#xA;&lt;p&gt;而其他 Go 网站，例如：blog.golang.org、play.golang.org、talks.golang.org 以及tour.golang.org，又都拥有额外的 Go 资料。这一切都有些零散和混乱。&lt;/p&gt;&#xA;&lt;p&gt;在软件发展来讲，这种被称之为 ”绞杀者“ 模式：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/4645e874-771a-452a-b4ba-e41d8ffe5673.png&#34; alt=&#34;图来自网络&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;也就是旧系统不断地被新系统取代，在两边都还存在流量时，会保持两边的运行。随着时间的推移，新系统不断地取代老系统，最终完全取代。&lt;/p&gt;&#xA;&lt;p&gt;Go 官方&lt;strong&gt;计划在接下来的一两个月里，将把 golang.org 网站合并成一个统一的网站&lt;/strong&gt;，也就是在 go.dev 上。&lt;/p&gt;&#xA;&lt;p&gt;所有的现有 URL（例如：Go 标准库、Go 博客等）都会重定向到新的 URL 地址中，不会出现破坏性修改（无法访问），一切按计划都是兼容的。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/0708fcf4-90b2-4451-8a3d-5bf5ce2a1dfa.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;咱们将会有一个更统一的 Go 网站，部分 Go 初学者也不再需要辛辛苦苦再翻山倒海先找个天梯了，因为是&lt;strong&gt;可以直接访问的&lt;/strong&gt;，这是一种利好！&lt;/p&gt;&#xA;&lt;p&gt;不得不感叹语句，Russ Cox 推动事情的能力真是杠杠的，虽然也会引来不少争议和讨论，但我们依然可以从中学习到好的部分！&lt;/p&gt;&#xA;&lt;p&gt;大家&lt;strong&gt;有没有什么也想让 Go 团队优化的呢，欢迎在评论区交流和讨论&lt;/strong&gt;！&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 1.17 支持泛型了？具体怎么用</title>
      <link>https://note.lican.site/posts/posts/go/117-generics/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:02 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/117-generics/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;千呼万唤的，Go1.17 前几天终于发布了：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/45fe4f8a-ef8e-41a0-abba-ecdc05fe61c5.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;先前我写了几篇 Go1.17 新特性的文章，有兴趣的小伙伴可以看看：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s/v1czjzlUsaSQTpAOG9Ub3w&#34;&gt;一个新细节，Go 1.17 将允许切片转换为数组指针！&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s/zdsrmlwVR0bP1Q_Xg_VlpQ&#34;&gt;我要提高 Go 程序健壮性，Fuzzing 来了！&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s/5kLFIuI0UJl_o8vMmZNfoA&#34;&gt;提了 3 年，Go1.17 终于增强构建约束！&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;今天的主题是泛型，众所皆知 Go1.18 泛型就会正式释出，都很期待，毕竟大更新，所有配套都会陆续有来！&#xA;其实，&lt;strong&gt;在 Go1.17 的此刻其实可以使用泛型了&lt;/strong&gt;，泛型代码已合入 master 分支。&lt;/p&gt;&#xA;&lt;p&gt;咱们只需要一点点操作，就能提前过上 Go 泛型的实验生活了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;升级-go117&#34;&gt;升级 Go1.17&lt;/h2&gt;&#xA;&lt;p&gt;你需要先升级 Go1.17，如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/a3260cf0-f9bc-4c86-aafd-4d08480143b9.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;安装后查看版本信息是否正常输出：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go1.17 version&#xA;go version go1.17 darwin/amd64&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;使用泛型&#34;&gt;使用泛型&lt;/h2&gt;&#xA;&lt;p&gt;接着写入一个基本的泛型 Demo：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Print&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Print&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Print&lt;/span&gt;([]&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;你好, &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进了煎鱼\n&amp;#34;&lt;/span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Print&lt;/span&gt;([]&lt;span style=&#34;color:#66d9ef&#34;&gt;int64&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只需要在 run 和 build 的命令执行时指定 &lt;code&gt;-G&lt;/code&gt; 标识就好了。不过有的小伙伴可能会疑惑，为什么要这么干？&lt;/p&gt;</description>
    </item>
    <item>
      <title>Goroutine 一泄露就看到他，这是个什么？</title>
      <link>https://note.lican.site/posts/posts/go/runtimepark/</link>
      <pubDate>Fri, 31 Dec 2021 12:55:01 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/runtimepark/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;作为一个 Go 语言的使用大户，常常就有人冷不丁的，一下就泄露了&amp;hellip;泄露了啥？&lt;/p&gt;&#xA;&lt;p&gt;表象来看当然是 goroutine 泄露了，这时候就会有小伙伴开始跑去拉取 PProf。就会看到类似下面这张图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/78c8dadf-4f5c-4dfe-be36-d39bfa9d13d3.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;重点会看到 &lt;code&gt;runtime.gopark&lt;/code&gt; 这个函数，在所有的 goroutine 泄露中都会看到有，并且都会是大头。&lt;/p&gt;&#xA;&lt;p&gt;既然是大头，也就会有许多朋友以为他是泄漏点，在那一顿猛查，那这个函数到底是什么，作用是？&lt;/p&gt;&#xA;&lt;h2 id=&#34;runtimegopark-是何物&#34;&gt;runtime.gopark 是何物&lt;/h2&gt;&#xA;&lt;p&gt;想要知道 &lt;code&gt;runtime.gopark&lt;/code&gt; 函数是作用，最快的办法就是看源码了。其实现细节在 src/runtime/proc.go 文件中。&lt;/p&gt;&#xA;&lt;p&gt;源代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;gopark&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;unlockf&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;lock&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;reason&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;waitReason&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;traceEv&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;traceskip&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;mp&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;acquirem&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gp&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;curg&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;readgstatus&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;gp&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;mp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;waitlock&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;lock&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;mp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;waitunlockf&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;unlockf&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;waitreason&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;reason&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;mp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;waittraceev&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;traceEv&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;mp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;waittraceskip&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;traceskip&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;releasem&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;mp&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;mcall&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;park_m&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该函数主要作用有三大点：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;acquirem&lt;/code&gt; 函数：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;获取当前 goroutine 所绑定的 m，设置各类所需数据。&lt;/li&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;releasem&lt;/code&gt; 函数&lt;strong&gt;将当前 goroutine 和其 m 的绑定关系解除&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;park_m&lt;/code&gt; 函数：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;将当前 goroutine 的状态从 &lt;code&gt;_Grunning&lt;/code&gt; 切换为 &lt;code&gt;_Gwaiting&lt;/code&gt;，也就是等待状态。&lt;/li&gt;&#xA;&lt;li&gt;删除 m 和当前 goroutine m-&amp;gt;curg（简称gp）之间的关联。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;mcall&lt;/code&gt; 函数，仅会在需要进行 goroutiine 切换时会被调用：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;切换当前线程的堆栈，从 g 的堆栈切换到 g0 的堆栈并调用 fn(g) 函数。&lt;/li&gt;&#xA;&lt;li&gt;将 g 的当前 PC/SP 保存在 g-&amp;gt;sched 中，以便后续调用 goready 函数时可以恢复运行现场。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;熟读了其源码后，我们可得知该函数的关键作用就是&lt;strong&gt;将当前的 goroutine 放入等待状态&lt;/strong&gt;，这意味着 goroutine 被暂时被搁置了，也就是被运行时调度器暂停了。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 插件系统，一个凉了快半截的特性？</title>
      <link>https://note.lican.site/posts/posts/go/plugin/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:59 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/plugin/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在 Go 语言中，有一个好像很好用，但却比较少人提及的功能，那就是 Go Plugin。目前在 Go 工程中普遍还没广泛的使用起来，覆盖率不高。&lt;/p&gt;&#xA;&lt;p&gt;前段时间小咸鱼的同事问了他这玩具怎么用，他正想甩出一个链接，但发现&amp;hellip;煎鱼竟然没写过，这不，Go 知识板块的文章地图得补全。&lt;/p&gt;&#xA;&lt;p&gt;今天煎鱼就大家一起学习 Go Plugin！&lt;/p&gt;&#xA;&lt;h2 id=&#34;是什么&#34;&gt;是什么&lt;/h2&gt;&#xA;&lt;p&gt;Go Team 最早在 Go1.7 实验，在 Go1.8 正式引入了 Go Plugin 的机制。于 2016 年发布，一开始仅支持 Linux 实现：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/9a791ced-39db-4f09-a027-60569ccc95dd.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Go Plugin 机制实现了 Go 插件的加载和符号解析，能够支持将我们所编写的 Go 包编译为共享库（.so）。&lt;/p&gt;&#xA;&lt;p&gt;这样 Go 工程就可以加载所编译好的 Go Plugin（已经变成了共享库文件），在程序中调用共享库中的函数、常量、变量等使用。也称其为 Go 语言中的热插拔的插件系统。&lt;/p&gt;&#xA;&lt;p&gt;截止 Go1.17 为止，Go Plugin 仅支持在 Linux、FreeBSD 和 MacOS 上运行，还不支持 Windows。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么需要&#34;&gt;为什么需要&lt;/h2&gt;&#xA;&lt;p&gt;Go 语言是静态语言，正常我们写一个程序，分如下两个角度来看：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;从代码编写的角度来看：我们在程序编写的时候就已经把所有的功能实现给确定了，不会发生什么根本性的变化。&lt;/li&gt;&#xA;&lt;li&gt;从程序的角度来看：在 Go 进行编译时，就已经把所有引用的标准库、第三方库等都编译打包好进二进制文件了，因此也就无法在运行时去动态加载，所以没法有其它的可能性。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;那么为什么需要 Go Plugin 呢，原因如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;可插拔的插件：程序能够随时的安装插件，也能够卸载他，获得更多运行时的自定义能力。&lt;/li&gt;&#xA;&lt;li&gt;可动态加载运行时模块：随时安装了插件，自然也就需要可自行决定运行哪个插件的模块了。&lt;/li&gt;&#xA;&lt;li&gt;可独立开发插件、模块：主系统和子插件，可能由不同的团队开发和提供，也更有价值。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;其实本质上还是&lt;strong&gt;希望程序能够在运行时实现动态的外部加载&lt;/strong&gt;，根据不同的条件、场景加载不同的插件功能。&lt;/p&gt;&#xA;&lt;h2 id=&#34;使用方法&#34;&gt;使用方法&lt;/h2&gt;&#xA;&lt;h3 id=&#34;通用概念&#34;&gt;通用概念&lt;/h3&gt;&#xA;&lt;p&gt;Go 官方给出的例子非常简单，只需要在 Go 编译时指定为插件就可以了。&lt;/p&gt;</description>
    </item>
    <item>
      <title>迷惑了，Go len() 是怎么计算出来的？</title>
      <link>https://note.lican.site/posts/posts/go/len/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:58 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/len/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近看到了一个很有意思的话题，我们平时常常会用 Go 的内置函数 &lt;code&gt;len&lt;/code&gt; 去获取各种 map、slice 的长度，那他是怎么实现的呢？&lt;/p&gt;&#xA;&lt;p&gt;正当我想去看看 &lt;code&gt;len&lt;/code&gt; 的具体实现时，一展身手，却发现竟然是个空方法：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; len(&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看注解也没有 link 到其他 runtime 函数，那么 len 函数是如何被调用的呢？&lt;/p&gt;&#xA;&lt;p&gt;先前看国外讨论 Go 计算 len 的文章时做了一些翻译和笔记（底下有参考链接），在此分享给大家，共同进步。&lt;/p&gt;&#xA;&lt;h2 id=&#34;谜底&#34;&gt;谜底&lt;/h2&gt;&#xA;&lt;p&gt;今天就由煎鱼带大家一同解开这个谜底。既然是谜底，那就一开始就揭开。&lt;/p&gt;&#xA;&lt;p&gt;其实 Go 语言中并没有 len 函数的具体实现代码，他其实&lt;strong&gt;是 Go 编译器的 &amp;ldquo;魔法&amp;rdquo; ，不是实际的函数调用&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;接下来将展开这部分，我们可以更深入地了解 Go 编译器的内部工作原理。&lt;/p&gt;&#xA;&lt;h2 id=&#34;编译器&#34;&gt;编译器&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 编译器编译时会解析命令行参数中指定的标志和 Go 源文件，对解析后的 Go 包进行类型检查，将函数编译为机器代码。代码，最后将编译后的包定义写到磁盘上。&lt;/p&gt;&#xA;&lt;p&gt;内部定义基本类型、内置函数和操作函数的阶段是在 types/universe.go 当中。同时会进行内置函数和具体的操作符匹配，可以明确知道内置函数 len 对应的是 OLEN：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;builtinFuncs&lt;/span&gt; = [&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;]&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;op&lt;/span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Op&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;append&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;OAPPEND&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cap&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;OCAP&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;close&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;OCLOSE&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;complex&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;OCOMPLEX&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;copy&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;OCOPY&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;delete&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ODELETE&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;imag&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;OIMAG&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;len&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;OLEN&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在编译时，上分为五个阶段进行类型检查：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 提案：增加泛型版 slices 和 maps 新包</title>
      <link>https://note.lican.site/posts/posts/go/news-slices-maps/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:57 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/news-slices-maps/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;现在是 2021 年 8 月份了，根据 Go 语言发布周期的 2，8 原则。Go 1.17 即将发布，在写这篇文章时，现在已经进行到了 rc2：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/ced4caa3-98ac-4c8a-8077-c72e11f69b56.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;这意味着离 Go1.18 释出泛型的正式支持又近了一点点，社区中讨论泛型相关的周边功能的声音又多了起来。&lt;/p&gt;&#xA;&lt;p&gt;今天要讨论的泛型版功能支持也是如此，分别包含：map（#47330）、slice（#45955）、container/set（#47331） 三种通用类型的支持。&lt;/p&gt;&#xA;&lt;p&gt;我们主要展开 maps 和 slices，其余的都大同小异，理解核心思想就好。&lt;/p&gt;&#xA;&lt;h2 id=&#34;maps&#34;&gt;maps&lt;/h2&gt;&#xA;&lt;p&gt;该提案建议定义一个新的包 maps，它将提供可用于任何类型的 map 的函数：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/fc067e0f-0972-4f92-898e-d64404b5bfa4.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;下面的描述侧重于描述 API 的提供：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maps&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Keys&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;comparable&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;) []&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Values&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;comparable&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;) []&lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Equal&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;comparable&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;m1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;m2&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;EqualFunc&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;comparable&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V2&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;m1&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;V1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;m2&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;V2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;cmp&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;V1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V2&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Clear&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;comparable&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Clone&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;comparable&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;comparable&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;dst&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Filter&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;comparable&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]&lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;keep&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Keys：返回 map 的键值。这些键将是一个不确定的顺序。&lt;/li&gt;&#xA;&lt;li&gt;Values：返回 map 值。这些值将以不确定的顺序出现。&lt;/li&gt;&#xA;&lt;li&gt;Equal：匹配两个 map 是否包含相同的键/值对。&lt;/li&gt;&#xA;&lt;li&gt;EqualFunc：EqualFunc 与 Equal 类似，但使用 cmp 进行数值比较。&lt;/li&gt;&#xA;&lt;li&gt;Clear：从 map 中清除所有条目，使其为空。&lt;/li&gt;&#xA;&lt;li&gt;Clone：返回一个 map 的副本，这是一个浅层克隆。新的键和值使用普通的赋值来设置。&lt;/li&gt;&#xA;&lt;li&gt;Add：将 src 中的所有键/值对添加到 dst 中。当 src 中的一个键已经存在于 dst 中时，dst 中的值将被 src 中的键相关的值覆盖。&lt;/li&gt;&#xA;&lt;li&gt;Filter：过滤器从 map 中删除任何 keep 返回结果为 false 的键/值对。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;slice&#34;&gt;slice&lt;/h2&gt;&#xA;&lt;p&gt;该提案建议定义一个新的包 slices，它将提供可用于任何类型的 slice 的函数：&lt;/p&gt;</description>
    </item>
    <item>
      <title>一个 Demo 学会使用 Go Delve 调试</title>
      <link>https://note.lican.site/posts/posts/go/delve/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:57 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/delve/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在 Go 语言中，除了 go tool 工具链中的 pprof、trace 等剖析工具的大利器外。常常还会有小伙伴问，有没有更好用，更精细的，&lt;/p&gt;&#xA;&lt;p&gt;大家总嫌弃 pprof、trace 等工具，不够细，没法一口气看到根因，或者具体变量&amp;hellip;希望能够最好能追到代码级别调试的，看到具体变量的值是怎么样的，随意想怎么看怎么看的那种。&lt;/p&gt;&#xA;&lt;p&gt;为此今天给大家介绍 Go 语言强大的 Delve （dlv）调试工具，来更深入问题剖析。&lt;/p&gt;&#xA;&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;&#xA;&lt;p&gt;我们需要先安装 Go delve，若是 Go1.16 及以后的版本，可以直接执行下述命令安装：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go install github.com/go-delve/delve/cmd/dlv@latest&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;也可以通过 git clone 的方式安装：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git clone https://github.com/go-delve/delve&#xA;$ cd delve&#xA;$ go install github.com/go-delve/delve/cmd/dlv&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在安装完毕后，我们执行 &lt;code&gt;dlv version&lt;/code&gt; 命令，查看安装情况：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ dlv version&#xA;Delve Debugger&#xA;Version: 1.7.0&#xA;Build: $Id: e353a65161e6ed74952b96bbb62ebfc56090832b $&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以明确看到我们所安装的版本是 v1.7.0。&lt;/p&gt;&#xA;&lt;h2 id=&#34;演示程序&#34;&gt;演示程序&lt;/h2&gt;&#xA;&lt;p&gt;我们计划用一个反转字符串的演示程序来进行 Go 程序的调试。第一部分先是完成 &lt;code&gt;stringer&lt;/code&gt; 包的 &lt;code&gt;Reverse&lt;/code&gt; 方法。&lt;/p&gt;&#xA;&lt;p&gt;代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stringer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Reverse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; []rune(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, len(&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; len(&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;], &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt;] = &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt;], &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; string(&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;再在具体的 &lt;code&gt;main&lt;/code&gt; 启动函数中进行调用。代码如下：&lt;/p&gt;</description>
    </item>
    <item>
      <title>学会使用 GDB 调试 Go 代码</title>
      <link>https://note.lican.site/posts/posts/go/gdb/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:57 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gdb/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;上一篇文章《一个 Demo 学会使用 Go Delve 调试》我们详细介绍了 Go 语言如何使用 Delve 进行排查和调试，对于问题的解决非常的有帮助。&lt;/p&gt;&#xA;&lt;p&gt;但调试工具肯定不止只有 Delve，今天我们来介绍第二个神器，那就是：GDB。&lt;/p&gt;&#xA;&lt;h2 id=&#34;gdb-是什么&#34;&gt;GDB 是什么&lt;/h2&gt;&#xA;&lt;p&gt;GDB 是一个类 UNIX 系统下的程序调试工具，允许你看到另一个程序在执行时 &amp;ldquo;内部 &amp;ldquo;发生了什么，或者程序在崩溃时正在做什么。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/35fc6475-ec0d-44a0-ae8c-920121130edb.png&#34; alt=&#34;GDB Logo&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;主要可以做四类事情：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;启动你的程序，指定任何可能影响其行为的东西。&lt;/li&gt;&#xA;&lt;li&gt;使你的程序在指定的条件下停止。&lt;/li&gt;&#xA;&lt;li&gt;检查当你的程序停止时发生了什么。&lt;/li&gt;&#xA;&lt;li&gt;改变你程序中的东西，这样你就可以试验纠正一个错误的影响，并继续了解另一个错误。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;&#xA;&lt;p&gt;如果是在 MacOS 上的话，可以直接使用 brew 安装：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;brew install gdb&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果是在 Linux ，则使用自带的包管理工具进行安装即可，但需要注意安装完毕后需要在 HOME 目录进行相关配置。&lt;/p&gt;&#xA;&lt;p&gt;安装完毕后，执行 &lt;code&gt;gdb&lt;/code&gt; 就可以看到：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ gdb&#xA;GNU gdb (GDB) 10.2&#xA;...&#xA;(gdb) &#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;写此文时最新的 gdb 版本已经是 10.2 了，我也升级了上去。问题不大，还多了不少功能。&lt;/p&gt;&#xA;&lt;h2 id=&#34;编译&#34;&gt;编译&lt;/h2&gt;&#xA;&lt;p&gt;我们还是使用先前的演示程序来进行调试。但由于 Go 语言的不少编译优化，因此在编译运行程序时，有以下几点需要注意：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;go build 编译时需要增加 &lt;code&gt;-gcflags=all=&amp;quot;-N -l&amp;quot;&lt;/code&gt; 指令来关闭内联优化，方便接下来的调试。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;若是 MacOS，在 go build 编译时需要增加 &lt;code&gt;-ldflags=&#39;-compressdwarf=false&#39;&lt;/code&gt; 指令。&lt;/p&gt;</description>
    </item>
    <item>
      <title>时隔 3 年，Go1.17 增强构建约束！</title>
      <link>https://note.lican.site/posts/posts/go/117-build/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:56 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/117-build/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;Go1.17rc1 在前几天终于正式发布了：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/a9dd1134-e4f8-4d9c-9c3c-2608728ddf69.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;看到 Go1.17 增加了一个新特性，是面向 Go 构建时的构建约束的增强。认真一看，是一个时隔 3 年的提案了，原本还在 Go2 和 Go1 之间左右摇摆，这下在 6 月底 Russ Cox 就输出了新草案：《Bug-resistant build constraints — Draft Design》。紧接着直接计划在 Go1.17 发布了。&lt;/p&gt;&#xA;&lt;p&gt;一气呵成，真实版高效能了。&lt;/p&gt;&#xA;&lt;p&gt;如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/10037273-887d-45c0-9eca-6ef36d7c4d72.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;之前小咸鱼有遇到好几个朋友，在报错时压根不知道 Go 有这个约束语法，以为只是个单纯的注释，直接不明所以然，感觉科普之路任重道远。&lt;/p&gt;&#xA;&lt;p&gt;今天这篇文章煎鱼就来讲讲构建约束这事。&lt;/p&gt;&#xA;&lt;p&gt;注：下个月 Go1.17 就会正式发布，距离 Go1.18 泛型出山只差一点点距离了，值得期待！&lt;/p&gt;&#xA;&lt;h2 id=&#34;构建约束的背景&#34;&gt;构建约束的背景&lt;/h2&gt;&#xA;&lt;p&gt;简单来讲，在真实环境中，可能需要为不同的编译环境编写不同的 Go 代码，所以需要做构建约束。&lt;/p&gt;&#xA;&lt;p&gt;划重点，Go 语言对这一问题的解决方案是&lt;strong&gt;在文件层面进行有条件的编译：每个文件要么在编译中，要么不在&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;也就是，假设不符合构建约束的场景。那么会直接不编译这个文件，因为他不在编译范围内。那在程序想运行时就会报错，表示找不到文件。因此有许多的同学看着报错信息，经常找不着北&amp;hellip;&lt;/p&gt;&#xA;&lt;h2 id=&#34;现有的构建约束&#34;&gt;现有的构建约束&lt;/h2&gt;&#xA;&lt;p&gt;既然是叫 “增强”。说明现有就有构建约束。最早的构建约束是在 2011 年 9 月引入的构建约束。&lt;/p&gt;&#xA;&lt;p&gt;我们平时常见的构建约束（build constraint），也叫做构建标记（build tag），构建约束必须出现在 &lt;code&gt;package&lt;/code&gt; 之前。&lt;/p&gt;&#xA;&lt;p&gt;平时会在 Go 工程的文件中的最开始会看到如下行注解：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// +build&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;为了将构建约束与包文档区分开来，构建约束后必须跟一个空行。&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// +build linux,386 darwin,!cgo&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;又或是：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// +build linux darwin&#xA;// +build amd64&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;还可以根据 Go 版本来约束：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 内存模型：happens-before 原则</title>
      <link>https://note.lican.site/posts/posts/go/memory-model/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:54 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/memory-model/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在日常工作中，如果我们能够了解 Go 语言内存模型，那会带来非常大的作用。这样在看一些极端情况，又或是变态面试题的时候，就能够明白程序运行表现下的很多根本原因了。&lt;/p&gt;&#xA;&lt;p&gt;当然，靠一篇普通文章讲完 Go 内存模型，不可能。因此今天这篇文章，把重点划在给大家&lt;strong&gt;讲解 Go 语言的 happens-before 原则&lt;/strong&gt;这 1 个细节。&lt;/p&gt;&#xA;&lt;p&gt;开吸，和煎鱼揭开他的神秘面纱！&lt;/p&gt;&#xA;&lt;h2 id=&#34;内存模型定义是什么&#34;&gt;内存模型定义是什么&lt;/h2&gt;&#xA;&lt;p&gt;既然要了解 happens-before 原则，我们得先知道 The Go Memory Model（Go 内存模型）定义的是什么，官方解释如下：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The Go memory model specifies the conditions under which reads of a variable in one goroutine can be guaranteed to observe values produced by writes to the same variable in a different goroutine.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;在 Go 内存模型规定：“在一个 goroutine 中读取一个变量时，可以保证观察到不同 goroutine 中对同一变量的写入所产生的值” 的条件。&lt;/p&gt;&#xA;&lt;p&gt;这是学习后续知识的一个大前提。&lt;/p&gt;&#xA;&lt;h2 id=&#34;happens-before-是什么&#34;&gt;happens-before 是什么&lt;/h2&gt;&#xA;&lt;p&gt;Happens Before 是一个专业术语，与 Go 语言没有直接关系，也就是并非是特有的。用大白话来讲，其定义是：&lt;/p&gt;</description>
    </item>
    <item>
      <title>分享 Go 使用 Context 的正式姿势</title>
      <link>https://note.lican.site/posts/posts/go/real-context/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:54 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/real-context/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在 Go 语言中，Goroutine（协程），也就是关键字 &lt;code&gt;go&lt;/code&gt; 是一个家喻户晓的高级用法。这起的非常妙，说到 Go，就会想到这一门语言，想到 goroutine 这一关键字，而与之关联最深的就是 context。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/2f26bd64-e9bd-4c89-a9a0-326b8c2b9d00.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;&#xA;&lt;p&gt;平时在 Go 工程中开发中，几乎所有服务端（例如：HTTP Server）的默认实现，都在处理请求时新起 goroutine 进行处理。&lt;/p&gt;&#xA;&lt;p&gt;但一开始存在一个问题，那就是当一个请求被取消或超时时，所有在该请求上工作的 goroutines 应该迅速退出，以便系统可以回收他们正在使用的任何资源。&lt;/p&gt;&#xA;&lt;p&gt;当年可没有 context 标准库。很折腾。因此 Go 官方在 2014 年正式宣发了 context 标准库，形成一个完整的闭环。&lt;/p&gt;&#xA;&lt;p&gt;但有了 context 标准库，Go 爱好者们又奇怪了，前段时间我就被问到了：“Go context 的正确使用姿势是怎么样的”？&lt;/p&gt;&#xA;&lt;p&gt;（一张忘记在哪里被问的隐形截图）&lt;/p&gt;&#xA;&lt;p&gt;今天这篇文章就由煎鱼带你看看。&lt;/p&gt;&#xA;&lt;h2 id=&#34;context-用法&#34;&gt;Context 用法&lt;/h2&gt;&#xA;&lt;p&gt;在 Go context 用法中，我们常常将其与 select 关键字结合使用，用于监听其是否结束、取消等。&lt;/p&gt;&#xA;&lt;p&gt;代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shortDuration&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Millisecond&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;cancel&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WithTimeout&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Background&lt;/span&gt;(), &lt;span style=&#34;color:#a6e22e&#34;&gt;shortDuration&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cancel&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;After&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Second&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Done&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Err&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;context deadline exceeded&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果是更进一步结合 goroutine 的话，常见的例子是：&lt;/p&gt;</description>
    </item>
    <item>
      <title>详解 Go 团队不建议用的 unsafe.Pointer</title>
      <link>https://note.lican.site/posts/posts/go/unsafe-pointer/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:54 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/unsafe-pointer/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;大家在学习 Go 的时候，肯定都学过 “Go 的指针是不支持指针运算和转换” 这个知识点。为什么呢？&lt;/p&gt;&#xA;&lt;p&gt;首先，Go 是一门静态语言，所有的变量都必须为标量类型。不同的类型不能够进行赋值、计算等跨类型的操作。&lt;/p&gt;&#xA;&lt;p&gt;那么指针也对应着相对的类型，也在 Compile 的静态类型检查的范围内。同时静态语言，也称为强类型。也就是一旦定义了，就不能再改变它。&lt;/p&gt;&#xA;&lt;h2 id=&#34;错误的示例&#34;&gt;错误的示例&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main(){&#xA; num := 5&#xA; numPointer := &amp;amp;num&#xA;&#xA; flnum := (*float32)(numPointer)&#xA; fmt.Println(flnum)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# command-line-arguments&#xA;...: cannot convert numPointer (type *int) to type *float32&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在示例中，我们创建了一个 &lt;code&gt;num&lt;/code&gt; 变量，值为 5，类型为 &lt;code&gt;int&lt;/code&gt;，准备干一番大事。&lt;/p&gt;&#xA;&lt;p&gt;接下来我们取了其对于的指针地址后，试图强制转换为 &lt;code&gt;*float32&lt;/code&gt;，结果失败&amp;hellip;&lt;/p&gt;&#xA;&lt;h2 id=&#34;万能的破壁-unsafe&#34;&gt;万能的破壁 unsafe&lt;/h2&gt;&#xA;&lt;p&gt;针对刚刚的 “错误示例”，我们可以采用今天的男主角 &lt;code&gt;unsafe&lt;/code&gt; 标准库来解决。它是一个神奇的包，在官方的诠释中，有如下概述：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;围绕 Go 程序内存安全及类型的操作。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;很可能会是不可移植的。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;不受 Go 1 兼容性指南的保护。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;简单来讲就是，不怎么推荐你使用，因为它是 unsafe（不安全的）。&lt;/p&gt;&#xA;&lt;p&gt;但是在特殊的场景下，使用了它，可以打破 Go 的类型和内存安全机制，让你获得眼前一亮的惊喜效果。&lt;/p&gt;&#xA;&lt;h3 id=&#34;unsafepointer&#34;&gt;unsafe.Pointer&lt;/h3&gt;&#xA;&lt;p&gt;为了解决这个问题，需要用到 &lt;code&gt;unsafe.Pointer&lt;/code&gt;。它表示任意类型且可寻址的指针值，可以在不同的指针类型之间进行转换（类似 C 语言的 void * 的用途）。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go SliceHeader 和 StringHeader，你知道吗？</title>
      <link>https://note.lican.site/posts/posts/go/slice-string-header/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:53 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/slice-string-header/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在 Go 语言中总是有一些看上去奇奇怪怪的东西，咋一眼一看感觉很熟悉，但又不理解其在 Go 代码中的实际意义，面试官却爱问&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;今天要给大家介绍的是 SliceHeader 和 StringHeader 结构体，了解清楚他到底是什么，又有什么用，并且会在最后给大家介绍 0 拷贝转换的内容。&lt;/p&gt;&#xA;&lt;p&gt;一起愉快地开始吸鱼之路。&lt;/p&gt;&#xA;&lt;h2 id=&#34;sliceheader&#34;&gt;SliceHeader&lt;/h2&gt;&#xA;&lt;p&gt;SliceHeader 如其名，Slice + Header，看上去很直观，实际上是 Go Slice（切片）的运行时表现。&lt;/p&gt;&#xA;&lt;p&gt;SliceHeader 的定义如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type SliceHeader struct {&#xA; Data uintptr&#xA; Len  int&#xA; Cap  int&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Data：指向具体的底层数组。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Len：代表切片的长度。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Cap：代表切片的容量。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;既然知道了切片的运行时表现，那是不是就意味着我们可以自己造一个？&lt;/p&gt;&#xA;&lt;p&gt;在日常程序中，可以利用标准库 &lt;code&gt;reflect&lt;/code&gt; 提供的 &lt;code&gt;SliceHeader&lt;/code&gt; 结构体造一个：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA;  // 初始化底层数组&#xA; s := [4]string{&amp;#34;脑子&amp;#34;, &amp;#34;进&amp;#34;, &amp;#34;煎鱼&amp;#34;, &amp;#34;了&amp;#34;}&#xA; s1 := s[0:1]&#xA; s2 := s[:]&#xA;&#xA;  // 构造 SliceHeader&#xA; sh1 := (*reflect.SliceHeader)(unsafe.Pointer(&amp;amp;s1))&#xA; sh2 := (*reflect.SliceHeader)(unsafe.Pointer(&amp;amp;s2))&#xA; fmt.Println(sh1.Len, sh1.Cap, sh1.Data)&#xA; fmt.Println(sh2.Len, sh2.Cap, sh2.Data)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你认为输出结果是什么，这两个新切片会指向同一个底层数组的内存地址吗？&lt;/p&gt;</description>
    </item>
    <item>
      <title>群里又吵起来了，Go 是传值还是传引用？</title>
      <link>https://note.lican.site/posts/posts/go/value-quote/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:52 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/value-quote/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在咱们的 Go 交流群里，有一个小伙伴问了 “xxx 是不是引用类型？” 这个问题，引发了将近 5 小时的讨论：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9be6b8a7ae844766afa6c2a79c3666d5~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;兜兜转转回到了日经的问题，几乎每个月都要有人因此吵一架。就是 &lt;strong&gt;Go 语言到底是传值（值传递），还是传引用（引用传递）&lt;/strong&gt;？&lt;/p&gt;&#xA;&lt;h2 id=&#34;go-官方的定义&#34;&gt;Go 官方的定义&lt;/h2&gt;&#xA;&lt;p&gt;本部分引用 Go 官方 FAQ 的 “When are function parameters passed by value?”，内容如下。&lt;/p&gt;&#xA;&lt;p&gt;如同 C 系列的所有语言一样，&lt;strong&gt;Go 语言中的所有东西都是以值传递的&lt;/strong&gt;。也就是说，一个函数总是得到一个被传递的东西的副本，就像有一个赋值语句将值赋给参数一样。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4dc92546d5494e5b9a2a2a5083aa0a5b~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;例如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;向一个函数传递一个 int 值，就会得到 int 的副本。&lt;/p&gt;&#xA;&lt;p&gt;而传递一个指针值就会得到指针的副本，但不会得到它所指向的数据。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;map 和 slice 的行为类似于指针：它们是包含指向底层 map 或 slice 数据的指针的描述符。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;复制一个 map 或 slice 值并不会复制它所指向的数据。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;复制一个接口值会复制存储在接口值中的东西。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;如果接口值持有一个结构，复制接口值就会复制该结构。如果接口值持有一个指针，复制接口值会复制该指针，但同样不会复制它所指向的数据。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;划重点，Go 语言中一切都是值传递，没有引用传递。不要直接把其他概念硬套上来，会犯先入为主的错误的。&lt;/p&gt;&#xA;&lt;h2 id=&#34;传值和传引用&#34;&gt;传值和传引用&lt;/h2&gt;&#xA;&lt;h3 id=&#34;传值&#34;&gt;传值&lt;/h3&gt;&#xA;&lt;p&gt;传值，也叫做值传递（pass by value）。其&lt;strong&gt;指的是在调用函数时将实际参数复制一份传递到函数中&lt;/strong&gt;，这样在函数中如果对参数进行修改，将不会影响到实际参数。&lt;/p&gt;&#xA;&lt;p&gt;简单来讲，值传递，所传递的是该参数的副本，是复制了一份的，本质上不能认为是一个东西，指向的不是一个内存地址。&lt;/p&gt;&#xA;&lt;p&gt;案例一如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA; s := &amp;#34;脑子进煎鱼了&amp;#34;&#xA; fmt.Printf(&amp;#34;main 内存地址：%p\n&amp;#34;, &amp;amp;s)&#xA; hello(&amp;amp;s)&#xA;}&#xA;&#xA;func hello(s *string) {&#xA; fmt.Printf(&amp;#34;hello 内存地址：%p\n&amp;#34;, &amp;amp;s)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;</description>
    </item>
    <item>
      <title>详解 Go 空结构体的 3 种使用场景</title>
      <link>https://note.lican.site/posts/posts/go/empty-struct/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:51 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/empty-struct/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在大家初识 Go 语言时，总会拿其他语言的基本特性来类比 Go 语言，说白了就是老知识和新知识产生关联，实现更高的学习效率。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/87c66858abf04b848643b6fa03f4bdab~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;最常见的类比，就是 “Go 语言如何实现面向对象？”，进一步展开就是 Go 语言如何实现面向对象特性中的继承。&lt;/p&gt;&#xA;&lt;p&gt;这不仅在学习中才用到类比，在业内的 Go 面试中也有非常多的面试官喜欢问：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d1cc9c2b2e174fc4a82506b368591a70~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;来自读者微信群&lt;/p&gt;&#xA;&lt;p&gt;在今天这篇文章中，煎鱼带大家具体展开了解这块的知识。一起愉快地开始吸鱼之路。&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是面向对象&#34;&gt;什么是面向对象&lt;/h2&gt;&#xA;&lt;p&gt;在了解 Go 语言是不是面向对象（简称：OOP） 之前，我们必须先知道 OOP 是啥，得先给他 “下定义”。&lt;/p&gt;&#xA;&lt;p&gt;根据 Wikipedia 的定义，我们梳理出 OOP 的几个基本认知：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;面向对象编程（OOP）是一种基于 &amp;ldquo;对象&amp;rdquo; 概念的编程范式，它可以包含数据和代码：数据以字段的形式存在（通常称为属性或属性），代码以程序的形式存在（通常称为方法）。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;对象自己的程序可以访问并经常修改自己的数据字段。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;对象经常被定义为类的一个实例。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;对象利用属性和方法的私有/受保护/公共可见性，对象的内部状态受到保护，不受外界影响（被封装）。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;基于这几个基本认知进行一步延伸出，面向对象的三大基本特性：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;封装。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;继承。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;多态。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;至此对面向对象的基本概念讲解结束，想更进一步了解的可自行网上冲浪。&lt;/p&gt;&#xA;&lt;h2 id=&#34;go-是面向对象的语言吗&#34;&gt;Go 是面向对象的语言吗&lt;/h2&gt;&#xA;&lt;p&gt;“Go 语言是否一门面向对象的语言？”，这是一个日经话题。官方 FAQ 给出的答复是：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bb8296e58a6d4c7c882984dcceaaf9c0~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;是的，也不是。原因是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Go 有类型和方法，并且允许面向对象的编程风格，但没有类型层次。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Go 中的 &amp;ldquo;接口 &amp;ldquo;概念提供了一种不同的方法，我们认为这种方法易于使用，而且在某些方面更加通用。还有一些方法可以将类型嵌入到其他类型中，以提供类似的东西，但不等同于子类。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Go 中的方法比 C++ 或 Java 中的方法更通用：它们可以为任何类型的数据定义，甚至是内置类型，如普通的、&amp;ldquo;未装箱的 &amp;ldquo;整数。它们并不局限于结构（类）。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Go 由于缺乏类型层次，Go 中的 &amp;ldquo;对象 &amp;ldquo;比 C++ 或 Java 等语言更轻巧。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 凭什么不支持三元运算符？</title>
      <link>https://note.lican.site/posts/posts/go/ternary-operator/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:50 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/ternary-operator/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;这是一个很多其他语言工程师转 Go 语言的时间节点，这就难免不论一番比较。其中一个经典的运算上的就是 “三元运算符”：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/05766eebaf454d7fb91b60ab1c851cf4~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;为什么 Go 语言不支持三元运算符，Go 不支持三元运算符就是设计的不好，是历史在开倒车吗？&lt;/p&gt;&#xA;&lt;p&gt;今天就由煎鱼来和大家一起摸索为什么。&lt;/p&gt;&#xA;&lt;h2 id=&#34;三元运算符是什么&#34;&gt;三元运算符是什么&lt;/h2&gt;&#xA;&lt;p&gt;三元运算符，在典型的数学意义上，或者从解析器的角度来看，是一个需要三个参数的运算符。而我们日常中，最常见的是二元运算符：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;x + y&#xA;x / y&#xA;x * y&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;还有一元运算符：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;-a&#xA;~b&#xA;!c&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;以及今天的男主角 “三元运算符”。在 C/C++ 等多种语言中，我们可以根据条件声明和初始化变量的习惯来选择性使用三元条件运算符：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int index = val &amp;gt; 0 ? val : -val&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;go-使用三元运算符&#34;&gt;Go 使用三元运算符&lt;/h2&gt;&#xA;&lt;p&gt;想在 Go 语言里也使用三元运算符时，发现居然没有&amp;hellip;想要实现与上面相同的代码段的方式似乎只能：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var index int&#xA;&#xA;if val &amp;gt; 0 {&#xA;    index = val&#xA;} else {&#xA;    index = -val&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;看上去十分的冗余，不够简洁。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么-go-没有三元运算符&#34;&gt;为什么 Go 没有三元运算符&lt;/h2&gt;&#xA;&lt;p&gt;为什么 Go 没有 &lt;code&gt;?:&lt;/code&gt; 操作符，没有的话，官方推荐的方式是怎么样的。&lt;/p&gt;&#xA;&lt;p&gt;通过 Go FAQ 我们可以得知：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 并发读写 sync.map 的强大之处</title>
      <link>https://note.lican.site/posts/posts/go/sync-map/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:50 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/sync-map/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在之前的 《&lt;a href=&#34;http://mp.weixin.qq.com/s?__biz=MzUxMDI4MDc1NA==&amp;amp;mid=2247489045&amp;amp;idx=1&amp;amp;sn=197bda427246e16907c7b471a5dc0572&amp;amp;chksm=f9040348ce738a5ebf541954a4de29ce746238ab7f6e5a2af8a1765c5383ad4208f43b2bac4f&amp;amp;scene=21#wechat_redirect&#34;&gt;为什么 Go map 和 slice 是非线程安全的？&lt;/a&gt;》 文章中，我们讨论了 Go 语言的 map 和 slice 非线程安全的问题，基于此引申出了 map 的两种目前在业界使用的最多的并发支持的模式。&lt;/p&gt;&#xA;&lt;p&gt;分别是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;原生 map + 互斥锁或读写锁 mutex。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;标准库 sync.Map（Go1.9及以后）。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;有了选择，总是有选择困难症的，这&lt;strong&gt;两种到底怎么选，谁的性能更加的好&lt;/strong&gt;？我有一个朋友说 标准库 sync.Map 性能菜的很，不要用。我到底听谁的&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;今天煎鱼就带你揭秘 Go sync.map，我们先会了解清楚什么场景下，Go map 的多种类型怎么用，谁的性能最好！&lt;/p&gt;&#xA;&lt;p&gt;接着根据各 map 性能分析的结果，针对性的对 sync.map 进行源码解剖，了解 WHY。&lt;/p&gt;&#xA;&lt;p&gt;一起愉快地开始吸鱼之路。&lt;/p&gt;&#xA;&lt;h2 id=&#34;syncmap-优势&#34;&gt;sync.Map 优势&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 官方文档中明确指出 Map 类型的一些建议：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6500ee41a08a415ca681fa427f5032b3~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;多个 goroutine 的并发使用是安全的，不需要额外的锁定或协调控制。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;大多数代码应该使用原生的 map，而不是单独的锁定或协调控制，以获得更好的类型安全性和维护性。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;同时 Map 类型，还针对以下场景进行了性能优化：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;当一个给定的键的条目只被写入一次但被多次读取时。例如在仅会增长的缓存中，就会有这种业务场景。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;当多个 goroutines 读取、写入和覆盖不相干的键集合的条目时。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这两种情况与 Go map 搭配单独的 Mutex 或 RWMutex 相比较，使用 Map 类型可以大大减少锁的争夺。&lt;/p&gt;</description>
    </item>
    <item>
      <title>回答我，停止 Goroutine 有几种方法？</title>
      <link>https://note.lican.site/posts/posts/go/stop-goroutine/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:50 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/stop-goroutine/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;协程（goroutine）作为 Go 语言的扛把子，经常在各种 Go 工程项目中频繁露面，甚至有人会为了用 goroutine 而强行用他。&lt;/p&gt;&#xA;&lt;p&gt;在 Go 工程师的面试中，也绕不开他，会有人问 ”如何停止一个 goroutine？”，一下子就把话题范围扩大了，这是一个涉及多个知识点的话题，能进一步深入问。&lt;/p&gt;&#xA;&lt;p&gt;为此，今天煎鱼就带大家了解一下停止 goroutine 的方法！&lt;/p&gt;&#xA;&lt;h2 id=&#34;goroutine-案例&#34;&gt;goroutine 案例&lt;/h2&gt;&#xA;&lt;p&gt;在日常的工作中，我们常会有这样的 Go 代码，go 关键字一把搜起一个 goroutine：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() { &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;初入 goroutine 大门的开发者可能就完事了，但跑一段时间后，他就可能会遇到一些问题，苦苦排查&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;像是：当 goroutine 内的任务，运行的太久，又或是卡死了&amp;hellip;就会一直阻塞在系统中，变成 goroutine 泄露，或是间接造成资源暴涨，会带来许多的问题。&lt;/p&gt;&#xA;&lt;p&gt;如何在停止 goroutine，就成了一门必修技能了，不懂就没法用好 goroutine。&lt;/p&gt;&#xA;&lt;h2 id=&#34;关闭-channel&#34;&gt;关闭 channel&lt;/h2&gt;&#xA;&lt;p&gt;第一种方法，就是借助 channel 的 close 机制来完成对 goroutine 的精确控制。&lt;/p&gt;&#xA;&lt;p&gt;代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ok&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; !&lt;span style=&#34;color:#a6e22e&#34;&gt;ok&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;结束&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼还没进锅里...&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼进脑子里了！&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;close(&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Second&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 Go 语言的 channel 中，channel 接受数据有两种方法：&lt;/p&gt;</description>
    </item>
    <item>
      <title>多 Goroutine 如何优雅处理错误？</title>
      <link>https://note.lican.site/posts/posts/go/goroutine-errors/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:50 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/goroutine-errors/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在 Go 语言中，goroutine 的使用是非常频繁的，因此在日常编码的时候我们会遇到一个问题，那就是 goroutine 里面的错误处理，怎么做比较好？&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/72758b15-f9b7-4437-ba17-b37a36f285ae.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;这是来自我读者群的问题。作为一个宠粉煎鱼，我默默记下了这个技术话题。今天煎鱼就大家来看看多 goroutine 的错误处理机制也有哪些！&lt;/p&gt;&#xA;&lt;p&gt;一般来讲，我们的业务代码会是：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;wg&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sync&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WaitGroup&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;wg&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;wg&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Done&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼想报错...&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;wg&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Done&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Second&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，我们运行了多个 goroutine。但我想抛出 error 的错误信息出来，似乎没什么好办法&amp;hellip;&lt;/p&gt;&#xA;&lt;h2 id=&#34;通过错误日志记录&#34;&gt;通过错误日志记录&lt;/h2&gt;&#xA;&lt;p&gt;为此，业务代码中常见的第一种方法：通过把错误记录写入日志文件中，再结合相关的 logtail 进行采集和梳理。&lt;/p&gt;&#xA;&lt;p&gt;但这又会引入新的问题，那就是调用错误日志的方法写的到处都是。代码结构也比较乱，不直观。&lt;/p&gt;&#xA;&lt;p&gt;最重要的是无法针对 error 做特定的逻辑处理和流转。&lt;/p&gt;&#xA;&lt;h2 id=&#34;利用-channel-传输&#34;&gt;利用 channel 传输&lt;/h2&gt;&#xA;&lt;p&gt;这时候大家可能会想到 Go 的经典哲学：&lt;strong&gt;不要通过共享内存来通信，而是通过通信来实现内存共享&lt;/strong&gt;（Do not communicate by sharing memory; instead, share memory by communicating）。&lt;/p&gt;&#xA;&lt;p&gt;第二种的方法：利用 channel 来传输多个 goroutine 中的 errors：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gerrors&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;wgDone&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;wg&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sync&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WaitGroup&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;wg&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;wg&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Done&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;returnError&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gerrors&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;wg&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Done&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;wg&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wait&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;close(&lt;span style=&#34;color:#a6e22e&#34;&gt;wgDone&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;wgDone&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gerrors&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;close(&lt;span style=&#34;color:#a6e22e&#34;&gt;gerrors&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Second&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;returnError&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;errors&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼报错了...&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;</description>
    </item>
    <item>
      <title>小技巧分享：在 Go 如何实现枚举？</title>
      <link>https://note.lican.site/posts/posts/go/enum/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:50 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/enum/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在日常的业务工程开发中，我们常常会有使用枚举值的诉求，枚举控的好，测试值边界一遍过&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;有的小伙伴会说，在 Go 语言不是有 &lt;code&gt;iota&lt;/code&gt; 类型做枚举吗，那煎鱼你这篇文章还讲什么？&lt;/p&gt;&#xA;&lt;p&gt;讲道理，Go 语言并没有 enum 关键字，有用过 Protobuf 等的小伙伴知道，Go 语言只是 ”有限的枚举“ 支持，我们也会用常量来定义，枚举值也需要有字面意思的映射。&lt;/p&gt;&#xA;&lt;h2 id=&#34;示例&#34;&gt;示例&lt;/h2&gt;&#xA;&lt;p&gt;在一些业务场景下，是没法达到我们的诉求的。示例如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type FishType int&#xA;&#xA;const (&#xA; A FishType = iota&#xA; B&#xA; C&#xA; D&#xA;)&#xA;&#xA;func main() {&#xA; fmt.Println(A, B, C, D)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果为：“0 1 2 3”。这时候就一脸懵逼了&amp;hellip;枚举值，应该除了键以外，还得有个对应的值。也就是这个 “0 1 2 3” 分别对应着什么含义，是不是应该输出 ”A B C D“&lt;/p&gt;&#xA;&lt;p&gt;但 Go 语言这块就没有直接的支撑了，因此这不是一个完整的枚举类型的实现。&lt;/p&gt;&#xA;&lt;p&gt;同时假设我们传入超过 &lt;code&gt;FishType&lt;/code&gt; 类型声明范围的枚举值，在 Go 语言中默认也不会有任何控制，是正常输出的。&lt;/p&gt;&#xA;&lt;p&gt;上述这种 Go 枚举实现，在某种情况下是不完全的，严格意义上不能成为 enum（枚举）。&lt;/p&gt;&#xA;&lt;h2 id=&#34;使用-string-做枚举&#34;&gt;使用 String 做枚举&lt;/h2&gt;&#xA;&lt;p&gt;如果要支持枚举值的对应输出的话，我们可以通过如下方式：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type FishType int&#xA;&#xA;const (&#xA; A FishType = iota&#xA; B&#xA; C&#xA; D&#xA;)&#xA;&#xA;func (f FishType) String() string {&#xA; return [...]string{&amp;#34;A&amp;#34;, &amp;#34;B&amp;#34;, &amp;#34;C&amp;#34;, &amp;#34;D&amp;#34;}[f]&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;运行程序：&lt;/p&gt;</description>
    </item>
    <item>
      <title>提高 Go 程序健壮性，Fuzzing 要来了！</title>
      <link>https://note.lican.site/posts/posts/go/fuzzing/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:50 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/fuzzing/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;就在前几天，Go1.17 beta1 正式发布：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/fb4ceb3d-ef1c-4c32-a7c3-a4188a0d74b0.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;兴冲冲本想着看一下当初在 Go1.17 的计划中，预计会支持的新特性：模糊测试（Fuzzing）。不过没想到&amp;hellip;计划赶不上变化，官方正式宣告 Fuzzing 不会出现在 Go1.17 的新功能中。&lt;/p&gt;&#xA;&lt;p&gt;煎鱼在悲伤之际，发现 Go 在 dev.fuzz 分支上提供了该功能的 Beta 测试，因此今天带大家一起来深入该特性。&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是-fuzzing&#34;&gt;什么是 Fuzzing&lt;/h2&gt;&#xA;&lt;p&gt;Fuzzing 是一种自动测试技术，包括向计算机程序提供随机数据作为输入。然后监测程序是否出现恐慌、断言失败、无限循环等。&lt;/p&gt;&#xA;&lt;p&gt;Fuzzing 不是使用一个小的、预先定义好的手动创建的输入集（如单元测试），而是用新的案例不断地测试代码，以努力 ”锻炼“ 有关软件的所有方面。&lt;/p&gt;&#xA;&lt;p&gt;这听起来很 ”难“。但在过去的几年里，Fuzzing 的技术水平有了很大的提高。Fuzzing 不再是需要专业知识才能成功使用的东西，现代模糊测试策略能更快、更有效地找到有用的输入。&lt;/p&gt;&#xA;&lt;p&gt;在应用程序中，就是你只要引入一个 package，对着 API 一顿用就可以了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么要做-fuzzing&#34;&gt;为什么要做 Fuzzing&lt;/h2&gt;&#xA;&lt;p&gt;可能会有小伙伴说，测试？直接人工测试，再把测试数据准备一下，配套 YAPI 等接口管理平台，把自动化接口测试一弄就好了。还需要 Fuzzing 吗？&lt;/p&gt;&#xA;&lt;p&gt;其实 Fuzzing 是对其他形式的测试、代码审查和静态分析的补充，它通过生成一个随机测试用例去覆盖人为测不到的各种复杂场景。而这些输入几乎不可能人为去构造，总会被传统测试所遗漏。&lt;/p&gt;&#xA;&lt;h2 id=&#34;发生在身边的-fuzzing&#34;&gt;发生在身边的 Fuzzing&lt;/h2&gt;&#xA;&lt;p&gt;实际上 Go-fuzz 对 Go 标准库进行过测试，依然这这之中发现了 200  多个 bug：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://files.mdnice.com/user/3610/9b00972c-2231-4406-84b8-2e8e44f57d6d.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;这还是建立在标准库已经比较成熟，且由非常有经验的开发者编写，在生产中使用多年的情况下，依然有如此多的问题。&lt;/p&gt;&#xA;&lt;h2 id=&#34;快速上手&#34;&gt;快速上手&lt;/h2&gt;&#xA;&lt;p&gt;我们需要在本地执行如下命令，需开启 GO111MODULE 和天梯：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;golang&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;org&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;dl&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gotip&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;gotip&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;download&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dev&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fuzz&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行完毕后会从 dev.fuzz 分支构建 Go 工具链，同时 gotip 可以作为 go 命令的替代者命令，也就是可以运行 Fuzzing 的相关代码了。&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么 Go map 和 slice 是非线程安全的？</title>
      <link>https://note.lican.site/posts/posts/go/map-slice-concurrency/</link>
      <pubDate>Fri, 31 Dec 2021 12:54:49 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/map-slice-concurrency/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;初入 Go 语言的大门，有不少的小伙伴会快速的 3 天精通 Go，5 天上手项目，14 天上线业务迭代，21 天排查、定位问题，顺带捎个反省报告。&lt;/p&gt;&#xA;&lt;p&gt;其中最常见的初级错误，Go 面试较最爱问的问题之一：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8d1a0506617a4729abe35927d87508d0~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&#xA;（来自读者提问）&lt;/p&gt;&#xA;&lt;p&gt;为什么在 Go 语言里，map 和 slice 不支持并发读写，也就是是非线程安全的，为什么不支持？&lt;/p&gt;&#xA;&lt;p&gt;见招拆招后，紧接着就会开始讨论如何让他们俩 ”冤家“ 支持并发读写？&lt;/p&gt;&#xA;&lt;p&gt;今天我们这篇文章就来理一理，了解其前因后果，一起吸鱼学懂 Go 语言。&lt;/p&gt;&#xA;&lt;h2 id=&#34;非线程安全的例子&#34;&gt;非线程安全的例子&lt;/h2&gt;&#xA;&lt;h3 id=&#34;slice&#34;&gt;slice&lt;/h3&gt;&#xA;&lt;p&gt;我们使用多个 goroutine 对类型为 slice 的变量进行操作，看看结果会变的怎么样。&lt;/p&gt;&#xA;&lt;p&gt;如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA; var s []string&#xA; for i := 0; i &amp;lt; 9999; i++ {&#xA;  go func() {&#xA;   s = append(s, &amp;#34;脑子进煎鱼了&amp;#34;)&#xA;  }()&#xA; }&#xA;&#xA; fmt.Printf(&amp;#34;进了 %d 只煎鱼&amp;#34;, len(s))&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// 第一次执行&#xA;进了 5790 只煎鱼&#xA;// 第二次执行&#xA;进了 7370 只煎鱼&#xA;// 第三次执行&#xA;进了 6792 只煎鱼&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你会发现无论你执行多少次，每次输出的值大概率都不会一样。也就是追加进 slice 的值，出现了覆盖的情况。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 数组比切片好在哪？</title>
      <link>https://note.lican.site/posts/posts/go/go-array-slice/</link>
      <pubDate>Fri, 17 Sep 2021 12:43:08 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-array-slice/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前段时间有播放一条快讯，就是 Go1.17 会正式支持切片（Slice）转换到数据（Array），不再需要用以前那种骚办法了，安全了许多。&lt;/p&gt;&#xA;&lt;p&gt;但是也有同学提出了新的疑惑，在 Go 语言中，数组其实是用的相对较少的，甚至会有同学认为在 Go 里可以把数组给去掉。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9764cd832d1e4875953b66434beab8e4~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;数组相较切片到底有什么优势，我们又应该在什么场景下使用呢？&lt;/p&gt;&#xA;&lt;p&gt;这是一个我们需要深究的问题，因此今天就跟大家一起来一探究竟，本文会先简单介绍数组和切片是什么，再进一步对数组的使用场景剖析。&lt;/p&gt;&#xA;&lt;p&gt;一起愉快地开始吸鱼之路。&lt;/p&gt;&#xA;&lt;h2 id=&#34;数组是什么&#34;&gt;数组是什么&lt;/h2&gt;&#xA;&lt;p&gt;Go 语言中有一种基本数据类型，叫数组。其格式为：&lt;code&gt;[n]T&lt;/code&gt;。是一个包含 N 个类型 T 的值的数组。&lt;/p&gt;&#xA;&lt;p&gt;基本声明格式为：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var a [10]int&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;代表的是声明了一个变量 a 是一个包含 10 个整数的数组。数组的长度是其类型的一部分，所以数组不能被随意调整大小。&lt;/p&gt;&#xA;&lt;p&gt;在使用例子上：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA; var a [2]string&#xA; a[0] = &amp;#34;脑子进&amp;#34;&#xA; a[1] = &amp;#34;煎鱼了&amp;#34;&#xA; fmt.Println(a[0], a[1])&#xA; fmt.Println(a)&#xA;&#xA; primes := [6]int{2, 3, 5, 7, 11, 13}&#xA; fmt.Println(primes)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;脑子进 煎鱼了&#xA;[脑子进 煎鱼了]&#xA;[2 3 5 7 11 13]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在赋值和访问上，数组可以针对不同的索引，进行单独操作。在内存布局上，数组的索引 0 和 1&amp;hellip;是会在相邻区域，可直接访问。&lt;/p&gt;&#xA;&lt;h2 id=&#34;切片是什么&#34;&gt;切片是什么&lt;/h2&gt;&#xA;&lt;p&gt;为什么数组在业务代码似乎用的很少。因为 Go 语言有一个切片的数据类型：&lt;/p&gt;</description>
    </item>
    <item>
      <title>上帝视角看 “Go 项目标准布局” 之争</title>
      <link>https://note.lican.site/posts/posts/go/go-standards/</link>
      <pubDate>Mon, 13 Sep 2021 23:34:23 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-standards/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前段时间 Go 语言社区有一件事情引爆了热议，那就是 &lt;code&gt;golang-standards/project-layout&lt;/code&gt; 项目的 “Go 项目的标准布局” 之争。&lt;/p&gt;&#xA;&lt;p&gt;没想到，五一假期，认真一看，这个 issues 已经提出将近一个月了，仍然在热议阶段，我想，咱们需要好好的聊聊这个话题。&lt;/p&gt;&#xA;&lt;p&gt;煎鱼带你了解下的前因后果，再分享我的看法和业务真实情况。&lt;/p&gt;&#xA;&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;&#xA;&lt;h3 id=&#34;问题发生地&#34;&gt;问题发生地&lt;/h3&gt;&#xA;&lt;p&gt;在 GitHub 上有一个项目 Spaghetti（github.com/adonovan/spaghetti），是 Go 软件包的一个依赖性分析工具。&lt;/p&gt;&#xA;&lt;p&gt;该项目的目录结构如下：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/11b7f74aa3944e509c89bcbfc51a2c99~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;看上去并不复杂，代码量不多，文件平铺也不超过一屏，就是一个布局比较简单的项目。&lt;/p&gt;&#xA;&lt;p&gt;有一位老哥提出了一个 PR，明确的期望该项目按照 &lt;code&gt;golang-standards/project-layout&lt;/code&gt; 项目给出的 “标准” 布局来调整。：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e0da59130ce94ee190d51491396d6919~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;我猜测该项目可能是因为把 Go、HTML、JS、PNG 和 go.mod 文件等摆在了一起，引起了该同学的一丝丝纠结，觉得比较乱？&lt;/p&gt;&#xA;&lt;h3 id=&#34;标准布局-长什么样子&#34;&gt;“标准布局“ 长什么样子&lt;/h3&gt;&#xA;&lt;p&gt;在 &lt;code&gt;golang-standards/project-layout&lt;/code&gt; 项目中，其自称：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/55511ceb43cb48c59d0ce302783eec59~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;项目的组织名也是 &amp;ldquo;golang-standards&amp;rdquo;，其提供了一个基本的 Go 项目布局，精简展示如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;project-layout&#xA;├── api&#xA;├── cmd&#xA;├── configs&#xA;├── docs&#xA;├── go.mod&#xA;├── init&#xA;├── internal&#xA;├── pkg&#xA;├── scripts&#xA;├── vendor&#xA;├── ...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;/cmd：项目主要的应用程序。&lt;/p&gt;</description>
    </item>
    <item>
      <title>用 Go map 要注意这个细节，避免依赖他！</title>
      <link>https://note.lican.site/posts/posts/go/go-map-access/</link>
      <pubDate>Sun, 12 Sep 2021 17:47:29 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-map-access/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近又有同学问我这个日经话题，想转他文章时，结果发现我的公众号竟然没有发过，因此今天我再唠叨两句，好让大家避开这个 “坑”。&lt;/p&gt;&#xA;&lt;p&gt;有的小伙伴没留意过 Go map 输出、遍历顺序，&lt;strong&gt;以为它是稳定的有序的，会在业务程序中直接依赖这个结果集顺序，结果栽了个大跟头&lt;/strong&gt;，吃了线上 BUG。&lt;/p&gt;&#xA;&lt;p&gt;有的小伙伴知道是无序的，但却不知道为什么,有的却理解错误？&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;http://wx2.sinaimg.cn/large/006fVPCvly1g1s1ah84k8j30k70dvaac.jpg&#34; alt=&#34;奇怪的输出结果&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;今天通过本文，我们将揭开 &lt;code&gt;for range map&lt;/code&gt; 输出的 “神秘” 面纱，看看它内部实现到底是怎么样的，顺序到底是怎么样？&lt;/p&gt;&#xA;&lt;p&gt;开始吸鱼之路。&lt;/p&gt;&#xA;&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;例子如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#66d9ef&#34;&gt;int32&lt;/span&gt;]&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY3&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY4&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY5&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;k: %v, v: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;假设运行这段代码，输出的结果是怎么样？是有序，还是无序输出呢？&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;k: 3, v: EDDYCJY4&#xA;k: 4, v: EDDYCJY5&#xA;k: 0, v: EDDYCJY1&#xA;k: 1, v: EDDYCJY2&#xA;k: 2, v: EDDYCJY3&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从输出结果上来讲，是非固定顺序输出的，也就是每次都不一样。但这是为什么呢？&lt;/p&gt;&#xA;&lt;p&gt;首先&lt;strong&gt;建议你先自己想想原因&lt;/strong&gt;。其次我在面试时听过一些说法。有人说因为是哈希的所以就是无（乱）序等等说法。当时我是有点 ？？？&lt;/p&gt;&#xA;&lt;p&gt;这也是这篇文章出现的原因，希望大家可以一起研讨一下，理清这个问题 ：）&lt;/p&gt;&#xA;&lt;h2 id=&#34;看一下汇编&#34;&gt;看一下汇编&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    ...&#xA;&#x9;0x009b 00155 (main.go:11)&#x9;LEAQ&#x9;type.map[int32]string(SB), AX&#xA;&#x9;0x00a2 00162 (main.go:11)&#x9;PCDATA&#x9;$2, $0&#xA;&#x9;0x00a2 00162 (main.go:11)&#x9;MOVQ&#x9;AX, (SP)&#xA;&#x9;0x00a6 00166 (main.go:11)&#x9;PCDATA&#x9;$2, $2&#xA;&#x9;0x00a6 00166 (main.go:11)&#x9;LEAQ&#x9;&amp;#34;&amp;#34;..autotmp_3+24(SP), AX&#xA;&#x9;0x00ab 00171 (main.go:11)&#x9;PCDATA&#x9;$2, $0&#xA;&#x9;0x00ab 00171 (main.go:11)&#x9;MOVQ&#x9;AX, 8(SP)&#xA;&#x9;0x00b0 00176 (main.go:11)&#x9;PCDATA&#x9;$2, $2&#xA;&#x9;0x00b0 00176 (main.go:11)&#x9;LEAQ&#x9;&amp;#34;&amp;#34;..autotmp_2+72(SP), AX&#xA;&#x9;0x00b5 00181 (main.go:11)&#x9;PCDATA&#x9;$2, $0&#xA;&#x9;0x00b5 00181 (main.go:11)&#x9;MOVQ&#x9;AX, 16(SP)&#xA;&#x9;0x00ba 00186 (main.go:11)&#x9;CALL&#x9;runtime.mapiterinit(SB)&#xA;&#x9;0x00bf 00191 (main.go:11)&#x9;JMP&#x9;207&#xA;&#x9;0x00c1 00193 (main.go:11)&#x9;PCDATA&#x9;$2, $2&#xA;&#x9;0x00c1 00193 (main.go:11)&#x9;LEAQ&#x9;&amp;#34;&amp;#34;..autotmp_2+72(SP), AX&#xA;&#x9;0x00c6 00198 (main.go:11)&#x9;PCDATA&#x9;$2, $0&#xA;&#x9;0x00c6 00198 (main.go:11)&#x9;MOVQ&#x9;AX, (SP)&#xA;&#x9;0x00ca 00202 (main.go:11)&#x9;CALL&#x9;runtime.mapiternext(SB)&#xA;&#x9;0x00cf 00207 (main.go:11)&#x9;CMPQ&#x9;&amp;#34;&amp;#34;..autotmp_2+72(SP), $0&#xA;&#x9;0x00d5 00213 (main.go:11)&#x9;JNE&#x9;193&#xA;&#x9;...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我们大致看一下整体过程，重点处理 Go map 循环迭代的是两个 runtime 方法，如下：&lt;/p&gt;</description>
    </item>
    <item>
      <title>生产环境遇到一个 Go 问题，整组人都懵逼了...</title>
      <link>https://note.lican.site/posts/posts/go/go-errors-boom/</link>
      <pubDate>Wed, 07 Jul 2021 12:43:49 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-errors-boom/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前段时间正在疯狂写代码的时候，突然有一个读者给我提了一个问题，让我有了一定的兴趣：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1011e66fe104d228dcaa2083b554fc8~tplv-k3u1fbpfcp-watermark.image&#34; alt=&#34;image.png&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;我还是比较感兴趣的，因为是生产环境、有代码，且整组人都懵逼的问题。&lt;/p&gt;&#xA;&lt;p&gt;在征求了小伙伴的意见后，今天分享出来，大家也思考一下原因，一起规避这个 “坑”。&lt;/p&gt;&#xA;&lt;h2 id=&#34;案例一&#34;&gt;案例一&lt;/h2&gt;&#xA;&lt;p&gt;代码示例如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyErr&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Msg&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;GetErr&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetErr&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;MyErr&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;MyErr&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Error&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请思考一下，&lt;strong&gt;这段程序的输出结果是什么&lt;/strong&gt;？&lt;/p&gt;&#xA;&lt;p&gt;该程序所调用的 &lt;code&gt;GetErr&lt;/code&gt; 方法所返回的是 &lt;code&gt;nil&lt;/code&gt;，而外部判断是 &lt;code&gt;e == nil&lt;/code&gt;，因此最终的输出结果是 true，对吗？&lt;/p&gt;&#xA;&lt;p&gt;输出结果如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;2021/04/04 08:39:04 false&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;答案是：false。&lt;/p&gt;&#xA;&lt;h2 id=&#34;案例二&#34;&gt;案例二&lt;/h2&gt;&#xA;&lt;p&gt;代码示例如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Base&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;do&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;App&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;base&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Base&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;base&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;GetApp&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;base&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;base&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetApp&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;App&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;App&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;do&lt;/span&gt;() {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请思考一下，&lt;strong&gt;这段程序的输出结果是什么&lt;/strong&gt;？&lt;/p&gt;</description>
    </item>
    <item>
      <title>经典面试题：你觉得 Go 在什么时候会抢占 P？</title>
      <link>https://note.lican.site/posts/posts/go/gmp-why-p/</link>
      <pubDate>Thu, 24 Jun 2021 12:42:05 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gmp-why-p/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天我们有聊到《单核 CPU，开两个 Goroutine，其中一个死循环，会怎么样？》的问题，我们在一个细节部分有提到：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d9cdfb09590496daeca3675aae08611~tplv-k3u1fbpfcp-zoom-1.image&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;有新的小伙伴会产生更多的疑问，那就是在 Go 语言中，是如何抢占 P 的呢，这里面是怎么做的？&lt;/p&gt;&#xA;&lt;p&gt;今天这篇文章我们就来解密抢占 P。&lt;/p&gt;&#xA;&lt;h2 id=&#34;调度器的发展史&#34;&gt;调度器的发展史&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 语言中，Goroutine 早期是没有设计成抢占式的，早期 Goroutine 只有读写、主动让出、锁等操作时才会触发调度切换。&lt;/p&gt;&#xA;&lt;p&gt;这样有一个严重的问题，就是垃圾回收器进行 STW 时，如果有一个 Goroutine 一直都在阻塞调用，垃圾回收器就会一直等待他，不知道等到什么时候&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;这种情况下就需要抢占式调度来解决问题。如果一个 Goroutine 运行时间过久，就需要进行抢占来解决。&lt;/p&gt;&#xA;&lt;p&gt;这块 Go 语言在 Go1.2 起开始实现抢占式调度器，不断完善直至今日：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Go0.x：基于单线程的程调度器。&lt;/li&gt;&#xA;&lt;li&gt;Go1.0：基于多线程的调度器。&lt;/li&gt;&#xA;&lt;li&gt;Go1.1：基于任务窃取的调度器。&lt;/li&gt;&#xA;&lt;li&gt;Go1.2 - Go1.13：基于协作的抢占式调度器。&lt;/li&gt;&#xA;&lt;li&gt;Go1.14：基于信号的抢占式调度器。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;调度器的新提案：非均匀存储器访问调度（Non-uniform memory access，NUMA），&#xA;但由于实现过于复杂，优先级也不够高，因此迟迟未提上日程。&lt;/p&gt;&#xA;&lt;p&gt;有兴趣的小伙伴可以详见 Dmitry Vyukov, dvyukov 所提出的 &lt;a href=&#34;https://docs.google.com/document/u/0/d/1d3iI2QWURgDIsSR6G2275vMeQ_X7w-qxM2Vp7iGwwuM/pub&#34;&gt;NUMA-aware scheduler for Go&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么要抢占-p&#34;&gt;为什么要抢占 P&lt;/h2&gt;&#xA;&lt;p&gt;为什么会要想去抢占 P 呢，说白了就是不抢，就没机会运行，会 hang 死。又或是资源分配不均了，&lt;/p&gt;&#xA;&lt;p&gt;这在调度器设计中显然是不合理的。&lt;/p&gt;&#xA;&lt;p&gt;跟这个例子一样：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Main Goroutine &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 模拟单核 CPU&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;runtime&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GOMAXPROCS&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 模拟 Goroutine 死循环&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Millisecond&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个例子在老版本的 Go 语言中，就会一直阻塞，没法重见天日，是一个需要做抢占的场景。&lt;/p&gt;</description>
    </item>
    <item>
      <title>用 Go struct 不能犯的一个低级错误！</title>
      <link>https://note.lican.site/posts/posts/go/go-empty-struct/</link>
      <pubDate>Thu, 17 Jun 2021 12:44:27 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-empty-struct/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前段时间我分享了 《手撕 Go 面试官：Go 结构体是否可以比较，为什么？》的文章，把基本 Go struct 的比较依据研究了一番。这不，最近有一位读者，遇到了一个关于 struct 的新问题，不得解。&lt;/p&gt;&#xA;&lt;p&gt;大家一起来看看，建议大家在看到代码例子后先思考一下答案，再往下看。&lt;/p&gt;&#xA;&lt;p&gt;独立思考很重要。&lt;/p&gt;&#xA;&lt;h2 id=&#34;疑惑的例子&#34;&gt;疑惑的例子&lt;/h2&gt;&#xA;&lt;p&gt;其给出的例子一如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;People&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;People&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;People&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你认为输出结果是什么呢？&lt;/p&gt;&#xA;&lt;p&gt;输出结果是：false。&lt;/p&gt;&#xA;&lt;p&gt;再稍加改造一下，例子二如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;People&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;People&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;People&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%p\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%p\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果是：true。&lt;/p&gt;&#xA;&lt;p&gt;他的问题是 &amp;ldquo;&lt;strong&gt;为什么第一个返回 false 第二个返回 true，是什么原因导致的&lt;/strong&gt;？&lt;/p&gt;&#xA;&lt;p&gt;煎鱼进一步的精简这个例子，得到最小示例：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; new(&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; new(&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;println(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; new(&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; new(&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;println(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// a, b; a &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; b&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0xc00005cf57 0xc00005cf57 false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// c, d&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;amp;&lt;span style=&#34;color:#f92672&#34;&gt;{}&lt;/span&gt; &amp;amp;&lt;span style=&#34;color:#f92672&#34;&gt;{}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// c, d, c &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; d&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0x118c370 0x118c370 true&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一段代码的结果是 false，第二段的结果是 true，且可以看到内存地址指向的完全一样，也就是排除了输出后变量内存指向改变导致的原因。&lt;/p&gt;</description>
    </item>
    <item>
      <title>详解 Go 程序的启动流程，你知道 g0，m0 是什么吗？</title>
      <link>https://note.lican.site/posts/posts/go/go-bootstrap0/</link>
      <pubDate>Thu, 17 Jun 2021 12:42:42 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-bootstrap0/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;自古应用程序均从 Hello World 开始，你我所写的 Go 语言亦然：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import &amp;#34;fmt&amp;#34;&#xA;&#xA;func main() {&#xA;&#x9;fmt.Println(&amp;#34;hello world.&amp;#34;)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这段程序的输出结果为 &lt;code&gt;hello world.&lt;/code&gt;，就是这么的简单又直接。但这时候又不禁思考了起来，这个 &lt;code&gt;hello world.&lt;/code&gt; 是怎么输出来，经历了什么过程。&lt;/p&gt;&#xA;&lt;p&gt;真是非常的好奇，今天我们就一起来探一探 Go 程序的启动流程。&#xA;其中涉及到 Go Runtime 的调度器启动，g0，m0 又是什么？&lt;/p&gt;&#xA;&lt;p&gt;车门焊死，正式开始吸鱼之路。&lt;/p&gt;&#xA;&lt;h2 id=&#34;go-引导阶段&#34;&gt;Go 引导阶段&lt;/h2&gt;&#xA;&lt;h3 id=&#34;查找入口&#34;&gt;查找入口&lt;/h3&gt;&#xA;&lt;p&gt;首先编译上文提到的示例程序：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ GOFLAGS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-ldflags=-compressdwarf=false&amp;#34;&lt;/span&gt; go build &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在命令中指定了 GOFLAGS 参数，这是因为在 Go1.11 起，为了减少二进制文件大小，调试信息会被压缩。导致在 MacOS 上使用 gdb 时无法理解压缩的 DWARF 的含义是什么（而我恰恰就是用的 MacOS）。&lt;/p&gt;&#xA;&lt;p&gt;因此需要在本次调试中将其关闭，再使用 gdb 进行调试，以此达到观察的目的：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ gdb awesomeProject &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;gdb&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; info files&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Symbols from &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/Users/eddycjy/go-application/awesomeProject/awesomeProject&amp;#34;&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Local exec file:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;/Users/eddycjy/go-application/awesomeProject/awesomeProject&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;, file type mach-o-x86-64.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Entry point: 0x1063c80&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;0x0000000001001000 - 0x00000000010a6aca is .text&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;gdb&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; b *0x1063c80&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Breakpoint &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; at 0x1063c80: file /usr/local/Cellar/go/1.15/libexec/src/runtime/rt0_darwin_amd64.s, line 8.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过 Entry point 的调试，可看到真正的程序入口在 runtime 包中，不同的计算机架构指向不同。例如：&lt;/p&gt;</description>
    </item>
    <item>
      <title>跟面试官聊 Goroutine 泄露的 6 种方法，真刺激！</title>
      <link>https://note.lican.site/posts/posts/go/goroutine-leak/</link>
      <pubDate>Fri, 11 Jun 2021 12:54:49 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/goroutine-leak/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天分享 Go 群友提问的文章时，有读者在朋友圈下提到，希望我能够针对 Goroutine 泄露这块进行讲解，他在面试的时候经常被问到。&lt;/p&gt;&#xA;&lt;p&gt;今天的男主角，就是 Go 语言的著名品牌标识 Goroutine，一个随随便便就能开几十万个快车进车道的大杀器。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本文会聚焦于 Goroutine 泄露的 N 种方法，进行详解和说明。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么要问&#34;&gt;为什么要问&lt;/h2&gt;&#xA;&lt;p&gt;面试官为啥会问 Goroutine（协程）泄露这种奇特的问题呢？&lt;/p&gt;&#xA;&lt;p&gt;可以猜测是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Goroutine 实在是使用门槛实在是太低了，随手就一个就能起，出现了不少滥用的情况。例如：并发 map。&lt;/li&gt;&#xA;&lt;li&gt;Goroutine 本身在 Go 语言的标准库、复合类型、底层源码中应用广泛。例如：HTTP Server 对每一个请求的处理就是一个协程去运行。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;很多 Go 工程在线上出事故时，基本 Goroutine 的关联，大家都会作为救火队长，风风火火的跑去看指标、看日志，通过 PProf 采集 Goroutine 运行情况等。&lt;/p&gt;&#xA;&lt;p&gt;自然他也就是最受瞩目的那颗 “星” 了，所以在日常面试中，被问几率也就极高了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;goroutine-泄露&#34;&gt;Goroutine 泄露&lt;/h2&gt;&#xA;&lt;p&gt;了解清楚大家爱问的原因后，我们开始对 Goroutine 泄露的 N 种方法进行研究，希望通过前人留下的 “坑”，了解其原理和避开这些问题。&lt;/p&gt;&#xA;&lt;p&gt;泄露的原因大多集中在：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Goroutine 内正在进行 channel/mutex 等读写操作，但由于逻辑问题，某些情况下会被一直阻塞。&lt;/li&gt;&#xA;&lt;li&gt;Goroutine 内的业务逻辑进入死循环，资源一直无法释放。&lt;/li&gt;&#xA;&lt;li&gt;Goroutine 内的业务逻辑进入长时间等待，有不断新增的 Goroutine 进入等待。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;接下来我会引用在网上冲浪收集到的一些 Goroutine 泄露例子（会在文末参考注明出处）。&lt;/p&gt;&#xA;&lt;h3 id=&#34;channel-使用不当&#34;&gt;channel 使用不当&lt;/h3&gt;&#xA;&lt;p&gt;Goroutine+Channel 是最经典的组合，因此不少泄露都出现于此。&lt;/p&gt;</description>
    </item>
    <item>
      <title>你知道 Go 结构体和结构体指针调用有什么区别吗？</title>
      <link>https://note.lican.site/posts/posts/go/struct-pointer/</link>
      <pubDate>Sun, 06 Jun 2021 12:21:30 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/struct-pointer/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在分享《Go 结构体是否可以比较，为什么？》时，有小伙伴提出了新的问题：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/a4d34c5312339b9909e482c18f0cdf4a.png&#34; alt=&#34;来自文章评论区&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;虽然大家提问题的速度已经超出了本鱼写文章的速度&amp;hellip;不过作为宠粉狂鱼，在此刻清明假期时还是写下了这篇文章。&lt;/p&gt;&#xA;&lt;p&gt;我在网上冲浪时搜索了相关问题，发现 6 年前就有 Go 开发者有一模一样的疑问，真是困扰了一代又一代的小伙伴。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/dbe910917c86e103648e314f79896a81.png&#34; alt=&#34;来自 stackoverflow.com&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;本期的男主角是《&lt;strong&gt;Go 结构体和结构体指针调用有什么区别&lt;/strong&gt;》，希望对大家有所帮助，带来一些思考。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;请在此处默念自己心目中的答案&lt;/strong&gt;，再和煎鱼一同研讨一波 Go 的技术哲学。&lt;/p&gt;&#xA;&lt;h2 id=&#34;结构体是什么&#34;&gt;结构体是什么&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 语言中有个基本类型，开发者们称之为结构体（struct）。是 Go 语言中非常常用的，基本定义：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;struct_variable_type&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;简单示例：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vertex&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name1&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vertex&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进了&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;蒸鱼&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;蒸鱼&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这部分属于基础知识，因此不再过多解释。如果看不懂，建议重学 Go 语言语法基础。&lt;/p&gt;&#xA;&lt;h2 id=&#34;结构体和指针调用&#34;&gt;结构体和指针调用&lt;/h2&gt;&#xA;&lt;p&gt;讲解前置概要后，直接进入本文主题。如下例子：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyStruct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyStruct&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;SetName1&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;MyStruct&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;SetName2&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该程序声明了一个 &lt;code&gt;User&lt;/code&gt; 结构体，其包含两个结构体方法，分别是 &lt;code&gt;SetName1&lt;/code&gt; 和 &lt;code&gt;SetName2&lt;/code&gt; 方法，两者之间的差异就是&lt;strong&gt;引用的方式不同&lt;/strong&gt;。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 面试官：单核 CPU，开两个 Goroutine，其中一个死循环，会怎么样？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-goroutineloop/</link>
      <pubDate>Mon, 05 Apr 2021 16:17:23 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-goroutineloop/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近金三银四，是面试的季节。在我的 Go 读者交流群里出现了许多小伙伴在讨论自己面试过程中所遇到的一些 Go 面试题。&lt;/p&gt;&#xA;&lt;p&gt;今天的男主角，是与 Go 工程师有调度相关的知识，那就是 “&lt;strong&gt;单核 CPU，开两个 Goroutine，其中一个死循环，会怎么样？&lt;/strong&gt;”&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;请在此处默念自己心目中的答案&lt;/strong&gt;，再往和煎鱼一起研讨一波 Go 的技术哲学。&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题定义&#34;&gt;问题定义&lt;/h2&gt;&#xA;&lt;p&gt;针对这个问题，我们需要把问题剖开来看看，其具有以下几个元素：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;运行 Go 程序的计算机只有一个单核 CPU。&lt;/li&gt;&#xA;&lt;li&gt;两个 Goroutine 在运行。&lt;/li&gt;&#xA;&lt;li&gt;一个 Goroutine 死循环。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;根据这道题的题意，可大致理解其想要问的是 Go 调度相关的一些知识理解。&lt;/p&gt;&#xA;&lt;h3 id=&#34;单核-cpu&#34;&gt;单核 CPU&lt;/h3&gt;&#xA;&lt;p&gt;第一个要点，就是要明确 “计算机只有一个单核 CPU” 这一个变量定义，对 Go 程序会产生什么影响，否则很难继续展开。&lt;/p&gt;&#xA;&lt;p&gt;既然明确涉及 Goroutine，这里就会考察到你对 Go 的调度模型 GMP 的基本理解了。&lt;/p&gt;&#xA;&lt;p&gt;从单核 CPU 来看，最大的影响就是 GMP 模型中的 P，因为 P 的数量默认是与 CPU 核数（GOMAXPROCS）保持一致的。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;G：Goroutine，实际上我们每次调用 &lt;code&gt;go func&lt;/code&gt; 就是生成了一个 G。&lt;/li&gt;&#xA;&lt;li&gt;P：Processor，处理器，一般 P 的数量就是处理器的核数，可以通过 &lt;code&gt;GOMAXPROCS&lt;/code&gt; 进行修改。&lt;/li&gt;&#xA;&lt;li&gt;M：Machine，系统线程。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这三者交互实际来源于 Go 的 M: N 调度模型。也就是 M 必须与 P 进行绑定，然后不断地在 M 上循环寻找可运行的 G 来执行相应的任务。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 内存泄露之痛，这篇把 Go timer.After 问题根因讲透了！</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-timer-memory/</link>
      <pubDate>Mon, 05 Apr 2021 16:16:47 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-timer-memory/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在公众号分享了一篇 Go timer 源码解析的文章《难以驾驭的 Go timer，一文带你参透计时器的奥秘》。&lt;/p&gt;&#xA;&lt;p&gt;在评论区有小伙伴提到了经典的 &lt;code&gt;timer.After&lt;/code&gt; 泄露问题，希望我能聊聊，这是一个不能不知的一个大 “坑”。&lt;/p&gt;&#xA;&lt;p&gt;今天这篇文章煎鱼就带大家来研讨一下这个问题。&lt;/p&gt;&#xA;&lt;h2 id=&#34;timerafter&#34;&gt;timer.After&lt;/h2&gt;&#xA;&lt;p&gt;今天是男主角是Go 标准库 time 所提供的 &lt;code&gt;After&lt;/code&gt; 方法。函数签名如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;After&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Duration&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Time&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该方法可以在一定时间（根据所传入的 Duration）后主动返回 &lt;code&gt;time.Time&lt;/code&gt; 类型的 channel 消息。&lt;/p&gt;&#xA;&lt;p&gt;在常见的场景下，我们会基于此方法做一些计时器相关的功能开发，例子如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Second&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;After&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Second&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼出去了，超时了！！！&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在运行 1 秒钟后，输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;煎鱼出去了，超时了！！！&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上述程序在在运行 1 秒钟后将触发 &lt;code&gt;time.After&lt;/code&gt; 方法的定时消息返回，输出了超时的结果。&lt;/p&gt;&#xA;&lt;h2 id=&#34;坑在哪里&#34;&gt;坑在哪里&lt;/h2&gt;&#xA;&lt;p&gt;从例子来看似乎非常正常，也没什么 “坑” 的样子。难道是 &lt;code&gt;timer.After&lt;/code&gt; 方法的虚晃一枪？&lt;/p&gt;&#xA;&lt;p&gt;我们再看一个不像是有问题例子，这在 Go 工程中经常能看见，只是大家都没怎么关注。&lt;/p&gt;&#xA;&lt;p&gt;代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// do something...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;After&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Minute&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;现在是：%d，我脑子进煎鱼了！&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Now&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Unix&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，我们构造了一个 &lt;code&gt;for+select+channel&lt;/code&gt; 的一个经典的处理模式。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 面试官：Go 结构体是否可以比较，为什么？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-sturct/</link>
      <pubDate>Mon, 05 Apr 2021 16:16:00 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-sturct/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近金三银四，是面试的季节。在我的 Go 读者交流群里出现了许多小伙伴在讨论自己面试过程中所遇到的一些 Go 面试题。&lt;/p&gt;&#xA;&lt;p&gt;今天的男主角，是 Go 工程师的必修技能，也是极容易踩坑的地方，就是 “&lt;strong&gt;Go 面试题：Go 结构体（struct）是否可以比较？&lt;/strong&gt;”&lt;/p&gt;&#xA;&lt;p&gt;如果可以比较，是为什么？如果不可以比较，又是为什么？&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;请在此处默念自己心目中的答案&lt;/strong&gt;，再往和煎鱼一起研讨一波 Go 的技术哲学。&lt;/p&gt;&#xA;&lt;h2 id=&#34;结构体是什么&#34;&gt;结构体是什么&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 语言中有个基本类型，开发者们称之为结构体（struct）。是 Go 语言中非常常用的，基本定义：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;struct_variable_type&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;definition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;简单示例：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vertex&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name1&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Vertex&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进了&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;蒸鱼&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Name2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;蒸鱼&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这部分属于基础知识，因此不再过多解释。如果看不懂，建议重学 Go 语言语法基础。&lt;/p&gt;&#xA;&lt;h2 id=&#34;比较两下&#34;&gt;比较两下&lt;/h2&gt;&#xA;&lt;h3 id=&#34;例子一&#34;&gt;例子一&lt;/h3&gt;&#xA;&lt;p&gt;接下来正式开始研讨 Go 结构体比较的问题，第一个例子如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Gender&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Gender&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;男&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Gender&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;男&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v2&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子进煎鱼了&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;脑子没进煎鱼&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们声明了两个变量，分别是 v1 和 v2。其都是 &lt;code&gt;Value&lt;/code&gt; 结构体的实例化，是同一个结构体的两个实例。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 面试官：GMP 模型，为什么要有 P？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-gmp-p/</link>
      <pubDate>Mon, 05 Apr 2021 16:15:20 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-gmp-p/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近金三银四，是面试的季节。在我的 Go 读者交流群里出现了许多小伙伴在讨论自己面试过程中所遇到的一些 Go 面试题。&lt;/p&gt;&#xA;&lt;p&gt;今天的主角，是 Go 面试的万能题 GMP 模型的延伸题（疑问），那就是 ”&lt;strong&gt;GMP 模型，为什么要有 P&lt;/strong&gt;？“&lt;/p&gt;&#xA;&lt;p&gt;进一步推敲问题的背后，其实这个面试题本质是想问：”&lt;strong&gt;GMP 模型，为什么不是 G 和 M 直接绑定就完了，还要搞多一个 P 出来，那么麻烦，为的是什么，是要解决什么问题吗&lt;/strong&gt;？“&lt;/p&gt;&#xA;&lt;p&gt;这篇文章煎鱼就带你一同探索，GM、GMP 模型的变迁是因为什么原因。&lt;/p&gt;&#xA;&lt;h2 id=&#34;gm-模型&#34;&gt;GM 模型&lt;/h2&gt;&#xA;&lt;p&gt;在 Go1.1 之前 Go 的调度模型其实就是 GM 模型，也就是没有 P。&lt;/p&gt;&#xA;&lt;p&gt;今天带大家一起回顾过去的设计。&lt;/p&gt;&#xA;&lt;h3 id=&#34;解密-go10-源码&#34;&gt;解密 Go1.0 源码&lt;/h3&gt;&#xA;&lt;p&gt;我们了解一个东西的办法之一就是看源码，和煎鱼一起看看 Go1.0.1 的&lt;a href=&#34;https://github.com/golang/go/blob/go1.0.1/src/pkg/runtime/proc.c&#34;&gt;调度器源码&lt;/a&gt;的核心关键步骤：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;schedule&lt;/span&gt;(G &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;gp)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;schedlock&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(gp &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; nil) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt;(gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; Grunnable:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; Gdead:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#75715e&#34;&gt;// Shouldn&amp;#39;t have been running!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;runtime&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;·&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;throw&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bad gp-&amp;gt;status in sched&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; Grunning:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Grunnable;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gput&lt;/span&gt;(gp);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;gp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;nextgandunlock&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;readyonstop &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Grunning;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;m&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;curg &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; gp;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;m &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; m;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;runtime&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;·&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gogo&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;gp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;sched, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;schedlock&lt;/code&gt; 方法来获取全局锁。&lt;/li&gt;&#xA;&lt;li&gt;获取全局锁成功后，将当前 Goroutine 状态从 Running（正在被调度） 状态修改为 Runnable（可以被调度）状态。&lt;/li&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;gput&lt;/code&gt; 方法来保存当前 Goroutine 的运行状态等信息，以便于后续的使用；&lt;/li&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;nextgandunlock&lt;/code&gt; 方法来寻找下一个可运行 Goroutine，并且释放全局锁给其他调度使用。&lt;/li&gt;&#xA;&lt;li&gt;获取到下一个待运行的 Goroutine 后，将其的运行状态修改为 Running。&lt;/li&gt;&#xA;&lt;li&gt;调用 &lt;code&gt;runtime·gogo&lt;/code&gt; 方法，将刚刚所获取到的下一个待执行的 Goroutine 运行起来。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;思考-gm-模型&#34;&gt;思考 GM 模型&lt;/h3&gt;&#xA;&lt;p&gt;通过对 Go1.0.1 的调度器源码剖析，我们可以发现一个比较有趣的点。那就是调度器本身（schedule 方法），在正常流程下，是不会返回的，也就是不会结束主流程。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 群友提问：进程、线程都有 ID，为什么 Goroutine 没有 ID？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-goroutineid/</link>
      <pubDate>Mon, 05 Apr 2021 16:14:14 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-goroutineid/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近金三银四，是面试的季节。在我的 Go 读者交流群里出现了许多小伙伴在讨论自己面试过程中所遇到的一些 Go 面试题。&lt;/p&gt;&#xA;&lt;p&gt;今天的主角，是大家在既有语言基础的情况下，学 Goroutine 时会容易纠结的一点。就是 “&lt;strong&gt;进程、线程都有 ID，为什么 Goroutine 没有 GoroutineID？&lt;/strong&gt;”。&lt;/p&gt;&#xA;&lt;p&gt;这是为什么呢，怎么做那些跨协程处理呢？&lt;/p&gt;&#xA;&lt;h2 id=&#34;goroutineid-是什么&#34;&gt;GoroutineID 是什么&lt;/h2&gt;&#xA;&lt;p&gt;我们要知道，为什么大家会下意识的想去要 GoroutineID，下面引用 Go 语言圣经中的表述：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;在大多数支持多线程的操作系统和程序语言中，当前的线程都有一个独特的身份（ID），并且这个身份信息可以以一个普通值的形式被很容易地获取到，典型的可以是一个 integer 或者指针值。这种情况下我们做一个抽象化的 thread-local storage（线程本地存储，多线程编程中不希望其它线程访问的内容）就很容易，只需要以线程的 ID 作为 key 的一个 map 就可以解决问题，每一个线程以其 ID 就能从中获取到值，且和其它线程互不冲突。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;也就在常规的进程、线程中都有其 ID 的概念，我们可以在程序中通过 ID 来获取其他进程、线程中的数据，甚至是传输数据。就像一把钥匙一样，有了他干啥都可以。&lt;/p&gt;&#xA;&lt;p&gt;GoroutineID 的概念也是类似的，也就是协程的 ID。我们下意识的就期望通过协程 ID 来进行跨协程的操作。&lt;/p&gt;&#xA;&lt;p&gt;但，在 Go 语言中 GoroutineID 并没有显式获取的办法，这就要打个大大的疑惑了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么没有-goroutineid&#34;&gt;为什么没有 GoroutineID&lt;/h2&gt;&#xA;&lt;p&gt;为什么在 Go 语言中没有 GoroutineID 呢，是从一开始就没有的，还是，这样子设计的原因是什么呢？&lt;/p&gt;&#xA;&lt;p&gt;其实 Go 语言在以前是有暴露方法去获取 GoroutineID 的，但在 Go1.4 后就把该方法给隐藏起来了，不建议大家使用。&lt;/p&gt;&#xA;&lt;p&gt;也就是明面上没有 GoroutineID，是一个有意而为之的行为。原因是：&lt;strong&gt;根据以往的经验，认为 thread-local storage 存在被滥用的可能性，且带来许多不必要的复杂度&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;简单来讲，Andrew Gerrand 的回答是 ”&lt;strong&gt;thread-local storage 的成本远远超过了它们的收益。它们只是不适合 Go 语言&lt;/strong&gt;。”&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 面试官：Go interface 的一个 “坑” 及原理分析</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-interface/</link>
      <pubDate>Mon, 05 Apr 2021 16:12:59 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-interface/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在读者交流群里看到一位小伙伴，针对 interface 的使用有了比较大的疑惑。&lt;/p&gt;&#xA;&lt;p&gt;无独有偶，我也在网上看到有小伙伴在 Go 面试的时候被问到了：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/36d7ecb0da9e4b32a493dedce6ebc691~tplv-k3u1fbpfcp-watermark.image&#34; alt=&#34;来自网上博客的截图&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;今天特意分享出来让大家避开这个坑。&lt;/p&gt;&#xA;&lt;h2 id=&#34;例子一&#34;&gt;例子一&lt;/h2&gt;&#xA;&lt;p&gt;第一个例子，如下代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; = (&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;)(&lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你觉得输出结果是什么呢？&lt;/p&gt;&#xA;&lt;p&gt;答案是：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;false&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;为什么不是 &lt;code&gt;true&lt;/code&gt;。明明都已经强行置为 &lt;code&gt;nil&lt;/code&gt; 了。是不是 Go 编译器有问题？&lt;/p&gt;&#xA;&lt;h2 id=&#34;例子二&#34;&gt;例子二&lt;/h2&gt;&#xA;&lt;p&gt;第二个例子，如下代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你觉得输出结果是什么呢？&lt;/p&gt;&#xA;&lt;p&gt;答案是：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;nil&amp;gt; true&#xA;&amp;lt;nil&amp;gt; true&#xA;&amp;lt;nil&amp;gt; false&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这可就更奇怪了，为什么刚刚声明出来的 &lt;code&gt;data&lt;/code&gt; 和 &lt;code&gt;in&lt;/code&gt; 变量，确实是输出结果是 &lt;code&gt;nil&lt;/code&gt;，判断结果也是 &lt;code&gt;true&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;怎么把变量 &lt;code&gt;data&lt;/code&gt; 一赋予给变量 &lt;code&gt;in&lt;/code&gt;，世界就变了？输出结果依然是 &lt;code&gt;nil&lt;/code&gt;，但判定却变成了 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;和上面的第一个例子结果类似，真是神奇。&lt;/p&gt;&#xA;&lt;h2 id=&#34;原因&#34;&gt;原因&lt;/h2&gt;&#xA;&lt;p&gt;interface 判断与想象中不一样的根本原因是，interface 并不是一个指针类型，虽然他看起来很像，以至于误导了不少人。&lt;/p&gt;&#xA;&lt;p&gt;我们钻下去 interface，interface 共有两类数据结构：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://imgkr2.cn-bj.ufileos.com/560dcab5-e436-4a2c-bfee-6eba6faee1d2.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&amp;amp;Signature=HF1wka8F9MTc%252FVCTrMjhASpeeE4%253D&amp;amp;Expires=1615900476&#34; alt=&#34;&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 群友提问：学习 defer 时很懵逼，这道不会做！</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-defer/</link>
      <pubDate>Mon, 05 Apr 2021 16:10:51 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-defer/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在读者交流群里看到一位小伙伴，在向大家咨询 Go 相关的技术问题。&#xA;疑问是：“&lt;strong&gt;各位大佬，我在学习 defer 遇到闭包的时候很懵逼，谁比较明白，能指点？&lt;/strong&gt;”&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/36e2b86b536909f265b84db24dcd80c6.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;疑问&#34;&gt;疑问&lt;/h2&gt;&#xA;&lt;p&gt;他的疑问是下面这道 Go 语言的 defer 题目，大家一起看看：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA;&#x9;var whatever [6]struct{}&#xA;&#x9;for i := range whatever {&#xA;&#x9;&#x9;defer func() {&#xA;&#x9;&#x9;&#x9;fmt.Println(i)&#xA;&#x9;&#x9;}()&#xA;&#x9;}&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;请自己先想一下输出的结果答案是什么。&lt;/p&gt;&#xA;&lt;p&gt;这位小伙伴按自己的理解后，认为应当输出 xx。但最终的输出结果，可能与其思考的有所偏差，一时想不通。&lt;/p&gt;&#xA;&lt;h3 id=&#34;解惑&#34;&gt;解惑&lt;/h3&gt;&#xA;&lt;p&gt;这段程序的输出结果是：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;5&#xA;5&#xA;5&#xA;5&#xA;5&#xA;5&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;为什么全是 5，为什么不是 0, 1, 2, 3, 4, 5 这样的输出结果呢？&lt;/p&gt;&#xA;&lt;p&gt;其根本原因是&lt;strong&gt;闭包&lt;/strong&gt;所导致的，有两点原因：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在 &lt;code&gt;for&lt;/code&gt; 循环结束后，局部变量 &lt;code&gt;i&lt;/code&gt; 的值已经是 5 了，并且 &lt;code&gt;defer &lt;/code&gt;的闭包是直接引用变量的 i。&lt;/li&gt;&#xA;&lt;li&gt;结合&lt;code&gt;defer&lt;/code&gt; 关键字的特性，可得知会在 &lt;code&gt;main&lt;/code&gt; 方法主体结束后再执行。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;结合上述，最终输出的结果是已经自增完毕的 5。&lt;/p&gt;&#xA;&lt;h3 id=&#34;进一步思考&#34;&gt;进一步思考&lt;/h3&gt;&#xA;&lt;p&gt;既然了解了为什么，我们再变形一下。再看看另外一种情况，代码如下：&lt;/p&gt;</description>
    </item>
    <item>
      <title>问个 Go 问题，字符串 len 为 0 和 字符串为空 ，有啥区别？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-lenstr/</link>
      <pubDate>Mon, 05 Apr 2021 16:09:14 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-lenstr/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在微信群看到几位大佬在讨论一个问题： ”&lt;strong&gt;字符串 len == 0 和 字符串 == &amp;quot;&amp;quot; ，有啥区别&lt;/strong&gt;？“&lt;/p&gt;&#xA;&lt;p&gt;这是一个比较小的细节点，同时也勾起了我的好奇心，因此今天这篇文章就和大家一起研究一下他们两者有没有区别，谁的性能更好一些？&lt;/p&gt;&#xA;&lt;h2 id=&#34;测试方法&#34;&gt;测试方法&lt;/h2&gt;&#xA;&lt;p&gt;在测试的方法中，我们分别声明了 &lt;code&gt;Test1&lt;/code&gt; 和 &lt;code&gt;Test2&lt;/code&gt; 方法：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Test1() bool {&#xA;&#x9;var v string&#xA;&#x9;if v == &amp;#34;&amp;#34; {&#xA;&#x9;&#x9;return true&#xA;&#x9;}&#xA;&#x9;return false&#xA;}&#xA;&#xA;func Test2() bool {&#xA;&#x9;var v string&#xA;&#x9;if len(v) == 0 {&#xA;&#x9;&#x9;return true&#xA;&#x9;}&#xA;&#x9;return false&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在方法内部仅做了简单的变量类型声明，分别以 字符串 == &amp;quot;&amp;quot; 和 字符串 len == 0 为判断依据。&lt;/p&gt;&#xA;&lt;h2 id=&#34;测试用例&#34;&gt;测试用例&lt;/h2&gt;&#xA;&lt;p&gt;编写两个方法的 Benchmark，用于后续的性能测试：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func BenchmarkTest1(b *testing.B) {&#xA;&#x9;for i := 0; i &amp;lt; b.N; i++ {&#xA;&#x9;&#x9;Test1()&#xA;&#x9;}&#xA;}&#xA;&#xA;func BenchmarkTest2(b *testing.B) {&#xA;&#x9;for i := 0; i &amp;lt; b.N; i++ {&#xA;&#x9;&#x9;Test2()&#xA;&#x9;}&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;结果分析&#34;&gt;结果分析&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go test --bench=. -benchmem&#xA;goos: darwin&#xA;goarch: amd64&#xA;BenchmarkTest1-4   &#x9;1000000000&#x9;         0.305 ns/op&#x9;       0 B/op&#x9;       0 allocs/op&#xA;BenchmarkTest2-4   &#x9;1000000000&#x9;         0.305 ns/op&#x9;       0 B/op&#x9;       0 allocs/op&#xA;PASS&#xA;ok  &#x9;_/Users/eddycjy/go-application/awesomeProject/tests&#x9;0.688s&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从多次测试的结果来看，两者比较：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 群友提问：Goroutine 数量控制在多少合适，会影响 GC 和调度？</title>
      <link>https://note.lican.site/posts/posts/go/go-tips-goroutinenums/</link>
      <pubDate>Mon, 05 Apr 2021 16:08:18 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-tips-goroutinenums/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天在读者交流群里看到一位小伙伴，发出了一个致命提问，那就是：“&lt;strong&gt;单机的 goroutine 数量控制在多少比较合适？&lt;/strong&gt;”。&lt;/p&gt;&#xA;&lt;p&gt;也许你和群内小伙伴第一反应一样，会答复 “控制多少，我觉得没有定论”。&lt;/p&gt;&#xA;&lt;p&gt;紧接着延伸出了更进一步的疑惑：“&lt;strong&gt;goroutine 太多了会影响 gc 和调度吧，主要是怎么预算这个数是合理的呢？&lt;/strong&gt;”&lt;/p&gt;&#xA;&lt;p&gt;这是本文要进行探讨的主体，因此本文的结构会是先探索基础知识，再一步步揭开，深入理解这个问题。&lt;/p&gt;&#xA;&lt;h2 id=&#34;goroutine-是什么&#34;&gt;Goroutine 是什么&lt;/h2&gt;&#xA;&lt;p&gt;Go 语言作为一个新生编程语言，其令人喜爱的特性之一就是 goroutine。Goroutine 是一个由 Go 运行时管理的轻量级线程，一般称其为 “协程”。&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go f(x, y, z)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;操作系统本身是无法明确感知到 Goroutine 的存在的，Goroutine 的操作和切换归属于 “用户态” 中。&lt;/p&gt;&#xA;&lt;p&gt;Goroutine 由特定的调度模式来控制，以 “多路复用” 的形式运行在操作系统为 Go 程序分配的几个系统线程上。&lt;/p&gt;&#xA;&lt;p&gt;同时创建 Goroutine 的开销很小，初始只需要 2-4k 的栈空间。Goroutine 本身会根据实际使用情况进行自伸缩，非常轻量。&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func say(s string) {&#xA;&#x9;for i := 0; i &amp;lt; 9999999; i++ {&#xA;&#x9;&#x9;time.Sleep(100 * time.Millisecond)&#xA;&#x9;&#x9;fmt.Println(s)&#xA;&#x9;}&#xA;}&#xA;&#xA;func main() {&#xA;&#x9;go say(&amp;#34;煎鱼&amp;#34;)&#xA;&#x9;say(&amp;#34;你好&amp;#34;)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;人称可以开几百几千万个的协程小霸王，是 Go 语言的得意之作之一。&lt;/p&gt;&#xA;&lt;h2 id=&#34;调度是什么&#34;&gt;调度是什么&lt;/h2&gt;&#xA;&lt;p&gt;既然有了用户态的代表 Goroutine，操作系统又看不到他。必然需要有某个东西去管理他，才能更好的运作起来。&lt;/p&gt;</description>
    </item>
    <item>
      <title>令人激动！Go 泛型代码合入 master（附尝鲜方法）</title>
      <link>https://note.lican.site/posts/posts/go/go-typeparams-master/</link>
      <pubDate>Mon, 05 Apr 2021 16:06:50 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-typeparams-master/</guid>
      <description>&lt;p&gt;大家好，我是慢一拍的后方记者煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;按照先前官方和文章的说法，Go 泛型预计是在 Go1.18 正式释出。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://imgkr2.cn-bj.ufileos.com/8971e01e-75f8-47c2-b9d3-f6b0ebd85d0d.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&amp;amp;Signature=8eKquOL0aM7GzJXegZUzGkOmLPg%253D&amp;amp;Expires=1614491040&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;在 GopherCon 2020 Go Team AMA 时，要在今年底要能有生产环境的试用版上线，这是 rsc 所提出的一个管理目标。&lt;/p&gt;&#xA;&lt;h2 id=&#34;转折点&#34;&gt;转折点&lt;/h2&gt;&#xA;&lt;p&gt;近期出现了一个新的转折点，能够让大家在主干分支（master）上就能享受到泛型的功能。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://imgkr2.cn-bj.ufileos.com/3acc09a4-d5a2-49e7-b272-b2fa1571f589.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&amp;amp;Signature=zXcEIWPbc%252BUFBDmTyYrRVywwKuI%253D&amp;amp;Expires=1614491656&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;而 master 分支对应了 Go1.17 的版本。因此未来将可以在 Go1.17 使用到泛型，这是一个比较惊喜的事情。&lt;/p&gt;&#xA;&lt;h2 id=&#34;原因&#34;&gt;原因&lt;/h2&gt;&#xA;&lt;p&gt;这件事情为什么会突然发生呢？一切都得从背景说起。原本 Go 泛型是一直在 &lt;a href=&#34;https://github.com/golang/go/tree/dev.typeparams&#34;&gt;dev.typeparams&lt;/a&gt; 分支上进行研讨和开发。&lt;/p&gt;&#xA;&lt;p&gt;由于泛型不是简单的一两个模块的代码变更，而是涉及大量的代码变更。&lt;/p&gt;&#xA;&lt;p&gt;因此需要经常保持与 master 分支的代码同步（近两个月共 20+ 次），会涉及代码冲突/合并的处理，且对于一些冲突的模块他们也不熟悉，所以期望迁移到 master 分支上进行开发。&lt;/p&gt;&#xA;&lt;h2 id=&#34;如何不影响既有功能&#34;&gt;如何不影响既有功能&lt;/h2&gt;&#xA;&lt;p&gt;这类提前放入主版本的操作，在 Go 语言中并不少见。像是现在所见的 &lt;code&gt;GO111MODULE&lt;/code&gt;，早期的 &lt;code&gt;GO15VENDOREXPERIMENT&lt;/code&gt; 都有些这么个味道。都是逐步入场，分阶段使用，等确定成熟、完善后再渐渐去掉。&lt;/p&gt;&#xA;&lt;p&gt;因此本次泛型也采取了这种方法，按照提案，目前使用的是 &lt;code&gt;-G&lt;/code&gt; 标识做为泛型的开关。&lt;/p&gt;&#xA;&lt;p&gt;计划如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;-G=0&lt;/code&gt;：继续使用传统的类型检查器。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-G=1&lt;/code&gt;：使用 type2，但不支持泛型。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-G=2&lt;/code&gt;：使用 type2，支持泛型。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在完成 types2 的错误和现有的错误的开发协调后，计划在 Go 1.17 将 &lt;code&gt;-G=1&lt;/code&gt; 设置为默认值。&lt;/p&gt;&#xA;&lt;p&gt;未来也许可以在 Go 1.18 中放弃对 &lt;code&gt;-G=0&lt;/code&gt; 的支持，这样后续在默认启用 &lt;code&gt;-G=2&lt;/code&gt; 上会变得更容易。&lt;/p&gt;</description>
    </item>
    <item>
      <title>意见征集：Go1 要不要移除 GOPATH？</title>
      <link>https://note.lican.site/posts/posts/go/go-why-path/</link>
      <pubDate>Mon, 05 Apr 2021 16:02:34 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-why-path/</guid>
      <description>&lt;p&gt;大家好，我是在打自己脸的后方记者煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天我发表了文章《意见征集：Go1 要不要移除 GOPATH？》。&#xA;本次投票共有 592 人参与了投票。&#xA;结果如下：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://static01.imgkr.com/temp/d10e1354bc9d43c68439fb71d8935270.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;绝大部分人支持移除 GOPATH，但建议保留的人依然占相当一部分。&lt;/p&gt;&#xA;&lt;h2 id=&#34;最新进展&#34;&gt;最新进展&lt;/h2&gt;&#xA;&lt;p&gt;在近几天，在 go issues 上有人提出了新的提案：《proposal: cmd/go: maintain &amp;lsquo;classic&amp;rsquo; vendor behaviour》。&lt;/p&gt;&#xA;&lt;p&gt;该提案正式向 Go 官方提出了我们上一篇文章有提到的两点：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Go 历史项目的维护问题。&lt;/li&gt;&#xA;&lt;li&gt;Go1 兼容性保证的许诺。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;摘选一部分核心观点给大家看看。&lt;/p&gt;&#xA;&lt;h2 id=&#34;新的提案&#34;&gt;新的提案&lt;/h2&gt;&#xA;&lt;p&gt;从 Go1.17 开始，Go1.17 就将 GOPATH 从编译器工具链中移除 &lt;code&gt;GO111MODULE&lt;/code&gt; 标识。&lt;/p&gt;&#xA;&lt;p&gt;其依据如下：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://static01.imgkr.com/temp/bf314e0979b7481681c6293ee60715c3.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;重点在于执行 &lt;code&gt;-mod=vendor&lt;/code&gt; 命令时，其会忽略主模块根目录以外位置的软件目录，&lt;strong&gt;这意味着在 go mod 之前设计和编写的应用程序不能再编译（指的历史项目问题）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;提案本身并不是要求要保持 GOPATH 代码解析 1:1 的说法，而是希望能够允许项目代码通过其他的方式在移除 GOPATH 后也能正常运行，有其他方式能够导入（不需要转成 Go mod）。&lt;/p&gt;&#xA;&lt;p&gt;另外最后提到删除 GOPATH 这样的代码解析能力，&lt;strong&gt;与下面这段摘自 go1compat 文档的精神不一致&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;It is intended that programs written to the Go 1 specification will continue to compile and run correctly, unchanged, over the lifetime of that specification. At some indefinite point, a Go 2 specification may arise, but until that time, Go programs that work today should continue to work even as future &amp;ldquo;point&amp;rdquo; releases of Go 1 arise (Go 1.1, Go 1.2, etc.).&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.16 新特性：Go mod 的后悔药，仅需这一招</title>
      <link>https://note.lican.site/posts/posts/go/go1.16-mod/</link>
      <pubDate>Mon, 05 Apr 2021 16:00:13 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go1.16-mod/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;前几天 Go 官方正式发布了 1.16 版本。&lt;strong&gt;从这个版本起，环境变量 GO111MODULE 的默认值正式修改为 on&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://imgkr2.cn-bj.ufileos.com/64ac411e-6361-4b23-b437-78123e16ae5a.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&amp;amp;Signature=NWVlX2CGAsMjMs%252BJH54ZKW1s1fM%253D&amp;amp;Expires=1613623986&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;这也意味着 Go modules 将更进一步推进其业务覆盖面，有新老项目共存的小伙伴建议手动将 GO111MODULE 调整为 auto。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Go1.16 针对 Go modules 放出了一个新特性，能够让维护 Go mod 库的开发者拥有反复吃 “后悔药” 的权力，提醒开发者已发布的 “脏” 版本存在问题&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;这个新特性，对于许多维护和使用公共库（开源、企业等）的小伙伴简直是一个小福音，建议大家都应该要了解这个知识点。&lt;/p&gt;&#xA;&lt;p&gt;在接下来文章中将进行详细说明和介绍。&lt;/p&gt;&#xA;&lt;h2 id=&#34;后悔药go-mod-retract&#34;&gt;后悔药：Go mod retract&lt;/h2&gt;&#xA;&lt;p&gt;Go1.16 起可以在 go.mod 文件中使用 &lt;code&gt;retract&lt;/code&gt; 指令来声明该第三方模块的某些发行版本不能被其他模块使用。&lt;/p&gt;&#xA;&lt;p&gt;在使用场景上：在发现严重问题或无意发布某些版本后，模块的维护作者可以撤回该版本，支持撤回单个或多个版本。&lt;/p&gt;&#xA;&lt;p&gt;以前没有办法解决，因此一旦出现就非常麻烦。对应两者的操作如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;维护者：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;删除有问题版本的 tag。&lt;/li&gt;&#xA;&lt;li&gt;重新打一个新版本的 tag。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;使用者：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;发现有问题的版本 tag 丢失，需手动介入。&lt;/li&gt;&#xA;&lt;li&gt;不知道有问题，由于其他库依赖，因此被动升级而踩坑。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;因此在本次 Go1.16 发布后，就拥有了一个&lt;strong&gt;半止损&lt;/strong&gt;的新手段了，也可以作为 Go mod 自动更新的大坑的补全办法之一。&lt;/p&gt;&#xA;&lt;h2 id=&#34;实战演练&#34;&gt;实战演练&lt;/h2&gt;&#xA;&lt;p&gt;为了方便演示，首先创建一个 Demo 项目（github.com/eddycjy/go-retract-demo），其含有一个基础方法：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package go_retract_demo&#xA;&#xA;func HelloWorld() string {&#xA;&#x9;return &amp;#34;001：脑子进煎鱼了！&amp;#34;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;另外有一个应用工程依赖了该第三方库，代码如下：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.16 新特性：详解内存管理机制的变更，你需要了解</title>
      <link>https://note.lican.site/posts/posts/go/go1.16-3/</link>
      <pubDate>Thu, 11 Feb 2021 16:13:20 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go1.16-3/</guid>
      <description>&lt;p&gt;大家好，我是正在学习如何蒸鱼的煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在前面 Go1.16 特性介绍的文章中我们有提到，从 v1.16 起，Go 在 Linux 下的默认内存管理策略会从&lt;code&gt;MADV_FREE&lt;/code&gt; 改回 &lt;code&gt;MADV_DONTNEED&lt;/code&gt; 策略。&lt;/p&gt;&#xA;&lt;p&gt;这时候可能至少分两拨小伙伴，分别是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;知道是什么，被这个问题 “折磨“ 过的，瞬间眼前一亮。&lt;/li&gt;&#xA;&lt;li&gt;不知道是什么，出现了各种疑惑了，这说的都是些什么。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;灵魂拷问&#34;&gt;灵魂拷问&lt;/h2&gt;&#xA;&lt;p&gt;你有没有以下的疑问，或者是否清楚：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;文中所说的 &lt;code&gt;MADV_FREE&lt;/code&gt; 是什么？&lt;/li&gt;&#xA;&lt;li&gt;文中所说的 &lt;code&gt;MADV_DONTNEED&lt;/code&gt; 是什么？&lt;/li&gt;&#xA;&lt;li&gt;为什么特指 Go 语言的 Linux 环境？&lt;/li&gt;&#xA;&lt;li&gt;为什么是说从 &lt;code&gt;MADV_FREE&lt;/code&gt;改回 &lt;code&gt;MADV_DONTNEED&lt;/code&gt;？&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在今天这篇文章中我们都将进一步的展开和说明，让我们一同来了解这个改来改去的内存机制到底是何物。&lt;/p&gt;&#xA;&lt;h2 id=&#34;madvise-爱与恨&#34;&gt;madvise 爱与恨&lt;/h2&gt;&#xA;&lt;p&gt;在 Linux 系统中，在 Go Runtime 中通过系统调用 &lt;code&gt;madvise(addr, length, advise)&lt;/code&gt; 方法，能够告诉内核如何处理从 addr 开始的 length 字节。&lt;/p&gt;&#xA;&lt;p&gt;重点之一就是 ”如何处理“，在 Linux 下 Go 语言中目前支持两种策略，分别是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;MADV_FREE：内核会在进程的页表中将这些页标记为“未分配”，从而进程的 RSS 就会变小。OS 后续可以将对应的物理页分配给其他进程。&lt;/li&gt;&#xA;&lt;li&gt;MADV_DONTNEED：内核只会在页表中将这些进程页面标记为可回收，在需要的时候才回收这些页面。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;所带来的影响&#34;&gt;所带来的影响&lt;/h2&gt;&#xA;&lt;p&gt;Go 语言官方恰好就在 2019 年的 Go1.12 做了如下调整。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Go1.12 以前。&lt;/li&gt;&#xA;&lt;li&gt;Go.12-Go1.15.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;go112-以前&#34;&gt;Go1.12 以前&lt;/h3&gt;&#xA;&lt;p&gt;Go Runtime 在 Linux 上默认使用的是 &lt;code&gt;MADV_DONTNEED&lt;/code&gt; 策略。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.16 新特性：快速上手 Go embed</title>
      <link>https://note.lican.site/posts/posts/go/go1.16-2/</link>
      <pubDate>Thu, 11 Feb 2021 16:13:19 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go1.16-2/</guid>
      <description>&lt;p&gt;在以前，很多从其他语言转过来 Go 语言的同学会问到，或是踩到一个坑。就是以为 Go 语言所打包的二进制文件中会包含配置文件的联同编译和打包。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://imgkr2.cn-bj.ufileos.com/f59a06c7-2fa1-41f4-901c-990f7dd7d715.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&amp;amp;Signature=werBc5OkLhKUh0JhpYpcAtl3etA%253D&amp;amp;Expires=1612147306&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;结果往往一把二进制文件挪来挪去，就无法把应用程序运行起来了。因为无法读取到静态文件的资源。&lt;/p&gt;&#xA;&lt;p&gt;无法将静态资源编译打包进二进制文件的话，通常会有两种解决方法：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;第一种是识别这类静态资源，是否需要跟着程序走。&lt;/li&gt;&#xA;&lt;li&gt;第二种就是考虑将其打包进二进制文件中。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;第二种情况的话，Go 以前是不支持的，大家就会去借助各种花式的开源库，例如：go-bindata/go-bindata 来实现。&lt;/p&gt;&#xA;&lt;p&gt;但从在 Go1.16 起，Go 语言自身正式支持了该项特性，今天我们将通过这篇文章快速了解和学习这项特性。&lt;/p&gt;&#xA;&lt;h2 id=&#34;基本使用&#34;&gt;基本使用&lt;/h2&gt;&#xA;&lt;p&gt;演示代码：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import _ &amp;#34;embed&amp;#34;&#xA;&#xA;//go:embed hello.txt&#xA;var s string&#xA;&#xA;func main() {&#xA;&#x9;print(s)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我们首先在对应的目录下创建了 &lt;code&gt;hello.txt&lt;/code&gt; 文件，并且写入文本内容 “吃煎鱼”。&lt;/p&gt;&#xA;&lt;p&gt;在代码中编写了最为核心的 &lt;code&gt;//go:embed hello.txt&lt;/code&gt; 注解。注解的格式很简单，就是 &lt;code&gt;go:embed&lt;/code&gt; 指令声明，外加读取的内容的地址，可支持相对和绝对路径。&lt;/p&gt;&#xA;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;吃煎鱼&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;读取到静态文件中的内容后自动赋值给了变量 &lt;code&gt;s&lt;/code&gt;，并且在主函数中成功输出。&lt;/p&gt;&#xA;&lt;p&gt;而针对其他的基础类型，Go embed 也是支持的：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;//go:embed hello.txt&#xA;var s string&#xA;&#xA;//go:embed hello.txt&#xA;var b []byte&#xA;&#xA;//go:embed hello.txt&#xA;var f embed.FS&#xA;&#xA;func main() {&#xA;&#x9;print(s)&#xA;&#x9;print(string(b))&#xA;&#xA;&#x9;data, _ := f.ReadFile(&amp;#34;hello.txt&amp;#34;)&#xA;&#x9;print(string(data))&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.16 即将正式发布，以下变更你需要知道</title>
      <link>https://note.lican.site/posts/posts/go/go1.16-1/</link>
      <pubDate>Thu, 11 Feb 2021 16:13:15 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go1.16-1/</guid>
      <description>&lt;p&gt;大家好，我是正在努力学习的煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在前几天，Go1.16rc1 抢先发布了。结合常规的 28 发布规律，其将会在 2021.02 月份左右发布正式版本。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://imgkr2.cn-bj.ufileos.com/417219ea-578e-48f0-8a83-84544000a698.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&amp;amp;Signature=mbLHd49lXRyOoY%252FlyamD5YnGagw%253D&amp;amp;Expires=1612064951&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;这次 Go1.16 也带来了一些新特性或变更。那么作为一个 Gopher，想必不能错过这次的更新。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://imgkr2.cn-bj.ufileos.com/4161e3cd-9f66-4f3f-8f68-91f3ba496c18.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&amp;amp;Signature=CwpOhSJnk70gEkTF8ITIxC58PoM%253D&amp;amp;Expires=1612074386&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;今天这篇文章将会带大家了解一下 Go1.16 的几个需要关注的特性。&lt;/p&gt;&#xA;&lt;h3 id=&#34;废弃-ioioutil&#34;&gt;废弃 io/ioutil&lt;/h3&gt;&#xA;&lt;p&gt;Go 官方认为 io/ioutil 这个包的定义不明确且难以理解。所以 Russ Cox 在 2020.10.17 提出了废弃 io/ioutil 的提案。&lt;/p&gt;&#xA;&lt;p&gt;大致变更如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Discard =&amp;gt; io.Discard&lt;/li&gt;&#xA;&lt;li&gt;NopCloser =&amp;gt; io.NopCloser&lt;/li&gt;&#xA;&lt;li&gt;ReadAll =&amp;gt; io.ReadAll&lt;/li&gt;&#xA;&lt;li&gt;ReadDir =&amp;gt; os.ReadDir&lt;/li&gt;&#xA;&lt;li&gt;ReadFile =&amp;gt; os.ReadFile&lt;/li&gt;&#xA;&lt;li&gt;TempDir =&amp;gt; os.MkdirTemp&lt;/li&gt;&#xA;&lt;li&gt;TempFile =&amp;gt; os.CreateTemp&lt;/li&gt;&#xA;&lt;li&gt;WriteFile =&amp;gt; os.WriteFile&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;与此同时大家也不需要担心存在破坏性变更，因为有 Go1 兼容性的保证，在 Go1 中 io/ioutil 还会存在，只变更内部的方法调用：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func ReadAll(r io.Reader) ([]byte, error) {&#xA;    return io.ReadAll(r)&#xA;}&#xA;&#xA;func ReadFile(filename string) ([]byte, error) {&#xA;    return os.ReadFile(filename)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;大家在后续也可以改改调用习惯。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 错误处理：用 panic 取代 err != nil 的模式</title>
      <link>https://note.lican.site/posts/posts/go/go-error2panic/</link>
      <pubDate>Sat, 12 Dec 2020 17:21:42 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-error2panic/</guid>
      <description>&lt;p&gt;前段时间我分享了文章 《先睹为快，Go2 Error 的挣扎之路》后，和一位朋友进行了一次深度交流，他给我分享了他们项目组对于 Go 错误处理的方式调整。&lt;/p&gt;&#xA;&lt;p&gt;简单来讲，就是在业务代码中使用 &lt;code&gt;panic&lt;/code&gt; 的方式来替代 “永无止境” 的 &lt;code&gt;if err != nil&lt;/code&gt;。这就是今天本文的重点内容，我们一起来看看是怎么做，又有什么优缺点。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么想替换&#34;&gt;为什么想替换&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 语言中 &lt;code&gt;if err != nil&lt;/code&gt; 写的太多，还要管方法声明各种，嫌麻烦又不方便：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;err := foo()&#xA;if err != nil {&#xA;     //do something..&#xA;     return err&#xA;}&#xA;&#xA;err := foo()&#xA;if err != nil {&#xA;     //do something..&#xA;     return err&#xA;}&#xA;&#xA;err := foo()&#xA;if err != nil {&#xA;     //do something..&#xA;     return err&#xA;}&#xA;&#xA;err := foo()&#xA;if err != nil {&#xA;     //do something..&#xA;     return err&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上述还是示例代码，比较直面。若是在工程实践，还得各种 package 跳来跳去加 &lt;code&gt;if err != nil&lt;/code&gt;，总的来讲比较繁琐，要去关心整体的上下游。更具体的就不赘述了，可以翻看我先前的文章。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 并发：一些有趣的现象和要避开的 “坑”</title>
      <link>https://note.lican.site/posts/posts/go/go-concurrent-lock/</link>
      <pubDate>Thu, 10 Dec 2020 00:25:59 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-concurrent-lock/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;最近在看 Go 并发相关的内容，发现还是有不少细节容易让人迷迷糊糊的，一个不小心就踏入深坑里，且指不定要在上线跑了一些数据后才能发现，那可真是太人崩溃了。&lt;/p&gt;&#xA;&lt;p&gt;今天来分享几个案例，希望大家在编码时能够避开这几个 “坑”。&lt;/p&gt;&#xA;&lt;h2 id=&#34;案例一&#34;&gt;案例一&lt;/h2&gt;&#xA;&lt;h3 id=&#34;演示代码&#34;&gt;演示代码&lt;/h3&gt;&#xA;&lt;p&gt;第一个案例来自 @鸟窝 大佬在极客时间的分享，代码如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA;&#x9;count := 0&#xA;&#x9;wg := sync.WaitGroup{}&#xA;&#x9;wg.Add(10)&#xA;&#x9;for i := 0; i &amp;lt; 10; i++ {&#xA;&#x9;&#x9;go func() {&#xA;&#x9;&#x9;&#x9;defer wg.Done()&#xA;&#x9;&#x9;&#x9;for j := 0; j &amp;lt; 100000; j++ {&#xA;&#x9;&#x9;&#x9;&#x9;count++&#xA;&#x9;&#x9;&#x9;}&#xA;&#x9;&#x9;}()&#xA;&#x9;}&#xA;&#x9;wg.Wait()&#xA;&#xA;&#x9;fmt.Println(count)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;思考一下，最后输出的 &lt;code&gt;count&lt;/code&gt; 变量的值是多少？是不是一百万？&lt;/p&gt;&#xA;&lt;h3 id=&#34;输出结果&#34;&gt;输出结果&lt;/h3&gt;&#xA;&lt;p&gt;在上述代码中，我们通过 &lt;code&gt;for-loop &lt;/code&gt; 循环起 &lt;code&gt;goroutine&lt;/code&gt; 进行自增，并使用了 &lt;code&gt;sync.WaitGroup&lt;/code&gt; 来保证所有的 goroutine 都执行完毕才输出最终的结果值。&lt;/p&gt;&#xA;&lt;p&gt;最终的输出结果如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// 第一次执行&#xA;638853&#xA;&#xA;// 第二次执行&#xA;654473&#xA;&#xA;// 第三次执行&#xA;786193&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出的结果值不是恒定的，也就是每次输出的都不一样，且基本不会达到想象中的一百万。&lt;/p&gt;</description>
    </item>
    <item>
      <title>先睹为快，Go2 Error 的挣扎之路</title>
      <link>https://note.lican.site/posts/posts/go/go2-errors/</link>
      <pubDate>Thu, 03 Dec 2020 20:56:47 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go2-errors/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;自从 Go 语言在国内火热以来，除去泛型，其次最具槽点的就是 Go 对错误的处理方式，一句经典的 &lt;code&gt;if err != nil&lt;/code&gt;  暗号就能认出你是一个 Go 语言爱好者。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/381fcb5e85923479666f5be14de3782c.jpeg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;自然，大家对 Go error 的关注度更是高涨，Go team 也是，因此在 &lt;a href=&#34;https://github.com/golang/proposal/blob/master/design/go2draft.md&#34;&gt;Go 2 Draft Designs&lt;/a&gt; 中正式提到了 error handling（错误处理）的相关草案，希望能够在未来正式的解决这个问题。&lt;/p&gt;&#xA;&lt;p&gt;在今天这篇文章中，我们将一同跟踪 Go2 error，看看他是怎么 “挣扎” 的，能不能破局？&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么要吐槽-go1&#34;&gt;为什么要吐槽 Go1&lt;/h2&gt;&#xA;&lt;p&gt;要吐槽 Go1 error，就得先知道为什么大家到底是在喷 Error 哪里处理的不好。在 Go 语言中，error 其实本质上只是个 Error 的 &lt;code&gt;interface&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type error interface {&#xA;    Error() string&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;实际的应用场景如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA;&#x9;x, err := foo()&#xA;&#x9;if err != nil {&#xA;&#x9;&#x9; // handle error&#xA;&#x9;}&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;单纯的看这个例子似乎没什么问题，但工程大了后呢？显然 &lt;code&gt;if err != nil&lt;/code&gt; 的逻辑是会堆积在工程代码中，Go 代码里的 &lt;code&gt;if err != nil&lt;/code&gt; 甚至会达到工程代码量的 30% 以上：&lt;/p&gt;</description>
    </item>
    <item>
      <title>重磅：Go errors 将不会有任何进一步的改进计划</title>
      <link>https://note.lican.site/posts/posts/go/gophercon2020-errors/</link>
      <pubDate>Sat, 14 Nov 2020 16:48:33 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gophercon2020-errors/</guid>
      <description>&lt;p&gt;今天在 Gophercon2020 上，&lt;strong&gt;Go 1.13 错误提案的作者事后提及他对目前错误格式化的缺失表示遗憾，而且在未来很长的好几年内都不会有任何进一步改进计划&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;对此他本人给出的原因之一是：对于错误处理这一领域特定的问题，在他的能力范围内实在是无法给出一个令所有人都满意的方案。&lt;/p&gt;&#xA;&lt;p&gt;尽管如此，在他演讲的最后，还是给出了一些关于错误嵌套的建议，即实现 &lt;code&gt;fmt.Formatter&lt;/code&gt;，图中给出了一个简单的例子，大家可以参考如下代码：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type DetailError struct {&#xA;&#x9;msg, detail string&#xA;&#x9;err         error&#xA;}&#xA;&#xA;func (e *DetailError) Unwrap() error { return e.err }&#xA;&#xA;func (e *DetailError) Error() string {&#xA;&#x9;if e.err == nil {&#xA;&#x9;&#x9;return e.msg&#xA;&#x9;}&#xA;&#x9;return e.msg + &amp;#34;: &amp;#34; + e.err.Error()&#xA;}&#xA;&#xA;func (e *DetailError) Format(s fmt.State, c rune) {&#xA;&#x9;if s.Flag(&amp;#39;#&amp;#39;) &amp;amp;&amp;amp; c == &amp;#39;v&amp;#39; {&#xA;&#x9;&#x9;type nomethod DetailError&#xA;&#x9;&#x9;fmt.Fprintf(s, &amp;#34;%#v&amp;#34;, (*nomethod)(e))&#xA;&#x9;&#x9;return&#xA;&#x9;}&#xA;&#x9;if !s.Flag(&amp;#39;+&amp;#39;) || c != &amp;#39;v&amp;#39; {&#xA;&#x9;&#x9;fmt.Fprintf(s, spec(s, c), e.Error())&#xA;&#x9;&#x9;return&#xA;&#x9;}&#xA;&#x9;fmt.Fprintln(s, e.msg)&#xA;&#x9;if e.detail != &amp;#34;&amp;#34; {&#xA;&#x9;&#x9;io.WriteString(s, &amp;#34;\t&amp;#34;)&#xA;&#x9;&#x9;fmt.Fprintln(s, e.detail)&#xA;&#x9;}&#xA;&#x9;if e.err != nil {&#xA;&#x9;&#x9;if ferr, ok := e.err.(fmt.Formatter); ok {&#xA;&#x9;&#x9;&#x9;ferr.Format(s, c)&#xA;&#x9;&#x9;} else {&#xA;&#x9;&#x9;&#x9;fmt.Fprintf(s, spec(s, c), e.err)&#xA;&#x9;&#x9;&#x9;io.WriteString(s, &amp;#34;\n&amp;#34;)&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;}&#xA;&#xA;func spec(s fmt.State, c rune) string {&#xA;&#x9;buf := []byte{&amp;#39;%&amp;#39;}&#xA;&#x9;for _, f := range []int{&amp;#39;+&amp;#39;, &amp;#39;-&amp;#39;, &amp;#39;#&amp;#39;, &amp;#39; &amp;#39;, &amp;#39;0&amp;#39;} {&#xA;&#x9;&#x9;if s.Flag(f) {&#xA;&#x9;&#x9;&#x9;buf = append(buf, byte(f))&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;&#x9;if w, ok := s.Width(); ok {&#xA;&#x9;&#x9;buf = strconv.AppendInt(buf, int64(w), 10)&#xA;&#x9;}&#xA;&#x9;if p, ok := s.Precision(); ok {&#xA;&#x9;&#x9;buf = append(buf, &amp;#39;.&amp;#39;)&#xA;&#x9;&#x9;buf = strconv.AppendInt(buf, int64(p), 10)&#xA;&#x9;}&#xA;&#x9;buf = append(buf, byte(c))&#xA;&#x9;return string(buf)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;此处的内容来源于欧神（@changkun）在知识星球里的线上分享，作为 Go 夜读 SIG 成员的一员，借此也安利下咱们《Go 夜读》的星球，欢迎大家一起来学习和分享：&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么 Go 的泛型一拖再拖？</title>
      <link>https://note.lican.site/posts/posts/go/go16-preview/</link>
      <pubDate>Thu, 12 Nov 2020 23:47:16 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go16-preview/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。前段时间 Go 语言的泛型讨论频频出现在各微信群，且又冲上了国内外各大文章的 “头条”：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/c9c48e9479c7036f7d5a33b6ab49e855.jpg&#34; alt=&#34;来自 p 神公众号的截图&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;信息汇总来看，Go 泛型这几年会出，但大体来讲现在 Go 泛型又又又推迟了，好家伙。我最早了解到时是考虑 Go1.16 释出，后面又推到了 Go1.17，接着现在又延期到了 Go1.18 了（2021 年底）。&lt;/p&gt;&#xA;&lt;p&gt;看到了信息的表象后，再想想为什么泛型 “这件事情” 突然醒目起来了，其原因之一是由官方 &lt;a href=&#34;https://blog.golang.org/11years&#34;&gt;Go，11 岁&lt;/a&gt; 的博文所引爆的。&lt;/p&gt;&#xA;&lt;p&gt;同时近日举办的 GopherCon2020 大会，Robert Griesemer 分享的 Typing [Generic] Go。更正式的让 Go 泛型更面向了大众，也侧面的说明官方认为其已经到达了一个新的阶段了，进入最终实现阶段。&lt;/p&gt;&#xA;&lt;p&gt;事不宜迟，既然官方都已经摩拳擦掌了，我们的学习之路也得跟上，因此本文将会介绍 Go 泛型现在的情况，并通过在介绍过程中不断思考最后得出一个为什么。&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是泛型&#34;&gt;什么是泛型&lt;/h2&gt;&#xA;&lt;p&gt;泛型程序设计（generic programming）是程序设计语言的一种风格或范式。泛型允许程序员在强类型语言中编写代码时，使用一些以后才确定的类型，其在真正实例化时才会为这些参数指确定类型。另外各语言和其编译器、运行环境对泛型的支持均不一样，因此需要针对来辩证。&lt;/p&gt;&#xA;&lt;p&gt;简单来讲，泛型就是参数化多态。其可根据实参类型生成不同的版本，支持任意数量的调用：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func F(a, b T) T{ return a+b }&#xA;&#xA;// T 为 int&#xA;F(1, 2)&#xA;&#xA;// T 为 string&#xA;F(&amp;#34;1&amp;#34;, &amp;#34;2&amp;#34;)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在编译时期编译器便确定其 T 的入参类型。这也是 Go 泛型实现的要求之一 “编译时类型安全”。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么需要泛型&#34;&gt;为什么需要泛型&lt;/h2&gt;&#xA;&lt;p&gt;这时候可能会有人说，没有泛型也可以啊&amp;hellip;感觉写业务代码没什么影响，与其搞泛型不如搞好 errors（具体新消息可参见：重磅：Go errors 将不会有任何进一步的改进计划）。&lt;/p&gt;&#xA;&lt;p&gt;但泛型是有其所需的场景，最常见的是像基础库在处理获取配置中心数据时，就要处理类型，时常遇到下述场景：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 语言今年 11 岁，何去何从，现状到底如何？</title>
      <link>https://note.lican.site/posts/posts/go/go11/</link>
      <pubDate>Wed, 11 Nov 2020 21:21:58 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go11/</guid>
      <description>&lt;p&gt;不说不知道，一说下一跳。Go 语言已经开源 11 周年了，感觉是一路高歌，Release History （polarisxu 整理）如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;2011 年 3 月 16 日，Go 语言的第一个稳定版本 r56 发布；&lt;/li&gt;&#xA;&lt;li&gt;2012 年 3 月  28 日，Go 语言的第一个正式版本 Go1 发布，并承诺 1.x 的 兼容性；&lt;/li&gt;&#xA;&lt;li&gt;2013 年  5 月 13 日，Go1.1 正式版才发布。&lt;/li&gt;&#xA;&lt;li&gt;2013 年 12 月 1 日，Go1.2 正式发布；&lt;/li&gt;&#xA;&lt;li&gt;2014 年 6 月 18 日，Go1.3 正式发布；&lt;/li&gt;&#xA;&lt;li&gt;2014 年 12 月 10 日，Go1.4 正式发布；&lt;/li&gt;&#xA;&lt;li&gt;2015 年 8 月 19 日，Go1.5 正式发布。该版本实现了自举，即移除了 C 代码，使用 Go 开发 Go 语言；&lt;/li&gt;&#xA;&lt;li&gt;2016 年 2 月 17 日，Go1.6 正式发布；&lt;/li&gt;&#xA;&lt;li&gt;2016 年 8 月 15 日，Go1.7 正式发布；引入 context 包；&lt;/li&gt;&#xA;&lt;li&gt;2017 年 2 月 17 日，Go1.8 正式发布；&lt;/li&gt;&#xA;&lt;li&gt;2017 年 8 月 24 日，Go1.9 正式发布；引入别名；&lt;/li&gt;&#xA;&lt;li&gt;2018 年 2 月 16 日，Go1.10 正式发布；&lt;/li&gt;&#xA;&lt;li&gt;2018 年 8 月 25 日，Go1.11 正式发布。开始强势支持 Go modules；&lt;/li&gt;&#xA;&lt;li&gt;2019 年 3 月 1 日，Go1.12 正式发布；&lt;/li&gt;&#xA;&lt;li&gt;2019 年 9 月 3 日，Go1.13 正式发布；&lt;/li&gt;&#xA;&lt;li&gt;2020 年 2 月 25 日，Go1.14 正式发布；goroutine 支持异步抢占调度；&lt;/li&gt;&#xA;&lt;li&gt;2020 年 8 月 11 日，Go1.15 正式发布；&lt;/li&gt;&#xA;&lt;li&gt;2021 年 2 月，预计 Go1.16 正式发布；将包含新的文件系统接口和支持在构建时的静态文件嵌入，以及链接器的重写，且正式对 Apple Silicon（GOARCH=arm64）Mac 进行支持。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;目视现在&#34;&gt;目视现在&lt;/h2&gt;&#xA;&lt;p&gt;现在的 Go 语言在国内已经掀起了一浪又一浪的热潮，炒的非常火热。各大平台极客时间、拉勾教育、掘金小册、慕课网等纷纷出现了大量 Go 语言相关的付费专栏/视频。&lt;/p&gt;</description>
    </item>
    <item>
      <title>解密 Go 语言之反射 reflect</title>
      <link>https://note.lican.site/posts/posts/go/reflect/</link>
      <pubDate>Sat, 07 Nov 2020 15:01:51 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/reflect/</guid>
      <description>&lt;p&gt;大家好，我是煎鱼。&lt;/p&gt;&#xA;&lt;p&gt;在所有的语言中，反射这一功能基本属于必不可少的模块。虽说 “反射” 这个词让人根深蒂固，但更多的还是 WHY。反射到底是什么，反射又是基于什么法则实现的？&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/47976eb32b9cb5bdbe1869123fefb92b.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;今天我们通过这篇文章来一一揭晓，以 Go 语言为例，了解反射到底为何物，其底层又是如何实现的。&lt;/p&gt;&#xA;&lt;h2 id=&#34;反射是什么&#34;&gt;反射是什么&lt;/h2&gt;&#xA;&lt;p&gt;在计算机学中，反射是指计算机程序在运行时（runtime）可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说，反射就是程序在运行的时候能够 “观察” 并且修改自己的行为（来自维基百科）。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/96394a2bb7b1dd964b5197837781e348.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;简单来讲就是，应用程序能够在运行时观察到变量的值，并且能够修改他。&lt;/p&gt;&#xA;&lt;h2 id=&#34;一个例子&#34;&gt;一个例子&lt;/h2&gt;&#xA;&lt;p&gt;最常见的 reflect 标准库例子，如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import (&#xA;&#x9;&amp;#34;fmt&amp;#34;&#xA;&#x9;&amp;#34;reflect&amp;#34;&#xA;)&#xA;&#xA;func main() {&#xA;&#x9;rv := []interface{}{&amp;#34;hi&amp;#34;, 42, func() {}}&#xA;&#x9;for _, v := range rv {&#xA;&#x9;&#x9;switch v := reflect.ValueOf(v); v.Kind() {&#xA;&#x9;&#x9;case reflect.String:&#xA;&#x9;&#x9;&#x9;fmt.Println(v.String())&#xA;&#x9;&#x9;case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:&#xA;&#x9;&#x9;&#x9;fmt.Println(v.Int())&#xA;&#x9;&#x9;default:&#xA;&#x9;&#x9;&#x9;fmt.Printf(&amp;#34;unhandled kind %s&amp;#34;, v.Kind())&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;hi&#xA;42&#xA;unhandled kind func&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在程序中主要是声明了 rv 变量，变量类型为 &lt;code&gt;interface{}&lt;/code&gt;，其包含 3 个不同类型的值，分别是字符串、数字、闭包。一般 &lt;code&gt;interface{}&lt;/code&gt; 的使用常见于不知道入参者具体的基本类型是什么，那么就会用 &lt;code&gt;interface{}&lt;/code&gt; 类型来做一个伪 “泛型”。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 应用程序是怎么运行起来的？</title>
      <link>https://note.lican.site/posts/posts/go/go-bootstrap/</link>
      <pubDate>Thu, 08 Oct 2020 15:57:18 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-bootstrap/</guid>
      <description>&lt;p&gt;自古应用程序均从 Hello World 开始，你我所写的 Go 语言亦然：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import &amp;#34;fmt&amp;#34;&#xA;&#xA;func main() {&#xA;&#x9;fmt.Println(&amp;#34;hello world.&amp;#34;)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这段程序的输出结果为 &lt;code&gt;hello world.&lt;/code&gt;，就是这么的简单又直接。但这时候又不禁思考了起来，这个 &lt;code&gt;hello world.&lt;/code&gt; 是怎么输出来，经历了什么过程。&lt;/p&gt;&#xA;&lt;p&gt;真是非常的好奇，今天我们就通过这篇文章来一探究竟。&lt;/p&gt;&#xA;&lt;h2 id=&#34;引导阶段&#34;&gt;引导阶段&lt;/h2&gt;&#xA;&lt;h3 id=&#34;查找入口&#34;&gt;查找入口&lt;/h3&gt;&#xA;&lt;p&gt;开始剖析之路，首先编译上文提到的示例程序：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ GOFLAGS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-ldflags=-compressdwarf=false&amp;#34;&lt;/span&gt; go build &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在命令中指定了 GOFLAGS 参数，这是因为在 Go1.11 起，为了减少二进制文件大小，调试信息会被压缩。导致在 MacOS 上使用 gdb 时无法理解压缩的 DWARF 的含义是什么（而我恰恰就是用的 MacOS）。&lt;/p&gt;&#xA;&lt;p&gt;因此需要在本次调试中将其关闭，再使用 gdb 进行调试，以此达到观察的目的：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ gdb awesomeProject &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;gdb&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; info files&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Symbols from &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/Users/eddycjy/go-application/awesomeProject/awesomeProject&amp;#34;&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Local exec file:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;/Users/eddycjy/go-application/awesomeProject/awesomeProject&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;, file type mach-o-x86-64.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Entry point: 0x1063c80&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;0x0000000001001000 - 0x00000000010a6aca is .text&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;gdb&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; b *0x1063c80&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Breakpoint &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; at 0x1063c80: file /usr/local/Cellar/go/1.15/libexec/src/runtime/rt0_darwin_amd64.s, line 8.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过 Entry point 的调试，可看到真正的程序入口在 runtime 包中，不同的计算机架构指向不同，例如：MacOS 在 &lt;code&gt;src/runtime/rt0_darwin_amd64.s&lt;/code&gt;，Linux 在 &lt;code&gt;src/runtime/rt0_linux_amd64.s&lt;/code&gt;。&lt;/p&gt;</description>
    </item>
    <item>
      <title>新书《Go语言编程之旅：一起用Go做项目》出版啦！</title>
      <link>https://note.lican.site/posts/posts/go-programming-tour-book/</link>
      <pubDate>Fri, 03 Jul 2020 21:06:33 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/go-programming-tour-book/</guid>
      <description>&lt;p&gt;从我开始写技术文章起，不知不觉近三年过去了，咨询和催我出书和读者逐年递增，在 2019 年算是达到一个高峰。当然，综合考虑下我也是一直拒绝的，觉得火候还不够。&lt;/p&gt;&#xA;&lt;p&gt;直至 2019.09 月，polaris 主动找到了我，说有事情想找我商量，本着 “如果你在纠结一件事情做还是不做，不如先做了看看结果，至少不会后悔” 的想法，更何况是长期被 Ping，因此我一口答应下来，故事自此开始了。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/04737f7b3e5567224fd2bc93f352203d.jpeg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;本书定位&#34;&gt;本书定位&lt;/h2&gt;&#xA;&lt;p&gt;本书不直接介绍 Go 语言的语法基础，内容将面向项目实践，同时会针对核心细节进行分析。而在实际项目迭代中，常常会出现或多或少的事故，因此本书也针对 Go 语言的大杀器（分析工具）以及常见问题进行了全面讲解。&lt;/p&gt;&#xA;&lt;p&gt;本书适合已经大致学习了 Go 语言的基础语法后，想要跨越到下一个阶段的开发人员，可以填补该阶段的空白和进一步拓展你的思维方向。&lt;/p&gt;&#xA;&lt;h2 id=&#34;读者定位&#34;&gt;读者定位&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;基本了解 Go 语言的语法和使用方式的开发人员。&lt;/li&gt;&#xA;&lt;li&gt;想要进行 Go 相关项目实践和进一步摸索的开发人员。&lt;/li&gt;&#xA;&lt;li&gt;希望熟悉 Go 常用分析工具的开发人员。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本书大纲&#34;&gt;本书大纲&lt;/h2&gt;&#xA;&lt;p&gt;本书针对常见的项目类型，主要细分为 5 + 1 板块，分别是命令行、HTTP、RPC、Websocket 应用、进程内缓存以及 Go 中的大杀器。&lt;/p&gt;&#xA;&lt;p&gt;同时我们在项目开发、细节分析、运行时分析等方方面面都进行了较深入的介绍和说明，能够为 Go 语言开发者提供相对完整的项目实践经验，而如果深入阅读第六章的章节，更能够为未来各类问题出现时的问题排查提供一份强大的知识板块。&lt;/p&gt;&#xA;&lt;p&gt;如下为本书的思维导图概览：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/e5eafb17140fdc06830b838eb7fb0468.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;如何阅读这本书&#34;&gt;如何阅读这本书&lt;/h2&gt;&#xA;&lt;p&gt;常规的列目录未免太无趣。我想不如说说从我个人的角度，所看到读者们在近 3 年来是如何阅读/实践我的实践系列文章的，其面向的读者群体是完全一致的。希望能够从另外一个角度告诉你，应当如何阅读这本书，尽可能的效益最大化。&lt;/p&gt;&#xA;&lt;p&gt;首先，图书，买来要读，而与实战结合的图书，势必需要实践，实践最常见又分为脑内思考和上机实践：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/a6faa89061d62be755b715607e2563b8.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;而在持续的交流中，可以发现至少会延伸出以下几类深入层次的不同：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/e3b17b0867e66bda4b5c6fb24ddcebc9.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;第一层&lt;/strong&gt;：只阅读，留有印象，需要时再唤醒，也行。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;第二层&lt;/strong&gt;：阅读并实践，实打实的完成项目实践，收获丰满。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;第三层&lt;/strong&gt;：实践的过程中，&lt;strong&gt;一定会遇到或大或小的问题&lt;/strong&gt;，有的人会放弃，这就是分叉点。但有的读者会持续排查，其提升了个人能力（排错能力很重要）。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;第四层&lt;/strong&gt;：实践完毕后，有自己的想法，认为某某地方还可以这样，也可以再实现更多的功能，举一反三，进一步拓展，并对项目提 issues 或进行 pr。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;第五层&lt;/strong&gt;：完成整体项目后，抽离业务代码，标准化框架，实现框架的应用脚手架，并有的读者会进一步开源。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;第六层&lt;/strong&gt;：形成脚手架后，在自己业务组开始落地，实际在项目中使用，由业务学习转化为企业实践。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;第七层&lt;/strong&gt;：在内部落地实践稳妥后，开始在其它业务组开始推广该框架脚手架，进一步标准化，拓展思路。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;通过上图中 “七层金字塔” 的理解，我们不难发现其对于实践项目的理解和应用已经不再是单单这个项目，而是有了更深远的意义，抽象一下，对照着著名的 “学习效率金字塔” 来看：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/a35394d0ab562efaac8367c3eeff4b07.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;在单纯的 “阅读” 时，其基本处于 “被动学习” 的阶段，而单进入阅读并实践时，已经转为了 “主动学习”，且绝大部分的读者做完实践后，表示 “嗯，实践完，挺好的，有所得”。&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么容器内存占用居高不下，频频 OOM（续）</title>
      <link>https://note.lican.site/posts/posts/why-container-memory-exceed2/</link>
      <pubDate>Fri, 19 Jun 2020 21:29:08 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/why-container-memory-exceed2/</guid>
      <description>&lt;p&gt;在上周的文章&lt;a href=&#34;https://note.lican.site/posts/why-container-memory-exceed/&#34;&gt;《为什么容器内存占用居高不下，频频 OOM》&lt;/a&gt; 中，我根据现状进行了分析和说明，收到了很多读者的建议和疑惑，因此有了这一篇文章，包含更进一步的说明和排查。&lt;/p&gt;&#xA;&lt;h2 id=&#34;疑问&#34;&gt;疑问&lt;/h2&gt;&#xA;&lt;p&gt;一般系统内存过高的情况下，可以通过 &lt;code&gt;free -m&lt;/code&gt; 查看当前系统的内存使用情况：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/daf2a1d53f4bf0f21e315d2333e08159.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;在发现是系统内存占用高后，就会有读者会提到，为什么不 “手动清理 Cache”，因为 Cache 高的话，可以通过 drop_caches 的方式来清理：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;清理 page cache：&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo 1 &amp;gt; /proc/sys/vm/drop_caches&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;清理 dentries 和 inodes：&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo 2 &amp;gt; /proc/sys/vm/drop_caches&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;3&#34;&gt;&#xA;&lt;li&gt;清理 page cache、dentries 和 inodes：&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; &amp;gt; /proc/sys/vm/drop_caches&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但新问题又出现了，因为我们的命题是在容器中，在 Kubernetes 中，若执行 drop_caches 相关命令，将会对 Node 节点上的所有其他应用程序产生影响，尤其是那些占用大量 IO 并由于缓冲区高速缓存而获得更好性能的应用程序，可能会产生 “负面” 后果。&lt;/p&gt;&#xA;&lt;p&gt;我想这并不是一个好办法。&lt;/p&gt;&#xA;&lt;h2 id=&#34;表象&#34;&gt;表象&lt;/h2&gt;&#xA;&lt;p&gt;回归原始，那就是为什么要排查这个问题，本质原因就是容器设置了 Memory Limits，而容器在运行中达到了 Limits 上限，被 OOM 掉了，所以我们想知道为什么会出现这个情况。&lt;/p&gt;&#xA;&lt;p&gt;在前文中我们针对了五大类情况进行了猜想：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;频繁申请重复对象。&lt;/li&gt;&#xA;&lt;li&gt;不知名内存泄露。&lt;/li&gt;&#xA;&lt;li&gt;madvise 策略变更。&lt;/li&gt;&#xA;&lt;li&gt;监控/判别条件有问题。&lt;/li&gt;&#xA;&lt;li&gt;容器环境的机制。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在逐一排除后，后续发现容器的 Memory OOM 判定标准是 container_memory_working_set_bytes 指标，其实际组成为 RSS + Cache（最近访问的内存、脏内存和内核内存）。&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么容器内存占用居高不下，频频 OOM</title>
      <link>https://note.lican.site/posts/posts/why-container-memory-exceed/</link>
      <pubDate>Sun, 07 Jun 2020 14:52:19 +0800</pubDate>
      <guid>https://note.lican.site/posts/posts/why-container-memory-exceed/</guid>
      <description>&lt;p&gt;最近我在回顾思考（写 PPT），整理了现状，发现了这个问题存在多时，经过一番波折，最终确定了元凶和相对可行的解决方案，因此也在这里分享一下排查历程。&lt;/p&gt;&#xA;&lt;p&gt;时间线：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在上 Kubernetes 的前半年，只是用 Kubernetes，开发没有权限，业务服务极少，忙着写新业务，风平浪静。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在上 Kubernetes 的后半年，业务服务较少，偶尔会阶段性被运维唤醒，问之 “&lt;strong&gt;为什么你们的服务内存占用这么高，赶紧查&lt;/strong&gt;”。此时大家还在为新业务冲刺，猜测也许是业务代码问题，但没有调整代码去尝试解决。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在上 Kubernetes 的第二年，业务服务逐渐增多，普遍增加了容器限额 Limits，出现了好几个业务服务是内存小怪兽，因此如果不限制的话，服务过度占用会导致驱逐，因此反馈语也就变成了：“&lt;strong&gt;为什么你们的服务内存占用这么高，老被 OOM Kill，赶紧查&lt;/strong&gt;”。据闻也有几个业务大佬有去排查（因为 OOM 反馈），似乎没得出最终解决方案。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;不禁让我们思考，为什么个别 Go 业务服务，Memory 总是提示这么高，经常达到容器限额，以至于被动 OOM Kill，是不是有什么安全隐患？&lt;/p&gt;&#xA;&lt;h2 id=&#34;现象&#34;&gt;现象&lt;/h2&gt;&#xA;&lt;h3 id=&#34;内存居高不下&#34;&gt;内存居高不下&lt;/h3&gt;&#xA;&lt;p&gt;发现个别业务服务内存占用挺高，触发告警，且通过 Grafana 发现在凌晨（没有什么流量）的情况下，内存占用量依然拉平，没有打算下降的样子，高峰更是不得了，像是个内存炸弹：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/2e6c8c153836b29175dff7623ec67a0a.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;并且我所观测的这个服务，早年还只是 100MB。现在随着业务迭代和上升，目前已经稳步 4GB，容器限额 Limits 纷纷给它开道，但我想总不能是无休止的增加资源吧，这是一个大问题。&lt;/p&gt;&#xA;&lt;h3 id=&#34;进入重启怪圈&#34;&gt;进入重启怪圈&lt;/h3&gt;&#xA;&lt;p&gt;有的业务服务，业务量小，自然也就没有调整容器限额，因此得不到内存资源，又超过额度，就会进入疯狂的重启怪圈：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/95644633a2d55cb2f2684a23d3f3f189.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;重启将近 300 次，非常不正常了，更不用提所接受到的告警通知。&lt;/p&gt;&#xA;&lt;h2 id=&#34;排查&#34;&gt;排查&lt;/h2&gt;&#xA;&lt;h3 id=&#34;猜想一频繁申请重复对象&#34;&gt;猜想一：频繁申请重复对象&lt;/h3&gt;&#xA;&lt;p&gt;出现问题的个别业务服务都有几个特点，那就是基本为图片处理类的功能，例如：图片解压缩、批量生成二维码、PDF 生成等，因此就怀疑是否在量大时频繁申请重复对象，而 Go 本身又没有及时释放内存，因此导致持续占用。&lt;/p&gt;&#xA;&lt;h4 id=&#34;syncpool&#34;&gt;sync.Pool&lt;/h4&gt;&#xA;&lt;p&gt;基本上想解决 “频繁申请重复对象”，我们大多会采用多级内存池的方式，也可以用最常见的 sync.Pool，这里可参考全成所借述的《Go 夜读》上关于 sync.Pool 的分享，关于这类情况的场景：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;当多个 goroutine 都需要创建同⼀个对象的时候，如果 goroutine 数过多，导致对象的创建数⽬剧增，进⽽导致 GC 压⼒增大。形成 “并发⼤－占⽤内存⼤－GC 缓慢－处理并发能⼒降低－并发更⼤”这样的恶性循环。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h4 id=&#34;验证场景&#34;&gt;验证场景&lt;/h4&gt;&#xA;&lt;p&gt;在描述中关注到几个关键字，分别是并发大，Goroutine 数过多，GC 压力增大，GC 缓慢。也就是需要满足上述几个硬性条件，才可以认为是符合猜想的。&lt;/p&gt;&#xA;&lt;p&gt;通过拉取 PProf goroutine，可得知 Goroutine 数并不高：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go Modules 终极入门</title>
      <link>https://note.lican.site/posts/posts/go/go-moduels/2020-02-28-go-modules/</link>
      <pubDate>Fri, 28 Feb 2020 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-moduels/2020-02-28-go-modules/</guid>
      <description>&lt;p&gt;Go modules 是 Go 语言中正式官宣的项目依赖解决方案，Go modules（前身为vgo）于 Go1.11 正式发布，在 Go1.14 已经准备好，并且可以用在生产上（ready for production）了，Go官方也鼓励所有用户从其他依赖项管理工具迁移到 Go modules。&lt;/p&gt;&#xA;&lt;p&gt;而 Go1.14，在近期也终于正式发布，Go 官方亲自 “喊” 你来用：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/243fb2cca64972b2f36827f59b26d840.jpeg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;因此在今天这篇文章中，我将给大家带来 Go modules 的 “终极入门”，欢迎大家一起共同探讨。&lt;/p&gt;&#xA;&lt;p&gt;Go modules 是 Go 语言中正式官宣的项目依赖管理工具，Go modules（前身为vgo）于 Go1.11 正式发布，在 Go1.14 已经准备好，并且可以用在生产上（ready for production）了，鼓励所有用户从其他依赖项管理工具迁移到 Go modules。&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是go-modules&#34;&gt;什么是Go Modules&lt;/h2&gt;&#xA;&lt;p&gt;Go modules 是 Go 语言的依赖解决方案，发布于 Go1.11，成长于 Go1.12，丰富于 Go1.13，正式于 Go1.14 推荐在生产上使用。&lt;/p&gt;&#xA;&lt;p&gt;Go moudles 目前集成在 Go 的工具链中，只要安装了 Go，自然而然也就可以使用 Go moudles 了，而 Go modules 的出现也解决了在 Go1.11 前的几个常见争议问题：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Go 语言长久以来的依赖管理问题。&lt;/li&gt;&#xA;&lt;li&gt;“淘汰”现有的 GOPATH 的使用模式。&lt;/li&gt;&#xA;&lt;li&gt;统一社区中的其它的依赖管理工具（提供迁移功能）。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;gopath的那些点点滴滴&#34;&gt;GOPATH的那些点点滴滴&lt;/h2&gt;&#xA;&lt;p&gt;我们有提到 Go modules 的解决的问题之一就是“淘汰”掉 GOPATH，但是 GOPATH 又是什么呢，为什么在 Go1.11 前就使用 GOPATH，而 Go1.11 后就开始逐步建议使用 Go modules，不再推荐 GOPATH 的模式了呢？&lt;/p&gt;</description>
    </item>
    <item>
      <title>干货满满的 Go Modules 和 goproxy.cn</title>
      <link>https://note.lican.site/posts/posts/go/go-moduels/2019-09-29-goproxy-cn/</link>
      <pubDate>Sun, 29 Sep 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/go-moduels/2019-09-29-goproxy-cn/</guid>
      <description>&lt;p&gt;大家好，我是一只普通的煎鱼，周四晚上很有幸邀请到 goproxy.cn 的作者 @盛傲飞（@aofei） 到 Go 夜读给我们进行第 61 期 《Go Modules、Go Module Proxy 和 goproxy.cn》的技术分享。&lt;/p&gt;&#xA;&lt;p&gt;本次 @盛傲飞 的夜读分享，是对 Go Modules 的一次很好的解读，比较贴近工程实践，我必然希望把这块的知识更多的分享给大家，因此有了今天本篇文章，同时大家也可以多关注 Go 夜读，每周会通过 zoom 在线直播的方式分享 Go 相关的技术话题，希望对大家有所帮助。&lt;/p&gt;&#xA;&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;Go 1.11 推出的模块（Modules）为 Go 语言开发者打开了一扇新的大门，理想化的依赖管理解决方案使得 Go 语言朝着计算机编程史上的第一个依赖乌托邦（Deptopia）迈进。随着模块一起推出的还有模块代理协议（Module proxy protocol），通过这个协议我们可以实现 Go 模块代理（Go module proxy），也就是依赖镜像。&lt;/p&gt;&#xA;&lt;p&gt;Go 1.13 的发布为模块带来了大量的改进，所以模块的扶正就是这次 Go 1.13 发布中开发者能直接感觉到的最大变化。而问题在于，Go 1.13 中的 GOPROXY 环境变量拥有了一个在中国大陆无法访问到的默认值 &lt;code&gt;proxy.golang.org&lt;/code&gt;，经过大家在 golang/go#31755 中激烈的讨论（有些人甚至将话提上升到了“自由世界”的层次），最终 Go 核心团队仍然无法为中国开发者提供一个可在中国大陆访问的官方模块代理。&lt;/p&gt;&#xA;&lt;p&gt;为了今后中国的 Go 语言开发者能更好地进行开发，七牛云推出了非营利性项目 &lt;code&gt;goproxy.cn&lt;/code&gt;，其目标是为中国和世界上其他地方的 Gopher 们提供一个免费的、可靠的、持续在线的且经过 CDN 加速的模块代理。可以预见未来是属于模块化的，所以 Go 语言开发者能越早切入模块就能越早进入未来。&lt;/p&gt;&#xA;&lt;p&gt;如果说 Go 1.11 和 Go 1.12 时由于模块的不完善你不愿意切入，那么 Go 1.13 你则可以大胆地开始放心使用。本次分享将讨论如何使用模块和模块代理，以及在它们的使用中会常遇见的坑，还会讲解如何快速搭建自己的私有模块代理，并简单地介绍一下七牛云推出的 &lt;code&gt;goproxy.cn&lt;/code&gt; 以及它的出现对于中国 Go 语言开发者来说重要在何处。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 应用内存占用太多，让排查？（VSZ篇）</title>
      <link>https://note.lican.site/posts/posts/go/talk/2019-09-24-why-vsz-large/</link>
      <pubDate>Tue, 24 Sep 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2019-09-24-why-vsz-large/</guid>
      <description>&lt;p&gt;前段时间，某同学说某服务的容器因为超出内存限制，不断地重启，问我们是不是有内存泄露，赶紧排查，然后解决掉，省的出问题。我们大为震惊，赶紧查看监控+报警系统和性能分析，发现应用指标压根就不高，不像有泄露的样子。&lt;/p&gt;&#xA;&lt;p&gt;那么问题是出在哪里了呢，我们进入某个容器里查看了 &lt;code&gt;top&lt;/code&gt; 的系统指标，结果如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PID       VSZ    RSS   ... COMMAND&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;67459&lt;/span&gt;     2007m  136m  ... ./eddycjy-server&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从结果上来看，也没什么大开销的东西，主要就一个 Go 进程，一看，某同学就说 VSZ 那么高，而某云上的容器内存指标居然恰好和 VSZ 的值相接近，因此某同学就怀疑是不是 VSZ 所导致的，觉得存在一定的关联关系。&lt;/p&gt;&#xA;&lt;p&gt;而从最终的结论上来讲，上述的表述是不全对的，那么在今天，本篇文章将&lt;strong&gt;主要围绕 Go 进程的 VSZ 来进行剖析&lt;/strong&gt;，看看到底它为什么那么 &amp;ldquo;高&amp;rdquo;，而在正式开始分析前，第一节为前置的补充知识，大家可按顺序阅读。&lt;/p&gt;&#xA;&lt;h2 id=&#34;基础知识&#34;&gt;基础知识&lt;/h2&gt;&#xA;&lt;h3 id=&#34;什么是-vsz&#34;&gt;什么是 VSZ&lt;/h3&gt;&#xA;&lt;p&gt;VSZ 是该进程所能使用的虚拟内存总大小，它包括进程可以访问的所有内存，其中包括了被换出的内存（Swap）、已分配但未使用的内存以及来自共享库的内存。&lt;/p&gt;&#xA;&lt;h3 id=&#34;为什么要虚拟内存&#34;&gt;为什么要虚拟内存&lt;/h3&gt;&#xA;&lt;p&gt;在前面我们有了解到 VSZ 其实就是该进程的虚拟内存总大小，那&lt;strong&gt;如果我们想了解 VSZ 的话，那我们得先了解 “为什么要虚拟内存？”&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;本质上来讲，在一个系统中的进程是与其他进程共享 CPU 和主存资源的，而在现代的操作系统中，多进程的使用非常的常见，那么如果太多的进程需要太多的内存，那么在没有虚拟内存的情况下，物理内存很可能会不够用，就会导致其中有些任务无法运行，更甚至会出现一些很奇怪的现象，例如 “某一个进程不小心写了另一个进程使用的内存”，就会造成内存破坏，因此虚拟内存是非常重要的一个媒介。&lt;/p&gt;&#xA;&lt;h3 id=&#34;虚拟内存包含了什么&#34;&gt;虚拟内存包含了什么&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/3062dec8cd187490adadbdbcf50c17d4.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;而虚拟内存，又分为内核虚拟内存和进程虚拟内存，每一个进程的虚拟内存都是独立的， 呈现如上图所示。&lt;/p&gt;&#xA;&lt;p&gt;这里也补充说明一下，在内核虚拟内存中，是包含了内核中的代码和数据结构，而内核虚拟内存中的某些区域会被映射到所有进程共享的物理页面中去，因此你会看到 ”内核虚拟内存“ 中实际上是包含了 ”物理内存“ 的，它们两者存在映射关系。而在应用场景上来讲，每个进程也会去共享内核的代码和全局数据结构，因此就会被映射到所有进程的物理页面中去。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/3ba1352075ace855104f4bd57752a2ad.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;虚拟内存的重要能力&#34;&gt;虚拟内存的重要能力&lt;/h3&gt;&#xA;&lt;p&gt;为了更有效地管理内存并且减少出错，现代系统提供了一种对主存的抽象概念，也就是今天的主角，叫做虚拟内存（VM），虚拟内存是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件交互的地方，它为每个进程提供了一个大的、一致的和私有的地址空间，虚拟内存提供了三个重要的能力：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;它将主存看成是一个存储在磁盘上的地址空间的高速缓存，在主存中只保存活动区域，并根据需要在磁盘和主存之间来回传送数据，通过这种方式，它高效地使用了主存。&lt;/li&gt;&#xA;&lt;li&gt;它为每个进程提供了一致的地址空间，从而简化了内存管理。&lt;/li&gt;&#xA;&lt;li&gt;它保护了每个进程的地址空间不被其他进程破坏。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;小结&#34;&gt;小结&lt;/h3&gt;&#xA;&lt;p&gt;上面发散的可能比较多，简单来讲，对于本文我们重点关注这些知识点，如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;虚拟内存它是有各式各样内存交互的地方，它包含的不仅仅是 &amp;ldquo;自己&amp;rdquo;，&lt;strong&gt;而在本文中，我们只需要关注 VSZ，也就是进程虚拟内存，它包含了你的代码、数据、堆、栈段和共享库&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;li&gt;虚拟内存作为内存保护的工具，能够保证进程之间的内存空间独立，不受其他进程的影响，因此每一个进程的 VSZ 大小都不一样，互不影响。&lt;/li&gt;&#xA;&lt;li&gt;虚拟内存的存在，系统给各进程分配的内存之和是可以大于实际可用的物理内存的，因此你也会发现你进程的物理内存总是比虚拟内存低的多的多。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;排查问题&#34;&gt;排查问题&lt;/h2&gt;&#xA;&lt;p&gt;在了解了基础知识后，我们正式开始排查问题，第一步我们先编写一个测试程序，看看没有什么业务逻辑的 Go 程序，它初始的 VSZ 是怎么样的。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.13 defer 的性能是如何提高的</title>
      <link>https://note.lican.site/posts/posts/go/talk/2019-09-07-go1.13-defer/</link>
      <pubDate>Sat, 07 Sep 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2019-09-07-go1.13-defer/</guid>
      <description>&lt;p&gt;最近 Go1.13 终于发布了，其中一个值得关注的特性就是 &lt;strong&gt;defer 在大部分的场景下性能提升了30%&lt;/strong&gt;，但是官方并没有具体写是怎么提升的，这让大家非常的疑惑。而我因为之前写过&lt;a href=&#34;https://book.eddycjy.com/golang/defer/defer.html&#34;&gt;《深入理解 Go defer》&lt;/a&gt; 和 &lt;a href=&#34;https://book.eddycjy.com/golang/talk/defer-loss.html&#34;&gt;《Go defer 会有性能损耗，尽量不要用？》&lt;/a&gt; 这类文章，因此我挺感兴趣它是做了什么改变才能得到这样子的结果，所以今天和大家一起探索其中奥妙。&lt;/p&gt;&#xA;&lt;h2 id=&#34;一测试&#34;&gt;一、测试&lt;/h2&gt;&#xA;&lt;h3 id=&#34;go112&#34;&gt;Go1.12&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go test -bench=. -benchmem -run=none&#xA;goos: darwin&#xA;goarch: amd64&#xA;pkg: github.com/EDDYCJY/awesomeDefer&#xA;BenchmarkDoDefer-4      &#x9;20000000&#x9;        91.4 ns/op&#x9;      48 B/op&#x9;       1 allocs/op&#xA;BenchmarkDoNotDefer-4   &#x9;30000000&#x9;        41.6 ns/op&#x9;      48 B/op&#x9;       1 allocs/op&#xA;PASS&#xA;ok  &#x9;github.com/EDDYCJY/awesomeDefer&#x9;3.234s&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;go113&#34;&gt;Go1.13&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go test -bench=. -benchmem -run=none&#xA;goos: darwin&#xA;goarch: amd64&#xA;pkg: github.com/EDDYCJY/awesomeDefer&#xA;BenchmarkDoDefer-4      &#x9;15986062&#x9;        74.7 ns/op&#x9;      48 B/op&#x9;       1 allocs/op&#xA;BenchmarkDoNotDefer-4   &#x9;29231842&#x9;        40.3 ns/op&#x9;      48 B/op&#x9;       1 allocs/op&#xA;PASS&#xA;ok  &#x9;github.com/EDDYCJY/awesomeDefer&#x9;3.444s&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在开场，我先以不标准的测试基准验证了先前的测试用例，确确实实在这两个版本中，&lt;code&gt;defer&lt;/code&gt; 的性能得到了提高，但是看上去似乎不是百分百提高 30 %。&lt;/p&gt;</description>
    </item>
    <item>
      <title>用 GODEBUG 看 GC</title>
      <link>https://note.lican.site/posts/posts/go/tools/2019-09-02-godebug-gc/</link>
      <pubDate>Mon, 02 Sep 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/tools/2019-09-02-godebug-gc/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/b07f55c7fd136392763729b9782f7776.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是-gc&#34;&gt;什么是 GC&lt;/h2&gt;&#xA;&lt;p&gt;在计算机科学中，垃圾回收（GC）是一种自动管理内存的机制，垃圾回收器会去尝试回收程序不再使用的对象及其占用的内存。而最早 John McCarthy 在 1959 年左右发明了垃圾回收，以简化 Lisp 中的手动内存管理的机制（来自 wikipedia）。&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么要-gc&#34;&gt;为什么要 GC&lt;/h2&gt;&#xA;&lt;p&gt;手动管理内存挺麻烦，管错或者管漏内存也很糟糕，将会直接导致程序不稳定（持续泄露）甚至直接崩溃。&lt;/p&gt;&#xA;&lt;h2 id=&#34;gc-带来的问题&#34;&gt;GC 带来的问题&lt;/h2&gt;&#xA;&lt;p&gt;硬要说会带来什么问题的话，也就数大家最关注的 Stop The World（STW），STW 代指在执行某个垃圾回收算法的某个阶段时，需要将整个应用程序暂停去处理 GC 相关的工作事项。例如：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;行为&lt;/th&gt;&#xA;          &lt;th&gt;会不会 STW&lt;/th&gt;&#xA;          &lt;th&gt;为什么&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;标记开始&lt;/td&gt;&#xA;          &lt;td&gt;会&lt;/td&gt;&#xA;          &lt;td&gt;在开始标记时，准备根对象的扫描，会打开写屏障（Write Barrier） 和 辅助GC（mutator assist），而回收器和应用程序是并发运行的，因此会暂停当前正在运行的所有 Goroutine。&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;并发标记中&lt;/td&gt;&#xA;          &lt;td&gt;不会&lt;/td&gt;&#xA;          &lt;td&gt;标记阶段，主要目的是标记堆内存中仍在使用的值。&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;标记结束&lt;/td&gt;&#xA;          &lt;td&gt;会&lt;/td&gt;&#xA;          &lt;td&gt;在完成标记任务后，将重新扫描部分根对象，这时候会禁用写屏障（Write Barrier）和辅助GC（mutator assist），而标记阶段和应用程序是并发运行的，所以在标记阶段可能会有新的对象产生，因此在重新扫描时需要进行 STW。&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;如何调整-gc-频率&#34;&gt;如何调整 GC 频率&lt;/h2&gt;&#xA;&lt;p&gt;可以通过 GOGC 变量设置初始垃圾收集器的目标百分比值，对比的规则为当新分配的数值与上一次收集后剩余的实时数值的比例达到设置的目标百分比时，就会触发 GC，默认值为 GOGC=100。如果将其设置为 GOGC=off 可以完全禁用垃圾回收器，要不试试？&lt;/p&gt;&#xA;&lt;p&gt;简单来讲就是，GOGC 的值设置的越大，GC 的频率越低，但每次最终所触发到 GC 的堆内存也会更大。&lt;/p&gt;&#xA;&lt;h2 id=&#34;各版本-gc-情况&#34;&gt;各版本 GC 情况&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;版本&lt;/th&gt;&#xA;          &lt;th&gt;GC 算法&lt;/th&gt;&#xA;          &lt;th&gt;STW 时间&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Go 1.0&lt;/td&gt;&#xA;          &lt;td&gt;STW（强依赖 tcmalloc）&lt;/td&gt;&#xA;          &lt;td&gt;百ms到秒级别&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Go 1.3&lt;/td&gt;&#xA;          &lt;td&gt;Mark STW, Sweep 并行&lt;/td&gt;&#xA;          &lt;td&gt;百ms级别&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Go 1.5&lt;/td&gt;&#xA;          &lt;td&gt;三色标记法, 并发标记清除。同时运行时从 C 和少量汇编，改为 Go 和少量汇编实现&lt;/td&gt;&#xA;          &lt;td&gt;10-50ms级别&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Go 1.6&lt;/td&gt;&#xA;          &lt;td&gt;1.5 中一些与并发 GC 不协调的地方更改，集中式的 GC 协调协程，改为状态机实现&lt;/td&gt;&#xA;          &lt;td&gt;5ms级别&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Go 1.7&lt;/td&gt;&#xA;          &lt;td&gt;GC 时由 mark 栈收缩改为并发，span 对象分配机制由 freelist 改为 bitmap 模式，SSA引入&lt;/td&gt;&#xA;          &lt;td&gt;ms级别&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Go 1.8&lt;/td&gt;&#xA;          &lt;td&gt;混合写屏障（hybrid write barrier）, 消除 re-scanning stack&lt;/td&gt;&#xA;          &lt;td&gt;sub ms&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Go 1.12&lt;/td&gt;&#xA;          &lt;td&gt;Mark Termination 流程优化&lt;/td&gt;&#xA;          &lt;td&gt;sub ms, 但几乎减少一半&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;注：资料来源于 @boya 在深圳 Gopher Meetup 的分享。&lt;/p&gt;</description>
    </item>
    <item>
      <title>用 GODEBUG 看调度跟踪</title>
      <link>https://note.lican.site/posts/posts/go/tools/2019-08-19-godebug-sched/</link>
      <pubDate>Mon, 19 Aug 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/tools/2019-08-19-godebug-sched/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/b01c2ce25e34f80d499f0488d034b00b.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;让 Go 更强大的原因之一莫过于它的 GODEBUG 工具，GODEBUG 的设置可以让 Go 程序在运行时输出调试信息，可以根据你的要求很直观的看到你想要的调度器或垃圾回收等详细信息，并且还不需要加装其它的插件，非常方便，今天我们将先讲解 GODEBUG 的调度器相关内容，希望对你有所帮助。&lt;/p&gt;&#xA;&lt;p&gt;不过在开始前，没接触过的小伙伴得先补补如下前置知识，便于更好的了解调试器输出的信息内容。&lt;/p&gt;&#xA;&lt;h2 id=&#34;前置知识&#34;&gt;前置知识&lt;/h2&gt;&#xA;&lt;p&gt;Go scheduler 的主要功能是针对在处理器上运行的 OS 线程分发可运行的 Goroutine，而我们一提到调度器，就离不开三个经常被提到的缩写，分别是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;G：Goroutine，实际上我们每次调用 &lt;code&gt;go func&lt;/code&gt; 就是生成了一个 G。&lt;/li&gt;&#xA;&lt;li&gt;P：处理器，一般为处理器的核数，可以通过 &lt;code&gt;GOMAXPROCS&lt;/code&gt; 进行修改。&lt;/li&gt;&#xA;&lt;li&gt;M：OS 线程&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这三者交互实际来源于 Go 的 M: N 调度模型，也就是 M 必须与 P 进行绑定，然后不断地在 M 上循环寻找可运行的 G 来执行相应的任务，如果想具体了解可以详细阅读 &lt;a href=&#34;https://speakerdeck.com/retervision/go-runtime-scheduler&#34;&gt;《Go Runtime Scheduler》&lt;/a&gt;，我们抽其中的工作流程图进行简单分析，如下:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/fb4c6c92c93af3bc2dfc4f13dc167cdf.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;当我们执行 &lt;code&gt;go func()&lt;/code&gt; 时，实际上就是创建一个全新的 Goroutine，我们称它为 G。&lt;/li&gt;&#xA;&lt;li&gt;新创建的 G 会被放入 P 的本地队列（Local Queue）或全局队列（Global Queue）中，准备下一步的动作。&lt;/li&gt;&#xA;&lt;li&gt;唤醒或创建 M 以便执行 G。&lt;/li&gt;&#xA;&lt;li&gt;不断地进行事件循环&lt;/li&gt;&#xA;&lt;li&gt;寻找在可用状态下的 G 进行执行任务&lt;/li&gt;&#xA;&lt;li&gt;清除后，重新进入事件循环&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;而在描述中有提到全局和本地这两类队列，其实在功能上来讲都是用于存放正在等待运行的 G，但是不同点在于，本地队列有数量限制，不允许超过 256 个。并且在新建 G 时，会优先选择 P 的本地队列，如果本地队列满了，则将 P 的本地队列的一半的 G 移动到全局队列，这其实可以理解为调度资源的共享和再平衡。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 大杀器之跟踪剖析 trace</title>
      <link>https://note.lican.site/posts/posts/go/tools/2019-07-12-go-tool-trace/</link>
      <pubDate>Fri, 12 Jul 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/tools/2019-07-12-go-tool-trace/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/15/1x1phF.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;在 Go 中有许许多多的分析工具，在之前我有写过一篇 《Golang 大杀器之性能剖析 PProf》 来介绍 PProf，如果有小伙伴感兴趣可以去我博客看看。&lt;/p&gt;&#xA;&lt;p&gt;但单单使用 PProf 有时候不一定足够完整，因为在真实的程序中还包含许多的隐藏动作，例如 Goroutine 在执行时会做哪些操作？执行/阻塞了多长时间？在什么时候阻止？在哪里被阻止的？谁又锁/解锁了它们？GC 是怎么影响到 Goroutine 的执行的？这些东西用 PProf 是很难分析出来的，但如果你又想知道上述的答案的话，你可以用本文的主角 &lt;code&gt;go tool trace&lt;/code&gt; 来打开新世界的大门。目录如下：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/15/1x1P1J.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;初步了解&#34;&gt;初步了解&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;runtime/trace&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;trace&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Start&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Stderr&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;trace&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Stop&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ch&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;生成跟踪文件：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go run main.go 2&amp;gt; trace.out&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;启动可视化界面：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go tool trace trace.out&#xA;2019/06/22 16:14:52 Parsing trace...&#xA;2019/06/22 16:14:52 Splitting trace...&#xA;2019/06/22 16:14:52 Opening browser. Trace viewer is listening on http://127.0.0.1:57321&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;查看可视化界面：&lt;/p&gt;</description>
    </item>
    <item>
      <title>从实践到原理，带你参透 gRPC</title>
      <link>https://note.lican.site/posts/posts/go/talk/2019-06-29-talking-grpc/</link>
      <pubDate>Sat, 29 Jun 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2019-06-29-talking-grpc/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/4a47a0db6e60853dedfcfdf08a5ca249.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;gRPC 在 Go 语言中大放异彩，越来越多的小伙伴在使用，最近也在公司安利了一波，希望这一篇文章能带你一览 gRPC 的巧妙之处，本文篇幅比较长，请做好阅读准备。本文目录如下：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/156005c5baf40ff51a327f1c34f2975b.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;简述&#34;&gt;简述&lt;/h2&gt;&#xA;&lt;p&gt;gRPC 是一个高性能、开源和通用的 RPC 框架，面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本，分别是：grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持。&lt;/p&gt;&#xA;&lt;p&gt;gRPC 基于 HTTP/2 标准设计，带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特性。这些特性使得其在移动设备上表现更好，更省电和节省空间占用。&lt;/p&gt;&#xA;&lt;h2 id=&#34;调用模型&#34;&gt;调用模型&lt;/h2&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/10fb15c77258a991b0028080a64fb42d.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;1、客户端（gRPC Stub）调用 A 方法，发起 RPC 调用。&lt;/p&gt;&#xA;&lt;p&gt;2、对请求信息使用 Protobuf 进行对象序列化压缩（IDL）。&lt;/p&gt;&#xA;&lt;p&gt;3、服务端（gRPC Server）接收到请求后，解码请求体，进行业务逻辑处理并返回。&lt;/p&gt;&#xA;&lt;p&gt;4、对响应结果使用 Protobuf 进行对象序列化压缩（IDL）。&lt;/p&gt;&#xA;&lt;p&gt;5、客户端接受到服务端响应，解码请求体。回调被调用的 A 方法，唤醒正在等待响应（阻塞）的客户端调用并返回响应结果。&lt;/p&gt;&#xA;&lt;h2 id=&#34;调用方式&#34;&gt;调用方式&lt;/h2&gt;&#xA;&lt;h3 id=&#34;一unary-rpc一元-rpc&#34;&gt;一、Unary RPC：一元 RPC&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/09dd8c2662b96ce14928333f055c5580.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;server&#34;&gt;Server&lt;/h4&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SearchService&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;SearchService&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Search&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SearchRequest&lt;/span&gt;) (&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SearchResponse&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SearchResponse&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetRequest&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; Server&amp;#34;&lt;/span&gt;}, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PORT&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9001&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;server&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;grpc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewServer&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;pb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RegisterSearchServiceServer&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;server&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;SearchService&lt;/span&gt;{})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lis&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;net&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Listen&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;PORT&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;server&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Serve&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;lis&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;创建 gRPC Server 对象，你可以理解为它是 Server 端的抽象对象。&lt;/li&gt;&#xA;&lt;li&gt;将 SearchService（其包含需要被调用的服务端接口）注册到 gRPC Server。 的内部注册中心。这样可以在接受到请求时，通过内部的 “服务发现”，发现该服务端接口并转接进行逻辑处理。&lt;/li&gt;&#xA;&lt;li&gt;创建 Listen，监听 TCP 端口。&lt;/li&gt;&#xA;&lt;li&gt;gRPC Server 开始 lis.Accept，直到 Stop 或 GracefulStop。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;client&#34;&gt;Client&lt;/h4&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;conn&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;grpc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Dial&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;PORT&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;grpc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WithInsecure&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;conn&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewSearchServiceClient&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;conn&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;resp&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Search&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Background&lt;/span&gt;(), &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SearchRequest&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gRPC&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;创建与给定目标（服务端）的连接句柄。&lt;/li&gt;&#xA;&lt;li&gt;创建 SearchService 的客户端对象。&lt;/li&gt;&#xA;&lt;li&gt;发送 RPC 请求，等待同步响应，得到回调后返回响应结果。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;二server-side-streaming-rpc服务端流式-rpc&#34;&gt;二、Server-side streaming RPC：服务端流式 RPC&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/8266e4bfeda1bd42d8f9794eb4ea0a13.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载四」gRPC&#43;gRPC Gateway 能不能不用证书？</title>
      <link>https://note.lican.site/posts/posts/go/grpc-gateway/2019-06-22-grpc-gateway-tls/</link>
      <pubDate>Sat, 22 Jun 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc-gateway/2019-06-22-grpc-gateway-tls/</guid>
      <description>&lt;p&gt;如果你以前有涉猎过 gRPC+gRPC Gateway 这两个组件，你肯定会遇到这个问题，就是 &lt;strong&gt;“为什么非得开 TLS，才能够实现同端口双流量，能不能不开？”&lt;/strong&gt; 又或是 &lt;strong&gt;“我不想用证书就实现这些功能，行不行？”&lt;/strong&gt;。我被无数的人问过无数次这些问题，也说服过很多人，但说服归说服，不代表放弃。前年不行，不代表今年不行，在今天我希望分享来龙去脉和具体的实现方式给你。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3dLBAx.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;过去&#34;&gt;过去&lt;/h2&gt;&#xA;&lt;h3 id=&#34;为什么-h2-不行&#34;&gt;为什么 h2 不行&lt;/h3&gt;&#xA;&lt;p&gt;因为 &lt;code&gt;net/http2&lt;/code&gt; 仅支持 &amp;ldquo;h2&amp;rdquo; 标识，而 &amp;ldquo;h2&amp;rdquo; 标识 HTTP/2 必须使用传输层安全性（TLS）的协议，此标识符用于 TLS 应用层协议协商字段以及识别 HTTP/2 over TLS。&lt;/p&gt;&#xA;&lt;p&gt;简单来讲，也就 &lt;code&gt;net/http2&lt;/code&gt; 必须使用 TLS 来交互。通俗来讲就要用证书，那么理所当然，也就无法支持非 TLS 的情况了。&lt;/p&gt;&#xA;&lt;h3 id=&#34;寻找-h2c&#34;&gt;寻找 h2c&lt;/h3&gt;&#xA;&lt;p&gt;那这条路不行，我们再想想别的路？那就是 HTTP/2 规范中的 &amp;ldquo;h2c&amp;rdquo; 标识了，&amp;ldquo;h2c&amp;rdquo; 标识允许通过明文 TCP 运行 HTTP/2 的协议，此标识符用于 HTTP/1.1 升级标头字段以及标识 HTTP/2 over TCP。&lt;/p&gt;&#xA;&lt;p&gt;但是这条路，早在 2015 年就已经有在 &lt;a href=&#34;https://github.com/golang/go/issues/13128#issuecomment-153193762&#34;&gt;issue&lt;/a&gt; 中进行讨论，当时 @bradfitz 明确表示 “不打算支持 h2c，对仅支持 TLS 的情况非常满意，一年后再问我一次”，原文回复如下：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;We do not plan to support h2c. I don&amp;rsquo;t want to receive bug reports from users who get bitten by transparent proxies messing with h2c. Also, until there&amp;rsquo;s widespread browser support, it&amp;rsquo;s not interesting. I am also not interested in being the chicken or the egg to get browser support going. I&amp;rsquo;m very happy with the TLS-only situation, and things like &lt;a href=&#34;https://LetsEncrypt.org/&#34;&gt;https://LetsEncrypt.org/&lt;/a&gt; will make TLS much easier (and automatic) soon.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go1.12 defer 会有性能损耗，尽量不要用？</title>
      <link>https://note.lican.site/posts/posts/go/talk/2019-06-16-defer-loss/</link>
      <pubDate>Sun, 16 Jun 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2019-06-16-defer-loss/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3wuUYV.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;上个月在 @polaris @轩脉刃 的全栈技术群里看到一个小伙伴问 &lt;strong&gt;“说 defer 在栈退出时执行，会有性能损耗，尽量不要用，这个怎么解？”&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;恰好前段时间写了一篇 &lt;a href=&#34;https://segmentfault.com/a/1190000019303572&#34;&gt;《深入理解 Go defer》&lt;/a&gt; 去详细剖析 &lt;code&gt;defer&lt;/code&gt; 关键字。那么这一次简单结合前文对这个问题进行探讨一波，希望对你有所帮助，但在此之前希望你花几分钟，自己思考一下答案，再继续往下看。&lt;/p&gt;&#xA;&lt;h2 id=&#34;测试&#34;&gt;测试&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DoDefer&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }(&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DoNotDefer&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;基准测试：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BenchmarkDoDefer&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;N&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;DoDefer&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://github.com/EDDYCJY/blog&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BenchmarkDoNotDefer&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;N&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;DoNotDefer&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;煎鱼&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://github.com/EDDYCJY/blog&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go test -bench=. -benchmem -run=none&#xA;goos: darwin&#xA;goarch: amd64&#xA;pkg: github.com/EDDYCJY/awesomeDefer&#xA;BenchmarkDoDefer-4          20000000            91.4 ns/op        48 B/op          1 allocs/op&#xA;BenchmarkDoNotDefer-4       30000000            41.6 ns/op        48 B/op          1 allocs/op&#xA;PASS&#xA;ok      github.com/EDDYCJY/awesomeDefer 3.234s&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从结果上来，使用 &lt;code&gt;defer&lt;/code&gt; 后的函数开销确实比没使用高了不少，这损耗用到哪里去了呢？&lt;/p&gt;</description>
    </item>
    <item>
      <title>深入理解 Go defer</title>
      <link>https://note.lican.site/posts/posts/go/defer/2019-05-27-defer/</link>
      <pubDate>Mon, 27 May 2019 12:30:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/defer/2019-05-27-defer/</guid>
      <description>&lt;p&gt;在上一章节 《深入理解 Go panic and recover》中，我们发现了 &lt;code&gt;defer&lt;/code&gt; 与其关联性极大，还是觉得非常有必要深入一下。希望通过本章节大家可以对 &lt;code&gt;defer&lt;/code&gt; 关键字有一个深刻的理解，那么我们开始吧。你先等等，请排好队，我们这儿采取后进先出 LIFO 的出站方式&amp;hellip;&lt;/p&gt;&#xA;&lt;h2 id=&#34;特性&#34;&gt;特性&lt;/h2&gt;&#xA;&lt;p&gt;我们简单的过一下 &lt;code&gt;defer&lt;/code&gt; 关键字的基础使用，让大家先有一个基础的认知&lt;/p&gt;&#xA;&lt;h3 id=&#34;一延迟调用&#34;&gt;一、延迟调用&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY.&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;end.&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go run main.go&#xA;2019/05/19 21:15:02 end.&#xA;2019/05/19 21:15:02 EDDYCJY.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;二后进先出&#34;&gt;二、后进先出&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Itoa&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;end.&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go run main.go&#xA;2019/05/19 21:19:17 end.&#xA;2019/05/19 21:19:17 EDDYCJY5.&#xA;2019/05/19 21:19:17 EDDYCJY4.&#xA;2019/05/19 21:19:17 EDDYCJY3.&#xA;2019/05/19 21:19:17 EDDYCJY2.&#xA;2019/05/19 21:19:17 EDDYCJY1.&#xA;2019/05/19 21:19:17 EDDYCJY0.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;三运行时间点&#34;&gt;三、运行时间点&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9; &lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;defer.EDDYCJY.&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;main.EDDYCJY.&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;</description>
    </item>
    <item>
      <title>深入理解 Go panic and recover</title>
      <link>https://note.lican.site/posts/posts/go/panic/2019-05-21-panic-and-recover/</link>
      <pubDate>Tue, 21 May 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/panic/2019-05-21-panic-and-recover/</guid>
      <description>&lt;p&gt;作为一个 gophper，我相信你对于 &lt;code&gt;panic&lt;/code&gt; 和 &lt;code&gt;recover&lt;/code&gt; 肯定不陌生，但是你有没有想过。当我们执行了这两条语句之后。底层到底发生了什么事呢？前几天和同事刚好聊到相关的话题，发现其实大家对这块理解还是比较模糊的。希望这篇文章能够从更深入的角度告诉你为什么，它到底做了什么事？&lt;/p&gt;&#xA;&lt;h2 id=&#34;思考&#34;&gt;思考&lt;/h2&gt;&#xA;&lt;h3 id=&#34;一为什么会中止运行&#34;&gt;一、为什么会中止运行&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA;&#x9;panic(&amp;#34;EDDYCJY.&amp;#34;)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go run main.go&#xA;panic: EDDYCJY.&#xA;&#xA;goroutine 1 [running]:&#xA;main.main()&#xA;&#x9;/Users/eddycjy/go/src/github.com/EDDYCJY/awesomeProject/main.go:4 +0x39&#xA;exit status 2&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;请思考一下，为什么执行 &lt;code&gt;panic&lt;/code&gt; 后会导致应用程序运行中止？（而不是单单说执行了 &lt;code&gt;panic&lt;/code&gt; 所以就结束了这么含糊）&lt;/p&gt;&#xA;&lt;h3 id=&#34;二为什么不会中止运行&#34;&gt;二、为什么不会中止运行&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA;&#x9;defer func() {&#xA;&#x9;&#x9;if err := recover(); err != nil {&#xA;&#x9;&#x9;&#x9;log.Printf(&amp;#34;recover: %v&amp;#34;, err)&#xA;&#x9;&#x9;}&#xA;&#x9;}()&#xA;&#xA;&#x9;panic(&amp;#34;EDDYCJY.&amp;#34;)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go run main.go &#xA;2019/05/11 23:39:47 recover: EDDYCJY.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;请思考一下，为什么加上 &lt;code&gt;defer&lt;/code&gt; + &lt;code&gt;recover&lt;/code&gt; 组合就可以保护应用程序？&lt;/p&gt;&#xA;&lt;h3 id=&#34;三不设置-defer-行不&#34;&gt;三、不设置 defer 行不&lt;/h3&gt;&#xA;&lt;p&gt;上面问题二是 &lt;code&gt;defer&lt;/code&gt; + &lt;code&gt;recover&lt;/code&gt; 组合，那我去掉 &lt;code&gt;defer&lt;/code&gt; 是不是也可以呢？如下：&lt;/p&gt;</description>
    </item>
    <item>
      <title>我要在栈上。不，你应该在堆上</title>
      <link>https://note.lican.site/posts/posts/go/talk/2019-05-20-stack-heap/</link>
      <pubDate>Mon, 20 May 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2019-05-20-stack-heap/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3wK39K.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;我们在写代码的时候，有时候会想这个变量到底分配到哪里了？这时候可能会有人说，在栈上，在堆上。信我准没错&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;但从结果上来讲你还是一知半解，这可不行，万一被人懵了呢。今天我们一起来深挖下 Go 在这块的奥妙，自己动手丰衣足食&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题&#34;&gt;问题&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;User&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ID&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;int64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Avatar&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetUserInfo&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;User&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;User&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;ID&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;13746731&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Avatar&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://avatars0.githubusercontent.com/u/13746731&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;GetUserInfo&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;开局就是一把问号，带着问题进行学习。请问 main 调用 &lt;code&gt;GetUserInfo&lt;/code&gt; 后返回的 &lt;code&gt;&amp;amp;User{...}&lt;/code&gt;。这个变量是分配到栈上了呢，还是分配到堆上了？&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是堆栈&#34;&gt;什么是堆/栈&lt;/h2&gt;&#xA;&lt;p&gt;在这里并不打算详细介绍堆栈，仅简单介绍本文所需的基础知识。如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;堆（Heap）：一般来讲是人为手动进行管理，手动申请、分配、释放。一般所涉及的内存大小并不定，一般会存放较大的对象。另外其分配相对慢，涉及到的指令动作也相对多&lt;/li&gt;&#xA;&lt;li&gt;栈（Stack）：由编译器进行管理，自动申请、分配、释放。一般不会太大，我们常见的函数参数（不同平台允许存放的数量不同），局部变量等等都会存放在栈上&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;今天我们介绍的 Go 语言，它的堆栈分配是通过 Compiler 进行分析，GC 去管理的，而对其的分析选择动作就是今天探讨的重点&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是逃逸分析&#34;&gt;什么是逃逸分析&lt;/h2&gt;&#xA;&lt;p&gt;在编译程序优化理论中，逃逸分析是一种确定指针动态范围的方法，简单来说就是分析在程序的哪些地方可以访问到该指针&lt;/p&gt;&#xA;&lt;p&gt;通俗地讲，逃逸分析就是确定一个变量要放堆上还是栈上，规则如下：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;是否有在其他地方（非局部）被引用。只要&lt;strong&gt;有可能&lt;/strong&gt;被引用了，那么它&lt;strong&gt;一定&lt;/strong&gt;分配到堆上。否则分配到栈上&lt;/li&gt;&#xA;&lt;li&gt;即使没有被外部引用，但对象过大，无法存放在栈区上。依然有可能分配到堆上&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;对此你可以理解为，逃逸分析是编译器用于决定变量分配到堆上还是栈上的一种行为&lt;/p&gt;&#xA;&lt;h2 id=&#34;在什么阶段确立逃逸&#34;&gt;在什么阶段确立逃逸&lt;/h2&gt;&#xA;&lt;p&gt;在编译阶段确立逃逸，注意并不是在运行时&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么需要逃逸&#34;&gt;为什么需要逃逸&lt;/h2&gt;&#xA;&lt;p&gt;这个问题我们可以反过来想，如果变量都分配到堆上了会出现什么事情？例如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;垃圾回收（GC）的压力不断增大&lt;/li&gt;&#xA;&lt;li&gt;申请、分配、回收内存的系统开销增大（相对于栈）&lt;/li&gt;&#xA;&lt;li&gt;动态分配产生一定量的内存碎片&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;其实总的来说，就是频繁申请、分配堆内存是有一定 “代价” 的。会影响应用程序运行的效率，间接影响到整体系统。因此 “按需分配” 最大限度的灵活利用资源，才是正确的治理之道。这就是为什么需要逃逸分析的原因，你觉得呢？&lt;/p&gt;&#xA;&lt;h2 id=&#34;怎么确定是否逃逸&#34;&gt;怎么确定是否逃逸&lt;/h2&gt;&#xA;&lt;p&gt;第一，通过编译器命令，就可以看到详细的逃逸分析过程。而指令集 &lt;code&gt;-gcflags&lt;/code&gt; 用于将标识参数传递给 Go 编译器，涉及如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;code&gt;-m&lt;/code&gt; 会打印出逃逸分析的优化策略，实际上最多总共可以用 4 个 &lt;code&gt;-m&lt;/code&gt;，但是信息量较大，一般用 1 个就可以了&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么遍历 Go map 是无序的</title>
      <link>https://note.lican.site/posts/posts/go/map/2019-04-07-why-map-no-order/</link>
      <pubDate>Sun, 07 Apr 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/map/2019-04-07-why-map-no-order/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;http://wx2.sinaimg.cn/large/006fVPCvly1g1s1ah84k8j30k70dvaac.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;有的小伙伴没留意过 Go map 输出顺序，以为它是稳定的有序的；有的小伙伴知道是无序的，但却不知道为什么？有的却理解错误？今天我们将通过本文，揭开 &lt;code&gt;for range map&lt;/code&gt; 的 “神秘” 面纱，看看它内部实现到底是怎么样的，输出顺序到底是怎么样？&lt;/p&gt;&#xA;&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#66d9ef&#34;&gt;int32&lt;/span&gt;]&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY3&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY4&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY5&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;k: %v, v: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;假设运行这段代码，输出结果是按顺序？还是无序输出呢？&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;2019/04/03 23:27:29 k: 3, v: EDDYCJY4&#xA;2019/04/03 23:27:29 k: 4, v: EDDYCJY5&#xA;2019/04/03 23:27:29 k: 0, v: EDDYCJY1&#xA;2019/04/03 23:27:29 k: 1, v: EDDYCJY2&#xA;2019/04/03 23:27:29 k: 2, v: EDDYCJY3&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从输出结果上来讲，是非固定顺序输出的，也就是每次都不一样（标题也讲了）。但这是为什么呢？&lt;/p&gt;&#xA;&lt;p&gt;首先&lt;strong&gt;建议你先自己想想原因&lt;/strong&gt;。其次我在面试时听过一些说法。有人说因为是哈希的所以就是无（乱）序等等说法。当时我是有点 ？？？&lt;/p&gt;&#xA;&lt;p&gt;这也是这篇文章出现的原因，希望大家可以一起研讨一下，理清这个问题 ：）&lt;/p&gt;</description>
    </item>
    <item>
      <title>简单围观一下有趣的 //go: 指令</title>
      <link>https://note.lican.site/posts/posts/go/talk/2019-03-31-go-ins/</link>
      <pubDate>Sun, 31 Mar 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2019-03-31-go-ins/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;http://wx2.sinaimg.cn/large/006fVPCvly1g1m1bplu3mj30xc0m8myg.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;如果你平时有翻看源码的习惯，你肯定会发现。咦，怎么有的方法上面总是写着 &lt;code&gt;//go:&lt;/code&gt;  这类指令呢。他们到底是干嘛用的？&lt;/p&gt;&#xA;&lt;p&gt;今天我们一同揭开他们的面纱，我将简单给你介绍一下，它们都负责些什么&lt;/p&gt;&#xA;&lt;h2 id=&#34;golinkname&#34;&gt;go:linkname&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;//go:linkname localname importpath.name&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;该指令指示编译器使用 &lt;code&gt;importpath.name&lt;/code&gt; 作为源代码中声明为 &lt;code&gt;localname&lt;/code&gt; 的变量或函数的目标文件符号名称。但是由于这个伪指令，可以破坏类型系统和包模块化。因此只有引用了 unsafe 包才可以使用&lt;/p&gt;&#xA;&lt;p&gt;简单来讲，就是 &lt;code&gt;importpath.name&lt;/code&gt; 是 &lt;code&gt;localname&lt;/code&gt; 的符号别名，编译器实际上会调用 &lt;code&gt;localname&lt;/code&gt; 。但前提是使用了 &lt;code&gt;unsafe&lt;/code&gt; 包才能使用&lt;/p&gt;&#xA;&lt;h3 id=&#34;案例&#34;&gt;案例&lt;/h3&gt;&#xA;&lt;h4 id=&#34;timetimego&#34;&gt;time/time.go&lt;/h4&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;...&#xA;func now() (sec int64, nsec int32, mono int64)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;runtimetimestubgo&#34;&gt;runtime/timestub.go&lt;/h4&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import _ &amp;#34;unsafe&amp;#34; // for go:linkname&#xA;&#xA;//go:linkname time_now time.now&#xA;func time_now() (sec int64, nsec int32, mono int64) {&#xA;&#x9;sec, nsec = walltime()&#xA;&#x9;return sec, nsec, nanotime() - startNano&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在这个案例中可以看到 &lt;code&gt;time.now&lt;/code&gt;，它并没有具体的实现。如果你初看可能会懵逼。这时候建议你全局搜索一下源码，你就会发现其实现在 &lt;code&gt;runtime.time_now&lt;/code&gt; 中&lt;/p&gt;&#xA;&lt;p&gt;配合先前的用法解释，可得知在 runtime 包中，我们声明了 &lt;code&gt;time_now&lt;/code&gt; 方法是 &lt;code&gt;time.now&lt;/code&gt; 的符号别名。并且在文件头引入了 &lt;code&gt;unsafe&lt;/code&gt; 达成前提条件&lt;/p&gt;</description>
    </item>
    <item>
      <title>深入理解 Go map：赋值和扩容迁移</title>
      <link>https://note.lican.site/posts/posts/go/map/2019-03-24-map-assign/</link>
      <pubDate>Sun, 24 Mar 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/map/2019-03-24-map-assign/</guid>
      <description>&lt;h2 id=&#34;概要&#34;&gt;概要&lt;/h2&gt;&#xA;&lt;p&gt;在 &lt;a href=&#34;https://book.eddycjy.com/golang/map/map-access.html&#34;&gt;上一章节&lt;/a&gt; 中，数据结构小节里讲解了大量基础字段，可能你会疑惑需要 #&amp;amp;（！……#（！￥！ 来干嘛？接下来我们一起简单了解一下基础概念。再开始研讨今天文章的重点内容。我相信这样你能更好的读懂这篇文章&lt;/p&gt;&#xA;&lt;h3 id=&#34;哈希函数&#34;&gt;哈希函数&lt;/h3&gt;&#xA;&lt;p&gt;哈希函数，又称散列算法、散列函数。主要作用是通过特定算法将数据根据一定规则组合重新生成得到一个&lt;strong&gt;散列值&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;而在哈希表中，其生成的散列值常用于寻找其键映射到哪一个桶上。而一个好的哈希函数，应当尽量少的出现哈希冲突，以此保证操作哈希表的时间复杂度（但是哈希冲突在目前来讲，是无法避免的。我们需要 “解决” 它）&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;http://wx3.sinaimg.cn/large/006fVPCvly1g161h7r7hgj30is0dmjro.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;链地址法&#34;&gt;链地址法&lt;/h3&gt;&#xA;&lt;p&gt;在哈希操作中，相当核心的一个处理动作就是 “哈希冲突” 的解决。而在 Go map 中采用的就是 &amp;ldquo;链地址法 &amp;quot; 去解决哈希冲突，又称 &amp;ldquo;拉链法&amp;rdquo;。其主要做法是数组 + 链表的数据结构，其溢出节点的存储内存都是动态申请的，因此相对更灵活。而每一个元素都是一个链表。如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;http://wx4.sinaimg.cn/large/006fVPCvly1g1dw2b8t0ej30e60cy747.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;桶溢出桶&#34;&gt;桶/溢出桶&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hmap&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;buckets&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mapextra&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mapextra&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;overflow&lt;/span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldoverflow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nextOverflow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上章节中，我们介绍了 Go map 中的桶和溢出桶的概念，在其桶中只能存储 8 个键值对元素。当超过 8 个时，将会使用溢出桶进行存储或进行扩容&lt;/p&gt;&#xA;&lt;p&gt;你可能会有疑问，hint 大于 8 又会怎么样？答案很明显，性能问题，其时间复杂度改变（也就是执行效率出现问题）&lt;/p&gt;&#xA;&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;概要复习的差不多后，接下来我们将一同研讨 Go map 的另外三个核心行为：赋值、扩容、迁移。正式开始我们的研讨之旅吧 ：）&lt;/p&gt;&#xA;&lt;h2 id=&#34;赋值&#34;&gt;赋值&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; make(&lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#66d9ef&#34;&gt;int32&lt;/span&gt;]&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;函数原型&#34;&gt;函数原型&lt;/h3&gt;&#xA;&lt;p&gt;在 map 的赋值动作中，依旧是针对 32/64 位、string、pointer 类型有不同的转换处理，总的函数原型如下：&lt;/p&gt;</description>
    </item>
    <item>
      <title>深入理解 Go map：初始化和访问元素</title>
      <link>https://note.lican.site/posts/posts/go/map/2019-03-05-map-access/</link>
      <pubDate>Tue, 05 Mar 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/map/2019-03-05-map-access/</guid>
      <description>&lt;p&gt;从本文开始咱们一起探索 Go map 里面的奥妙吧，看看它的内在是怎么构成的，又分别有什么值得留意的地方？&lt;/p&gt;&#xA;&lt;p&gt;第一篇将探讨&lt;strong&gt;初始化和访问元素&lt;/strong&gt;相关板块，咱们带着疑问去学习，例如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;初始化的时候会马上分配内存吗？&lt;/li&gt;&#xA;&lt;li&gt;底层数据是如何存储的？&lt;/li&gt;&#xA;&lt;li&gt;底层是如何使用 key 去寻找数据的？&lt;/li&gt;&#xA;&lt;li&gt;底层是用什么方式解决哈希冲突的？&lt;/li&gt;&#xA;&lt;li&gt;数据类型那么多，底层又是怎么处理的呢？&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&amp;hellip;&lt;/p&gt;&#xA;&lt;h2 id=&#34;数据结构&#34;&gt;数据结构&lt;/h2&gt;&#xA;&lt;p&gt;首先我们一起看看 Go map 的基础数据结构，先有一个大致的印象&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3dLgjH.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;hmap&#34;&gt;hmap&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hmap&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;flags&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;         &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;noverflow&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;hash0&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;buckets&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldbuckets&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nevacuate&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uintptr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mapextra&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mapextra&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;overflow&lt;/span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;oldoverflow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nextOverflow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bmap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;count：map 的大小，也就是 len() 的值。代指 map 中的键值对个数&lt;/li&gt;&#xA;&lt;li&gt;flags：状态标识，主要是 goroutine 写入和扩容机制的相关状态控制。并发读写的判断条件之一就是该值&lt;/li&gt;&#xA;&lt;li&gt;B：桶，最大可容纳的元素数量，值为 &lt;strong&gt;负载因子（默认 6.5） * 2 ^ B&lt;/strong&gt;，是 2 的指数&lt;/li&gt;&#xA;&lt;li&gt;noverflow：溢出桶的数量&lt;/li&gt;&#xA;&lt;li&gt;hash0：哈希因子&lt;/li&gt;&#xA;&lt;li&gt;buckets：保存当前桶数据的指针地址（指向一段连续的内存地址，主要存储键值对数据）&lt;/li&gt;&#xA;&lt;li&gt;oldbuckets，保存旧桶的指针地址&lt;/li&gt;&#xA;&lt;li&gt;nevacuate：迁移进度&lt;/li&gt;&#xA;&lt;li&gt;extra：原有 buckets 满载后，会发生扩容动作，在 Go 的机制中使用了增量扩容，如下为细项：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;overflow&lt;/code&gt; 为 &lt;code&gt;hmap.buckets&lt;/code&gt; （当前）溢出桶的指针地址&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;oldoverflow&lt;/code&gt; 为 &lt;code&gt;hmap.oldbuckets&lt;/code&gt; （旧）溢出桶的指针地址&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;nextOverflow&lt;/code&gt; 为空闲溢出桶的指针地址&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在这里我们要注意几点，如下：&lt;/p&gt;</description>
    </item>
    <item>
      <title>for-loop 与 json.Unmarshal 性能分析概要</title>
      <link>https://note.lican.site/posts/posts/go/talk/2019-02-17-for-loop-json-unmarshal/</link>
      <pubDate>Sun, 17 Feb 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2019-02-17-for-loop-json-unmarshal/</guid>
      <description>&lt;p&gt;在项目中，常常会遇到循环交换赋值的数据处理场景，尤其是 RPC，数据交互格式要转为 Protobuf，赋值是无法避免的。一般会有如下几种做法：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;for&lt;/li&gt;&#xA;&lt;li&gt;for range&lt;/li&gt;&#xA;&lt;li&gt;json.Marshal/Unmarshal&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这时候又面临 “选择困难症”，用哪个好？又想代码量少，又担心性能有没有影响啊&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;为了弄清楚这个疑惑，接下来将分别编写三种使用场景。来简单看看它们的性能情况，看看谁更 “好”&lt;/p&gt;&#xA;&lt;h2 id=&#34;功能代码&#34;&gt;功能代码&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Person&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;name&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Age&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;age&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Avatar&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;avatar&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;type&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AgainPerson&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;name&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Age&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;age&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Avatar&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;avatar&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;type&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MAX&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;10000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;InitPerson&lt;/span&gt;() []&lt;span style=&#34;color:#a6e22e&#34;&gt;Person&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;persons&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Person&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MAX&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;persons&lt;/span&gt; = append(&lt;span style=&#34;color:#a6e22e&#34;&gt;persons&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Person&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Name&lt;/span&gt;:   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Age&lt;/span&gt;:    &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Avatar&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://github.com/EDDYCJY&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt;:   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Person&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;persons&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ForStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Person&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ForRangeStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Person&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;JsonToStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;againPerson&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;AgainPerson&lt;/span&gt;) ([]&lt;span style=&#34;color:#a6e22e&#34;&gt;AgainPerson&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unmarshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;againPerson&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;againPerson&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;JsonIteratorToStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;againPerson&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;AgainPerson&lt;/span&gt;) ([]&lt;span style=&#34;color:#a6e22e&#34;&gt;AgainPerson&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;jsonIter&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;jsoniter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ConfigCompatibleWithStandardLibrary&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;jsonIter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unmarshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;againPerson&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;againPerson&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;测试代码&#34;&gt;测试代码&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BenchmarkForStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;person&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;InitPerson&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; len(&lt;span style=&#34;color:#a6e22e&#34;&gt;person&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ResetTimer&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;N&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ForStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;person&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BenchmarkForRangeStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;person&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;InitPerson&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ResetTimer&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;N&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ForRangeStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;person&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BenchmarkJsonToStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;person&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;InitPerson&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;againPersons&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;AgainPerson&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Marshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;person&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatalf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;json.Marshal err: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ResetTimer&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;N&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;JsonToStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;againPersons&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BenchmarkJsonIteratorToStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;person&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;InitPerson&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;againPersons&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;AgainPerson&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Marshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;person&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatalf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;json.Marshal err: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ResetTimer&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;N&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;JsonIteratorToStruct&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;againPersons&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;测试结果&#34;&gt;测试结果&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;BenchmarkForStruct-4              &#x9;  500000&#x9;      3289 ns/op&#x9;       0 B/op&#x9;       0 allocs/op&#xA;BenchmarkForRangeStruct-4         &#x9;  200000&#x9;      9178 ns/op&#x9;       0 B/op&#x9;       0 allocs/op&#xA;BenchmarkJsonToStruct-4           &#x9;     100&#x9;  19173117 ns/op&#x9; 2618509 B/op&#x9;   40036 allocs/op&#xA;BenchmarkJsonIteratorToStruct-4   &#x9;     300&#x9;   4116491 ns/op&#x9; 3694017 B/op&#x9;   30047 allocs/op&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从测试结果来看，性能排名为：for &amp;lt; for range &amp;lt; json-iterator &amp;lt; encoding/json。接下来我们看看是什么原因导致了这样子的排名？&lt;/p&gt;</description>
    </item>
    <item>
      <title>来，控制一下 goroutine 的并发数量</title>
      <link>https://note.lican.site/posts/posts/go/talk/2019-01-20-control-goroutine/</link>
      <pubDate>Sun, 20 Jan 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2019-01-20-control-goroutine/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3wnOsJ.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题&#34;&gt;问题&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;userCount&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;math&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MaxInt64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;userCount&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;    &lt;span style=&#34;color:#75715e&#34;&gt;// 做一些各种各样的业务逻辑处理&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;go func: %d\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Second&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这里，假设 &lt;code&gt;userCount&lt;/code&gt; 是一个外部传入的参数（不可预测，有可能值非常大），有人会全部丢进去循环。想着全部都并发 goroutine 去同时做某一件事。觉得这样子会效率会更高，对不对！&lt;/p&gt;&#xA;&lt;p&gt;那么，你觉得这里有没有什么问题？&lt;/p&gt;&#xA;&lt;h2 id=&#34;噩梦般的开始&#34;&gt;噩梦般的开始&lt;/h2&gt;&#xA;&lt;p&gt;当然，在&lt;strong&gt;特定场景下&lt;/strong&gt;，问题可大了。因为在本文被丢进去同时并发的可是一个极端值。我们可以一起观察下图的指标分析，看看情况有多 “崩溃”。下图是上述代码的表现：&lt;/p&gt;&#xA;&lt;h3 id=&#34;输出结果&#34;&gt;输出结果&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;...&#xA;go func: 5839&#xA;go func: 5840&#xA;go func: 5841&#xA;go func: 5842&#xA;go func: 5915&#xA;go func: 5524&#xA;go func: 5916&#xA;go func: 8209&#xA;go func: 8264&#xA;signal: killed&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你自己执行过代码，在 “输出结果” 上你会遇到如下问题：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;系统资源占用率不断上涨&lt;/li&gt;&#xA;&lt;li&gt;输出一定数量后：控制台就不再刷新输出最新的值了&lt;/li&gt;&#xA;&lt;li&gt;信号量：signal: killed&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;系统负载&#34;&gt;系统负载&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3wnxd1.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;cpu&#34;&gt;CPU&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3wuKW8.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go Slice 最大容量大小是怎么来的</title>
      <link>https://note.lican.site/posts/posts/go/slice/2019-01-06-why-slice-max/</link>
      <pubDate>Sun, 06 Jan 2019 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/slice/2019-01-06-why-slice-max/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3wnHRU.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;在《深入理解 Go Slice》中，我们提到了 “根据其类型大小去获取能够申请的最大容量大小” 的处理逻辑。今天我们将更深入地去探究一下，底层到底做了什么东西，涉及什么知识点？&lt;/p&gt;&#xA;&lt;p&gt;Go Slice 对应代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;makeslice&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;et&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_type&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;len&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;cap&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;maxElements&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maxSliceCap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;et&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;len&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; uintptr(&lt;span style=&#34;color:#a6e22e&#34;&gt;len&lt;/span&gt;) &amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maxElements&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cap&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;len&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; uintptr(&lt;span style=&#34;color:#a6e22e&#34;&gt;cap&lt;/span&gt;) &amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maxElements&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mallocgc&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;et&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;uintptr(&lt;span style=&#34;color:#a6e22e&#34;&gt;cap&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;et&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;len&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;cap&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;根据想要追寻的逻辑，定位到了 &lt;code&gt;maxSliceCap&lt;/code&gt; 方法，它会根据&lt;strong&gt;当前类型的大小获取到了所允许的最大容量大小&lt;/strong&gt;来进行阈值判断，也就是安全检查。这是浅层的了解，我们继续追下去看看还做了些什么？&lt;/p&gt;&#xA;&lt;h2 id=&#34;maxslicecap&#34;&gt;maxSliceCap&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maxSliceCap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;elemsize&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uintptr&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;uintptr&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;elemsize&lt;/span&gt; &amp;lt; uintptr(len(&lt;span style=&#34;color:#a6e22e&#34;&gt;maxElems&lt;/span&gt;)) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maxElems&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;elemsize&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;elemsize&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;maxelems&#34;&gt;maxElems&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maxElems&lt;/span&gt; = [&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;]&lt;span style=&#34;color:#66d9ef&#34;&gt;uintptr&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;^uintptr(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;14&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;17&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;18&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;19&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;21&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;22&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;23&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;25&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;26&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;27&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;28&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;29&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;31&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;maxAlloc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;maxElems&lt;/code&gt; 是包含一些预定义的切片最大容量值的查找表，索引是切片元素的类型大小。而值看起来 “奇奇怪怪” 不大眼熟，都是些什么呢。主要是以下三个核心点：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;^uintptr(0)&lt;/li&gt;&#xA;&lt;li&gt;maxAlloc&lt;/li&gt;&#xA;&lt;li&gt;maxAlloc / typeSize&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;uintptr0&#34;&gt;^uintptr(0)&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;uintptr: %v\n&amp;#34;&lt;/span&gt;, uintptr(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;^uintptr: %v\n&amp;#34;&lt;/span&gt;, ^uintptr(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;</description>
    </item>
    <item>
      <title>在 Go 中恰到好处的内存对齐</title>
      <link>https://note.lican.site/posts/posts/go/talk/2018-12-26-go-memory-align/</link>
      <pubDate>Wed, 26 Dec 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2018-12-26-go-memory-align/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3wuT0A.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题&#34;&gt;问题&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Part1&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int32&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在开始之前，希望你计算一下 &lt;code&gt;Part1&lt;/code&gt; 共占用的大小是多少呢？&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bool size: %d\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sizeof&lt;/span&gt;(bool(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;)))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;int32 size: %d\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sizeof&lt;/span&gt;(int32(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;int8 size: %d\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sizeof&lt;/span&gt;(int8(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;int64 size: %d\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sizeof&lt;/span&gt;(int64(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;byte size: %d\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sizeof&lt;/span&gt;(byte(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;string size: %d\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sizeof&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDDYCJY&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;bool size: 1&#xA;int32 size: 4&#xA;int8 size: 1&#xA;int64 size: 8&#xA;byte size: 1&#xA;string size: 16&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这么一算，&lt;code&gt;Part1&lt;/code&gt; 这一个结构体的占用内存大小为 1+4+1+8+1 = 15 个字节。相信有的小伙伴是这么算的，看上去也没什么毛病&lt;/p&gt;&#xA;&lt;p&gt;真实情况是怎么样的呢？我们实际调用看看，如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Part1&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int32&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;part1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Part1&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;part1 size: %d, align: %d\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sizeof&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;part1&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;unsafe&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Alignof&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;part1&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;</description>
    </item>
    <item>
      <title>有点不安全却又一亮的 Go unsafe.Pointer</title>
      <link>https://note.lican.site/posts/posts/go/pkg/2018-12-15-unsafe/</link>
      <pubDate>Sat, 15 Dec 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/pkg/2018-12-15-unsafe/</guid>
      <description>&lt;p&gt;在上一篇文章《深入理解 Go Slice》中，大家会发现其底层数据结构使用了 &lt;code&gt;unsafe.Pointer&lt;/code&gt;。因此想着再介绍一下其关联知识&lt;/p&gt;&#xA;&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;在大家学习 Go 的时候，肯定都学过 “Go 的指针是不支持指针运算和转换” 这个知识点。为什么呢？&lt;/p&gt;&#xA;&lt;p&gt;首先，Go 是一门静态语言，所有的变量都必须为标量类型。不同的类型不能够进行赋值、计算等跨类型的操作。那么指针也对应着相对的类型，也在 Compile 的静态类型检查的范围内。同时静态语言，也称为强类型。也就是一旦定义了，就不能再改变它&lt;/p&gt;&#xA;&lt;h2 id=&#34;错误示例&#34;&gt;错误示例&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main(){&#xA;&#x9;num := 5&#xA;&#x9;numPointer := &amp;amp;num&#xA;&#xA;&#x9;flnum := (*float32)(numPointer)&#xA;&#x9;fmt.Println(flnum)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# command-line-arguments&#xA;...: cannot convert numPointer (type *int) to type *float32&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在示例中，我们创建了一个 &lt;code&gt;num&lt;/code&gt; 变量，值为 5，类型为 &lt;code&gt;int&lt;/code&gt;。取了其对于的指针地址后，试图强制转换为 &lt;code&gt;*float32&lt;/code&gt;，结果失败&amp;hellip;&lt;/p&gt;&#xA;&lt;h2 id=&#34;unsafe&#34;&gt;unsafe&lt;/h2&gt;&#xA;&lt;p&gt;针对刚刚的 “错误示例”，我们可以采用今天的男主角 &lt;code&gt;unsafe&lt;/code&gt; 标准库来解决。它是一个神奇的包，在官方的诠释中，有如下概述：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;围绕 Go 程序内存安全及类型的操作&lt;/li&gt;&#xA;&lt;li&gt;很可能会是不可移植的&lt;/li&gt;&#xA;&lt;li&gt;不受 Go 1 兼容性指南的保护&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;简单来讲就是，不怎么推荐你使用。因为它是 unsafe（不安全的），但是在特殊的场景下，使用了它。可以打破 Go 的类型和内存安全机制，让你获得眼前一亮的惊喜效果 😄&lt;/p&gt;&#xA;&lt;h3 id=&#34;pointer&#34;&gt;Pointer&lt;/h3&gt;&#xA;&lt;p&gt;为了解决这个问题，需要用到 &lt;code&gt;unsafe.Pointer&lt;/code&gt;。它表示任意类型且可寻址的指针值，可以在不同的指针类型之间进行转换（类似 C 语言的 void * 的用途）&lt;/p&gt;</description>
    </item>
    <item>
      <title>深入理解 Go Slice</title>
      <link>https://note.lican.site/posts/posts/go/slice/2018-12-11-slice/</link>
      <pubDate>Tue, 11 Dec 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/slice/2018-12-11-slice/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3dXSeA.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;是什么&#34;&gt;是什么&lt;/h2&gt;&#xA;&lt;p&gt;在 Go 中，Slice（切片）是抽象在 Array（数组）之上的特殊类型。为了更好地了解 Slice，第一步需要先对 Array 进行理解。深刻了解 Slice 与 Array 之间的区别后，就能更好的对其底层一番摸索 😄&lt;/p&gt;&#xA;&lt;h2 id=&#34;用法&#34;&gt;用法&lt;/h2&gt;&#xA;&lt;h3 id=&#34;array&#34;&gt;Array&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nums&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;]&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nums&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] = &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;nums&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nums: %v\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;nums&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;n: %d\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们可得知在 Go 中，数组类型需要指定长度和元素类型。在上述代码中，可得知 &lt;code&gt;[3]int{}&lt;/code&gt; 表示 3 个整数的数组，并进行了初始化。底层数据存储为一段连续的内存空间，通过固定的索引值（下标）进行检索&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://s2.ax1x.com/2020/02/27/3dXASS.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;数组在声明后，其元素的初始值（也就是零值）为 0。并且该变量可以直接使用，不需要特殊操作&lt;/p&gt;&#xA;&lt;p&gt;同时数组的长度是固定的，它的长度是类型的一部分，因此 &lt;code&gt;[3]int&lt;/code&gt; 和 &lt;code&gt;[4]int&lt;/code&gt; 在类型上是不同的，不能称为 “一个东西”&lt;/p&gt;&#xA;&lt;h4 id=&#34;输出结果&#34;&gt;输出结果&lt;/h4&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;nums: [1 0 0]&#xA;n: 2&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;slice&#34;&gt;Slice&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nums&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;]&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nums&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] = &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;dnums&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;nums&lt;/span&gt;[:]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dnums: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dnums&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Slice 是对 Array 的抽象，类型为 &lt;code&gt;[]T&lt;/code&gt;。在上述代码中，&lt;code&gt;dnums&lt;/code&gt; 变量通过 &lt;code&gt;nums[:]&lt;/code&gt; 进行赋值。需要注意的是，Slice 和 Array 不一样，它不需要指定长度。也更加的灵活，能够自动扩容&lt;/p&gt;</description>
    </item>
    <item>
      <title>fmt 标准库 --- Print* 是怎么样输出的？</title>
      <link>https://note.lican.site/posts/posts/go/pkg/2018-12-04-fmt/</link>
      <pubDate>Tue, 04 Dec 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/pkg/2018-12-04-fmt/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main&#xA;&#xA;import (&#xA;&#x9;&amp;#34;fmt&amp;#34;&#xA;)&#xA;&#xA;func main() {&#xA;&#x9;fmt.Println(&amp;#34;Hello World!&amp;#34;)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;标准开场见多了，那内部标准库又是怎么输出这段英文的呢？今天一起来围观下源码吧 🤭&lt;/p&gt;&#xA;&lt;h2 id=&#34;原型&#34;&gt;原型&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Print(a ...interface{}) (n int, err error) {&#xA;&#x9;return Fprint(os.Stdout, a...)&#xA;}&#xA;&#xA;func Println(a ...interface{}) (n int, err error) {&#xA;&#x9;return Fprintln(os.Stdout, a...)&#xA;}&#xA;&#xA;func Printf(format string, a ...interface{}) (n int, err error) {&#xA;&#x9;return Fprintf(os.Stdout, format, a...)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;Print：使用默认格式说明符打印格式并写入标准输出。当两者都不是字符串时，在操作数之间添加空格&lt;/li&gt;&#xA;&lt;li&gt;Println：同上，不同的地方是始终在操作数之间添加空格，并附加换行符&lt;/li&gt;&#xA;&lt;li&gt;Printf：根据格式说明符进行格式化并写入标准输出&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;以上三类就是最常见的格式化 I/O 的方法，我们将基于此去进行拆解描述&lt;/p&gt;&#xA;&lt;h2 id=&#34;执行流程&#34;&gt;执行流程&lt;/h2&gt;&#xA;&lt;h3 id=&#34;案例一print&#34;&gt;案例一：Print&lt;/h3&gt;&#xA;&lt;p&gt;在这里我们使用 &lt;code&gt;Print&lt;/code&gt; 方法做一个分析，便于后面的加深理解 😄&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Print(a ...interface{}) (n int, err error) {&#xA;&#x9;return Fprint(os.Stdout, a...)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Print&lt;/code&gt; 使用默认格式说明符打印格式并写入标准输出。另外当两者都为非空字符串时将插入一个空格&lt;/p&gt;</description>
    </item>
    <item>
      <title>使用 Gomock 进行单元测试</title>
      <link>https://note.lican.site/posts/posts/go/talk/2018-11-25-gomock/</link>
      <pubDate>Sun, 25 Nov 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2018-11-25-gomock/</guid>
      <description>&lt;p&gt;在实际项目中，需要进行单元测试的时候。却往往发现有一大堆依赖项。这时候就是 &lt;a href=&#34;https://github.com/golang/mock&#34;&gt;Gomock&lt;/a&gt; 大显身手的时候了&lt;/p&gt;&#xA;&lt;p&gt;Gomock 是 Go 语言的一个 mock 框架，官方的那种 🤪&lt;/p&gt;&#xA;&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go get -u github.com/golang/mock/gomock&#xA;$ go install github.com/golang/mock/mockgen&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;第一步：我们将安装 gomock 第三方库和 mock 代码的生成工具 mockgen。而后者可以大大的节省我们的工作量。只需要了解其使用方式就可以&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;第二步：输入 &lt;code&gt;mockgen&lt;/code&gt; 验证代码生成工具是否安装正确。若无法正常响应，请检查 &lt;code&gt;bin&lt;/code&gt; 目录下是否包含该二进制文件&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;用法&#34;&gt;用法&lt;/h3&gt;&#xA;&lt;p&gt;在 &lt;code&gt;mockgen&lt;/code&gt; 命令中，支持两种生成模式：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;source：从源文件生成 mock 接口（通过 -source 启用）&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mockgen -source=foo.go [other options]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;reflect：通过使用反射程序来生成 mock 接口。它通过传递两个非标志参数来启用：导入路径和逗号分隔的接口列表&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mockgen database/sql/driver Conn,Driver&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从本质上来讲，两种方式生成的 mock 代码并没有什么区别。因此选择合适的就可以了&lt;/p&gt;&#xA;&lt;h2 id=&#34;写测试用例&#34;&gt;写测试用例&lt;/h2&gt;&#xA;&lt;p&gt;在本文将模拟一个简单 Demo 来编写测试用例，熟悉整体的测试流程&lt;/p&gt;&#xA;&lt;h3 id=&#34;步骤&#34;&gt;步骤&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;想清楚整体逻辑&lt;/li&gt;&#xA;&lt;li&gt;定义想要（模拟）依赖项的 interface（接口）&lt;/li&gt;&#xA;&lt;li&gt;使用 &lt;code&gt;mockgen&lt;/code&gt; 命令对所需 mock 的 interface 生成 mock 文件&lt;/li&gt;&#xA;&lt;li&gt;编写单元测试的逻辑，在测试中使用 mock&lt;/li&gt;&#xA;&lt;li&gt;进行单元测试的验证&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;目录&#34;&gt;目录&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;├── mock&#xA;├── person&#xA;│   └── male.go&#xA;└── user&#xA;    ├── user.go&#xA;    └── user_test.go&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;编写&#34;&gt;编写&lt;/h3&gt;&#xA;&lt;h4 id=&#34;interface-方法&#34;&gt;interface 方法&lt;/h4&gt;&#xA;&lt;p&gt;打开 person/male.go 文件，写入以下内容：&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载十」分布式链路追踪 gRPC &#43; Opentracing &#43; Zipkin</title>
      <link>https://note.lican.site/posts/posts/go/grpc/2018-10-20-zipkin/</link>
      <pubDate>Sat, 20 Oct 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc/2018-10-20-zipkin/</guid>
      <description>&lt;p&gt;在实际应用中，你做了那么多 Server 端，写了 N 个 RPC 方法。想看看方法的指标，却无处下手？&lt;/p&gt;&#xA;&lt;p&gt;本文将通过 gRPC + Opentracing + Zipkin 搭建一个&lt;strong&gt;分布式链路追踪系统&lt;/strong&gt;来实现查看整个系统的链路、性能等指标。&lt;/p&gt;&#xA;&lt;h2 id=&#34;opentracing&#34;&gt;Opentracing&lt;/h2&gt;&#xA;&lt;h3 id=&#34;是什么&#34;&gt;是什么&lt;/h3&gt;&#xA;&lt;p&gt;OpenTracing 通过提供平台无关、厂商无关的API，使得开发人员能够方便的添加（或更换）追踪系统的实现&lt;/p&gt;&#xA;&lt;p&gt;不过 OpenTracing 并不是标准。因为 CNCF 不是官方标准机构，但是它的目标是致力为分布式追踪创建更标准的 API 和工具&lt;/p&gt;&#xA;&lt;h3 id=&#34;名词解释&#34;&gt;名词解释&lt;/h3&gt;&#xA;&lt;h4 id=&#34;trace&#34;&gt;Trace&lt;/h4&gt;&#xA;&lt;p&gt;一个 trace 代表了一个事务或者流程在（分布式）系统中的执行过程&lt;/p&gt;&#xA;&lt;h4 id=&#34;span&#34;&gt;Span&lt;/h4&gt;&#xA;&lt;p&gt;一个 span 代表在分布式系统中完成的单个工作单元。也包含其他 span 的 “引用”，这允许将多个 spans 组合成一个完整的 Trace&lt;/p&gt;&#xA;&lt;p&gt;每个 span 根据 OpenTracing 规范封装以下内容：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;操作名称&lt;/li&gt;&#xA;&lt;li&gt;开始时间和结束时间&lt;/li&gt;&#xA;&lt;li&gt;key:value span Tags&lt;/li&gt;&#xA;&lt;li&gt;key:value span Logs&lt;/li&gt;&#xA;&lt;li&gt;SpanContext&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;tags&#34;&gt;Tags&lt;/h4&gt;&#xA;&lt;p&gt;Span tags（跨度标签）可以理解为用户自定义的 Span 注释。便于查询、过滤和理解跟踪数据&lt;/p&gt;&#xA;&lt;h4 id=&#34;logs&#34;&gt;Logs&lt;/h4&gt;&#xA;&lt;p&gt;Span logs（跨度日志）可以记录 Span 内特定时间或事件的日志信息。主要用于捕获特定 Span 的日志信息以及应用程序本身的其他调试或信息输出&lt;/p&gt;&#xA;&lt;h4 id=&#34;spancontext&#34;&gt;SpanContext&lt;/h4&gt;&#xA;&lt;p&gt;SpanContext 代表跨越进程边界，传递到子级 Span 的状态。常在追踪示意图中创建上下文时使用&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载九」gRPC Deadlines</title>
      <link>https://note.lican.site/posts/posts/go/grpc/2018-10-16-deadlines/</link>
      <pubDate>Tue, 16 Oct 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc/2018-10-16-deadlines/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;在前面的章节中，已经介绍了 gRPC 的基本用法。那你想想，让它这么裸跑真的没问题吗？&lt;/p&gt;&#xA;&lt;p&gt;那么，肯定是有问题了。今天将介绍 gRPC Deadlines 的用法，这一个必备技巧。内容也比较简单&lt;/p&gt;&#xA;&lt;h2 id=&#34;deadlines&#34;&gt;Deadlines&lt;/h2&gt;&#xA;&lt;p&gt;Deadlines 意指截止时间，在 gRPC 中强调 TL;DR（Too long, Don&amp;rsquo;t read）并建议&lt;strong&gt;始终设定截止日期&lt;/strong&gt;，为什么呢？&lt;/p&gt;&#xA;&lt;h3 id=&#34;为什么要设置&#34;&gt;为什么要设置&lt;/h3&gt;&#xA;&lt;p&gt;当未设置 Deadlines 时，将采用默认的 DEADLINE_EXCEEDED（这个时间非常大）&lt;/p&gt;&#xA;&lt;p&gt;如果产生了阻塞等待，就会造成大量正在进行的请求都会被保留，并且所有请求都有可能达到最大超时&lt;/p&gt;&#xA;&lt;p&gt;这会使服务面临资源耗尽的风险，例如内存，这会增加服务的延迟，或者在最坏的情况下可能导致整个进程崩溃&lt;/p&gt;&#xA;&lt;h2 id=&#34;grpc&#34;&gt;gRPC&lt;/h2&gt;&#xA;&lt;h3 id=&#34;client&#34;&gt;Client&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {&#xA;    ...&#xA;&#x9;ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Duration(5 * time.Second)))&#xA;&#x9;defer cancel()&#xA;&#xA;&#x9;client := pb.NewSearchServiceClient(conn)&#xA;&#x9;resp, err := client.Search(ctx, &amp;amp;pb.SearchRequest{&#xA;&#x9;&#x9;Request: &amp;#34;gRPC&amp;#34;,&#xA;&#x9;})&#xA;&#x9;if err != nil {&#xA;&#x9;&#x9;statusErr, ok := status.FromError(err)&#xA;&#x9;&#x9;if ok {&#xA;&#x9;&#x9;&#x9;if statusErr.Code() == codes.DeadlineExceeded {&#xA;&#x9;&#x9;&#x9;&#x9;log.Fatalln(&amp;#34;client.Search err: deadline&amp;#34;)&#xA;&#x9;&#x9;&#x9;}&#xA;&#x9;&#x9;}&#xA;&#xA;&#x9;&#x9;log.Fatalf(&amp;#34;client.Search err: %v&amp;#34;, err)&#xA;&#x9;}&#xA;&#xA;&#x9;log.Printf(&amp;#34;resp: %s&amp;#34;, resp.GetResponse())&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;context.WithDeadline：会返回最终上下文截止时间。第一个形参为父上下文，第二个形参为调整的截止时间。若父级时间早于子级时间，则以父级时间为准，否则以子级时间为最终截止时间&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {&#xA;&#x9;if cur, ok := parent.Deadline(); ok &amp;amp;&amp;amp; cur.Before(d) {&#xA;&#x9;&#x9;// The current deadline is already sooner than the new one.&#xA;&#x9;&#x9;return WithCancel(parent)&#xA;&#x9;}&#xA;&#x9;c := &amp;amp;timerCtx{&#xA;&#x9;&#x9;cancelCtx: newCancelCtx(parent),&#xA;&#x9;&#x9;deadline:  d,&#xA;&#x9;}&#xA;&#x9;propagateCancel(parent, c)&#xA;&#x9;dur := time.Until(d)&#xA;&#x9;if dur &amp;lt;= 0 {&#xA;&#x9;&#x9;c.cancel(true, DeadlineExceeded) // deadline has already passed&#xA;&#x9;&#x9;return c, func() { c.cancel(true, Canceled) }&#xA;&#x9;}&#xA;&#x9;c.mu.Lock()&#xA;&#x9;defer c.mu.Unlock()&#xA;&#x9;if c.err == nil {&#xA;&#x9;&#x9;c.timer = time.AfterFunc(dur, func() {&#xA;&#x9;&#x9;&#x9;c.cancel(true, DeadlineExceeded)&#xA;&#x9;&#x9;})&#xA;&#x9;}&#xA;&#x9;return c, func() { c.cancel(true, Canceled) }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;context.WithTimeout：很常见的另外一个方法，是便捷操作。实际上是对于 WithDeadline 的封装&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {&#xA;&#x9;return WithDeadline(parent, time.Now().Add(timeout))&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;status.FromError：返回 GRPCStatus 的具体错误码，若为非法，则直接返回 &lt;code&gt;codes.Unknown&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;server&#34;&gt;Server&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type SearchService struct{}&#xA;&#xA;func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) {&#xA;&#x9;for i := 0; i &amp;lt; 5; i++  {&#xA;&#x9;&#x9;if ctx.Err() == context.Canceled {&#xA;&#x9;&#x9;&#x9;return nil, status.Errorf(codes.Canceled, &amp;#34;SearchService.Search canceled&amp;#34;)&#xA;&#x9;&#x9;}&#xA;&#xA;&#x9;&#x9;time.Sleep(1 * time.Second)&#xA;&#x9;}&#xA;&#xA;&#x9;return &amp;amp;pb.SearchResponse{Response: r.GetRequest() + &amp;#34; Server&amp;#34;}, nil&#xA;}&#xA;&#xA;func main() {&#xA;&#x9;...&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;而在 Server 端，由于 Client 已经设置了截止时间。Server 势必要去检测它&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载八」对 RPC 方法做自定义认证</title>
      <link>https://note.lican.site/posts/posts/go/grpc/2018-10-14-per-rpc-credentials/</link>
      <pubDate>Sun, 14 Oct 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc/2018-10-14-per-rpc-credentials/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;在前面的章节中，我们介绍了两种（证书算一种）可全局认证的方法：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/EDDYCJY/blog/blob/master/grpc/grpc-tls.md&#34;&gt;TLS 证书认证&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/EDDYCJY/blog/blob/master/grpc/ca-tls.md&#34;&gt;基于 CA 的 TLS 证书认证&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/EDDYCJY/blog/blob/master/grpc/interceptor.md&#34;&gt;Unary and Stream interceptor&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;而在实际需求中，常常会对某些模块的 RPC 方法做特殊认证或校验。今天将会讲解、实现这块的功能点&lt;/p&gt;&#xA;&lt;h2 id=&#34;课前知识&#34;&gt;课前知识&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type PerRPCCredentials interface {&#xA;    GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)&#xA;    RequireTransportSecurity() bool&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在 gRPC 中默认定义了 PerRPCCredentials，它就是本章节的主角，是 gRPC 默认提供用于自定义认证的接口，它的作用是将所需的安全认证信息添加到每个 RPC 方法的上下文中。其包含 2 个方法：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;GetRequestMetadata：获取当前请求认证所需的元数据（metadata）&lt;/li&gt;&#xA;&lt;li&gt;RequireTransportSecurity：是否需要基于 TLS 认证进行安全传输&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;目录结构&#34;&gt;目录结构&lt;/h2&gt;&#xA;&lt;p&gt;新建 simple_token_server/server.go 和 simple_token_client/client.go，目录结构如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go-grpc-example&#xA;├── client&#xA;│   ├── simple_client&#xA;│   ├── simple_http_client&#xA;│   ├── simple_token_client&#xA;│   └── stream_client&#xA;├── conf&#xA;├── pkg&#xA;├── proto&#xA;├── server&#xA;│   ├── simple_http_server&#xA;│   ├── simple_server&#xA;│   ├── simple_token_server&#xA;│   └── stream_server&#xA;└── vendor&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;grpc&#34;&gt;gRPC&lt;/h2&gt;&#xA;&lt;h3 id=&#34;client&#34;&gt;Client&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main&#xA;&#xA;import (&#xA;&#x9;&amp;#34;context&amp;#34;&#xA;&#x9;&amp;#34;log&amp;#34;&#xA;&#xA;&#x9;&amp;#34;google.golang.org/grpc&amp;#34;&#xA;&#xA;&#x9;&amp;#34;github.com/EDDYCJY/go-grpc-example/pkg/gtls&amp;#34;&#xA;&#x9;pb &amp;#34;github.com/EDDYCJY/go-grpc-example/proto&amp;#34;&#xA;)&#xA;&#xA;const PORT = &amp;#34;9004&amp;#34;&#xA;&#xA;type Auth struct {&#xA;&#x9;AppKey    string&#xA;&#x9;AppSecret string&#xA;}&#xA;&#xA;func (a *Auth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {&#xA;&#x9;return map[string]string{&amp;#34;app_key&amp;#34;: a.AppKey, &amp;#34;app_secret&amp;#34;: a.AppSecret}, nil&#xA;}&#xA;&#xA;func (a *Auth) RequireTransportSecurity() bool {&#xA;&#x9;return true&#xA;}&#xA;&#xA;func main() {&#xA;&#x9;tlsClient := gtls.Client{&#xA;&#x9;&#x9;ServerName: &amp;#34;go-grpc-example&amp;#34;,&#xA;&#x9;&#x9;CertFile:   &amp;#34;../../conf/server/server.pem&amp;#34;,&#xA;&#x9;}&#xA;&#x9;c, err := tlsClient.GetTLSCredentials()&#xA;&#x9;if err != nil {&#xA;&#x9;&#x9;log.Fatalf(&amp;#34;tlsClient.GetTLSCredentials err: %v&amp;#34;, err)&#xA;&#x9;}&#xA;&#xA;&#x9;auth := Auth{&#xA;&#x9;&#x9;AppKey:    &amp;#34;eddycjy&amp;#34;,&#xA;&#x9;&#x9;AppSecret: &amp;#34;20181005&amp;#34;,&#xA;&#x9;}&#xA;&#x9;conn, err := grpc.Dial(&amp;#34;:&amp;#34;+PORT, grpc.WithTransportCredentials(c), grpc.WithPerRPCCredentials(&amp;amp;auth))&#xA;&#x9;...&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在 Client 端，重点实现 &lt;code&gt;type PerRPCCredentials interface&lt;/code&gt; 所需的方法，关注两点即可：&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载七」让你的服务同时提供 HTTP 接口</title>
      <link>https://note.lican.site/posts/posts/go/grpc/2018-10-12-grpc-http/</link>
      <pubDate>Fri, 12 Oct 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc/2018-10-12-grpc-http/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;接口需要提供给其他业务组访问，但是 RPC 协议不同无法内调，对方问能否走 HTTP 接口，怎么办？&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;微信（公众号、小程序）等第三方回调接口只支持 HTTP 接口，怎么办&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;我相信你在实际工作中都会遇到如上问题，在 gRPC 中都是有解决方案的，本章节将会进行介绍 🤔&lt;/p&gt;&#xA;&lt;h2 id=&#34;为什么可以同时提供-http-接口&#34;&gt;为什么可以同时提供 HTTP 接口&lt;/h2&gt;&#xA;&lt;p&gt;关键一点，gRPC 的协议是基于 HTTP/2 的，因此应用程序能够在单个 TCP 端口上提供 HTTP/1.1 和 gRPC 接口服务（两种不同的流量）&lt;/p&gt;&#xA;&lt;h2 id=&#34;怎么同时提供-http-接口&#34;&gt;怎么同时提供 HTTP 接口&lt;/h2&gt;&#xA;&lt;h3 id=&#34;检测协议&#34;&gt;检测协议&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if r.ProtoMajor == 2 &amp;amp;&amp;amp; strings.Contains(r.Header.Get(&amp;#34;Content-Type&amp;#34;), &amp;#34;application/grpc&amp;#34;) {&#xA;    server.ServeHTTP(w, r)&#xA;} else {&#xA;    mux.ServeHTTP(w, r)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;流程&#34;&gt;流程&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;检测请求协议是否为 HTTP/2&lt;/li&gt;&#xA;&lt;li&gt;判断 Content-Type 是否为 application/grpc（gRPC 的默认标识位）&lt;/li&gt;&#xA;&lt;li&gt;根据协议的不同转发到不同的服务处理&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;grpc&#34;&gt;gRPC&lt;/h2&gt;&#xA;&lt;h3 id=&#34;tls&#34;&gt;TLS&lt;/h3&gt;&#xA;&lt;p&gt;在前面的章节，为了便于展示因此没有简单封装&lt;/p&gt;&#xA;&lt;p&gt;在本节需复用代码，重新封装了，可详见：&lt;a href=&#34;https://github.com/EDDYCJY/go-grpc-example/tree/master/pkg/gtls&#34;&gt;go-grpc-example&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;目录结构&#34;&gt;目录结构&lt;/h3&gt;&#xA;&lt;p&gt;新建 simple_http_client、simple_http_server 目录，目录结构如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go-grpc-example&#xA;├── client&#xA;│   ├── simple_client&#xA;│   ├── simple_http_client&#xA;│   └── stream_client&#xA;├── conf&#xA;├── pkg&#xA;│   └── gtls&#xA;├── proto&#xA;├── server&#xA;│   ├── simple_http_server&#xA;│   ├── simple_server&#xA;│   └── stream_server&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;server&#34;&gt;Server&lt;/h3&gt;&#xA;&lt;p&gt;在 simple_http_server 目录下新建 server.go，写入文件内容：&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载六」Unary and Stream interceptor</title>
      <link>https://note.lican.site/posts/posts/go/grpc/2018-10-10-interceptor/</link>
      <pubDate>Wed, 10 Oct 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc/2018-10-10-interceptor/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;我想在每个 RPC 方法的前或后做某些事情，怎么做？&lt;/p&gt;&#xA;&lt;p&gt;本章节将要介绍的拦截器（interceptor），就能帮你在合适的地方实现这些功能。&lt;/p&gt;&#xA;&lt;h2 id=&#34;有几种方法&#34;&gt;有几种方法&lt;/h2&gt;&#xA;&lt;p&gt;在 gRPC 中，大类可分为两种 RPC 方法，与拦截器的对应关系是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;普通方法：一元拦截器（grpc.UnaryInterceptor）&lt;/li&gt;&#xA;&lt;li&gt;流方法：流拦截器（grpc.StreamInterceptor）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;看一看&#34;&gt;看一看&lt;/h2&gt;&#xA;&lt;h3 id=&#34;grpcunaryinterceptor&#34;&gt;grpc.UnaryInterceptor&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func UnaryInterceptor(i UnaryServerInterceptor) ServerOption {&#xA;&#x9;return func(o *options) {&#xA;&#x9;&#x9;if o.unaryInt != nil {&#xA;&#x9;&#x9;&#x9;panic(&amp;#34;The unary server interceptor was already set and may not be reset.&amp;#34;)&#xA;&#x9;&#x9;}&#xA;&#x9;&#x9;o.unaryInt = i&#xA;&#x9;}&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;函数原型：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;通过查看源码可得知，要完成一个拦截器需要实现 &lt;code&gt;UnaryServerInterceptor&lt;/code&gt; 方法。形参如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ctx context.Context：请求上下文&lt;/li&gt;&#xA;&lt;li&gt;req interface{}：RPC 方法的请求参数&lt;/li&gt;&#xA;&lt;li&gt;info *UnaryServerInfo：RPC 方法的所有信息&lt;/li&gt;&#xA;&lt;li&gt;handler UnaryHandler：RPC 方法本身&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;grpcstreaminterceptor&#34;&gt;grpc.StreamInterceptor&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func StreamInterceptor(i StreamServerInterceptor) ServerOption&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;函数原型：&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载五」基于 CA 的 TLS 证书认证</title>
      <link>https://note.lican.site/posts/posts/go/grpc/2018-10-08-ca-tls/</link>
      <pubDate>Mon, 08 Oct 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc/2018-10-08-ca-tls/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;在上一章节中，我们提出了一个问题。就是如何保证证书的可靠性和有效性？你如何确定你 Server、Client 的证书是对的呢？&lt;/p&gt;&#xA;&lt;h2 id=&#34;ca&#34;&gt;CA&lt;/h2&gt;&#xA;&lt;p&gt;为了保证证书的可靠性和有效性，在这里可引入 CA 颁发的根证书的概念。其遵守 X.509 标准&lt;/p&gt;&#xA;&lt;h3 id=&#34;根证书&#34;&gt;根证书&lt;/h3&gt;&#xA;&lt;p&gt;根证书（root certificate）是属于根证书颁发机构（CA）的公钥证书。我们可以通过验证 CA 的签名从而信任 CA ，任何人都可以得到 CA 的证书（含公钥），用以验证它所签发的证书（客户端、服务端）&lt;/p&gt;&#xA;&lt;p&gt;它包含的文件如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;公钥&lt;/li&gt;&#xA;&lt;li&gt;密钥&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;生成-key&#34;&gt;生成 Key&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl genrsa -out ca.key 2048&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;生成密钥&#34;&gt;生成密钥&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl req -new -x509 -days 7200 -key ca.key -out ca.pem&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;填写信息&#34;&gt;填写信息&lt;/h4&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Country Name (2 letter code) []:&#xA;State or Province Name (full name) []:&#xA;Locality Name (eg, city) []:&#xA;Organization Name (eg, company) []:&#xA;Organizational Unit Name (eg, section) []:&#xA;Common Name (eg, fully qualified host name) []:go-grpc-example&#xA;Email Address []:&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;server&#34;&gt;Server&lt;/h3&gt;&#xA;&lt;h4 id=&#34;生成-csr&#34;&gt;生成 CSR&lt;/h4&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl req -new -key server.key -out server.csr&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h5 id=&#34;填写信息-1&#34;&gt;填写信息&lt;/h5&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Country Name (2 letter code) []:&#xA;State or Province Name (full name) []:&#xA;Locality Name (eg, city) []:&#xA;Organization Name (eg, company) []:&#xA;Organizational Unit Name (eg, section) []:&#xA;Common Name (eg, fully qualified host name) []:go-grpc-example&#xA;Email Address []:&#xA;&#xA;Please enter the following &amp;#39;extra&amp;#39; attributes&#xA;to be sent with your certificate request&#xA;A challenge password []:&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;CSR 是 Cerificate Signing Request 的英文缩写，为证书请求文件。主要作用是 CA 会利用 CSR 文件进行签名使得攻击者无法伪装或篡改原有证书&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载四」TLS 证书认证</title>
      <link>https://note.lican.site/posts/posts/go/grpc/2018-10-07-grpc-tls/</link>
      <pubDate>Sun, 07 Oct 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc/2018-10-07-grpc-tls/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;在前面的章节里，我们介绍了 gRPC 的四种 API 使用方式。是不是很简单呢 😀&lt;/p&gt;&#xA;&lt;p&gt;此时存在一个安全问题，先前的例子中 gRPC Client/Server 都是明文传输的，会不会有被窃听的风险呢？&lt;/p&gt;&#xA;&lt;p&gt;从结论上来讲，是有的。在明文通讯的情况下，你的请求就是裸奔的，有可能被第三方恶意篡改或者伪造为“非法”的数据&lt;/p&gt;&#xA;&lt;h2 id=&#34;抓个包&#34;&gt;抓个包&lt;/h2&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/15e68df2ba9aa7cace3e26e35c79f200.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/ebebd3ea7d306ad2fcd311f1d8b46cc0.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;嗯，明文传输无误。这是有问题的，接下将改造我们的 gRPC，以便于解决这个问题 😤&lt;/p&gt;&#xA;&lt;h2 id=&#34;证书生成&#34;&gt;证书生成&lt;/h2&gt;&#xA;&lt;h3 id=&#34;私钥&#34;&gt;私钥&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl ecparam -genkey -name secp384r1 -out server.key&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;自签公钥&#34;&gt;自签公钥&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;填写信息&#34;&gt;填写信息&lt;/h4&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Country Name (2 letter code) []:&#xA;State or Province Name (full name) []:&#xA;Locality Name (eg, city) []:&#xA;Organization Name (eg, company) []:&#xA;Organizational Unit Name (eg, section) []:&#xA;Common Name (eg, fully qualified host name) []:go-grpc-example&#xA;Email Address []:&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;生成完毕&#34;&gt;生成完毕&lt;/h3&gt;&#xA;&lt;p&gt;生成证书结束后，将证书相关文件放到 conf/ 下，目录结构：&lt;/p&gt;</description>
    </item>
    <item>
      <title>log 标准库</title>
      <link>https://note.lican.site/posts/posts/go/pkg/2018-09-28-log/</link>
      <pubDate>Fri, 28 Sep 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/pkg/2018-09-28-log/</guid>
      <description>&lt;h2 id=&#34;日志&#34;&gt;日志&lt;/h2&gt;&#xA;&lt;h3 id=&#34;输出&#34;&gt;输出&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;2018/09/28 20:03:08 EDDYCJY Blog...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;构成&#34;&gt;构成&lt;/h3&gt;&#xA;&lt;p&gt;[日期]&amp;lt;空格&amp;gt;[时分秒]&amp;lt;空格&amp;gt;[内容]&amp;lt;\n&amp;gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;源码剖析&#34;&gt;源码剖析&lt;/h2&gt;&#xA;&lt;h3 id=&#34;logger&#34;&gt;Logger&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type Logger struct {&#xA;&#x9;mu     sync.Mutex &#xA;&#x9;prefix string&#xA;&#x9;flag   int&#xA;&#x9;out    io.Writer&#xA;&#x9;buf    []byte&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&#xA;&lt;li&gt;mu：互斥锁，用于确保原子的写入&lt;/li&gt;&#xA;&lt;li&gt;prefix：每行需写入的日志前缀内容&lt;/li&gt;&#xA;&lt;li&gt;flag：设置日志辅助信息（时间、文件名、行号）的写入。可选如下标识位：&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const (&#xA;&#x9;Ldate         = 1 &amp;lt;&amp;lt; iota       // value: 1&#xA;&#x9;Ltime                           // value: 2&#xA;&#x9;Lmicroseconds                   // value: 4&#xA;&#x9;Llongfile                       // value: 8&#xA;&#x9;Lshortfile                      // value: 16&#xA;&#x9;LUTC                            // value: 32&#xA;&#x9;LstdFlags     = Ldate | Ltime   // value: 3&#xA;)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;Ldate：当地时区的格式化日期：2009/01/23&lt;/li&gt;&#xA;&lt;li&gt;Ltime：当地时区的格式化时间：01:23:23&lt;/li&gt;&#xA;&lt;li&gt;Lmicroseconds：在 Ltime 的基础上，增加微秒的时间数值显示&lt;/li&gt;&#xA;&lt;li&gt;Llongfile：完整的文件名和行号：/a/b/c/d.go:23&lt;/li&gt;&#xA;&lt;li&gt;Lshortfile：当前文件名和行号：d.go：23，会覆盖 Llongfile 标识&lt;/li&gt;&#xA;&lt;li&gt;LUTC：如果设置 Ldate 或 Ltime，且设置 LUTC，则优先使用 UTC 时区而不是本地时区&lt;/li&gt;&#xA;&lt;li&gt;LstdFlags：Logger 的默认初始值（Ldate 和 Ltime）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ol start=&#34;4&#34;&gt;&#xA;&lt;li&gt;out：io.Writer&lt;/li&gt;&#xA;&lt;li&gt;buf：用于存储将要写入的日志内容&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;new&#34;&gt;New&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func New(out io.Writer, prefix string, flag int) *Logger {&#xA;&#x9;return &amp;amp;Logger{out: out, prefix: prefix, flag: flag}&#xA;}&#xA;&#xA;var std = New(os.Stderr, &amp;#34;&amp;#34;, LstdFlags)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;New 方法用于初始化 Logger，接受三个初始参数，可以定制化而在 log 包内默认会初始一个 std，它指向标准输入流。而默认的标准输出、标准错误就是显示器（输出到屏幕上），标准输入就是键盘。辅助的时间信息默认为 &lt;code&gt;Ldate | Ltime&lt;/code&gt;，也就是 &lt;code&gt;2009/01/23 01:23:23&lt;/code&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载三」gRPC Streaming, Client and Server</title>
      <link>https://note.lican.site/posts/posts/go/grpc/2018-09-24-stream-client-server/</link>
      <pubDate>Mon, 24 Sep 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc/2018-09-24-stream-client-server/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;本章节将介绍 gRPC 的流式，分为三种类型：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Server-side streaming RPC：服务器端流式 RPC&lt;/li&gt;&#xA;&lt;li&gt;Client-side streaming RPC：客户端流式 RPC&lt;/li&gt;&#xA;&lt;li&gt;Bidirectional streaming RPC：双向流式 RPC&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;流&#34;&gt;流&lt;/h2&gt;&#xA;&lt;p&gt;任何技术，因为有痛点，所以才有了存在的必要性。如果您想要了解 gRPC 的流式调用，请继续&lt;/p&gt;&#xA;&lt;h3 id=&#34;图&#34;&gt;图&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/8812038d20ffece377c0e4901c9a9231.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;gRPC Streaming 是基于 HTTP/2 的，后续章节再进行详细讲解&lt;/p&gt;&#xA;&lt;h3 id=&#34;为什么不用-simple-rpc&#34;&gt;为什么不用 Simple RPC&lt;/h3&gt;&#xA;&lt;p&gt;流式为什么要存在呢，是 Simple RPC 有什么问题吗？通过模拟业务场景，可得知在使用 Simple RPC 时，有如下问题：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;数据包过大造成的瞬时压力&lt;/li&gt;&#xA;&lt;li&gt;接收数据包时，需要所有数据包都接受成功且正确后，才能够回调响应，进行业务处理（无法客户端边发送，服务端边处理）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;为什么用-streaming-rpc&#34;&gt;为什么用 Streaming RPC&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;大规模数据包&lt;/li&gt;&#xA;&lt;li&gt;实时场景&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;模拟场景&#34;&gt;模拟场景&lt;/h4&gt;&#xA;&lt;p&gt;每天早上 6 点，都有一批百万级别的数据集要同从 A 同步到 B，在同步的时候，会做一系列操作（归档、数据分析、画像、日志等）。这一次性涉及的数据量确实大&lt;/p&gt;&#xA;&lt;p&gt;在同步完成后，也有人马上会去查阅数据，为了新的一天筹备。也符合实时性。&lt;/p&gt;&#xA;&lt;p&gt;两者相较下，这个场景下更适合使用 Streaming RPC&lt;/p&gt;&#xA;&lt;h2 id=&#34;grpc&#34;&gt;gRPC&lt;/h2&gt;&#xA;&lt;p&gt;在讲解具体的 gRPC 流式代码时，会&lt;strong&gt;着重在第一节讲解&lt;/strong&gt;，因为三种模式其实是不同的组合。希望你能够注重理解，举一反三，其实都是一样的知识点 👍&lt;/p&gt;&#xA;&lt;h3 id=&#34;目录结构&#34;&gt;目录结构&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ tree go-grpc-example&#xA;go-grpc-example&#xA;├── client&#xA;│   ├── simple_client&#xA;│   │   └── client.go&#xA;│   └── stream_client&#xA;│       └── client.go&#xA;├── proto&#xA;│   ├── search.proto&#xA;│   └── stream.proto&#xA;└── server&#xA;    ├── simple_server&#xA;    │   └── server.go&#xA;    └── stream_server&#xA;        └── server.go&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;增加 stream_server、stream_client 存放服务端和客户端文件，proto/stream.proto 用于编写 IDL&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载二」gRPC Client and Server</title>
      <link>https://note.lican.site/posts/posts/go/grpc/2018-09-23-client-and-server/</link>
      <pubDate>Sun, 23 Sep 2018 12:30:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc/2018-09-23-client-and-server/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;本章节将使用 Go 来编写 gRPC Server 和 Client，让其互相通讯。在此之上会使用到如下库：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;google.golang.org/grpc&lt;/li&gt;&#xA;&lt;li&gt;github.com/golang/protobuf/protoc-gen-go&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;&#xA;&lt;h3 id=&#34;grpc&#34;&gt;gRPC&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go get -u google.golang.org/grpc&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;protocol-buffers-v3&#34;&gt;Protocol Buffers v3&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;wget https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-all-3.5.1.zip&#xA;unzip protobuf-all-3.5.1.zip&#xA;cd protobuf-3.5.1/&#xA;./configure&#xA;make&#xA;make install&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;检查是否安装成功&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;protoc --version&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;若出现以下错误，执行 &lt;code&gt;ldconfig&lt;/code&gt; 命名就能解决这问题&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;protoc: error while loading shared libraries: libprotobuf.so.15: cannot open shared object file: No such file or directory&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;protoc-plugin&#34;&gt;Protoc Plugin&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go get -u github.com/golang/protobuf/protoc-gen-go&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;安装环境若有问题，可参考我先前的文章 &lt;a href=&#34;https://segmentfault.com/a/1190000013339403&#34;&gt;《介绍与环境安装》&lt;/a&gt; 内有详细介绍，不再赘述&lt;/p&gt;&#xA;&lt;h2 id=&#34;grpc-1&#34;&gt;gRPC&lt;/h2&gt;&#xA;&lt;p&gt;本小节开始正式编写 gRPC 相关的程序，一起上车吧 😄&lt;/p&gt;&#xA;&lt;h3 id=&#34;图示&#34;&gt;图示&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://image.eddycjy.com/415d9544fce1e774e1095ab99b6cc015.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;目录结构&#34;&gt;目录结构&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ tree go-grpc-example&#xA;go-grpc-example&#xA;├── client&#xA;├── proto&#xA;│   └── search.proto&#xA;└── server.go&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;idl&#34;&gt;IDL&lt;/h3&gt;&#xA;&lt;h4 id=&#34;编写&#34;&gt;编写&lt;/h4&gt;&#xA;&lt;p&gt;在 proto 文件夹下的 search.proto 文件中，写入如下内容：&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载一」gRPC及相关介绍</title>
      <link>https://note.lican.site/posts/posts/go/grpc/2018-09-22-install/</link>
      <pubDate>Sat, 22 Sep 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc/2018-09-22-install/</guid>
      <description>&lt;p&gt;项目地址：https://github.com/EDDYCJY/go-grpc-example&lt;/p&gt;&#xA;&lt;p&gt;作为开篇章，将会介绍 gRPC 相关的一些知识。简单来讲 gRPC 是一个 基于 HTTP/2 协议设计的 RPC 框架，它采用了 Protobuf 作为 IDL&lt;/p&gt;&#xA;&lt;p&gt;你是否有过疑惑，它们都是些什么？本文将会介绍一些常用的知识和概念，更详细的会给出手册地址去深入&lt;/p&gt;&#xA;&lt;h2 id=&#34;一rpc&#34;&gt;一、RPC&lt;/h2&gt;&#xA;&lt;h3 id=&#34;什么是-rpc&#34;&gt;什么是 RPC&lt;/h3&gt;&#xA;&lt;p&gt;RPC 代指远程过程调用（Remote Procedure Call），它的调用包含了传输协议和编码（对象序列号）协议等等。允许运行于一台计算机的程序调用另一台计算机的子程序，而开发人员无需额外地为这个交互作用编程&lt;/p&gt;&#xA;&lt;h4 id=&#34;实际场景&#34;&gt;实际场景：&lt;/h4&gt;&#xA;&lt;p&gt;有两台服务器，分别是 A、B。在 A 上的应用 C 想要调用 B 服务器上的应用 D，它们可以直接本地调用吗？&lt;br&gt;&#xA;答案是不能的，但走 RPC 的话，十分方便。因此常有人称使用 RPC，就跟本地调用一个函数一样简单&lt;/p&gt;&#xA;&lt;h3 id=&#34;rpc-框架&#34;&gt;RPC 框架&lt;/h3&gt;&#xA;&lt;p&gt;我认为，一个完整的 RPC 框架，应包含负载均衡、服务注册和发现、服务治理等功能，并具有可拓展性便于流量监控系统等接入&lt;br&gt;&#xA;那么它才算完整的，当然了。有些较单一的 RPC 框架，通过组合多组件也能达到这个标准&lt;/p&gt;&#xA;&lt;p&gt;你认为呢？&lt;/p&gt;&#xA;&lt;h3 id=&#34;常见-rpc-框架&#34;&gt;常见 RPC 框架&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://grpc.io/&#34;&gt;gRPC&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/apache/thrift&#34;&gt;Thrift&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/smallnest/rpcx&#34;&gt;Rpcx&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/apache/incubator-dubbo&#34;&gt;Dubbo&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;比较一下&#34;&gt;比较一下&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;\&lt;/th&gt;&#xA;          &lt;th&gt;跨语言&lt;/th&gt;&#xA;          &lt;th&gt;多 IDL&lt;/th&gt;&#xA;          &lt;th&gt;服务治理&lt;/th&gt;&#xA;          &lt;th&gt;注册中心&lt;/th&gt;&#xA;          &lt;th&gt;服务管理&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;gRPC&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Thrift&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Rpcx&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Dubbo&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;为什么要-rpc&#34;&gt;为什么要 RPC&lt;/h3&gt;&#xA;&lt;p&gt;简单、通用、安全、效率&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 大杀器之性能剖析 PProf</title>
      <link>https://note.lican.site/posts/posts/go/tools/2018-09-15-go-tool-pprof/</link>
      <pubDate>Sat, 15 Sep 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/tools/2018-09-15-go-tool-pprof/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;写了几吨代码，实现了几百个接口。功能测试也通过了，终于成功的部署上线了&lt;/p&gt;&#xA;&lt;p&gt;结果，性能不佳，什么鬼？😭&lt;/p&gt;&#xA;&lt;h2 id=&#34;想做性能分析&#34;&gt;想做性能分析&lt;/h2&gt;&#xA;&lt;h3 id=&#34;pprof&#34;&gt;PProf&lt;/h3&gt;&#xA;&lt;p&gt;想要进行性能优化，首先瞩目在 Go 自身提供的工具链来作为分析依据，本文将带你学习、使用 Go 后花园，涉及如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;runtime/pprof：采集程序（非 Server）的运行数据进行分析&lt;/li&gt;&#xA;&lt;li&gt;net/http/pprof：采集 HTTP Server 的运行时数据进行分析&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;是什么&#34;&gt;是什么&lt;/h3&gt;&#xA;&lt;p&gt;pprof 是用于可视化和分析性能分析数据的工具&lt;/p&gt;&#xA;&lt;p&gt;pprof 以 &lt;a href=&#34;https://github.com/google/pprof/blob/master/proto/profile.proto&#34;&gt;profile.proto&lt;/a&gt; 读取分析样本的集合，并生成报告以可视化并帮助分析数据（支持文本和图形报告）&lt;/p&gt;&#xA;&lt;p&gt;profile.proto 是一个 Protocol Buffer v3 的描述文件，它描述了一组 callstack 和 symbolization 信息， 作用是表示统计分析的一组采样的调用栈，是很常见的 stacktrace 配置文件格式&lt;/p&gt;&#xA;&lt;h3 id=&#34;支持什么使用模式&#34;&gt;支持什么使用模式&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Report generation：报告生成&lt;/li&gt;&#xA;&lt;li&gt;Interactive terminal use：交互式终端使用&lt;/li&gt;&#xA;&lt;li&gt;Web interface：Web 界面&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;可以做什么&#34;&gt;可以做什么&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;CPU Profiling：CPU 分析，按照一定的频率采集所监听的应用程序 CPU（含寄存器）的使用情况，可确定应用程序在主动消耗 CPU 周期时花费时间的位置&lt;/li&gt;&#xA;&lt;li&gt;Memory Profiling：内存分析，在应用程序进行堆分配时记录堆栈跟踪，用于监视当前和历史内存使用情况，以及检查内存泄漏&lt;/li&gt;&#xA;&lt;li&gt;Block Profiling：阻塞分析，记录 goroutine 阻塞等待同步（包括定时器通道）的位置&lt;/li&gt;&#xA;&lt;li&gt;Mutex Profiling：互斥锁分析，报告互斥锁的竞争情况&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;一个简单的例子&#34;&gt;一个简单的例子&lt;/h2&gt;&#xA;&lt;p&gt;我们将编写一个简单且有点问题的例子，用于基本的程序初步分析&lt;/p&gt;&#xA;&lt;h3 id=&#34;编写-demo-文件&#34;&gt;编写 demo 文件&lt;/h3&gt;&#xA;&lt;p&gt;（1）demo.go，文件内容：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;net/http&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;net/http/pprof&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/EDDYCJY/go-pprof-example/data&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://github.com/EDDYCJY&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;http&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ListenAndServe&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.0.0.0:6060&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;（2）data/d.go，文件内容：&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载十七」用Nginx部署Go应用</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-09-01-nginx/</link>
      <pubDate>Sat, 01 Sep 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-09-01-nginx/</guid>
      <description>&lt;h2 id=&#34;知识点&#34;&gt;知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Nginx。&lt;/li&gt;&#xA;&lt;li&gt;反向代理。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;简单部署后端服务。&lt;/p&gt;&#xA;&lt;h2 id=&#34;做什么&#34;&gt;做什么&lt;/h2&gt;&#xA;&lt;p&gt;在本章节，我们将简单介绍 Nginx 以及使用 Nginx 来完成对 &lt;a href=&#34;https://github.com/EDDYCJY/go-gin-example&#34;&gt;go-gin-example&lt;/a&gt; 的部署，会实现反向代理和简单负载均衡的功能。&lt;/p&gt;&#xA;&lt;h2 id=&#34;nginx&#34;&gt;Nginx&lt;/h2&gt;&#xA;&lt;h3 id=&#34;是什么&#34;&gt;是什么&lt;/h3&gt;&#xA;&lt;p&gt;Nginx 是一个 Web Server，可以用作反向代理、负载均衡、邮件代理、TCP / UDP、HTTP 服务器等等，它拥有很多吸引人的特性，例如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;以较低的内存占用率处理 10,000 多个并发连接（每 10k 非活动 HTTP 保持活动连接约 2.5 MB ）&lt;/li&gt;&#xA;&lt;li&gt;静态服务器（处理静态文件）&lt;/li&gt;&#xA;&lt;li&gt;正向、反向代理&lt;/li&gt;&#xA;&lt;li&gt;负载均衡&lt;/li&gt;&#xA;&lt;li&gt;通过 OpenSSL 对 TLS / SSL 与 SNI 和 OCSP 支持&lt;/li&gt;&#xA;&lt;li&gt;FastCGI、SCGI、uWSGI 的支持&lt;/li&gt;&#xA;&lt;li&gt;WebSockets、HTTP/1.1 的支持&lt;/li&gt;&#xA;&lt;li&gt;Nginx + Lua&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;安装&#34;&gt;安装&lt;/h3&gt;&#xA;&lt;p&gt;请右拐谷歌或百度，安装好 Nginx 以备接下来的使用&lt;/p&gt;&#xA;&lt;h3 id=&#34;简单讲解&#34;&gt;简单讲解&lt;/h3&gt;&#xA;&lt;h4 id=&#34;常用命令&#34;&gt;常用命令&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;nginx：启动 Nginx&lt;/li&gt;&#xA;&lt;li&gt;nginx -s stop：立刻停止 Nginx 服务&lt;/li&gt;&#xA;&lt;li&gt;nginx -s reload：重新加载配置文件&lt;/li&gt;&#xA;&lt;li&gt;nginx -s quit：平滑停止 Nginx 服务&lt;/li&gt;&#xA;&lt;li&gt;nginx -t：测试配置文件是否正确&lt;/li&gt;&#xA;&lt;li&gt;nginx -v：显示 Nginx 版本信息&lt;/li&gt;&#xA;&lt;li&gt;nginx -V：显示 Nginx 版本信息、编译器和配置参数的信息&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;涉及配置&#34;&gt;涉及配置&lt;/h4&gt;&#xA;&lt;p&gt;1、 proxy_pass：配置&lt;strong&gt;反向代理的路径&lt;/strong&gt;。需要注意的是如果 proxy_pass 的 url 最后为&#xA;/，则表示绝对路径。否则（不含变量下）表示相对路径，所有的路径都会被代理过去&lt;/p&gt;</description>
    </item>
    <item>
      <title>「番外」请入门 Makefile</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-08-26-makefile/</link>
      <pubDate>Sun, 26 Aug 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-08-26-makefile/</guid>
      <description>&lt;h2 id=&#34;知识点&#34;&gt;知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;写一个 Makefile&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;含一定复杂度的软件工程，基本上都是先编译 A，再依赖 B，再编译 C&amp;hellip;，最后才执行构建。如果每次都人为编排，又或是每新来一个同事就问你项目 D 怎么构建、重新构建需要注意什么&amp;hellip;等等情况，岂不是要崩溃？&lt;/p&gt;&#xA;&lt;p&gt;我们常常会在开源项目中发现 Makefile，你是否有过疑问？&lt;/p&gt;&#xA;&lt;p&gt;本章节会简单介绍 Makefile 的使用方式，最后建议深入学习。&lt;/p&gt;&#xA;&lt;h2 id=&#34;怎么解决&#34;&gt;怎么解决&lt;/h2&gt;&#xA;&lt;p&gt;对于构建编排，Docker 有 Dockerfile ，在 Unix 中有神器 &lt;a href=&#34;https://en.wikipedia.org/wiki/Make_%28software%29&#34;&gt;Make&lt;/a&gt; &amp;hellip;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;make&#34;&gt;Make&lt;/h2&gt;&#xA;&lt;h3 id=&#34;是什么&#34;&gt;是什么&lt;/h3&gt;&#xA;&lt;p&gt;Make 是一个构建自动化工具，会在当前目录下寻找 Makefile 或 makefile 文件。如果存在，会依据 Makefile 的&lt;strong&gt;构建规则&lt;/strong&gt;去完成构建&lt;/p&gt;&#xA;&lt;p&gt;当然了，实际上 Makefile 内都是你根据 make 语法规则，自己编写的特定 Shell 命令等&lt;/p&gt;&#xA;&lt;p&gt;它是一个工具，规则也很简单。在支持的范围内，编译 A， 依赖 B，再编译 C，完全没问题&lt;/p&gt;&#xA;&lt;h3 id=&#34;规则&#34;&gt;规则&lt;/h3&gt;&#xA;&lt;p&gt;Makefile 由多条规则组成，每条规则都以一个 target（目标）开头，后跟一个 : 冒号，冒号后是这一个目标的 prerequisites（前置条件）&lt;/p&gt;&#xA;&lt;p&gt;紧接着新的一行，必须以一个 tab 作为开头，后面跟随 command（命令），也就是你希望这一个 target 所执行的构建命令&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[target] ... : [prerequisites] ...&#xA;&amp;lt;tab&amp;gt;[command]&#xA;    ...&#xA;    ...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;target：一个目标代表一条规则，可以是一个或多个文件名。也可以是某个操作的名字（标签），称为&lt;strong&gt;伪目标（phony）&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;prerequisites：前置条件，这一项是&lt;strong&gt;可选参数&lt;/strong&gt;。通常是多个文件名、伪目标。它的作用是 target 是否需要重新构建的标准，如果前置条件不存在或有过更新（文件的最后一次修改时间）则认为 target 需要重新构建&lt;/li&gt;&#xA;&lt;li&gt;command：构建这一个 target 的具体命令集&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;简单的例子&#34;&gt;简单的例子&lt;/h3&gt;&#xA;&lt;p&gt;本文将以 &lt;a href=&#34;https://github.com/EDDYCJY/go-gin-example&#34;&gt;go-gin-example&lt;/a&gt; 去编写 Makefile 文件，请跨入 make 的大门&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载十六」在图片上绘制文字</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-07-07-font/</link>
      <pubDate>Sat, 07 Jul 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-07-07-font/</guid>
      <description>&lt;h2 id=&#34;知识点&#34;&gt;知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;字体库使用&lt;/li&gt;&#xA;&lt;li&gt;图片合成&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;主要实现&lt;strong&gt;合并后的海报上绘制文字&lt;/strong&gt;的功能（这个需求也是常见的很了），内容比较简单。&lt;/p&gt;&#xA;&lt;h2 id=&#34;实现&#34;&gt;实现&lt;/h2&gt;&#xA;&lt;p&gt;这里使用的是 &lt;a href=&#34;https://github.com/EDDYCJY/go-gin-example/blob/master/runtime/fonts/msyhbd.ttc&#34;&gt;微软雅黑&lt;/a&gt; 的字体，请点击进行下载并&lt;strong&gt;存放到 runtime/fonts 目录&lt;/strong&gt;下（字体文件占 16 MB 大小）&lt;/p&gt;&#xA;&lt;h3 id=&#34;安装&#34;&gt;安装&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go get -u github.com/golang/freetype&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;绘制文字&#34;&gt;绘制文字&lt;/h3&gt;&#xA;&lt;p&gt;打开 service/article_service/article_poster.go 文件，增加绘制文字的业务逻辑，如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DrawText&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;JPG&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;draw&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Image&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Merged&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;File&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;X0&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Y0&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Size0&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;SubTitle&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;X1&lt;/span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Y1&lt;/span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Size1&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ArticlePosterBg&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;DrawPoster&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;DrawText&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fontName&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fontSource&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AppSetting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RuntimeRootPath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AppSetting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FontSavePath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fontName&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fontSourceBytes&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ioutil&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ReadFile&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fontSource&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;trueTypeFont&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;freetype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ParseFont&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fontSourceBytes&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;freetype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewContext&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetDPI&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;72&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetFont&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;trueTypeFont&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetFontSize&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Size0&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetClip&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;JPG&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Bounds&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetDst&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;JPG&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetSrc&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;image&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Black&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;pt&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;freetype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pt&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;X0&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Y0&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;fc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DrawString&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;pt&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetFontSize&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Size1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;fc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DrawString&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SubTitle&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;freetype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pt&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;X1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Y1&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;jpeg&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Encode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Merged&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;JPG&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里主要使用了 freetype 包，分别涉及如下细项：&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载十五」生成二维码、合并海报</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-07-05-image/</link>
      <pubDate>Thu, 05 Jul 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-07-05-image/</guid>
      <description>&lt;h2 id=&#34;知识点&#34;&gt;知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;图片生成&lt;/li&gt;&#xA;&lt;li&gt;二维码生成&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;在文章的详情页中，我们常常会需要去宣传它，而目前最常见的就是发海报了，今天我们将实现如下功能：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;生成二维码&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;合并海报（背景图 + 二维码）&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;实现&#34;&gt;实现&lt;/h2&gt;&#xA;&lt;p&gt;首先，你需要在 App 配置项中增加二维码及其海报的存储路径，我们约定配置项名称为 &lt;code&gt;QrCodeSavePath&lt;/code&gt;，值为 &lt;code&gt;qrcode/&lt;/code&gt;，经过多节连载的你应该能够完成，若有不懂可参照 &lt;a href=&#34;https://github.com/EDDYCJY/go-gin-example/blob/master/conf/app.ini#L14&#34;&gt;go-gin-example&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;生成二维码&#34;&gt;生成二维码&lt;/h2&gt;&#xA;&lt;h3 id=&#34;安装&#34;&gt;安装&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go get -u github.com/boombuler/barcode&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;工具包&#34;&gt;工具包&lt;/h3&gt;&#xA;&lt;p&gt;考虑生成二维码这一动作贴合工具包的定义，且有公用的可能性，新建 pkg/qrcode/qrcode.go 文件，写入内容：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;qrcode&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image/jpeg&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/boombuler/barcode&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/boombuler/barcode/qr&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/EDDYCJY/go-gin-example/pkg/file&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/EDDYCJY/go-gin-example/pkg/setting&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/EDDYCJY/go-gin-example/pkg/util&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;QrCode&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Width&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Height&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Ext&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Level&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;qr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ErrorCorrectionLevel&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Mode&lt;/span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;qr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Encoding&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;EXT_JPG&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.jpg&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;NewQrCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;qr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ErrorCorrectionLevel&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;mode&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;qr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Encoding&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;QrCode&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;QrCode&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;:    &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Width&lt;/span&gt;:  &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Height&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Level&lt;/span&gt;:  &lt;span style=&#34;color:#a6e22e&#34;&gt;level&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Mode&lt;/span&gt;:   &lt;span style=&#34;color:#a6e22e&#34;&gt;mode&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Ext&lt;/span&gt;:    &lt;span style=&#34;color:#a6e22e&#34;&gt;EXT_JPG&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetQrCodePath&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AppSetting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;QrCodeSavePath&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetQrCodeFullPath&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AppSetting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RuntimeRootPath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AppSetting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;QrCodeSavePath&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetQrCodeFullUrl&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AppSetting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PrefixUrl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetQrCodePath&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetQrCodeFileName&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;util&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EncodeMD5&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;QrCode&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;GetQrCodeExt&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Ext&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;QrCode&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;CheckEncode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetQrCodeFileName&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetQrCodeExt&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CheckNotExist&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;QrCode&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Encode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) (&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetQrCodeFileName&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetQrCodeExt&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CheckNotExist&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;qr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Encode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Level&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Mode&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;barcode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scale&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Width&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;q&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Height&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MustOpen&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;jpeg&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Encode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里主要聚焦 &lt;code&gt;func (q *QrCode) Encode&lt;/code&gt; 方法，做了如下事情：&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载十四」实现导出、导入 Excel</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-06-14-excel/</link>
      <pubDate>Thu, 14 Jun 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-06-14-excel/</guid>
      <description>&lt;h2 id=&#34;知识点&#34;&gt;知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;导出功能的实现&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;在本节，我们将实现对标签信息的导出、导入功能，这是很标配功能了，希望你掌握基础的使用方式。&lt;/p&gt;&#xA;&lt;p&gt;另外在本文我们使用了 2 个 Excel 的包，excelize 最初的 XML 格式文件的一些结构，是通过 tealeg/xlsx 格式文件结构演化而来的，因此特意在此都展示了，你可以根据自己的场景和喜爱去使用。&lt;/p&gt;&#xA;&lt;h2 id=&#34;配置&#34;&gt;配置&lt;/h2&gt;&#xA;&lt;p&gt;首先要指定导出的 Excel 文件的存储路径，在 app.ini 中增加配置：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[app]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ExportSavePath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;export/&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;修改 setting.go 的 App struct：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;App&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;JwtSecret&lt;/span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;PageSize&lt;/span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;PrefixUrl&lt;/span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;RuntimeRootPath&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ImageSavePath&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ImageMaxSize&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ImageAllowExts&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ExportSavePath&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;LogSavePath&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;LogSaveName&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;LogFileExt&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;TimeFormat&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这里需增加 ExportSavePath 配置项，另外将先前 ImagePrefixUrl 改为 PrefixUrl 用于支撑两者的 HOST 获取&lt;/p&gt;&#xA;&lt;p&gt;（注意修改 image.go 的 GetImageFullUrl 方法）&lt;/p&gt;&#xA;&lt;h2 id=&#34;pkg&#34;&gt;pkg&lt;/h2&gt;&#xA;&lt;p&gt;新建 pkg/export/excel.go 文件，如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;export&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/EDDYCJY/go-gin-example/pkg/setting&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetExcelFullUrl&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AppSetting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PrefixUrl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetExcelPath&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetExcelPath&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AppSetting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ExportSavePath&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetExcelFullPath&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AppSetting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RuntimeRootPath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetExcelPath&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里编写了一些常用的方法，以后取值方式如果有变动，直接改内部代码即可，对外不可见&lt;/p&gt;</description>
    </item>
    <item>
      <title>用 Go 来了解一下 Redis 通讯协议</title>
      <link>https://note.lican.site/posts/posts/go/talk/2018-06-07-go-redis-protocol/</link>
      <pubDate>Thu, 07 Jun 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2018-06-07-go-redis-protocol/</guid>
      <description>&lt;p&gt;Go、PHP、Java&amp;hellip; 都有那么多包来支撑你使用 Redis，那你是否有想过&lt;/p&gt;&#xA;&lt;p&gt;有了服务端，有了客户端，他们俩是怎样通讯，又是基于什么通讯协议做出交互的呢？&lt;/p&gt;&#xA;&lt;h2 id=&#34;介绍&#34;&gt;介绍&lt;/h2&gt;&#xA;&lt;p&gt;基于我们的目的，本文主要讲解和实践 Redis 的通讯协议&lt;/p&gt;&#xA;&lt;p&gt;Redis 的客户端和服务端是通过 TCP 连接来进行数据交互， 服务器默认的端口号为 6379&lt;/p&gt;&#xA;&lt;p&gt;客户端和服务器发送的命令或数据一律以 \r\n （CRLF）结尾（这是一条约定）&lt;/p&gt;&#xA;&lt;h2 id=&#34;协议&#34;&gt;协议&lt;/h2&gt;&#xA;&lt;p&gt;在 Redis 中分为&lt;strong&gt;请求&lt;/strong&gt;和&lt;strong&gt;回复&lt;/strong&gt;，而请求协议又分为新版和旧版，新版统一请求协议在 Redis 1.2 版本中引入，最终在 Redis 2.0 版本成为 Redis 服务器通信的标准方式&lt;/p&gt;&#xA;&lt;p&gt;本文是基于新版协议来实现功能，不建议使用旧版（1.2 挺老旧了）。如下是新协议的各种范例：&lt;/p&gt;&#xA;&lt;h3 id=&#34;请求协议&#34;&gt;请求协议&lt;/h3&gt;&#xA;&lt;p&gt;1、 格式示例&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*&amp;lt;参数数量&amp;gt; CR LF&#xA;$&amp;lt;参数 1 的字节数量&amp;gt; CR LF&#xA;&amp;lt;参数 1 的数据&amp;gt; CR LF&#xA;...&#xA;$&amp;lt;参数 N 的字节数量&amp;gt; CR LF&#xA;&amp;lt;参数 N 的数据&amp;gt; CR LF&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在该协议下所有发送至 Redis 服务器的参数都是二进制安全（binary safe）的&lt;/p&gt;&#xA;&lt;p&gt;2、打印示例&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*3&#xA;$3&#xA;SET&#xA;$5&#xA;mykey&#xA;$7&#xA;myvalue&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;3、实际协议值&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n&amp;#34;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这就是 Redis 的请求协议规范，按照范例1编写客户端逻辑，最终发送的是范例3，相信你已经有大致的概念了，Redis 的协议非常的简洁易懂，这也是好上手的原因之一，你可以想想协议这么定义的好处在哪？&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载十三」优化你的应用结构和实现Redis缓存</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-06-02-application-redis/</link>
      <pubDate>Sat, 02 Jun 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-06-02-application-redis/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;之前就在想，不少教程或示例的代码设计都是一步到位的（也没问题）&lt;/p&gt;&#xA;&lt;p&gt;但实际操作的读者真的能够理解透彻为什么吗？左思右想，有了今天这一章的内容，我认为实际经历过一遍印象会更加深刻&lt;/p&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;在本章节，将介绍以下功能的整理：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;抽离、分层业务逻辑：减轻 routers.go 内的 api 方法的逻辑（但本文暂不分层 repository，这块逻辑还不重）。&lt;/li&gt;&#xA;&lt;li&gt;增加容错性：对 gorm 的错误进行判断。&lt;/li&gt;&#xA;&lt;li&gt;Redis 缓存：对获取数据类的接口增加缓存设置。&lt;/li&gt;&#xA;&lt;li&gt;减少重复冗余代码。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;问题在哪&#34;&gt;问题在哪？&lt;/h2&gt;&#xA;&lt;p&gt;在规划阶段我们发现了一个问题，这是目前的伪代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ! &lt;span style=&#34;color:#a6e22e&#34;&gt;HasErrors&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ExistArticleByID&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;DeleteArticle&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SUCCESS&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ERROR_NOT_EXIST_ARTICLE&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;valid&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Errors&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;logging&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Info&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Message&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;JSON&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;http&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;StatusOK&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;H&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;:  &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetMsg&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: make(&lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;]&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果加上规划内的功能逻辑呢，伪代码会变成：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ! &lt;span style=&#34;color:#a6e22e&#34;&gt;HasErrors&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;exists&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ExistArticleByID&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;exists&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;DeleteArticle&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#x9;&#x9;    &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SUCCESS&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#x9;&#x9;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#x9;&#x9;    &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ERROR_XXX&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#x9;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ERROR_NOT_EXIST_ARTICLE&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ERROR_XXX&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;valid&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Errors&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;logging&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Info&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Message&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;JSON&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;http&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;StatusOK&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;H&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;:  &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetMsg&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: make(&lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;]&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果缓存的逻辑也加进来，后面慢慢不断的迭代，岂不是会变成如下图一样？&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载十二」优化配置结构及实现图片上传</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-05-27-config-upload/</link>
      <pubDate>Sun, 27 May 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-05-27-config-upload/</guid>
      <description>&lt;h2 id=&#34;知识点&#34;&gt;知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;重构、调整结构&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;这个应用程序跑了那么久了，越来越大，越来越壮，仿佛我们的产品一样，现在它需要进行小范围重构了，以便于后续的使用，这非常重要。&lt;/p&gt;&#xA;&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;一天，产品经理突然跟你说文章列表，没有封面图，不够美观，！）&amp;amp;￥&lt;em&gt;！&amp;amp;）#&amp;amp;￥&lt;/em&gt;！加一个吧，几分钟的事&lt;/p&gt;&#xA;&lt;p&gt;你打开你的程序，分析了一波写了个清单：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;优化配置结构（因为配置项越来越多）&lt;/li&gt;&#xA;&lt;li&gt;抽离 原 logging 的 File 便于公用（logging、upload 各保有一份并不合适）&lt;/li&gt;&#xA;&lt;li&gt;实现上传图片接口（需限制文件格式、大小）&lt;/li&gt;&#xA;&lt;li&gt;修改文章接口（需支持封面地址参数）&lt;/li&gt;&#xA;&lt;li&gt;增加 blog_article （文章）的数据库字段&lt;/li&gt;&#xA;&lt;li&gt;实现 http.FileServer&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;嗯，你发现要较优的话，需要调整部分的应用程序结构，因为功能越来越多，原本的设计也要跟上节奏&lt;/p&gt;&#xA;&lt;p&gt;也就是在适当的时候，及时优化&lt;/p&gt;&#xA;&lt;h2 id=&#34;优化配置结构&#34;&gt;优化配置结构&lt;/h2&gt;&#xA;&lt;h3 id=&#34;一讲解&#34;&gt;一、讲解&lt;/h3&gt;&#xA;&lt;p&gt;在先前章节中，采用了直接读取 KEY 的方式去存储配置项，而本次需求中，需要增加图片的配置项，总体就有些冗余了&lt;/p&gt;&#xA;&lt;p&gt;我们采用以下解决方法：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;映射结构体：使用 MapTo 来设置配置参数&lt;/li&gt;&#xA;&lt;li&gt;配置统管：所有的配置项统管到 setting 中&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;映射结构体示例&#34;&gt;映射结构体（示例）&lt;/h4&gt;&#xA;&lt;p&gt;在 go-ini 中可以采用 MapTo 的方式来映射结构体，例如：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Server&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;RunMode&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;HttpPort&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ReadTimeout&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Duration&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;WriteTimeout&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Duration&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ServerSetting&lt;/span&gt; = &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Server&lt;/span&gt;{}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Cfg&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ini&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Load&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;conf/app.ini&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatalf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Fail to parse &amp;#39;conf/app.ini&amp;#39;: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;Cfg&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Section&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;server&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;MapTo&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ServerSetting&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatalf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Cfg.MapTo ServerSetting err: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这段代码中，可以注意 ServerSetting 取了地址，为什么 MapTo 必须地址入参呢？&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 的 fake-useragent 了解一下</title>
      <link>https://note.lican.site/posts/posts/go/talk/2018-05-21-go-fake-useragent/</link>
      <pubDate>Mon, 21 May 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2018-05-21-go-fake-useragent/</guid>
      <description>&lt;p&gt;有的网站会根据 User-Agent 的不同，跳转到不同（PC、M）的站点，也有根据版本的不同给出不一样的提示等等，而 User-Agent 的变化更是爬虫里的基础姿势&lt;/p&gt;&#xA;&lt;p&gt;使用 Go 编写网络爬虫或需要模拟浏览器头（User-Agent）的时候，你是否会觉得很麻烦，获取请求头（Request Headers）的 User-Agent 还得找来找去，挺繁琐。先前我也遇到了这个问题，因此有了这个项目 &lt;a href=&#34;https://github.com/EDDYCJY/fake-useragent&#34;&gt;fake-useragent&lt;/a&gt;，用来解决你我的痛点&lt;/p&gt;&#xA;&lt;p&gt;项目地址：https://github.com/EDDYCJY/fake-useragent&lt;/p&gt;&#xA;&lt;h2 id=&#34;支持&#34;&gt;支持&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;All User-Agent Random&lt;/li&gt;&#xA;&lt;li&gt;Chrome&lt;/li&gt;&#xA;&lt;li&gt;InternetExplorer (IE)&lt;/li&gt;&#xA;&lt;li&gt;Firefox&lt;/li&gt;&#xA;&lt;li&gt;Safari&lt;/li&gt;&#xA;&lt;li&gt;Android&lt;/li&gt;&#xA;&lt;li&gt;MacOSX&lt;/li&gt;&#xA;&lt;li&gt;IOS&lt;/li&gt;&#xA;&lt;li&gt;Linux&lt;/li&gt;&#xA;&lt;li&gt;IPhone&lt;/li&gt;&#xA;&lt;li&gt;IPad&lt;/li&gt;&#xA;&lt;li&gt;Computer&lt;/li&gt;&#xA;&lt;li&gt;Mobile&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go get github.com/EDDYCJY/fake-useragent&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;用法&#34;&gt;用法&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/EDDYCJY/fake-useragent&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#75715e&#34;&gt;// 推荐使用&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;random&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Random&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Random: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;random&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;chrome&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Chrome&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Chrome: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;chrome&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;internetExplorer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;InternetExplorer&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;IE: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;internetExplorer&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;firefox&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Firefox&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Firefox: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;firefox&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;safari&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Safari&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Safari: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;safari&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;android&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Android&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Android: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;android&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;macOSX&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MacOSX&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MacOSX: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;macOSX&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ios&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IOS&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;IOS: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ios&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;linux&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Linux&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Linux: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;linux&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;iphone&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IPhone&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;IPhone: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;iphone&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ipad&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IPad&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;IPad: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ipad&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;computer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Computer&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Computer: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;computer&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;mobile&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Mobile&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mobile: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;mobile&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;定制&#34;&gt;定制&lt;/h3&gt;&#xA;&lt;p&gt;你可以调整抓取数据源的最大页数、时间间隔以及最大超时时间。 如果不填写，则为默认值。&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载十一」Cron定时任务</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-04-29-cron/</link>
      <pubDate>Sun, 29 Apr 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-04-29-cron/</guid>
      <description>&lt;h2 id=&#34;知识点&#34;&gt;知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;完成定时任务的功能&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;在实际的应用项目中，定时任务的使用是很常见的。你是否有过 Golang 如何做定时任务的疑问，莫非是轮询，在本文中我们将结合我们的项目讲述 Cron。&lt;/p&gt;&#xA;&lt;h2 id=&#34;介绍&#34;&gt;介绍&lt;/h2&gt;&#xA;&lt;p&gt;我们将使用 &lt;a href=&#34;https://github.com/robfig/cron&#34;&gt;cron&lt;/a&gt; 这个包，它实现了 cron 规范解析器和任务运行器，简单来讲就是包含了定时任务所需的功能&lt;/p&gt;&#xA;&lt;h3 id=&#34;cron-表达式格式&#34;&gt;Cron 表达式格式&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;字段名&lt;/th&gt;&#xA;          &lt;th&gt;是否必填&lt;/th&gt;&#xA;          &lt;th&gt;允许的值&lt;/th&gt;&#xA;          &lt;th&gt;允许的特殊字符&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;秒（Seconds）&lt;/td&gt;&#xA;          &lt;td&gt;Yes&lt;/td&gt;&#xA;          &lt;td&gt;0-59&lt;/td&gt;&#xA;          &lt;td&gt;* / , -&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;分（Minutes）&lt;/td&gt;&#xA;          &lt;td&gt;Yes&lt;/td&gt;&#xA;          &lt;td&gt;0-59&lt;/td&gt;&#xA;          &lt;td&gt;* / , -&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;时（Hours）&lt;/td&gt;&#xA;          &lt;td&gt;Yes&lt;/td&gt;&#xA;          &lt;td&gt;0-23&lt;/td&gt;&#xA;          &lt;td&gt;* / , -&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;一个月中的某天（Day of month）&lt;/td&gt;&#xA;          &lt;td&gt;Yes&lt;/td&gt;&#xA;          &lt;td&gt;1-31&lt;/td&gt;&#xA;          &lt;td&gt;* / , - ?&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;月（Month）&lt;/td&gt;&#xA;          &lt;td&gt;Yes&lt;/td&gt;&#xA;          &lt;td&gt;1-12 or JAN-DEC&lt;/td&gt;&#xA;          &lt;td&gt;* / , -&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;星期几（Day of week）&lt;/td&gt;&#xA;          &lt;td&gt;Yes&lt;/td&gt;&#xA;          &lt;td&gt;0-6 or SUN-SAT&lt;/td&gt;&#xA;          &lt;td&gt;* / , - ?&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Cron 表达式表示一组时间，使用 6 个空格分隔的字段&lt;/p&gt;</description>
    </item>
    <item>
      <title>了解一下Golang的市场行情</title>
      <link>https://note.lican.site/posts/posts/go/crawler/2018-04-28-go2018/</link>
      <pubDate>Sat, 28 Apr 2018 12:30:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/crawler/2018-04-28-go2018/</guid>
      <description>&lt;p&gt;项目地址：https://github.com/go-crawler/lagou_jobs&lt;/p&gt;&#xA;&lt;p&gt;如果对你有所帮助，欢迎 Star，给文章来波赞，这样可以让更多的人看见  :)&lt;/p&gt;&#xA;&lt;h2 id=&#34;目标&#34;&gt;目标&lt;/h2&gt;&#xA;&lt;p&gt;在工作中 Golang 已是一份子，想让大家了解一下 Golang 的市场行情，也想让更多的人熟悉它。因此主要是展示数据分析的结果&lt;/p&gt;&#xA;&lt;p&gt;目标站点是 &lt;a href=&#34;https://www.lagou.com/&#34;&gt;某招聘网站&lt;/a&gt; 的职位数据抓取和分析，爬取城市分别为 北京、上海、广州、深圳、杭州、成都，再得出一个结论&lt;/p&gt;&#xA;&lt;h3 id=&#34;分析&#34;&gt;分析&lt;/h3&gt;&#xA;&lt;p&gt;首先需要进行页面分析，找到我们的抓取方向&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/04/26/5ae1e28a3412a.jpeg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;搜索 golang 关键字，打开页面 F12 就能看到它发送了四个请求，留意 positionAjax.json 这个请求&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/04/26/5ae1efe538791.jpeg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;我们仔细研判这个接口的入参和出参&lt;/p&gt;&#xA;&lt;h3 id=&#34;入参&#34;&gt;入参&lt;/h3&gt;&#xA;&lt;p&gt;1、Query String Param&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;city：请求的城市&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;needAddtionalResult：是否需要补充额外的参数，这里默认 false&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;2、Form Data&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;first：是否首页&lt;/li&gt;&#xA;&lt;li&gt;pn：页码&lt;/li&gt;&#xA;&lt;li&gt;kd：关键字&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;出参&#34;&gt;出参&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/04/26/5ae1f4c9920a9.jpeg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;就是它了，从返回结果可得出许多有用的信息&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;companyFullName：公司全称&lt;/li&gt;&#xA;&lt;li&gt;companyLabelList：公司标签&lt;/li&gt;&#xA;&lt;li&gt;companyShortName：公司简称&lt;/li&gt;&#xA;&lt;li&gt;companySize：公司规模&lt;/li&gt;&#xA;&lt;li&gt;education：学历要求&lt;/li&gt;&#xA;&lt;li&gt;financeStage：融资阶段&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;等等~&lt;/p&gt;&#xA;&lt;h3 id=&#34;分页&#34;&gt;分页&lt;/h3&gt;&#xA;&lt;p&gt;在上面两张图中，可以发现在 content 节点中包含 pageNo、pageSize 字段，content.positionResult 节点有 totalCount 字段，可以得知当前是第几页，每页显示多少条，当前的职位总条数&lt;/p&gt;&#xA;&lt;p&gt;需要注意一下，分页的计算是要向上取整的&lt;/p&gt;&#xA;&lt;h2 id=&#34;模拟浏览器头&#34;&gt;模拟浏览器头&lt;/h2&gt;&#xA;&lt;p&gt;User-Agent 可以用 &lt;a href=&#34;https://github.com/EDDYCJY/fake-useragent&#34;&gt;fake-useragent&lt;/a&gt; 这个项目来随机生成 UA 头 😄&lt;/p&gt;&#xA;&lt;h2 id=&#34;数据&#34;&gt;数据&lt;/h2&gt;&#xA;&lt;h3 id=&#34;一分布图&#34;&gt;一、分布图&lt;/h3&gt;&#xA;&lt;p&gt;不同工作、工种，自然也会遍布在不同的工作区域，我们先了解一下各个城市的 Golang 工程师都主要在哪个区上班，心里留个底&lt;/p&gt;&#xA;&lt;h4 id=&#34;北京&#34;&gt;北京&lt;/h4&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/04/27/5ae291859667c.jpeg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载十」定制 GORM Callbacks</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-04-15-gorm-callback/</link>
      <pubDate>Sun, 15 Apr 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-04-15-gorm-callback/</guid>
      <description>&lt;h2 id=&#34;涉及知识点&#34;&gt;涉及知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;GORM&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;GORM itself is powered by Callbacks, so you could fully customize GORM as you want&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;GORM 本身是由回调驱动的，所以我们可以根据需要完全定制 GORM，以此达到我们的目的，如下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;注册一个新的回调&lt;/li&gt;&#xA;&lt;li&gt;删除现有的回调&lt;/li&gt;&#xA;&lt;li&gt;替换现有的回调&lt;/li&gt;&#xA;&lt;li&gt;注册回调的顺序&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在 GORM 中包含以上四类 Callbacks，我们结合项目选用 “替换现有的回调” 来解决一个小痛点。&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题&#34;&gt;问题&lt;/h2&gt;&#xA;&lt;p&gt;在 models 目录下，我们包含 tag.go 和 article.go 两个文件，他们有一个问题，就是 BeforeCreate、BeforeUpdate 重复出现了，那难道 100 个文件，就要写一百次吗？&lt;/p&gt;&#xA;&lt;p&gt;1、tag.go&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/04/14/5ad20efdba409.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;2、article.go&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/04/14/5ad20ebacc4c9.jpg&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;显然这是不可能的，如果先前你已经意识到这个问题，那挺 OK，但没有的话，现在开始就要改&lt;/p&gt;&#xA;&lt;h3 id=&#34;解决&#34;&gt;解决&lt;/h3&gt;&#xA;&lt;p&gt;在这里我们通过 Callbacks 来实现功能，不需要一个个文件去编写&lt;/p&gt;&#xA;&lt;h3 id=&#34;实现-callbacks&#34;&gt;实现 Callbacks&lt;/h3&gt;&#xA;&lt;p&gt;打开 models 目录下的 models.go 文件，实现以下两个方法：&lt;/p&gt;&#xA;&lt;p&gt;1、updateTimeStampForCreateCallback&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// updateTimeStampForCreateCallback will set `CreatedOn`, `ModifiedOn` when creating&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;updateTimeStampForCreateCallback&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gorm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scope&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; !&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;HasError&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;nowTime&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Now&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Unix&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createTimeField&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ok&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FieldByName&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CreatedOn&amp;#34;&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;ok&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createTimeField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IsBlank&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;createTimeField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;nowTime&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;modifyTimeField&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ok&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FieldByName&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ModifiedOn&amp;#34;&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;ok&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;modifyTimeField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IsBlank&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;modifyTimeField&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;nowTime&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这段方法中，会完成以下功能&lt;/p&gt;</description>
    </item>
    <item>
      <title>爬取汽车之家 二手车产品库</title>
      <link>https://note.lican.site/posts/posts/go/crawler/2018-04-01-cars/</link>
      <pubDate>Sun, 01 Apr 2018 12:30:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/crawler/2018-04-01-cars/</guid>
      <description>&lt;p&gt;项目地址：https://github.com/go-crawler/car-prices&lt;/p&gt;&#xA;&lt;h2 id=&#34;目标&#34;&gt;目标&lt;/h2&gt;&#xA;&lt;p&gt;最近经常有人在耳边提起汽车之家，也好奇二手车在国内的价格是怎么样的，因此本次的目标站点是 &lt;a href=&#34;https://car.autohome.com.cn/2sc/440399/index.html&#34;&gt;汽车之家&lt;/a&gt; 的二手车产品库&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/03/30/5abe47f82a01f.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;分析目标源：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;一页共 24 条&lt;/li&gt;&#xA;&lt;li&gt;含分页，但这个老产品库，在 100 页后会存在问题，因此我们爬取 99 页&lt;/li&gt;&#xA;&lt;li&gt;可以获取全部城市&lt;/li&gt;&#xA;&lt;li&gt;共可爬取 19w+ 数据&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;开始&#34;&gt;开始&lt;/h2&gt;&#xA;&lt;p&gt;爬取步骤&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;获取全部的城市&lt;/li&gt;&#xA;&lt;li&gt;拼装全部城市 URL 入队列&lt;/li&gt;&#xA;&lt;li&gt;解析二手车页面结构&lt;/li&gt;&#xA;&lt;li&gt;下一页 URL 入队列&lt;/li&gt;&#xA;&lt;li&gt;循环拉取所有分页的二手车数据&lt;/li&gt;&#xA;&lt;li&gt;循环拉取队列中城市的二手车数据&lt;/li&gt;&#xA;&lt;li&gt;等待，确定队列中无新的 URL&lt;/li&gt;&#xA;&lt;li&gt;爬取的二手车数据入库&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;获取城市&#34;&gt;获取城市&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/03/31/5abeff11ef583.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;通过页面查看，可发现在城市筛选区可得到全部的二手车城市列表，但是你仔细查阅代码。会发现它是 JS 加载进来的，城市也统一放在了一个变量中&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/03/31/5abf056389cf0.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;有两种提取方法&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;分析 JS 变量，提取出来&lt;/li&gt;&#xA;&lt;li&gt;直接将 &lt;code&gt;areaJson&lt;/code&gt; 复制出来作为变量解析&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在这里我们直接将其复制粘贴出来即可，因为这是比较少变动的值&lt;/p&gt;&#xA;&lt;h3 id=&#34;获取分页&#34;&gt;获取分页&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/03/31/5abf08ec812e2.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;通过分析页面可以得知分页链接是有一定规律的，例如：&lt;code&gt;/2sc/hangzhou/a0_0msdgscncgpi1ltocsp2exb4/&lt;/code&gt;，可以发现 &lt;code&gt;sp%d&lt;/code&gt;，&lt;code&gt;sp&lt;/code&gt; 后面为页码&lt;/p&gt;&#xA;&lt;p&gt;按照常理，可以通过预测所有分页链接，推入队列后 &lt;code&gt;go routine&lt;/code&gt; 一波 即可快速拉取&lt;/p&gt;&#xA;&lt;p&gt;但是在这老产品库存在一个问题，在超过 100 页后，下一页永远是 101 页&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/03/31/5abf0e1e623ec.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;因此我们采取比较传统的做法，通过拉取下一页的链接去访问，以便适应可能的分页链接改变； 100 页以后的分页展示也很奇怪，先忽视&lt;/p&gt;&#xA;&lt;h3 id=&#34;获取二手车数据&#34;&gt;获取二手车数据&lt;/h3&gt;&#xA;&lt;p&gt;页面结构较为固定，常规的清洗 HTML 即可&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetCars&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;doc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;goquery&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Document&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;cars&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;QcCar&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;cityName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetCityName&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;doc&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;doc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.piclist ul li:not(.line)&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Each&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;goquery&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Selection&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.title a&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Text&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;price&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.detail .detail-r&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.colf8&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Text&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;kilometer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.detail .detail-l&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;p&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Eq&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Text&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;year&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.detail .detail-l&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;p&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Eq&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Text&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;kilometer&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;compileNumber&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FindAllString&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;kilometer&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;year&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;compileNumber&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FindAllString&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TrimSpace&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;year&lt;/span&gt;), &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;priceS&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ParseFloat&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;price&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;kilometerS&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ParseFloat&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;kilometer&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;yearS&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Atoi&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;year&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;cars&lt;/span&gt; = append(&lt;span style=&#34;color:#a6e22e&#34;&gt;cars&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;QcCar&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;CityName&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;cityName&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Price&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;priceS&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Kilometer&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;kilometerS&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Year&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;yearS&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cars&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;数据&#34;&gt;数据&lt;/h2&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/03/31/5abf1d8042196.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>「番外」Golang 交叉编译</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-03-26-cgo/</link>
      <pubDate>Mon, 26 Mar 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-03-26-cgo/</guid>
      <description>&lt;h2 id=&#34;知识点&#34;&gt;知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;跨平台编译&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;在 &lt;a href=&#34;https://segmentfault.com/a/1190000013960558&#34;&gt;连载九&lt;/a&gt; 讲解&lt;strong&gt;构建 Scratch 镜像&lt;/strong&gt;时，我们编译可执行文件用了另外一个形式的命令，如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o go-gin-example .&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我想你可能会有疑问，今天本文会针对这块进行讲解。&lt;/p&gt;&#xA;&lt;h2 id=&#34;说明&#34;&gt;说明&lt;/h2&gt;&#xA;&lt;p&gt;我们将讲解命令各个参数的作用，希望你在阅读时，将每一项串联起来，你会发现这就是&lt;strong&gt;交叉编译相关的小知识&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;也就是 &lt;code&gt;Golang&lt;/code&gt; 令人心动的特性之一&lt;strong&gt;跨平台编译&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;一cgo_enabled&#34;&gt;一、CGO_ENABLED&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;作用：&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;用于标识（声明） &lt;code&gt;cgo&lt;/code&gt; 工具是否可用&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;意义：&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;存在交叉编译的情况时，&lt;code&gt;cgo&lt;/code&gt; 工具是不可用的。在标准 go 命令的上下文环境中，交叉编译意味着程序构建环境的目标计算架构的标识与程序运行环境的目标计算架构的标识不同，或者程序构建环境的目标操作系统的标识与程序运行环境的目标操作系统的标识不同&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;小结：&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;结合案例来说，我们是在宿主机编译的可执行文件，而在 &lt;code&gt;Scratch&lt;/code&gt; 镜像运行的可执行文件；显然两者的计算机架构、运行环境标识你无法确定它是否一致（毕竟构建的 &lt;code&gt;docker&lt;/code&gt; 镜像还可以给他人使用），那么我们就要进行交叉编译，而交叉编译不支持 &lt;code&gt;cgo&lt;/code&gt;，因此这里要禁用掉它&lt;/p&gt;&#xA;&lt;p&gt;关闭 &lt;code&gt;cgo&lt;/code&gt; 后，在构建过程中会忽略 &lt;code&gt;cgo&lt;/code&gt; 并静态链接所有的依赖库，而开启 &lt;code&gt;cgo&lt;/code&gt; 后，方式将转为动态链接&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;补充：&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;golang&lt;/code&gt; 是默认开启 &lt;code&gt;cgo&lt;/code&gt; 工具的，可执行 &lt;code&gt;go env&lt;/code&gt; 命令查看&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go env&#xA;GOARCH=&amp;#34;amd64&amp;#34;&#xA;GOBIN=&amp;#34;&amp;#34;&#xA;GOCACHE=&amp;#34;/root/.cache/go-build&amp;#34;&#xA;GOEXE=&amp;#34;&amp;#34;&#xA;GOHOSTARCH=&amp;#34;amd64&amp;#34;&#xA;GOHOSTOS=&amp;#34;linux&amp;#34;&#xA;GOOS=&amp;#34;linux&amp;#34;&#xA;...&#xA;GCCGO=&amp;#34;gccgo&amp;#34;&#xA;CC=&amp;#34;gcc&amp;#34;&#xA;CXX=&amp;#34;g++&amp;#34;&#xA;CGO_ENABLED=&amp;#34;1&amp;#34;&#xA;...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;二goos&#34;&gt;二、GOOS&lt;/h3&gt;&#xA;&lt;p&gt;用于标识（声明）程序构建环境的目标操作系统&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载九」将Golang应用部署到Docker</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-03-24-golang-docker/</link>
      <pubDate>Sat, 24 Mar 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-03-24-golang-docker/</guid>
      <description>&lt;h2 id=&#34;涉及知识点&#34;&gt;涉及知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Go + Docker&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;将我们的 &lt;code&gt;go-gin-example&lt;/code&gt; 应用部署到一个 Docker 里，你需要先准备好如下东西：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;你需要安装好 &lt;code&gt;docker&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;如果上外网比较吃力，需要配好镜像源。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;docker&#34;&gt;Docker&lt;/h2&gt;&#xA;&lt;p&gt;在这里简单介绍下 Docker，建议深入学习&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://timgsa.baidu.com/timg?image&amp;amp;quality=80&amp;amp;size=b9999_10000&amp;amp;sec=1521800047226&amp;amp;di=28b2764fccca8a943aea7d79ad8aed98&amp;amp;imgtype=0&amp;amp;src=http%3A%2F%2Fwww.cww.net.cn%2FupLoadFile%2F2014%2F6%2F13%2F201461382247734.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Docker 是一个开源的轻量级容器技术，让开发者可以打包他们的应用以及应用运行的上下文环境到一个可移植的镜像中，然后发布到任何支持 Docker 的系统上运行。 通过容器技术，在几乎没有性能开销的情况下，Docker 为应用提供了一个隔离运行环境&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;简化配置&lt;/li&gt;&#xA;&lt;li&gt;代码流水线管理&lt;/li&gt;&#xA;&lt;li&gt;提高开发效率&lt;/li&gt;&#xA;&lt;li&gt;隔离应用&lt;/li&gt;&#xA;&lt;li&gt;快速、持续部署&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;接下来我们正式开始对项目进行 &lt;code&gt;docker&lt;/code&gt; 的所需处理和编写，每一个大标题为步骤大纲&lt;/p&gt;&#xA;&lt;h2 id=&#34;golang&#34;&gt;Golang&lt;/h2&gt;&#xA;&lt;h3 id=&#34;一编写-dockerfile&#34;&gt;一、编写 Dockerfile&lt;/h3&gt;&#xA;&lt;p&gt;在 &lt;code&gt;go-gin-example&lt;/code&gt; 项目根目录创建 Dockerfile 文件，写入内容&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;FROM golang:latest&#xA;&#xA;ENV GOPROXY https://goproxy.cn,direct&#xA;WORKDIR $GOPATH/src/github.com/EDDYCJY/go-gin-example&#xA;COPY . $GOPATH/src/github.com/EDDYCJY/go-gin-example&#xA;RUN go build .&#xA;&#xA;EXPOSE 8000&#xA;ENTRYPOINT [&amp;#34;./go-gin-example&amp;#34;]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;作用&#34;&gt;作用&lt;/h4&gt;&#xA;&lt;p&gt;&lt;code&gt;golang:latest&lt;/code&gt; 镜像为基础镜像，将工作目录设置为 &lt;code&gt;$GOPATH/src/go-gin-example&lt;/code&gt;，并将当前上下文目录的内容复制到 &lt;code&gt;$GOPATH/src/go-gin-example&lt;/code&gt; 中&lt;/p&gt;&#xA;&lt;p&gt;在进行 &lt;code&gt;go build&lt;/code&gt; 编译完毕后，将容器启动程序设置为 &lt;code&gt;./go-gin-example&lt;/code&gt;，也就是我们所编译的可执行文件&lt;/p&gt;&#xA;&lt;p&gt;注意 &lt;code&gt;go-gin-example&lt;/code&gt; 在 &lt;code&gt;docker&lt;/code&gt; 容器里编译，并没有在宿主机现场编译&lt;/p&gt;</description>
    </item>
    <item>
      <title>爬取豆瓣电影 Top250</title>
      <link>https://note.lican.site/posts/posts/go/crawler/2018-03-21-douban-top250/</link>
      <pubDate>Wed, 21 Mar 2018 12:30:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/crawler/2018-03-21-douban-top250/</guid>
      <description>&lt;p&gt;爬虫是标配了，看数据那一刻很有趣。第一个就从最最最简单最基础的爬虫开始写起吧！&lt;/p&gt;&#xA;&lt;p&gt;项目地址：https://github.com/go-crawler/douban-movie&lt;/p&gt;&#xA;&lt;h2 id=&#34;目标&#34;&gt;目标&lt;/h2&gt;&#xA;&lt;p&gt;我们的目标站点是 &lt;a href=&#34;https://movie.douban.com/top250&#34;&gt;豆瓣电影 Top250&lt;/a&gt;，估计大家都很眼熟了&lt;/p&gt;&#xA;&lt;p&gt;本次爬取 8 个字段，用于简单的概括分析。具体的字段如下：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/03/20/5ab11596b8810.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;简单的分析一下目标源&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;一页共 25 条&lt;/li&gt;&#xA;&lt;li&gt;含分页（共 10 页）且分页规则是正常的&lt;/li&gt;&#xA;&lt;li&gt;每一项的数据字段排序都是规则且不变&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;开始&#34;&gt;开始&lt;/h2&gt;&#xA;&lt;p&gt;由于量不大，我们的爬取步骤如下&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;分析页面，获取所有的分页&lt;/li&gt;&#xA;&lt;li&gt;分析页面，循环爬取所有页面的电影信息&lt;/li&gt;&#xA;&lt;li&gt;爬取的电影信息入库&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;安装&#34;&gt;安装&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go get -u github.com/PuerkitoBio/goquery&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;运行&#34;&gt;运行&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go run main.go&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;代码片段&#34;&gt;代码片段&lt;/h3&gt;&#xA;&lt;h4 id=&#34;1获取所有分页&#34;&gt;1、获取所有分页&lt;/h4&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ParsePages&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;doc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;goquery&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Document&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;pages&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Page&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;pages&lt;/span&gt; = append(&lt;span style=&#34;color:#a6e22e&#34;&gt;pages&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Page&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;Page&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Url&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;doc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#content &amp;gt; div &amp;gt; div.article &amp;gt; div.paginator &amp;gt; a&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Each&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;goquery&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Selection&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;page&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Atoi&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Text&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Attr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;pages&lt;/span&gt; = append(&lt;span style=&#34;color:#a6e22e&#34;&gt;pages&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Page&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Page&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;page&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Url&lt;/span&gt;:  &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pages&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;2分析豆瓣电影信息&#34;&gt;2、分析豆瓣电影信息&lt;/h4&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ParseMovies&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;doc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;goquery&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Document&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;movies&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Movie&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;doc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#content &amp;gt; div &amp;gt; div.article &amp;gt; ol &amp;gt; li&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Each&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;goquery&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Selection&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.hd a span&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Eq&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Text&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;movieDesc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Split&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;DescInfo&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;year&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TrimSpace&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;movieDesc&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;area&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TrimSpace&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;movieDesc&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;tag&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TrimSpace&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;movieDesc&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;star&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.bd .star .rating_num&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Text&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;comment&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TrimSpace&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.bd .star span&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Eq&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Text&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;compile&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;regexp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MustCompile&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[0-9]&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;comment&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;compile&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FindAllString&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;comment&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;quote&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Find&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.quote .inq&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Text&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;i: %d, movie: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;movie&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;movies&lt;/span&gt; = append(&lt;span style=&#34;color:#a6e22e&#34;&gt;movies&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;movie&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;movies&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;数据&#34;&gt;数据&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2018/03/21/5ab1309594741.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载八」为它加上Swagger</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-03-18-swagger/</link>
      <pubDate>Sun, 18 Mar 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-03-18-swagger/</guid>
      <description>&lt;h2 id=&#34;涉及知识点&#34;&gt;涉及知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Swagger&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;一个好的 &lt;code&gt;API&#39;s&lt;/code&gt;，必然离不开一个好的&lt;code&gt;API&lt;/code&gt;文档，如果要开发纯手写 &lt;code&gt;API&lt;/code&gt; 文档，不存在的（很难持续维护），因此我们要自动生成接口文档。&lt;/p&gt;&#xA;&lt;h2 id=&#34;安装-swag&#34;&gt;安装 swag&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go get -u github.com/swaggo/swag/cmd/swag@v1.6.5&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;若 &lt;code&gt;$GOROOT/bin&lt;/code&gt; 没有加入&lt;code&gt;$PATH&lt;/code&gt;中，你需要执行将其可执行文件移动到&lt;code&gt;$GOBIN&lt;/code&gt;下&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mv $GOPATH/bin/swag /usr/local/go/bin&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;验证是否安装成功&#34;&gt;验证是否安装成功&lt;/h3&gt;&#xA;&lt;p&gt;检查 $GOBIN 下是否有 swag 文件，如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ swag -v&#xA;swag version v1.6.5&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;安装-gin-swagger&#34;&gt;安装 gin-swagger&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go get -u github.com/swaggo/gin-swagger@v1.2.0 &#xA;$ go get -u github.com/swaggo/files&#xA;$ go get -u github.com/alecthomas/template&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注：若无科学上网，请务必配置 Go modules proxy。&lt;/p&gt;&#xA;&lt;h2 id=&#34;初始化&#34;&gt;初始化&lt;/h2&gt;&#xA;&lt;h3 id=&#34;编写-api-注释&#34;&gt;编写 API 注释&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;Swagger&lt;/code&gt; 中需要将相应的注释或注解编写到方法上，再利用生成器自动生成说明文件&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;gin-swagger&lt;/code&gt; 给出的范例：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @Summary Add a new pet to the store&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @Description get string by ID&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @Accept  json&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @Produce  json&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @Param   some_id     path    int     true        &amp;#34;Some ID&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @Success 200 {string} string&#x9;&amp;#34;ok&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @Failure 400 {object} web.APIError &amp;#34;We need ID!!&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @Failure 404 {object} web.APIError &amp;#34;Can not find ID&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// @Router /testapi/get-string-by-int/{some_id} [get]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们可以参照 &lt;code&gt;Swagger&lt;/code&gt; 的注解规范和范例去编写&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载七」优雅的重启服务</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-03-15-reload-http/</link>
      <pubDate>Thu, 15 Mar 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-03-15-reload-http/</guid>
      <description>&lt;h2 id=&#34;知识点&#34;&gt;知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;信号量的了解。&lt;/li&gt;&#xA;&lt;li&gt;应用热更新。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;在前面编写案例代码时，我相信你会想到，每次更新完代码，更新完配置文件后，就直接这么 &lt;code&gt;ctrl+c&lt;/code&gt; 真的没问题吗，&lt;code&gt;ctrl+c&lt;/code&gt;到底做了些什么事情呢？&lt;/p&gt;&#xA;&lt;p&gt;在这一节中我们简单讲述 &lt;code&gt;ctrl+c&lt;/code&gt; 背后的&lt;strong&gt;信号&lt;/strong&gt;以及如何在&lt;code&gt;Gin&lt;/code&gt;中&lt;strong&gt;优雅的重启服务&lt;/strong&gt;，也就是对 &lt;code&gt;HTTP&lt;/code&gt; 服务进行热更新。&lt;/p&gt;&#xA;&lt;h2 id=&#34;ctrl--c&#34;&gt;ctrl + c&lt;/h2&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;内核在某些情况下发送信号，比如在进程往一个已经关闭的管道写数据时会产生&lt;code&gt;SIGPIPE&lt;/code&gt;信号&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;在终端执行特定的组合键可以使系统发送特定的信号给此进程，完成一系列的动作&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;命令&lt;/th&gt;&#xA;          &lt;th&gt;信号&lt;/th&gt;&#xA;          &lt;th&gt;含义&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;ctrl + c&lt;/td&gt;&#xA;          &lt;td&gt;SIGINT&lt;/td&gt;&#xA;          &lt;td&gt;强制进程结束&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;ctrl + z&lt;/td&gt;&#xA;          &lt;td&gt;SIGTSTP&lt;/td&gt;&#xA;          &lt;td&gt;任务中断，进程挂起&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;ctrl + \&lt;/td&gt;&#xA;          &lt;td&gt;SIGQUIT&lt;/td&gt;&#xA;          &lt;td&gt;进程结束 和 &lt;code&gt;dump core&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;ctrl + d&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;EOF&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;SIGHUP&lt;/td&gt;&#xA;          &lt;td&gt;终止收到该信号的进程。若程序中没有捕捉该信号，当收到该信号时，进程就会退出（常用于 重启、重新加载进程）&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;因此在我们执行&lt;code&gt;ctrl + c&lt;/code&gt;关闭&lt;code&gt;gin&lt;/code&gt;服务端时，&lt;strong&gt;会强制进程结束，导致正在访问的用户等出现问题&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;常见的 &lt;code&gt;kill -9 pid&lt;/code&gt; 会发送 &lt;code&gt;SIGKILL&lt;/code&gt; 信号给进程，也是类似的结果&lt;/p&gt;&#xA;&lt;h3 id=&#34;信号&#34;&gt;信号&lt;/h3&gt;&#xA;&lt;p&gt;本段中反复出现&lt;strong&gt;信号&lt;/strong&gt;是什么呢？&lt;/p&gt;&#xA;&lt;p&gt;信号是 &lt;code&gt;Unix&lt;/code&gt; 、类 &lt;code&gt;Unix&lt;/code&gt; 以及其他 &lt;code&gt;POSIX&lt;/code&gt; 兼容的操作系统中进程间通讯的一种有限制的方式&lt;/p&gt;&#xA;&lt;p&gt;它是一种异步的通知机制，用来提醒进程一个事件（硬件异常、程序执行异常、外部发出信号）已经发生。当一个信号发送给一个进程，操作系统中断了进程正常的控制流程。此时，任何非原子操作都将被中断。如果进程定义了信号的处理函数，那么它将被执行，否则就执行默认的处理函数&lt;/p&gt;&#xA;&lt;h3 id=&#34;所有信号&#34;&gt;所有信号&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ kill -l&#xA; 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP&#xA; 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1&#xA;11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM&#xA;16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP&#xA;21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ&#xA;26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR&#xA;31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3&#xA;38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8&#xA;43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13&#xA;48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12&#xA;53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7&#xA;58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2&#xA;63) SIGRTMAX-1  64) SIGRTMAX&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;怎样算优雅&#34;&gt;怎样算优雅&lt;/h2&gt;&#xA;&lt;h3 id=&#34;目的&#34;&gt;目的&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;不关闭现有连接（正在运行中的程序）&lt;/li&gt;&#xA;&lt;li&gt;新的进程启动并替代旧进程&lt;/li&gt;&#xA;&lt;li&gt;新的进程接管新的连接&lt;/li&gt;&#xA;&lt;li&gt;连接要随时响应用户的请求，当用户仍在请求旧进程时要保持连接，新用户应请求新进程，不可以出现拒绝请求的情况&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;流程&#34;&gt;流程&lt;/h3&gt;&#xA;&lt;p&gt;1、替换可执行文件或修改配置文件&lt;/p&gt;</description>
    </item>
    <item>
      <title>聊一聊，Go 的相对路径问题</title>
      <link>https://note.lican.site/posts/posts/go/talk/2018-03-13-golang-relatively-path/</link>
      <pubDate>Tue, 13 Mar 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/talk/2018-03-13-golang-relatively-path/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;Golang&lt;/code&gt; 中存在各种运行方式，如何&lt;strong&gt;正确的引用文件路径&lt;/strong&gt;成为一个值得商议的问题&lt;/p&gt;&#xA;&lt;p&gt;以 &lt;a href=&#34;https://github.com/EDDYCJY/go-gin-example&#34;&gt;gin-blog&lt;/a&gt; 为例，当我们在项目根目录下，执行 &lt;code&gt;go run main.go&lt;/code&gt; 时能够正常运行（&lt;code&gt;go build&lt;/code&gt;也是正常的）&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[$ gin-blog]# go run main.go&#xA;[GIN-debug] [WARNING] Running in &amp;#34;debug&amp;#34; mode. Switch to &amp;#34;release&amp;#34; mode in production.&#xA; - using env:    export GIN_MODE=release&#xA; - using code:    gin.SetMode(gin.ReleaseMode)&#xA;&#xA;[GIN-debug] GET    /api/v1/tags              --&amp;gt; gin-blog/routers/api/v1.GetTags (3 handlers)&#xA;...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;那么在不同的目录层级下，不同的方式运行，又是怎么样的呢，带着我们的疑问去学习&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题&#34;&gt;问题&lt;/h2&gt;&#xA;&lt;p&gt;1、 go run&#xA;我们上移目录层级，到 &lt;code&gt;$GOPATH/src&lt;/code&gt; 下，执行 &lt;code&gt;go run gin-blog/main.go&lt;/code&gt;&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[$ src]# go run gin-blog/main.go&#xA;2018/03/12 16:06:13 Fail to parse &amp;#39;conf/app.ini&amp;#39;: open conf/app.ini: no such file or directory&#xA;exit status 1&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;2、 go build，执行 &lt;code&gt;./gin-blog/main&lt;/code&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载三」Swagger了解一下</title>
      <link>https://note.lican.site/posts/posts/go/grpc-gateway/2018-03-04-swagger/</link>
      <pubDate>Sun, 04 Mar 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc-gateway/2018-03-04-swagger/</guid>
      <description>&lt;p&gt;在&lt;a href=&#34;https://segmentfault.com/a/1190000013408485&#34;&gt;上一节&lt;/a&gt;，我们完成了一个服务端同时支持&lt;code&gt;Rpc&lt;/code&gt;和&lt;code&gt;RESTful Api&lt;/code&gt;后，你以为自己大功告成了，结果突然发现要写&lt;code&gt;Api&lt;/code&gt;文档和前端同事对接= = 。。。&lt;/p&gt;&#xA;&lt;p&gt;你寻思有没有什么组件能够自动化生成&lt;code&gt;Api&lt;/code&gt;文档来解决这个问题，就在这时你发现了&lt;code&gt;Swagger&lt;/code&gt;，一起了解一下吧！&lt;/p&gt;&#xA;&lt;h2 id=&#34;介绍&#34;&gt;介绍&lt;/h2&gt;&#xA;&lt;h3 id=&#34;swagger&#34;&gt;Swagger&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;Swagger&lt;/code&gt;是全球最大的&lt;code&gt;OpenAPI&lt;/code&gt;规范（OAS）API开发工具框架，支持从设计和文档到测试和部署的整个API生命周期的开发&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;Swagger&lt;/code&gt;是目前最受欢迎的&lt;code&gt;RESTful Api&lt;/code&gt;文档生成工具之一，主要的原因如下&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;跨平台、跨语言的支持&lt;/li&gt;&#xA;&lt;li&gt;强大的社区&lt;/li&gt;&#xA;&lt;li&gt;生态圈 Swagger Tools（&lt;a href=&#34;https://github.com/swagger-api/swagger-editor&#34;&gt;Swagger Editor&lt;/a&gt;、&lt;a href=&#34;https://github.com/swagger-api/swagger-codegen&#34;&gt;Swagger Codegen&lt;/a&gt;、&lt;a href=&#34;https://github.com/swagger-api/swagger-ui&#34;&gt;Swagger UI&lt;/a&gt; &amp;hellip;）&lt;/li&gt;&#xA;&lt;li&gt;强大的控制台&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;同时&lt;code&gt;grpc-gateway&lt;/code&gt;也支持&lt;code&gt;Swagger&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;[image]&lt;/p&gt;&#xA;&lt;h3 id=&#34;openapi规范&#34;&gt;&lt;code&gt;OpenAPI&lt;/code&gt;规范&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;OpenAPI&lt;/code&gt;规范是&lt;code&gt;Linux&lt;/code&gt;基金会的一个项目，试图通过定义一种用来描述API格式或API定义的语言，来规范&lt;code&gt;RESTful&lt;/code&gt;服务开发过程。&lt;code&gt;OpenAPI&lt;/code&gt;规范帮助我们描述一个API的基本信息，比如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;有关该API的一般性描述&lt;/li&gt;&#xA;&lt;li&gt;可用路径（/资源）&lt;/li&gt;&#xA;&lt;li&gt;在每个路径上的可用操作（获取/提交&amp;hellip;）&lt;/li&gt;&#xA;&lt;li&gt;每个操作的输入/输出格式&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;目前V2.0版本的&lt;a href=&#34;https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md&#34;&gt;OpenAPI规范&lt;/a&gt;（也就是SwaggerV2.0规范）已经发布并开源在github上。该文档写的非常好，结构清晰，方便随时查阅。&lt;/p&gt;&#xA;&lt;p&gt;注：&lt;code&gt;OpenAPI&lt;/code&gt;规范的介绍引用自&lt;a href=&#34;https://huangwenchao.gitbooks.io/swagger/content/&#34;&gt;原文&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;使用&#34;&gt;使用&lt;/h2&gt;&#xA;&lt;h3 id=&#34;生成swagger的说明文件&#34;&gt;生成&lt;code&gt;Swagger&lt;/code&gt;的说明文件&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;第一&lt;/strong&gt;，我们需要检查$GOBIN下是否包含&lt;code&gt;protoc-gen-swagger&lt;/code&gt;可执行文件&lt;/p&gt;&#xA;&lt;p&gt;若不存在则需要执行：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;等待执行完毕后，可在&lt;code&gt;$GOPATH/bin&lt;/code&gt;下发现该执行文件，将其移动到&lt;code&gt;$GOBIN&lt;/code&gt;下即可&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;第二&lt;/strong&gt;，回到&lt;code&gt;$GOPATH/src/grpc-hello-world/proto&lt;/code&gt;下，执行命令&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;protoc -I/usr/local/include -I. -I$GOPATH/src/grpc-hello-world/proto/google/api --swagger_out=logtostderr=true:. ./hello.proto&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;成功后执行&lt;code&gt;ls&lt;/code&gt;即可看到&lt;code&gt;hello.swagger.json&lt;/code&gt;文件&lt;/p&gt;&#xA;&lt;h3 id=&#34;下载swagger-ui文件&#34;&gt;下载&lt;code&gt;Swagger UI&lt;/code&gt;文件&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;Swagger&lt;/code&gt;提供可视化的&lt;code&gt;API&lt;/code&gt;管理平台，就是&lt;a href=&#34;https://github.com/swagger-api/swagger-ui&#34;&gt;Swagger UI&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;我们将其源码下载下来，并将其&lt;code&gt;dist&lt;/code&gt;目录下的所有文件拷贝到我们项目中的&lt;code&gt;$GOPATH/src/grpc-hello-world/third_party/swagger-ui&lt;/code&gt;去&lt;/p&gt;&#xA;&lt;h3 id=&#34;将swagger-ui转换为go源代码&#34;&gt;将&lt;code&gt;Swagger UI&lt;/code&gt;转换为&lt;code&gt;Go&lt;/code&gt;源代码&lt;/h3&gt;&#xA;&lt;p&gt;在这里我们使用的转换工具是&lt;a href=&#34;https://github.com/jteeuwen/go-bindata&#34;&gt;go-bindata&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;它支持将任何文件转换为可管理的&lt;code&gt;Go&lt;/code&gt;源代码。用于将二进制数据嵌入到&lt;code&gt;Go&lt;/code&gt;程序中。并且在将文件数据转换为原始字节片之前，可以选择压缩文件数据&lt;/p&gt;&#xA;&lt;h4 id=&#34;安装&#34;&gt;安装&lt;/h4&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go get -u github.com/jteeuwen/go-bindata/...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;完成后，将&lt;code&gt;$GOPATH/bin&lt;/code&gt;下的&lt;code&gt;go-bindata&lt;/code&gt;移动到&lt;code&gt;$GOBIN&lt;/code&gt;下&lt;/p&gt;&#xA;&lt;h4 id=&#34;转换&#34;&gt;转换&lt;/h4&gt;&#xA;&lt;p&gt;在项目下新建&lt;code&gt;pkg/ui/data/swagger&lt;/code&gt;目录，回到&lt;code&gt;$GOPATH/src/grpc-hello-world/third_party/swagger-ui&lt;/code&gt;下，执行命令&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go-bindata --nocompress -pkg swagger -o pkg/ui/data/swagger/datafile.go third_party/swagger-ui/...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;检查&#34;&gt;检查&lt;/h4&gt;&#xA;&lt;p&gt;回到&lt;code&gt;pkg/ui/data/swagger&lt;/code&gt;目录，检查是否存在&lt;code&gt;datafile.go&lt;/code&gt;文件&lt;/p&gt;&#xA;&lt;h3 id=&#34;swagger-ui文件服务器对外提供服务&#34;&gt;&lt;code&gt;Swagger UI&lt;/code&gt;文件服务器（对外提供服务）&lt;/h3&gt;&#xA;&lt;p&gt;在这一步，我们需要使用与其配套的&lt;a href=&#34;https://github.com/elazarl/go-bindata-assetfs/&#34;&gt;go-bindata-assetfs&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载二」Hello World</title>
      <link>https://note.lican.site/posts/posts/go/grpc-gateway/2018-02-27-hello-world/</link>
      <pubDate>Tue, 27 Feb 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc-gateway/2018-02-27-hello-world/</guid>
      <description>&lt;p&gt;这节将开始编写一个复杂的Hello World，涉及到许多的知识，建议大家认真思考其中的概念&lt;/p&gt;&#xA;&lt;h2 id=&#34;需求&#34;&gt;需求&lt;/h2&gt;&#xA;&lt;p&gt;由于本实践偏向&lt;code&gt;Grpc&lt;/code&gt;+&lt;code&gt;Grpc Gateway&lt;/code&gt;的方面，我们的需求是&lt;strong&gt;同一个服务端支持&lt;code&gt;Rpc&lt;/code&gt;和&lt;code&gt;Restful Api&lt;/code&gt;&lt;/strong&gt;，那么就意味着&lt;code&gt;http2&lt;/code&gt;、&lt;code&gt;TLS&lt;/code&gt;等等的应用，功能方面就是一个服务端能够接受来自&lt;code&gt;grpc&lt;/code&gt;和&lt;code&gt;Restful Api&lt;/code&gt;的请求并响应&lt;/p&gt;&#xA;&lt;h2 id=&#34;一初始化目录&#34;&gt;一、初始化目录&lt;/h2&gt;&#xA;&lt;p&gt;我们先在$GOPATH中新建&lt;code&gt;grpc-hello-world&lt;/code&gt;文件夹，我们项目的初始目录目录如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;grpc-hello-world/&#xA;├── certs&#xA;├── client&#xA;├── cmd&#xA;├── pkg&#xA;├── proto&#xA;│   ├── google&#xA;│   │   └── api&#xA;└── server&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;certs&lt;/code&gt;：证书凭证&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;client&lt;/code&gt;：客户端&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;cmd&lt;/code&gt;：命令行&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;pkg&lt;/code&gt;：第三方公共模块&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;proto&lt;/code&gt;：&lt;code&gt;protobuf&lt;/code&gt;的一些相关文件（含&lt;code&gt;.proto&lt;/code&gt;、&lt;code&gt;pb.go&lt;/code&gt;、&lt;code&gt;.pb.gw.go&lt;/code&gt;)，&lt;code&gt;google/api&lt;/code&gt;中用于存放&lt;code&gt;annotations.proto&lt;/code&gt;、&lt;code&gt;http.proto&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;server&lt;/code&gt;：服务端&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;二制作证书&#34;&gt;二、制作证书&lt;/h2&gt;&#xA;&lt;p&gt;在服务端支持&lt;code&gt;Rpc&lt;/code&gt;和&lt;code&gt;Restful Api&lt;/code&gt;，需要用到&lt;code&gt;TLS&lt;/code&gt;，因此我们要先制作证书&lt;/p&gt;&#xA;&lt;p&gt;进入&lt;code&gt;certs&lt;/code&gt;目录，生成&lt;code&gt;TLS&lt;/code&gt;所需的公钥密钥文件&lt;/p&gt;&#xA;&lt;h3 id=&#34;私钥&#34;&gt;私钥&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl genrsa -out server.key 2048&#xA;&#xA;openssl ecparam -genkey -name secp384r1 -out server.key&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;openssl genrsa&lt;/code&gt;：生成&lt;code&gt;RSA&lt;/code&gt;私钥，命令的最后一个参数，将指定生成密钥的位数，如果没有指定，默认512&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;openssl ecparam&lt;/code&gt;：生成&lt;code&gt;ECC&lt;/code&gt;私钥，命令为椭圆曲线密钥参数生成及操作，本文中&lt;code&gt;ECC&lt;/code&gt;曲线选择的是&lt;code&gt;secp384r1&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;自签名公钥&#34;&gt;自签名公钥&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;openssl req&lt;/code&gt;：生成自签名证书，&lt;code&gt;-new&lt;/code&gt;指生成证书请求、&lt;code&gt;-sha256&lt;/code&gt;指使用&lt;code&gt;sha256&lt;/code&gt;加密、&lt;code&gt;-key&lt;/code&gt;指定私钥文件、&lt;code&gt;-x509&lt;/code&gt;指输出证书、&lt;code&gt;-days 3650&lt;/code&gt;为有效期，此后则输入证书拥有者信息&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;填写信息&#34;&gt;填写信息&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Country Name (2 letter code) [XX]:&#xA;State or Province Name (full name) []:&#xA;Locality Name (eg, city) [Default City]:&#xA;Organization Name (eg, company) [Default Company Ltd]:&#xA;Organizational Unit Name (eg, section) []:&#xA;Common Name (eg, your name or your server&amp;#39;s hostname) []:grpc server name&#xA;Email Address []:&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;三proto&#34;&gt;三、&lt;code&gt;proto&lt;/code&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;编写&#34;&gt;编写&lt;/h3&gt;&#xA;&lt;p&gt;1、 &lt;code&gt;google.api&lt;/code&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载一」gRPC介绍与环境安装</title>
      <link>https://note.lican.site/posts/posts/go/grpc-gateway/2018-02-23-install/</link>
      <pubDate>Fri, 23 Feb 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/grpc-gateway/2018-02-23-install/</guid>
      <description>&lt;p&gt;假定我们有一个项目需求，希望用&lt;code&gt;Rpc&lt;/code&gt;作为内部&lt;code&gt;API&lt;/code&gt;的通讯，同时也想对外提供&lt;code&gt;Restful Api&lt;/code&gt;，写两套又太繁琐不符合&lt;/p&gt;&#xA;&lt;p&gt;于是我们想到了&lt;code&gt;Grpc&lt;/code&gt;以及&lt;code&gt;Grpc Gateway&lt;/code&gt;，这就是我们所需要的&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://camo.githubusercontent.com/e75a8b46b078a3c1df0ed9966a16c24add9ccb83/68747470733a2f2f646f63732e676f6f676c652e636f6d2f64726177696e67732f642f3132687034435071724e5046686174744c5f63496f4a707446766c41716d35774c513067677149356d6b43672f7075623f773d37343926683d333730&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;准备环节&#34;&gt;准备环节&lt;/h2&gt;&#xA;&lt;p&gt;在正式开始我们的&lt;code&gt;Grpc&lt;/code&gt;+&lt;code&gt;Grpc Gateway&lt;/code&gt;实践前，我们需要先配置好我们的开发环境&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Grpc&lt;/li&gt;&#xA;&lt;li&gt;Protoc Plugin&lt;/li&gt;&#xA;&lt;li&gt;Protocol Buffers&lt;/li&gt;&#xA;&lt;li&gt;Grpc-gateway&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;grpc&#34;&gt;Grpc&lt;/h2&gt;&#xA;&lt;h3 id=&#34;是什么&#34;&gt;是什么&lt;/h3&gt;&#xA;&lt;p&gt;Google对&lt;code&gt;Grpc&lt;/code&gt;的定义：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;A high performance, open-source universal RPC framework&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;也就是&lt;code&gt;Grpc&lt;/code&gt;是一个高性能、开源的通用RPC框架，具有以下特性：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;强大的&lt;code&gt;IDL&lt;/code&gt;，使用&lt;code&gt;Protocol Buffers&lt;/code&gt;作为数据交换的格式，支持&lt;code&gt;v2&lt;/code&gt;、&lt;code&gt;v3&lt;/code&gt;（推荐&lt;code&gt;v3&lt;/code&gt;）&lt;/li&gt;&#xA;&lt;li&gt;跨语言、跨平台，也就是&lt;code&gt;Grpc&lt;/code&gt;支持多种平台和语言&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;支持HTTP2&lt;/strong&gt;，双向传输、多路复用、认证等&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;安装&#34;&gt;安装&lt;/h3&gt;&#xA;&lt;p&gt;1、官方推荐（需科学上网）&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go get -u google.golang.org/grpc&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;2、通过&lt;code&gt;github.com&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;进入到第一个$GOPATH目录（因为&lt;code&gt;go get&lt;/code&gt; 会默认安装在第一个下）下，新建&lt;code&gt;google.golang.org&lt;/code&gt;目录，拉取&lt;code&gt;golang&lt;/code&gt;在&lt;code&gt;github&lt;/code&gt;上的镜像库：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cd /usr/local/go/path/src   &#xA;&#xA;mkdir google.golang.org&#xA;&#xA;cd google.golang.org/&#xA;&#xA;git clone https://github.com/grpc/grpc-go&#xA;&#xA;mv grpc-go/ grpc/&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;目录结构：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;google.golang.org/&#xA;└── grpc&#xA;    ...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;而在&lt;code&gt;grpc&lt;/code&gt;下有许多常用的包，例如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gowalker.org/google.golang.org/grpc/metadata&#34;&gt;metadata&lt;/a&gt;：定义了&lt;code&gt;grpc&lt;/code&gt;所支持的元数据结构，包中方法可以对&lt;code&gt;MD&lt;/code&gt;进行获取和处理&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gowalker.org/google.golang.org/grpc/credentials&#34;&gt;credentials&lt;/a&gt;：实现了&lt;code&gt;grpc&lt;/code&gt;所支持的各种认证凭据，封装了客户端对服务端进行身份验证所需要的所有状态，并做出各种断言&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gowalker.org/google.golang.org/grpc/codes&#34;&gt;codes&lt;/a&gt;：定义了&lt;code&gt;grpc&lt;/code&gt;使用的标准错误码，可通用&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;protoc-plugin&#34;&gt;Protoc Plugin&lt;/h2&gt;&#xA;&lt;h3 id=&#34;是什么-1&#34;&gt;是什么&lt;/h3&gt;&#xA;&lt;p&gt;编译器插件&lt;/p&gt;&#xA;&lt;h3 id=&#34;安装-1&#34;&gt;安装&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go get -u github.com/golang/protobuf/protoc-gen-go&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;将&lt;code&gt;Protoc Plugin&lt;/code&gt;的可执行文件从$GOPATH中移动到$GOBIN下&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mv /usr/local/go/path/bin/protoc-gen-go /usr/local/go/bin/&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;protocol-buffers-v3&#34;&gt;Protocol Buffers v3&lt;/h2&gt;&#xA;&lt;h3 id=&#34;是什么-2&#34;&gt;是什么&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Protocol buffers are a flexible, efficient, automated mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages. You can even update your data structure without breaking deployed programs that are compiled against the &amp;ldquo;old&amp;rdquo; format.&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载六」编写一个简单的文件日志</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-02-15-log/</link>
      <pubDate>Thu, 15 Feb 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-02-15-log/</guid>
      <description>&lt;h2 id=&#34;涉及知识点&#34;&gt;涉及知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;自定义 log。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;在上一节中，我们解决了 API&amp;rsquo;s 可以任意访问的问题，那么我们现在还有一个问题，就是我们的日志，都是输出到控制台上的，这显然对于一个项目来说是不合理的，因此我们这一节简单封装&lt;code&gt;log&lt;/code&gt;库，使其支持简单的文件日志！&lt;/p&gt;&#xA;&lt;h2 id=&#34;新建logging包&#34;&gt;新建&lt;code&gt;logging&lt;/code&gt;包&lt;/h2&gt;&#xA;&lt;p&gt;我们在&lt;code&gt;pkg&lt;/code&gt;下新建&lt;code&gt;logging&lt;/code&gt;目录，新建&lt;code&gt;file.go&lt;/code&gt;和&lt;code&gt;log.go&lt;/code&gt;文件，写入内容：&lt;/p&gt;&#xA;&lt;h3 id=&#34;编写file文件&#34;&gt;编写&lt;code&gt;file&lt;/code&gt;文件&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;1、 file.go：&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;logging&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;LogSavePath&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;runtime/logs/&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;LogSaveName&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;LogFileExt&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;TimeFormat&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;20060102&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getLogFilePath&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;LogSavePath&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getLogFileFullPath&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;prefixPath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getLogFilePath&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;suffixPath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%s%s.%s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;LogSaveName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Now&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Format&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;TimeFormat&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;LogFileExt&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%s%s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;prefixPath&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;suffixPath&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;openLogFile&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;filePath&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;File&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Stat&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;filePath&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IsNotExist&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;mkDir&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IsPermission&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatalf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Permission :%v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;handle&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OpenFile&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;filePath&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_APPEND&lt;/span&gt; | &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_CREATE&lt;/span&gt; | &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_WRONLY&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0644&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatalf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Fail to OpenFile :%v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;handle&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mkDir&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;dir&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Getwd&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MkdirAll&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;dir&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getLogFilePath&lt;/span&gt;(), &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ModePerm&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;panic(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;os.Stat&lt;/code&gt;：返回文件信息结构描述文件。如果出现错误，会返回&lt;code&gt;*PathError&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PathError&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Op&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Path&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;Err&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;os.IsNotExist&lt;/code&gt;：能够接受&lt;code&gt;ErrNotExist&lt;/code&gt;、&lt;code&gt;syscall&lt;/code&gt;的一些错误，它会返回一个布尔值，能够得知文件不存在或目录不存在&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;os.IsPermission&lt;/code&gt;：能够接受&lt;code&gt;ErrPermission&lt;/code&gt;、&lt;code&gt;syscall&lt;/code&gt;的一些错误，它会返回一个布尔值，能够得知权限是否满足&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;os.OpenFile&lt;/code&gt;：调用文件，支持传入文件名称、指定的模式调用文件、文件权限，返回的文件的方法可以用于 I/O。如果出现错误，则为&lt;code&gt;*PathError&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;O_RDONLY&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;syscall&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_RDONLY&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// 以只读模式打开文件&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;O_WRONLY&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;syscall&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_WRONLY&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// 以只写模式打开文件&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;O_RDWR&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;syscall&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_RDWR&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// 以读写模式打开文件&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// The remaining values may be or&amp;#39;ed in to control behavior.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;O_APPEND&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;syscall&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_APPEND&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// 在写入时将数据追加到文件中&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;O_CREATE&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;syscall&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_CREAT&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 如果不存在，则创建一个新文件&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;O_EXCL&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;syscall&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_EXCL&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// 使用O_CREATE时，文件必须不存在&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;O_SYNC&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;syscall&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_SYNC&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// 同步IO&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;O_TRUNC&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;syscall&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_TRUNC&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 如果可以，打开时&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;os.Getwd&lt;/code&gt;：返回与当前目录对应的根路径名&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;os.MkdirAll&lt;/code&gt;：创建对应的目录以及所需的子目录，若成功则返回&lt;code&gt;nil&lt;/code&gt;，否则返回&lt;code&gt;error&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;os.ModePerm&lt;/code&gt;：&lt;code&gt;const&lt;/code&gt;定义&lt;code&gt;ModePerm FileMode = 0777&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;编写log文件&#34;&gt;编写&lt;code&gt;log&lt;/code&gt;文件&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;2、log.go&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载五」使用 JWT 进行身份校验</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-02-14-jwt/</link>
      <pubDate>Wed, 14 Feb 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-02-14-jwt/</guid>
      <description>&lt;h2 id=&#34;涉及知识点&#34;&gt;涉及知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;JWT&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;p&gt;在前面几节中，我们已经基本的完成了 API&amp;rsquo;s 的编写，但是，还存在一些非常严重的问题，例如，我们现在的 API 是可以随意调用的，这显然还不安全全，在本文中我们通过 &lt;a href=&#34;https://github.com/dgrijalva/jwt-go&#34;&gt;jwt-go&lt;/a&gt; （&lt;a href=&#34;https://godoc.org/github.com/dgrijalva/jwt-go&#34;&gt;GoDoc&lt;/a&gt;）的方式来简单解决这个问题。&lt;/p&gt;&#xA;&lt;h2 id=&#34;下载依赖包&#34;&gt;下载依赖包&lt;/h2&gt;&#xA;&lt;p&gt;首先，我们下载 jwt-go 的依赖包，如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go get -u github.com/dgrijalva/jwt-go&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;编写-jwt-工具包&#34;&gt;编写 jwt 工具包&lt;/h2&gt;&#xA;&lt;p&gt;我们需要编写一个&lt;code&gt;jwt&lt;/code&gt;的工具包，我们在&lt;code&gt;pkg&lt;/code&gt;下的&lt;code&gt;util&lt;/code&gt;目录新建&lt;code&gt;jwt.go&lt;/code&gt;，写入文件内容：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;util&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;jwt&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/dgrijalva/jwt-go&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/EDDYCJY/go-gin-example/pkg/setting&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;jwtSecret&lt;/span&gt; = []byte(&lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;JwtSecret&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Claims&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Username&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;username&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Password&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;password&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;jwt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;StandardClaims&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GenerateToken&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) (&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;nowTime&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Now&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;expireTime&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;nowTime&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Hour&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;claims&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Claims&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;username&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;jwt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;StandardClaims&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;ExpiresAt&lt;/span&gt; : &lt;span style=&#34;color:#a6e22e&#34;&gt;expireTime&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unix&lt;/span&gt;(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;Issuer&lt;/span&gt; : &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gin-blog&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;tokenClaims&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;jwt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewWithClaims&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;jwt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SigningMethodHS256&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;claims&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;token&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tokenClaims&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SignedString&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;jwtSecret&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;token&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ParseToken&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;token&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) (&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Claims&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;tokenClaims&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;jwt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ParseWithClaims&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;token&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Claims&lt;/span&gt;{}, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;token&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;jwt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Token&lt;/span&gt;) (&lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt;{}, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;jwtSecret&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tokenClaims&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;claims&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ok&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tokenClaims&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Claims&lt;/span&gt;.(&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Claims&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;ok&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tokenClaims&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Valid&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;claims&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个工具包，我们涉及到&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载四」Gin搭建Blog API&#39;s （三）</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-02-13-api-03/</link>
      <pubDate>Tue, 13 Feb 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-02-13-api-03/</guid>
      <description>&lt;h2 id=&#34;涉及知识点&#34;&gt;涉及知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/gin-gonic/gin&#34;&gt;Gin&lt;/a&gt;：Golang 的一个微框架，性能极佳。&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/astaxie/beego/tree/master/validation&#34;&gt;beego-validation&lt;/a&gt;：本节采用的 beego 的表单验证库，&lt;a href=&#34;https://beego.me/docs/mvc/controller/validation.md&#34;&gt;中文文档&lt;/a&gt;。&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/jinzhu/gorm&#34;&gt;gorm&lt;/a&gt;，对开发人员友好的 ORM 框架，&lt;a href=&#34;http://gorm.io/docs/&#34;&gt;英文文档&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/Unknwon/com&#34;&gt;com&lt;/a&gt;，一个小而美的工具包。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;完成博客的文章类接口定义和编写&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;定义接口&#34;&gt;定义接口&lt;/h2&gt;&#xA;&lt;p&gt;本节编写文章的逻辑，我们定义一下接口吧！&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;获取文章列表：GET(&amp;quot;/articles&amp;quot;)&lt;/li&gt;&#xA;&lt;li&gt;获取指定文章：POST(&amp;quot;/articles/:id&amp;quot;)&lt;/li&gt;&#xA;&lt;li&gt;新建文章：POST(&amp;quot;/articles&amp;quot;)&lt;/li&gt;&#xA;&lt;li&gt;更新指定文章：PUT(&amp;quot;/articles/:id&amp;quot;)&lt;/li&gt;&#xA;&lt;li&gt;删除指定文章：DELETE(&amp;quot;/articles/:id&amp;quot;)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;编写路由逻辑&#34;&gt;编写路由逻辑&lt;/h2&gt;&#xA;&lt;p&gt;在&lt;code&gt;routers&lt;/code&gt;的 v1 版本下，新建&lt;code&gt;article.go&lt;/code&gt;文件，写入内容：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gin-gonic/gin&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//获取单个文章&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetArticle&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//获取多个文章&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetArticles&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//新增文章&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AddArticle&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//修改文章&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;EditArticle&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//删除文章&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DeleteArticle&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们打开&lt;code&gt;routers&lt;/code&gt;下的&lt;code&gt;router.go&lt;/code&gt;文件，修改文件内容为：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;routers&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gin-gonic/gin&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/EDDYCJY/go-gin-example/routers/api/v1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/EDDYCJY/go-gin-example/pkg/setting&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;InitRouter&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Engine&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Group&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/api/v1&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//获取文章列表&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GET&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/articles&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetArticles&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//获取指定文章&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GET&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/articles/:id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetArticle&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//新建文章&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;POST&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/articles&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AddArticle&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//更新指定文章&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PUT&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/articles/:id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EditArticle&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//删除指定文章&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DELETE&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/articles/:id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DeleteArticle&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当前目录结构：&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载三」Gin搭建Blog API&#39;s （二）</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-02-12-api-02/</link>
      <pubDate>Mon, 12 Feb 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-02-12-api-02/</guid>
      <description>&lt;h2 id=&#34;涉及知识点&#34;&gt;涉及知识点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/gin-gonic/gin&#34;&gt;Gin&lt;/a&gt;：Golang 的一个微框架，性能极佳。&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/astaxie/beego/tree/master/validation&#34;&gt;beego-validation&lt;/a&gt;：本节采用的 beego 的表单验证库，&lt;a href=&#34;https://beego.me/docs/mvc/controller/validation.md&#34;&gt;中文文档&lt;/a&gt;。&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/jinzhu/gorm&#34;&gt;gorm&lt;/a&gt;，对开发人员友好的 ORM 框架，&lt;a href=&#34;http://gorm.io/docs/&#34;&gt;英文文档&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/Unknwon/com&#34;&gt;com&lt;/a&gt;，一个小而美的工具包。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;完成博客的标签类接口定义和编写&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;定义接口&#34;&gt;定义接口&lt;/h2&gt;&#xA;&lt;p&gt;本节正是编写标签的逻辑，我们想一想，一般接口为增删改查是基础的，那么我们定义一下接口吧！&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;获取标签列表：GET(&amp;quot;/tags&amp;quot;)&lt;/li&gt;&#xA;&lt;li&gt;新建标签：POST(&amp;quot;/tags&amp;quot;)&lt;/li&gt;&#xA;&lt;li&gt;更新指定标签：PUT(&amp;quot;/tags/:id&amp;quot;)&lt;/li&gt;&#xA;&lt;li&gt;删除指定标签：DELETE(&amp;quot;/tags/:id&amp;quot;)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;编写路由空壳&#34;&gt;编写路由空壳&lt;/h2&gt;&#xA;&lt;p&gt;开始编写路由文件逻辑，在&lt;code&gt;routers&lt;/code&gt;下新建&lt;code&gt;api&lt;/code&gt;目录，我们当前是第一个 API 大版本，因此在&lt;code&gt;api&lt;/code&gt;下新建&lt;code&gt;v1&lt;/code&gt;目录，再新建&lt;code&gt;tag.go&lt;/code&gt;文件，写入内容：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gin-gonic/gin&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//获取多个文章标签&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GetTags&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//新增文章标签&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AddTag&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//修改文章标签&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;EditTag&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//删除文章标签&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DeleteTag&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Context&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;注册路由&#34;&gt;注册路由&lt;/h2&gt;&#xA;&lt;p&gt;我们打开&lt;code&gt;routers&lt;/code&gt;下的&lt;code&gt;router.go&lt;/code&gt;文件，修改文件内容为：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;routers&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gin-gonic/gin&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gin-blog/routers/api/v1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gin-blog/pkg/setting&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;InitRouter&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Engine&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Use&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Logger&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Use&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Recovery&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;gin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetMode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;setting&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RunMode&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Group&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/api/v1&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//获取标签列表&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GET&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tags&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetTags&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//新建标签&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;POST&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tags&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AddTag&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//更新指定标签&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PUT&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tags/:id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EditTag&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//删除指定标签&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;apiv1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DELETE&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tags/:id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DeleteTag&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当前目录结构：&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载二」Gin搭建Blog API&#39;s （一）</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-02-11-api-01/</link>
      <pubDate>Sun, 11 Feb 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-02-11-api-01/</guid>
      <description>&lt;h2 id=&#34;思考&#34;&gt;思考&lt;/h2&gt;&#xA;&lt;p&gt;首先，在一个初始项目开始前，大家都要思考一下&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;程序的文本配置写在代码中，好吗？&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;API 的错误码硬编码在程序中，合适吗？&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;db 句柄谁都去&lt;code&gt;Open&lt;/code&gt;，没有统一管理，好吗？&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;获取分页等公共参数，谁都自己写一套逻辑，好吗？&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;显然在较正规的项目中，这些问题的答案都是&lt;strong&gt;不可以&lt;/strong&gt;，为了解决这些问题，我们挑选一款读写配置文件的库，目前比较火的有 &lt;a href=&#34;https://github.com/spf13/viper&#34;&gt;viper&lt;/a&gt;，有兴趣你未来可以简单了解一下，没兴趣的话等以后接触到再说。&lt;/p&gt;&#xA;&lt;p&gt;但是本系列选用 &lt;a href=&#34;https://github.com/go-ini/ini&#34;&gt;go-ini/ini&lt;/a&gt; ，它的 &lt;a href=&#34;https://ini.unknwon.io/&#34;&gt;中文文档&lt;/a&gt;。大家是必须需要要简单阅读它的文档，再接着完成后面的内容。&lt;/p&gt;&#xA;&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;编写一个简单的 API 错误码包。&lt;/li&gt;&#xA;&lt;li&gt;完成一个 Demo 示例。&lt;/li&gt;&#xA;&lt;li&gt;讲解 Demo 所涉及的知识点。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;介绍和初始化项目&#34;&gt;介绍和初始化项目&lt;/h2&gt;&#xA;&lt;h3 id=&#34;初始化项目目录&#34;&gt;初始化项目目录&lt;/h3&gt;&#xA;&lt;p&gt;在前一章节中，我们初始化了一个 &lt;code&gt;go-gin-example&lt;/code&gt; 项目，接下来我们需要继续新增如下目录结构：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go-gin-example/&#xA;├── conf&#xA;├── middleware&#xA;├── models&#xA;├── pkg&#xA;├── routers&#xA;└── runtime&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;conf：用于存储配置文件&lt;/li&gt;&#xA;&lt;li&gt;middleware：应用中间件&lt;/li&gt;&#xA;&lt;li&gt;models：应用数据库模型&lt;/li&gt;&#xA;&lt;li&gt;pkg：第三方包&lt;/li&gt;&#xA;&lt;li&gt;routers 路由逻辑处理&lt;/li&gt;&#xA;&lt;li&gt;runtime：应用运行时数据&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;添加-go-modules-replace&#34;&gt;添加 Go Modules Replace&lt;/h3&gt;&#xA;&lt;p&gt;打开 &lt;code&gt;go.mod&lt;/code&gt; 文件，新增 &lt;code&gt;replace&lt;/code&gt; 配置项，如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;module github.com/EDDYCJY/go-gin-example&#xA;&#xA;go 1.13&#xA;&#xA;require (...)&#xA;&#xA;replace (&#xA;&#x9;&#x9;github.com/EDDYCJY/go-gin-example/pkg/setting =&amp;gt; ~/go-application/go-gin-example/pkg/setting&#xA;&#x9;&#x9;github.com/EDDYCJY/go-gin-example/conf    &#x9;  =&amp;gt; ~/go-application/go-gin-example/pkg/conf&#xA;&#x9;&#x9;github.com/EDDYCJY/go-gin-example/middleware  =&amp;gt; ~/go-application/go-gin-example/middleware&#xA;&#x9;&#x9;github.com/EDDYCJY/go-gin-example/models &#x9;  =&amp;gt; ~/go-application/go-gin-example/models&#xA;&#x9;&#x9;github.com/EDDYCJY/go-gin-example/routers &#x9;  =&amp;gt; ~/go-application/go-gin-example/routers&#xA;)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可能你会不理解为什么要特意跑来加 &lt;code&gt;replace&lt;/code&gt; 配置项，首先你要看到我们使用的是完整的外部模块引用路径（&lt;code&gt;github.com/EDDYCJY/go-gin-example/xxx&lt;/code&gt;），而这个模块还没推送到远程，是没有办法下载下来的，因此需要用 &lt;code&gt;replace&lt;/code&gt; 将其指定读取本地的模块路径，这样子就可以解决本地模块读取的问题。&lt;/p&gt;</description>
    </item>
    <item>
      <title>「连载一」Go 介绍与环境安装</title>
      <link>https://note.lican.site/posts/posts/go/gin/2018-02-10-install/</link>
      <pubDate>Sat, 10 Feb 2018 12:00:00 +0000</pubDate>
      <guid>https://note.lican.site/posts/posts/go/gin/2018-02-10-install/</guid>
      <description>&lt;h2 id=&#34;本文目标&#34;&gt;本文目标&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;学会安装 Go。&lt;/li&gt;&#xA;&lt;li&gt;知道什么是 Go。&lt;/li&gt;&#xA;&lt;li&gt;知道什么是 Go modules。&lt;/li&gt;&#xA;&lt;li&gt;了解 Go modules 的小历史。&lt;/li&gt;&#xA;&lt;li&gt;学会简单的使用 Go modules。&lt;/li&gt;&#xA;&lt;li&gt;了解 Gin，并简单跑起一个 Demo。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;准备环节&#34;&gt;准备环节&lt;/h2&gt;&#xA;&lt;h3 id=&#34;安装-go&#34;&gt;安装 Go&lt;/h3&gt;&#xA;&lt;h4 id=&#34;centos&#34;&gt;Centos&lt;/h4&gt;&#xA;&lt;p&gt;首先，根据对应的操作系统选择安装包 &lt;a href=&#34;https://studygolang.com/dl&#34;&gt;下载&lt;/a&gt;，在这里我使用的是 Centos 64 位系统，如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ wget https://studygolang.com/dl/golang/go1.13.1.linux-amd64.tar.gz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tar -zxvf go1.13.1.linux-amd64.tar.gz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ mv go/ /usr/local/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置 /etc/profile&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vi /etc/profile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;添加环境变量 GOROOT 和将 GOBIN 添加到 PATH 中&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export GOROOT&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/usr/local/go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$PATH:$GOROOT/bin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置完毕后，执行命令令其生效&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;source /etc/profile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在控制台输入&lt;code&gt;go version&lt;/code&gt;，若输出版本号则&lt;strong&gt;安装成功&lt;/strong&gt;，如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go version&#xA;go version go1.13.1 linux/amd64&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;macos&#34;&gt;MacOS&lt;/h4&gt;&#xA;&lt;p&gt;在 MacOS 上安装 Go 最方便的办法就是使用 brew，安装如下：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
