<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Suncle</title>
  
  
  <link href="https://suncle.me/atom.xml" rel="self"/>
  
  <link href="https://suncle.me/"/>
  <updated>2023-09-03T14:31:24.457Z</updated>
  <id>https://suncle.me/</id>
  
  <author>
    <name>Suncle Chen</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Go工程化01 - 项目目录结构</title>
    <link href="https://suncle.me/posts/3019844725/"/>
    <id>https://suncle.me/posts/3019844725/</id>
    <published>2023-02-17T18:31:37.000Z</published>
    <updated>2023-09-03T14:31:24.457Z</updated>
    
    <content type="html"><![CDATA[<p>如果你尝试学习 Go，或者你正在为自己建立一个 PoC 或一个玩具项目，项目布局是没啥必要的。从一些非常简单的事情开始(一个 <code>main.go</code> 文件绰绰有余)。随着项目的增长，请记住保持代码结构良好非常重要，否则你最终会得到一个凌乱的代码，这其中就包含大量隐藏的依赖项和全局状态。当有更多的人参与这个项目时，你将需要更多的结构。比如一个toolkit来方便生成项目的模版，尽可能让大家使用统一的工程目录布局。</p><h2 id="Go-标准项目布局"><a href="#Go-标准项目布局" class="headerlink" title="Go 标准项目布局"></a>Go 标准项目布局</h2><p>首先先简单介绍一下github上一个很有名的项目：**<code>Standard Go Project Layout</code>**，项目的README支持各种语言。</p><p>这是 Go 应用程序项目的基本布局，在Go生态系统中有很多大大小小的项目都遵循这个布局。</p><h3 id="x2F-cmd"><a href="#x2F-cmd" class="headerlink" title="&#x2F;cmd"></a>&#x2F;cmd</h3><p>本项目的主干。</p><p>每个应用程序的目录名应该与你想要的可执行文件的名称相匹配(例如，<code>/cmd/myapp</code>)。</p><p>不要在这个目录中放置太多代码。如果你认为代码可以导入并在其他项目中使用，那么它应该位于 <code>/pkg</code> 目录中。如果代码不是可重用的，或者你不希望其他人重用它，请将该代码放到 <code>/internal</code> 目录中。你会惊讶于别人会怎么做，所以要明确你的意图!</p><p>通常有一个小的 <code>main</code> 函数，从 <code>/internal</code> 和 <code>/pkg</code> 目录导入和调用代码，除此之外没有别的东西。</p><h3 id="x2F-internal"><a href="#x2F-internal" class="headerlink" title="&#x2F;internal"></a>&#x2F;internal</h3><p>私有应用程序和库代码。这是你不希望其他人在其应用程序或库中导入代码。请注意，这个布局模式是由 Go 编译器本身执行的。有关更多细节，请参阅Go 1.4 <a href="https://golang.org/doc/go1.4#internalpackages"><code>release notes</code></a> 。注意，你并不局限于顶级 <code>internal</code> 目录。在项目树的任何级别上都可以有多个内部目录。</p><p>你可以选择向 internal 包中添加一些额外的结构，以分隔共享和非共享的内部代码。这不是必需的(特别是对于较小的项目)，但是最好有有可视化的线索来显示预期的包的用途。你的实际应用程序代码可以放在 <code>/internal/app</code> 目录下(例如 <code>/internal/app/myapp</code>)，这些应用程序共享的代码可以放在 <code>/internal/pkg</code> 目录下(例如 <code>/internal/pkg/myprivlib</code>)。</p><p>因为我们习惯把相关的服务，比如账号服务，内部有pc、ob、admin等，相关的服务整合一起后，需要区分app。单一的服务可以去掉internal&#x2F;myapp。</p><h3 id="x2F-pkg"><a href="#x2F-pkg" class="headerlink" title="&#x2F;pkg"></a>&#x2F;pkg</h3><p>外部应用程序可以使用的库代码(例如 <code>/pkg/mypubliclib</code>)。其他项目会导入这些库，希望它们能正常工作，所以在这里放东西之前要三思:-)注意，<code>internal</code> 目录是确保私有包不可导入的更好方法，因为它是由 Go 强制执行的。<code>/pkg</code> 目录仍然是一种很好的方式，可以显式地表示该目录中的代码对于其他人来说是安全使用的好方法。</p><p><code>/pkg</code>目录内，可以参考g0标准库的组织方式，按照功能分类。&#x2F;internla&#x2F;pkg一般用于项目内的跨多个应用的公共共享代码，但其作用域仅在单个项目工程内。</p><p>由 Travis Jeffery  撰写的 <a href="https://travisjeffery.com/b/2019/11/i-ll-take-pkg-over-internal/"><code>I&#39;ll take pkg over internal</code></a> 博客文章提供了 <code>pkg</code> 和 <code>internal</code> 目录的一个很好的概述，以及什么时候使用它们是有意义的。</p><p>当根目录包含大量非 Go 组件和目录时，这也是一种将 Go 代码分组到一个位置的方法，这使得运行各种 Go 工具变得更加容易。</p><h2 id="基础库项目布局"><a href="#基础库项目布局" class="headerlink" title="基础库项目布局"></a>基础库项目布局</h2><p>每个公司都应当为不同的微服务建立一个统一的kt工具包项目（基础库&#x2F;框架)和app项目模板以及生成工具。</p><p>基础库kit为独立项目，公司级建议只有一个，但是在每个公司的实际运行中，一般都是组织架构决定技术栈，因此为一个部门的一种主要语言构建一套kit库就已经很不错了。</p><p>Kit库必须具备的特点：</p><ul><li>统一</li><li>标准库方式布局</li><li>高度抽象</li><li>支持插件</li></ul><h2 id="微服务项目布局"><a href="#微服务项目布局" class="headerlink" title="微服务项目布局"></a>微服务项目布局</h2><h2 id="服务应用程序目录"><a href="#服务应用程序目录" class="headerlink" title="服务应用程序目录"></a>服务应用程序目录</h2><h3 id="x2F-api"><a href="#x2F-api" class="headerlink" title="&#x2F;api"></a>&#x2F;api</h3><p>API协议定义目录，类似xxapi.proto这样的protobuf文件，以及生成的go文件。我们通常把api文档直接在proto文件中描述。</p><h3 id="x2F-configs"><a href="#x2F-configs" class="headerlink" title="&#x2F;configs"></a>&#x2F;configs</h3><p>配置文件模板或默认配置。</p><h3 id="x2F-scripts"><a href="#x2F-scripts" class="headerlink" title="&#x2F;scripts"></a>&#x2F;scripts</h3><p>执行各种构建、安装、分析等操作的脚本。这些脚本保持了根级别的 Makefile 变得小而简单，有关示例，请参见  &#x2F;scripts 目录。</p><h3 id="x2F-test"><a href="#x2F-test" class="headerlink" title="&#x2F;test"></a>&#x2F;test</h3><p>额外的外部测试应用程序和测试数据。你可以随时根据需求构造 <code>/test</code> 目录。对于较大的项目，有一个数据子目录是有意义的。例如，你可以使用 <code>/test/data</code> 或 <code>/test/testdata</code> (如果你需要忽略目录中的内容)。请注意，Go 还会忽略以<code>“.”</code>或<code>“_”</code>开头的目录或文件，因此在如何命名测试数据目录方面有更大的灵活性。</p><p>有关示例，请参见  <a href="https://github.com/golang-standards/project-layout/blob/master/test/README.md"><code>/test</code></a> 目录。</p><h3 id="x2F-docs"><a href="#x2F-docs" class="headerlink" title="&#x2F;docs"></a>&#x2F;docs</h3><p>设计和用户文档(除了 godoc 生成的文档之外)。有关示例，请参阅 <a href="https://github.com/golang-standards/project-layout/blob/master/docs/README.md"><code>/docs</code></a> 目录。</p><h3 id="x2F-examples"><a href="#x2F-examples" class="headerlink" title="&#x2F;examples"></a>&#x2F;examples</h3><p>你的应用程序和&#x2F;或公共库的示例。有关示例，请参见 <a href="https://github.com/golang-standards/project-layout/blob/master/examples/README.md"><code>/examples</code></a> 目录。</p><h3 id="x2F-third-party"><a href="#x2F-third-party" class="headerlink" title="&#x2F;third_party"></a>&#x2F;third_party</h3><p>外部辅助工具，分叉代码和其他第三方工具(例如 Swagger UI)。</p><h3 id="x2F-assets"><a href="#x2F-assets" class="headerlink" title="&#x2F;assets"></a>&#x2F;assets</h3><p>与存储库一起使用的其他资产(图像、徽标等)。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;如果你尝试学习 Go，或者你正在为自己建立一个 PoC 或一个玩具项目，项目布局是没啥必要的。从一些非常简单的事情开始(一个 &lt;code&gt;main.go&lt;/code&gt; 文件绰绰有余)。随着项目的增长，请记住保持代码结构良好非常重要，否则你最终会得到一个凌乱的代码，这其中就包</summary>
      
    
    
    
    <category term="Go进阶" scheme="https://suncle.me/categories/Go%E8%BF%9B%E9%98%B6/"/>
    
    <category term="02. Go 工程化" scheme="https://suncle.me/categories/Go%E8%BF%9B%E9%98%B6/02-Go-%E5%B7%A5%E7%A8%8B%E5%8C%96/"/>
    
    
    <category term="Go" scheme="https://suncle.me/tags/Go/"/>
    
    <category term="工程化" scheme="https://suncle.me/tags/%E5%B7%A5%E7%A8%8B%E5%8C%96/"/>
    
    <category term="结构" scheme="https://suncle.me/tags/%E7%BB%93%E6%9E%84/"/>
    
  </entry>
  
  <entry>
    <title>陈伟在字节的科学减重分享</title>
    <link href="https://suncle.me/posts/2507611857/"/>
    <id>https://suncle.me/posts/2507611857/</id>
    <published>2023-02-05T18:31:37.000Z</published>
    <updated>2023-09-03T14:31:24.481Z</updated>
    
    <content type="html"><![CDATA[<p>晚上听了一下北京协和医院临床营养主任医师陈伟关于科学减重的一个分享，顺便给大家分享一下。</p><p>肥胖是什么？1997年，WHO首次将肥胖症定义为疾病</p><p>各种危害也都不提了，但请记住这几句话，<strong>肥胖是疾病</strong>。</p><h2 id="肥胖的评价以及诊断标准"><a href="#肥胖的评价以及诊断标准" class="headerlink" title="肥胖的评价以及诊断标准"></a>肥胖的评价以及诊断标准</h2><p><strong>体质指数法BMI</strong></p><table><thead><tr><th>分类</th><th>BMI (KG&#x2F;m2)</th></tr></thead><tbody><tr><td>肥胖</td><td>BMI &gt;&#x3D; 28.0</td></tr><tr><td>超重</td><td>BMI &lt;&#x3D; 24.0 &amp;&amp; BMI &lt; 28.0</td></tr><tr><td>体重正常</td><td>BMI &gt;&#x3D; 18.5 &amp;&amp; BMI &lt; 24</td></tr><tr><td>体制过低</td><td>BMI &lt; 18.5</td></tr></tbody></table><p><strong>体脂肪率判断法</strong></p><table><thead><tr><th>性别</th><th>必须体脂</th><th>健康体脂</th></tr></thead><tbody><tr><td>男性</td><td>3%-8%</td><td>15%-20%</td></tr><tr><td>女性</td><td>12%-14%</td><td>25%-30%</td></tr></tbody></table><p><strong>腰围</strong></p><p>WHO规定亚太地区，男性腰围大于等于90cm，女性腰围大于等于80cm即为肥胖。</p><p><strong>腰臀比WHR</strong></p><p>男性大于0.9，女性大于0.85即中心型肥胖又名内脏型肥胖。平卧时，腹部的高度超过了胸骨的高度也可以诊断为肥胖。</p><h2 id="如何获得健康体重？"><a href="#如何获得健康体重？" class="headerlink" title="如何获得健康体重？"></a>如何获得健康体重？</h2><p>肥胖的根本原因：摄入能量 &gt; 消耗的能量<br>减肥的根本手段：摄入能量 &lt; 消耗的能量</p><p>按照下面的五步减肥法慢慢练习。</p><h3 id="第一步：坚持测体重"><a href="#第一步：坚持测体重" class="headerlink" title="第一步：坚持测体重"></a>第一步：坚持测体重</h3><p>每天早上测一次，形成自我监测的习惯</p><h3 id="第二步：好好吃饭"><a href="#第二步：好好吃饭" class="headerlink" title="第二步：好好吃饭"></a>第二步：好好吃饭</h3><p>降低食物的能量密度</p><p>吃好肉</p><ul><li>无腿的 &gt; 2条腿的 &gt; 四条腿的</li><li>白肉 &gt; 红肉</li><li>瘦肉 &gt; 肥瘦肉 &gt; 肥肉 &gt; 肉皮</li><li>去掉多余的肉皮</li><li>永远拒绝煎、炸的食物</li></ul><p>喝好水<br>只能喝四种水：</p><ul><li>水</li><li>茶</li><li>黑咖啡</li><li>气泡水</li></ul><p>吃好油</p><ul><li>植物油 &gt; 动物油</li><li>人工黄油 &gt; 天然黄油</li><li>胆固醇与脂肪并不相等</li><li>尽一切可能少用油</li></ul><p>会吃饭</p><ul><li>少量多餐，按时进餐</li><li>细嚼慢咽</li><li>少喝酒多饮水</li><li>先汤后饭，长肉很慢</li><li>不吃饭桌剩下的食品</li><li>不吃过小的零食</li><li>拒绝甜食</li><li>要吃主食</li></ul><p>合理烹调</p><ul><li>生吃，拌菜，蒸，煮，涮为主；煎炒点缀：避免油炸。尽量在家吃饭。</li><li>生吃&#x2F;果蔬汁：生菜，青椒，彩椒，洋葱，西芹菜心，胡萝卜，苦菊，黄瓜，西红柿，紫甘蓝，苤蓝等</li><li>焯-拌：菠菜，苦瓜，芹菜，菇类，小白菜等</li><li>白灼：白灼芥蓝，白灼菜心等</li><li>上汤：上汤鸡毛菜，上汤菜心等</li></ul><h3 id="第三步：记录饮食习惯"><a href="#第三步：记录饮食习惯" class="headerlink" title="第三步：记录饮食习惯"></a>第三步：记录饮食习惯</h3><blockquote><p>记录不来，不想这么麻烦，所以这一步跳过</p></blockquote><h3 id="第四步：坚持运动"><a href="#第四步：坚持运动" class="headerlink" title="第四步：坚持运动"></a>第四步：坚持运动</h3><ul><li>每周5次以上，每次40分钟以上，中等强度有氧运动。做到微汗、微喘，能说话，不能唱歌。</li><li>推荐运动方式：快走，游泳，健身车（椭圆机，动感单车)</li><li>最适心率&#x3D;170-年龄</li></ul><h3 id="第五步：自我评估"><a href="#第五步：自我评估" class="headerlink" title="第五步：自我评估"></a>第五步：自我评估</h3><ul><li>每月减1-2公斤</li><li>不需要忍饥挨饿</li><li>没有不良反应，精神好，可长期坚持</li></ul><h2 id="医院的2种减肥方式"><a href="#医院的2种减肥方式" class="headerlink" title="医院的2种减肥方式"></a>医院的2种减肥方式</h2><h3 id="高蛋白膳食减重"><a href="#高蛋白膳食减重" class="headerlink" title="高蛋白膳食减重"></a>高蛋白膳食减重</h3><p>高蛋白质膳食是一类每日蛋白质摄入量超过每日总能量的20%或海日1.5g&#x2F;kg体重以上，但一般不超过每日总能量的30%（或每日2.0g&#x2F;kg体重）的膳食模式。<br>推荐意见:</p><ol><li>对于单纯性肥胖以及合并高甘油三酯血症者、高胆固醇症者采用高蛋白膳食较正常蛋白膳食更有利于减轻体重以及改善血脂情况：并有利于控制减重后体重的反弹。</li><li>合并慢性肾病患者应慎重选择高蛋白饮食。</li></ol><h3 id="轻断食减重"><a href="#轻断食减重" class="headerlink" title="轻断食减重"></a>轻断食减重</h3><p>也称间歇式断食，一般采用5+2模式，即1周中5天相对正常进食，其他2天(（非连续）)则摄取平常的1&#x2F;4能量(（约女性500kcal&#x2F;d,男性600kcal&#x2F;d)的膳食模式。<br>推荐意见:</p><ol><li>轻断食模式有益于体重控制和代谢改善。</li><li>轻断食模式在体重控制的同时，或可通过代谢和炎性反应改善，间接增加体重控制获益；同时增强糖尿病、心脑血管疾病及其他慢性疾病的治疗获益。</li></ol><p>断食日的饮食方案：</p><table><thead><tr><th>断食日</th><th>女性(500kcal&#x2F;天)</th><th align="left">男性(600kcal&#x2F;天)</th></tr></thead><tbody><tr><td>早餐</td><td>脱脂奶250ml或酸奶100克，1个鸡蛋</td><td align="left">脱脂奶250ml或酸奶100克，1个鸡蛋</td></tr><tr><td>午餐</td><td>水果150-200克</td><td align="left">水果150克</td></tr><tr><td>晚餐</td><td>主食25克，蛋白质类食物50克，蔬菜250克</td><td align="left">主食50克，蛋白质类食物50克，蔬菜250克</td></tr></tbody></table><p>准备试验一下这个五步减肥法和轻断食减重方法。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;晚上听了一下北京协和医院临床营养主任医师陈伟关于科学减重的一个分享，顺便给大家分享一下。&lt;/p&gt;
&lt;p&gt;肥胖是什么？1997年，WHO首次将肥胖症定义为疾病&lt;/p&gt;
&lt;p&gt;各种危害也都不提了，但请记住这几句话，&lt;strong&gt;肥胖是疾病&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 </summary>
      
    
    
    
    <category term="随笔" scheme="https://suncle.me/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
  </entry>
  
  <entry>
    <title>2022年终总结</title>
    <link href="https://suncle.me/posts/536100284/"/>
    <id>https://suncle.me/posts/536100284/</id>
    <published>2022-12-31T18:31:37.000Z</published>
    <updated>2023-09-03T14:31:24.473Z</updated>
    
    <content type="html"><![CDATA[<h2 id="定个基调"><a href="#定个基调" class="headerlink" title="定个基调"></a>定个基调</h2><p>以前四处探索：什么都想做，什么都无法下定最终决心，也很难在某一个方向上尽全力，今天做个摄影，明天当个写手。<br>现在专注且坚定：心无旁骛，全神贯注</p><h2 id="小打小闹"><a href="#小打小闹" class="headerlink" title="小打小闹"></a>小打小闹</h2><p>简单介绍下，2022年遇到的典型的创业想法。为什么称之为典型呢？</p><ul><li>一个和钱走的近，</li><li>一个和人走的近。</li></ul><p>和钱走的近的量化交易。这种想法简单描述就是，几个产品经理研究一些策略，然后程序员去实现，然后进行盈利或者套利。</p><p>但其实我并不相信在极短时间内的过去能够预测未来，因为在这么短的时间之内，影响的因素实在太多，又岂是一个写死的程序可以穷举的，那根据这样并不完善的前提做出来的决策，又怎么能相信呢？</p><p>虽然这个世界上有很多量化交易的操盘手和机器人，华尔街也有大量的数学家在深入研究之后，也能找到一些其他人尚未发现的决策因子，但这个并不适合普通人，你永远都不要幻想能玩过华尔街。</p><p>和人走的进的社交小程序。简单说就是社交组局，想法很好，也确实是有痛点有需求，但是盈利模式不清晰，想做成平台经济，目测不太容易成功，但是在小范围内的精准人群中运营我认为是可行的。</p><h2 id="几点经验"><a href="#几点经验" class="headerlink" title="几点经验"></a>几点经验</h2><p>主要是和投资有关系的几个常识了，简单分享下，不深入分析</p><p><strong>高收益是不可持续的</strong>。luna发型的稳定币ust 20%+的收益显然是无法持续的，但是随着牛市的火爆以及无限发型ust买大饼锚定大饼，我开始怀疑自己是不是错了，但从最终的结果来看，luna暴雷，无数的机构随之倒下。</p><p><strong>牛市的顶和熊市的底，都是一个段而不是一个点</strong>。试图卖在最高点的，面临无尽的下跌，往往还期望着回到高点，迟迟舍不得卖。试图买在最低点的，在上涨的行情面前，往往还期待着回到低点抄到世纪大底，结果是一步一步错过，丢掉了低位上车机会，最终忍不住，追了高，摔得粉身碎骨。</p><p><strong>周期不以人的意志转移，也不以某项风险资产转移</strong>。你相信大饼是避险资产，但是周期告诉你，在周期的面前一切都是风险资产。买的资金多了价格就涨，卖的资金多了价格就跌，资金从哪里来？资金从短期的世界大势中来。全世界都要开始加息了，就不要幻想着你手里的风险资产能保持不跌。记住，永远不要和趋势为敌，永远不要和周期作对。</p><p><strong>大饼80%的时间都是横盘震荡，上涨和下跌往往是在很短的时间内完成的</strong>。普通人的应对之策就是一句话：低位横盘期定投，满仓待涨，高位横盘期定抛。切记：<strong>盈亏同源，牛熊一体</strong>。</p><h2 id="结婚旅行"><a href="#结婚旅行" class="headerlink" title="结婚旅行"></a>结婚旅行</h2><p>12月30号的一个朋友圈 </p><p>Q：好像这一年下来什么都没干</p><p>A：你这一年结了婚，还出去溜达了几个月，还没感染新冠。 </p><p>嗯，多看书，多走路，家庭甜蜜又幸福。</p><h2 id="巴林打工记"><a href="#巴林打工记" class="headerlink" title="巴林打工记"></a>巴林打工记</h2><p>国外打工怎么样？无聊但又好玩。</p><p>你看到的是遍地黄土，我看到的是漫天繁星。</p><p>不要害怕未知，不要害怕改变，只需要害怕你的生活一成不变。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/Xq47ut-1673726419470.png"></p><h2 id="学学英语"><a href="#学学英语" class="headerlink" title="学学英语"></a>学学英语</h2><p>想学好听力和口语，环境真的很重要。人的主观意志力是决定性作用，但是有不错的客观环境，会事半功倍。</p><p>每天听英语，必要时也不得不说一些英语，在某一个瞬间，突然发现，讲英语没有那么发怵了，是好的改变。</p><p>提供3个英语相关的参考链接：</p><ol><li><a href="https://wuyagege.substack.com/p/c51">https://wuyagege.substack.com/p/c51</a></li><li><a href="https://bewaters.me/limxtop/2021/08/18/English-introduction/">https://bewaters.me/limxtop/2021/08/18/English-introduction/</a></li><li><a href="https://www.youtube.com/watch?v=eEA0Y54-Ds8">https://www.youtube.com/watch?v=eEA0Y54-Ds8</a></li></ol><h2 id="真假段子"><a href="#真假段子" class="headerlink" title="真假段子"></a>真假段子</h2><p>3个有感触的段子，真真假假，假假真真。</p><p>段子1，绝大多数普通人的一生：</p><ol><li>5-18岁，不停的学习考试，往脑海里流水线一样的植入一些东西：普鲁士教育。</li><li>19-23岁，在对学科一无所知的情况下选择专业，懵懵懂懂学了4年。</li><li>24-28岁，在对社会运行机制毫无概念的情况下，被要求选择工作。</li><li>28-33岁，接下来还要在对爱情一知半解的情况下，被要求选择过一生的伴侣。</li><li>再之后，努力活着。</li></ol><p>段子2，杨绛<br>不要羡慕任何人的生活， 其实谁家的锅底都有灰。 生活，不是这样，就是那样。 不是别人风光无限， 而是他们一地鸡毛没给别人看， 所以顺其自然就好。</p><p>段子3，杨绛<br>人和人如果不在一个层次上， 那么无论你做什么， 对方都觉得不对， 所谓层次，不是社会地位， 而是认知事物的清晰程度。 </p><h2 id="The-last"><a href="#The-last" class="headerlink" title="The last"></a>The last</h2><p>2022的小目标达成，朝2023的小目标迈进</p><p>2023, Don’t hesitate, keep moving.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;定个基调&quot;&gt;&lt;a href=&quot;#定个基调&quot; class=&quot;headerlink&quot; title=&quot;定个基调&quot;&gt;&lt;/a&gt;定个基调&lt;/h2&gt;&lt;p&gt;以前四处探索：什么都想做，什么都无法下定最终决心，也很难在某一个方向上尽全力，今天做个摄影，明天当个写手。&lt;br&gt;现在专注且</summary>
      
    
    
    
    <category term="年度总结" scheme="https://suncle.me/categories/%E5%B9%B4%E5%BA%A6%E6%80%BB%E7%BB%93/"/>
    
    
    <category term="总结" scheme="https://suncle.me/tags/%E6%80%BB%E7%BB%93/"/>
    
  </entry>
  
  <entry>
    <title>Go工程化08 - 错误处理</title>
    <link href="https://suncle.me/posts/1358609975/"/>
    <id>https://suncle.me/posts/1358609975/</id>
    <published>2022-12-04T18:31:37.000Z</published>
    <updated>2023-09-03T14:31:24.457Z</updated>
    
    <content type="html"><![CDATA[<h2 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h2><p>先总结一下，在日常开发工作中，我们是怎么使用panic和error的。</p><h3 id="panic最佳实践"><a href="#panic最佳实践" class="headerlink" title="panic最佳实践"></a>panic最佳实践</h3><ol><li>在程序启动时，出现明显的配置错误可以直接panic，防止错误的配置产生错误的数据</li><li>在程序启动时，如果关键组件启动失败就可以直接panic，比如mysql和redis连接错误</li><li>在web框架的入口处，都会加recover避免程序panic直接退出程序</li><li>不使用panic和recover做常用的错误处理<ol><li>频繁 panic recover 性能不好</li><li>不可控，一旦 panic 就将处理逻辑移交给了外部，我们并不能预设外部包一定会进行处理</li></ol></li></ol><h3 id="error最佳实践"><a href="#error最佳实践" class="headerlink" title="error最佳实践"></a>error最佳实践</h3><ol><li>在应用程序中，我们一般使用<code>github.com/pkg/errors</code>这个库来处理应用错误<ol><li>在公共库中，一般不使用这个库，避免报错时产生多次堆栈</li><li>这个库当前已经归档，不少开源项目都不再使用这个库了，但是如果想要有完整的堆栈信息用于排查错误，这个库还是最合适的，等go2中关于错误的新语法出来之后，再决定是否要更换</li></ol></li><li>error应该是函数的最后一个返回值，当error不为nil时，函数的其他返回值我们都不应该再使用</li><li>错误处理的时候应该先判断错误<code>if err != nil</code>， 出现错误及时return，使代码是一条流畅的直线，避免过多的嵌套</li><li>在应用程序中<strong>首次</strong>出现错误时，使用 <code>errors.New</code>  或者 <code>errors.Errorf</code>  返回错误</li><li>如果是调用应用程序的其他函数出现错误，请直接返回，如果需要携带信息，请使用 <code>errors.WithMessage</code></li><li>如果是调用其他库（标准库、公共库、开源第三方库等）获取到错误时，请使用 <code>errors.Wrap</code>  添加堆栈信息<ol><li>为了避免多次堆栈，不要每个地方都是用 <code>errors.Wrap</code>  只需要在错误第一次出现时进行 <code>errors.Wrap</code>  即可</li></ol></li><li>根据“错误只处理一次”原则， <strong>禁止</strong>每个出错的地方都打日志，只需要在进程的最开始的地方使用 <code>%+v</code>  进行统一打印，例如 http&#x2F;rpc 服务的中间件，或者是公司的kit库里面封装统一的middleware</li><li>错误判断使用 <code>errors.Is</code></li><li>错误判断并赋值使用 <code>errors.As</code></li><li>如何判定错误的信息是否足够，想一想当你的代码出现问题需要排查的时候你的错误信息是否可以帮助你快速的定位问题，例如我们在请求中一般会输出参数信息，用于辅助判断错误</li><li>对于业务错误，推荐在一个统一的地方创建一个错误字典，比如protobuf文件。错误字典里面应该包含错误的 code，并且在日志中作为独立字段打印，方便做业务告警的判断，错误必须有清晰的错误文档</li><li>错误的时候，需要处理已分配的资源，使用 <code>defer</code>  进行清理，例如文件句柄，或者是保存错误的操作记录。</li></ol><h2 id="使用-error-处理有哪些好处？"><a href="#使用-error-处理有哪些好处？" class="headerlink" title="使用 error 处理有哪些好处？"></a>使用 error 处理有哪些好处？</h2><ol><li>简单</li><li>考虑失败，而不是成功(<strong>Plan for failure, not success</strong>)</li><li>没有隐藏的控制流</li><li>完全交给你来控制 error</li><li>Error are values<br>在写c++或者python代码时，错误往往会隐藏在一些值里面，开发者经常会忽略要去处理对应的错误，但是把error显式的当成value处理时，开发者没有办法不去处理。</li></ol><h2 id="error源码分析"><a href="#error源码分析" class="headerlink" title="error源码分析"></a>error源码分析</h2><p>Go error就是一个普通的接口</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// The error built-in interface type is the conventional interface for  </span><span class="token comment">// representing an error condition, with the nil value representing no error.  </span><span class="token keyword">type</span> <span class="token builtin">error</span> <span class="token keyword">interface</span> <span class="token punctuation">&#123;</span>     <span class="token function">Error</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">string</span>  <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>创建error对象的一个最常用方法就是使用errors.New()</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> errors    <span class="token comment">// New returns an error that formats as the given text.</span><span class="token comment">// Each call to New returns a distinct error value even if the text is identical.</span><span class="token keyword">func</span> <span class="token function">New</span><span class="token punctuation">(</span>text <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span>     <span class="token keyword">return</span> <span class="token operator">&amp;</span>errorString<span class="token punctuation">&#123;</span>text<span class="token punctuation">&#125;</span>  <span class="token punctuation">&#125;</span>    <span class="token comment">// errorString is a trivial implementation of error.</span><span class="token keyword">type</span> errorString <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>     s <span class="token builtin">string</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">func</span> <span class="token punctuation">(</span>e <span class="token operator">*</span>errorString<span class="token punctuation">)</span> <span class="token function">Error</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">string</span> <span class="token punctuation">&#123;</span>     <span class="token keyword">return</span> e<span class="token punctuation">.</span>s  <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>这个内置代码中有两个注意点：</p><ol><li>errorString就是一个实现了Error方法的struct，也就是实现了error这个interface，所以我们自定义error的时候就只需要实现Error方法</li><li>New方法返回的不是errorString对象，而是返回对象的指针，这是为了避免字符串text相同时，无法判定两个error是否相等。</li></ol><h2 id="错误类型"><a href="#错误类型" class="headerlink" title="错误类型"></a>错误类型</h2><p>总共有3种错误类型</p><ol><li>Sentinel Error</li><li>Error Type(struct)</li><li>Opaque Error</li></ol><h3 id="Sentinel-Error"><a href="#Sentinel-Error" class="headerlink" title="Sentinel Error"></a>Sentinel Error</h3><p>哨兵错误，就是定义一些包级别的错误变量，然后在调用的时候外部包可以直接对比变量进行判定，在标准库当中大量的使用了这种方式<br>例如下方 <code>net/http</code>  库中定义的错误</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// Errors used by the HTTP server.</span><span class="token keyword">var</span> <span class="token punctuation">(</span><span class="token comment">// ErrBodyNotAllowed is returned by ResponseWriter.Write calls</span><span class="token comment">// when the HTTP method or response code does not permit a</span><span class="token comment">// body.</span>ErrBodyNotAllowed <span class="token operator">=</span> errors<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token string">"http: request method or response status code does not allow body"</span><span class="token punctuation">)</span><span class="token comment">// ErrHijacked is returned by ResponseWriter.Write calls when</span><span class="token comment">// the underlying connection has been hijacked using the</span><span class="token comment">// Hijacker interface. A zero-byte write on a hijacked</span><span class="token comment">// connection will return ErrHijacked without any other side</span><span class="token comment">// effects.</span>ErrHijacked <span class="token operator">=</span> errors<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token string">"http: connection has been hijacked"</span><span class="token punctuation">)</span><span class="token comment">// ErrContentLength is returned by ResponseWriter.Write calls</span><span class="token comment">// when a Handler set a Content-Length response header with a</span><span class="token comment">// declared size and then attempted to write more bytes than</span><span class="token comment">// declared.</span>ErrContentLength <span class="token operator">=</span> errors<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token string">"http: wrote more than the declared Content-Length"</span><span class="token punctuation">)</span><span class="token comment">// Deprecated: ErrWriteAfterFlush is no longer returned by</span><span class="token comment">// anything in the net/http package. Callers should not</span><span class="token comment">// compare errors against this variable.</span>ErrWriteAfterFlush <span class="token operator">=</span> errors<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token string">"unused"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>当返回error之后如果我们想判断error是否时某个Sentinel Error，一般使用<code>errors.Is</code>  进行判断</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">if</span> errors<span class="token punctuation">.</span><span class="token function">Is</span><span class="token punctuation">(</span>err<span class="token punctuation">,</span> io<span class="token punctuation">.</span>EOF<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>这种错误处理方式有一个问题是，将 error 当做包的 API 暴露给了第三方，会增大包的表面积，并且会在两个包之间创建了依赖。这样会导致在做重构或者升级的时候很麻烦，并且这种方式包含的错误信息会十分的有限，所以我们应该尽可能避免使用Sentinel Error</p><h3 id="Error-Type"><a href="#Error-Type" class="headerlink" title="Error Type"></a>Error Type</h3><p>Error type是实现了error接口的自定义类型，比如io库的<code>PathError</code>记录了操作和文件路径</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// PathError records an error and the operation and file path that caused it.</span><span class="token keyword">type</span> PathError <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>     Op   <span class="token builtin">string</span>     Path <span class="token builtin">string</span>     Err  <span class="token builtin">error</span>  <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>因为PathError是一个type，调用者可以使用断言转换成这个类型，来获取更多的上下文信息。</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// IsCorruptedMnt return true if err is about corrupted mount point</span><span class="token keyword">func</span> <span class="token function">IsCorruptedMnt</span><span class="token punctuation">(</span>err <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token builtin">bool</span> <span class="token punctuation">&#123;</span>     <span class="token keyword">if</span> err <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span>        <span class="token keyword">return</span> <span class="token boolean">false</span>     <span class="token punctuation">&#125;</span>       <span class="token keyword">var</span> underlyingError <span class="token builtin">error</span>     <span class="token keyword">switch</span> pe <span class="token operator">:=</span> err<span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token keyword">type</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>     <span class="token keyword">case</span> <span class="token boolean">nil</span><span class="token punctuation">:</span>        <span class="token keyword">return</span> <span class="token boolean">false</span>     <span class="token keyword">case</span> <span class="token operator">*</span>os<span class="token punctuation">.</span>PathError<span class="token punctuation">:</span>        underlyingError <span class="token operator">=</span> pe<span class="token punctuation">.</span>Err     <span class="token keyword">case</span> <span class="token operator">*</span>os<span class="token punctuation">.</span>LinkError<span class="token punctuation">:</span>        underlyingError <span class="token operator">=</span> pe<span class="token punctuation">.</span>Err     <span class="token keyword">case</span> <span class="token operator">*</span>os<span class="token punctuation">.</span>SyscallError<span class="token punctuation">:</span>        underlyingError <span class="token operator">=</span> pe<span class="token punctuation">.</span>Err     <span class="token punctuation">&#125;</span>       <span class="token keyword">if</span> ee<span class="token punctuation">,</span> ok <span class="token operator">:=</span> underlyingError<span class="token punctuation">.</span><span class="token punctuation">(</span>syscall<span class="token punctuation">.</span>Errno<span class="token punctuation">)</span><span class="token punctuation">;</span> ok <span class="token punctuation">&#123;</span>        <span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> errno <span class="token operator">:=</span> <span class="token keyword">range</span> errorNoList <span class="token punctuation">&#123;</span>           <span class="token keyword">if</span> <span class="token function">int</span><span class="token punctuation">(</span>ee<span class="token punctuation">)</span> <span class="token operator">==</span> errno <span class="token punctuation">&#123;</span>              klog<span class="token punctuation">.</span><span class="token function">Warningf</span><span class="token punctuation">(</span><span class="token string">"IsCorruptedMnt failed with error: %v, error code: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">,</span> errno<span class="token punctuation">)</span>              <span class="token keyword">return</span> <span class="token boolean">true</span>           <span class="token punctuation">&#125;</span>        <span class="token punctuation">&#125;</span>   <span class="token punctuation">&#125;</span>     <span class="token keyword">return</span> <span class="token boolean">false</span>  <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>和Sentinel Error相比，Error Type的一大改进是他们能够包装底层错误以提供更多上下文，但是并没有改进Sentinel Error的劣势</p><h3 id="Opaque-Error"><a href="#Opaque-Error" class="headerlink" title="Opaque Error"></a>Opaque Error</h3><p>不透明的错误，这种方式最大的特点就是只返回错误，暴露错误判定接口，不返回类型，这样可以减少 API 的暴露，后续的处理会比较灵活，这个一般用在公共库会比较好。<br>比如，在net库中有定义这样一个Error，内部的error不对外暴露，但是对外暴露了2个方法。</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// An Error represents a network error.</span><span class="token keyword">type</span> Error <span class="token keyword">interface</span> <span class="token punctuation">&#123;</span><span class="token builtin">error</span><span class="token function">Timeout</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">bool</span> <span class="token comment">// Is the error a timeout?</span><span class="token comment">// Deprecated: Temporary errors are not well-defined.</span><span class="token comment">// Most "temporary" errors are timeouts, and the few exceptions are surprising.</span><span class="token comment">// Do not use this method.</span><span class="token function">Temporary</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">bool</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>使用Opaque Error我们可以断言错误实现了特定的行为，而不是断言错误是特定的类型或值</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">type</span> timeout <span class="token keyword">interface</span> <span class="token punctuation">&#123;</span><span class="token function">Timeout</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">bool</span><span class="token punctuation">&#125;</span><span class="token keyword">func</span> <span class="token function">IsTimeout</span><span class="token punctuation">(</span>err <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token builtin">bool</span> <span class="token punctuation">&#123;</span>te<span class="token punctuation">,</span> ok <span class="token operator">:=</span> err<span class="token punctuation">.</span><span class="token punctuation">(</span>timeout<span class="token punctuation">)</span><span class="token keyword">return</span> ok <span class="token operator">&amp;&amp;</span> te<span class="token punctuation">.</span><span class="token function">Timeout</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="错误处理优化"><a href="#错误处理优化" class="headerlink" title="错误处理优化"></a>错误处理优化</h2><p>在 go 中常常会存在大量的 <code>if err</code>  代码，下面介绍两种常见的减少这种代码的方式</p><h3 id="Bufio-scan"><a href="#Bufio-scan" class="headerlink" title="Bufio.scan"></a>Bufio.scan</h3><p>下面的两个CountLines方法，第一个对于err的判断代码比较多，第二个方法一次err判断都没有，极大的简化了代码，这是因为在 <code>sc.Scan</code>  做了很多处理，像很多类似的，需要循环读取的都可以考虑像这样包装之后进行处理</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">CountLines1</span><span class="token punctuation">(</span>r io<span class="token punctuation">.</span>Reader<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token keyword">var</span> <span class="token punctuation">(</span>br    <span class="token operator">=</span> bufio<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>r<span class="token punctuation">)</span>lines <span class="token builtin">int</span>err   <span class="token builtin">error</span><span class="token punctuation">)</span><span class="token keyword">for</span> <span class="token punctuation">&#123;</span><span class="token boolean">_</span><span class="token punctuation">,</span> err <span class="token operator">=</span> br<span class="token punctuation">.</span><span class="token function">ReadString</span><span class="token punctuation">(</span><span class="token char">'\n'</span><span class="token punctuation">)</span>lines<span class="token operator">++</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span><span class="token keyword">break</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token keyword">if</span> err <span class="token operator">!=</span> io<span class="token punctuation">.</span>EOF <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">,</span> err<span class="token punctuation">&#125;</span><span class="token keyword">return</span> lines<span class="token punctuation">,</span> err<span class="token punctuation">&#125;</span><span class="token keyword">func</span> <span class="token function">CountLines2</span><span class="token punctuation">(</span>r io<span class="token punctuation">.</span>Reader<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token keyword">var</span> <span class="token punctuation">(</span>sc    <span class="token operator">=</span> bufio<span class="token punctuation">.</span><span class="token function">NewScanner</span><span class="token punctuation">(</span>r<span class="token punctuation">)</span>lines <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token keyword">for</span> sc<span class="token punctuation">.</span><span class="token function">Scan</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>lines<span class="token operator">++</span><span class="token punctuation">&#125;</span><span class="token keyword">return</span> lines<span class="token punctuation">,</span> sc<span class="token punctuation">.</span><span class="token function">Err</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="error-writer"><a href="#error-writer" class="headerlink" title="error writer"></a>error writer</h3><p>下面的2个WriteResponse方法，简洁程度完全不一样，区别在于第二个方法使用了errWriter这个自定义的结构，将重复的逻辑进行了封装，然后把 error 暂存，然后我们就只需要在最后判断一下 error 就行了</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">type</span> Header <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>Key<span class="token punctuation">,</span> Value <span class="token builtin">string</span><span class="token punctuation">&#125;</span><span class="token keyword">type</span> Status <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>Code   <span class="token builtin">int</span>Reason <span class="token builtin">string</span><span class="token punctuation">&#125;</span><span class="token keyword">func</span> <span class="token function">WriteResponse</span><span class="token punctuation">(</span>w io<span class="token punctuation">.</span>Writer<span class="token punctuation">,</span> st Status<span class="token punctuation">,</span> headers <span class="token punctuation">[</span><span class="token punctuation">]</span>Header<span class="token punctuation">,</span> body io<span class="token punctuation">.</span>Reader<span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span><span class="token boolean">_</span><span class="token punctuation">,</span> err <span class="token operator">:=</span> fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"HTTP/1.1 %d %s\r\n"</span><span class="token punctuation">,</span> st<span class="token punctuation">.</span>Code<span class="token punctuation">,</span> st<span class="token punctuation">.</span>Reason<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> err<span class="token punctuation">&#125;</span><span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> h <span class="token operator">:=</span> <span class="token keyword">range</span> headers <span class="token punctuation">&#123;</span><span class="token boolean">_</span><span class="token punctuation">,</span> err <span class="token operator">:=</span> fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"%s:%s\r\n"</span><span class="token punctuation">,</span> h<span class="token punctuation">.</span>Key<span class="token punctuation">,</span> h<span class="token punctuation">.</span>Value<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> err<span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token keyword">if</span> <span class="token boolean">_</span><span class="token punctuation">,</span> err <span class="token operator">:=</span> fmt<span class="token punctuation">.</span><span class="token function">Fprint</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"\r\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> err<span class="token punctuation">&#125;</span><span class="token boolean">_</span><span class="token punctuation">,</span> err <span class="token operator">=</span> io<span class="token punctuation">.</span><span class="token function">Copy</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> body<span class="token punctuation">)</span><span class="token keyword">return</span> err<span class="token punctuation">&#125;</span><span class="token keyword">type</span> errWriter <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>w   io<span class="token punctuation">.</span>Writererr <span class="token builtin">error</span><span class="token punctuation">&#125;</span><span class="token keyword">func</span> <span class="token punctuation">(</span>e <span class="token operator">*</span>errWriter<span class="token punctuation">)</span> <span class="token function">Write</span><span class="token punctuation">(</span>p <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">byte</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token keyword">if</span> e<span class="token punctuation">.</span>err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span>err<span class="token punctuation">&#125;</span><span class="token keyword">var</span> n <span class="token builtin">int</span>n<span class="token punctuation">,</span> e<span class="token punctuation">.</span>err <span class="token operator">=</span> e<span class="token punctuation">.</span>w<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token keyword">return</span> n<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">&#125;</span><span class="token keyword">func</span> <span class="token function">WriteResponse2</span><span class="token punctuation">(</span>w io<span class="token punctuation">.</span>Writer<span class="token punctuation">,</span> st Status<span class="token punctuation">,</span> headers <span class="token punctuation">[</span><span class="token punctuation">]</span>Header<span class="token punctuation">,</span> body io<span class="token punctuation">.</span>Reader<span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span>ew <span class="token operator">:=</span> <span class="token operator">&amp;</span>errWriter<span class="token punctuation">&#123;</span>w<span class="token punctuation">:</span> w<span class="token punctuation">&#125;</span><span class="token boolean">_</span><span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>ew<span class="token punctuation">,</span> <span class="token string">"HTTP/1.1 %d %s\r\n"</span><span class="token punctuation">,</span> st<span class="token punctuation">.</span>Code<span class="token punctuation">,</span> st<span class="token punctuation">.</span>Reason<span class="token punctuation">)</span><span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> h <span class="token operator">:=</span> <span class="token keyword">range</span> headers <span class="token punctuation">&#123;</span><span class="token boolean">_</span><span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>ew<span class="token punctuation">,</span> <span class="token string">"%s:%s\r\n"</span><span class="token punctuation">,</span> h<span class="token punctuation">.</span>Key<span class="token punctuation">,</span> h<span class="token punctuation">.</span>Value<span class="token punctuation">)</span><span class="token punctuation">&#125;</span><span class="token boolean">_</span><span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>ew<span class="token punctuation">,</span> <span class="token string">"\r\n"</span><span class="token punctuation">)</span><span class="token boolean">_</span><span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> io<span class="token punctuation">.</span><span class="token function">Copy</span><span class="token punctuation">(</span>ew<span class="token punctuation">,</span> body<span class="token punctuation">)</span><span class="token keyword">return</span> ew<span class="token punctuation">.</span>err<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="错误包装"><a href="#错误包装" class="headerlink" title="错误包装"></a>错误包装</h2><p>原则：你应该只处理一次错误。<br>处理一个错误意味着检查错误值。并做出一个决定。要么返回错误，要么忽略错误打日志。</p><h3 id="使用-errors-Wrap-包装错误"><a href="#使用-errors-Wrap-包装错误" class="headerlink" title="使用 errors.Wrap 包装错误"></a>使用 errors.Wrap 包装错误</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">body<span class="token punctuation">,</span> err <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">Marshal</span><span class="token punctuation">(</span>payload<span class="token punctuation">)</span>  <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span>     err <span class="token operator">=</span> errors<span class="token punctuation">.</span><span class="token function">Wrap</span><span class="token punctuation">(</span>err<span class="token punctuation">,</span> <span class="token string">"failed to marshal the body"</span><span class="token punctuation">)</span>     <span class="token keyword">return</span>  <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="为什么不使用标准库的-fmt-Errorf-quot-w-quot"><a href="#为什么不使用标准库的-fmt-Errorf-quot-w-quot" class="headerlink" title="为什么不使用标准库的 fmt.Errorf(&quot;%w&quot;)"></a>为什么不使用标准库的 <code>fmt.Errorf(&quot;%w&quot;)</code></h3><p>我们先看一下标准库的源代码，我们可以发现当 <code>p.wrappedErr != nil</code>  的时候（也就是有 %w）的时候，会使用一个 <code>wrapError</code>  将错误包装，看 <code>wrapError</code>  的源码可以发现，这个方法只是包装了一下原始错误，并且可以做到附加一些文本信息，但是没有堆栈信息。</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> fmt<span class="token keyword">import</span> <span class="token string">"errors"</span><span class="token comment">// Errorf formats according to a format specifier and returns the string as a</span><span class="token comment">// value that satisfies error.</span><span class="token comment">//</span><span class="token comment">// If the format specifier includes a %w verb with an error operand,</span><span class="token comment">// the returned error will implement an Unwrap method returning the operand. It is</span><span class="token comment">// invalid to include more than one %w verb or to supply it with an operand</span><span class="token comment">// that does not implement the error interface. The %w verb is otherwise</span><span class="token comment">// a synonym for %v.</span><span class="token keyword">func</span> <span class="token function">Errorf</span><span class="token punctuation">(</span>format <span class="token builtin">string</span><span class="token punctuation">,</span> a <span class="token operator">...</span>any<span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span>p <span class="token operator">:=</span> <span class="token function">newPrinter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>p<span class="token punctuation">.</span>wrapErrs <span class="token operator">=</span> <span class="token boolean">true</span>p<span class="token punctuation">.</span><span class="token function">doPrintf</span><span class="token punctuation">(</span>format<span class="token punctuation">,</span> a<span class="token punctuation">)</span>s <span class="token operator">:=</span> <span class="token function">string</span><span class="token punctuation">(</span>p<span class="token punctuation">.</span>buf<span class="token punctuation">)</span><span class="token keyword">var</span> err <span class="token builtin">error</span><span class="token keyword">if</span> p<span class="token punctuation">.</span>wrappedErr <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span>err <span class="token operator">=</span> errors<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>err <span class="token operator">=</span> <span class="token operator">&amp;</span>wrapError<span class="token punctuation">&#123;</span>s<span class="token punctuation">,</span> p<span class="token punctuation">.</span>wrappedErr<span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span>p<span class="token punctuation">.</span><span class="token function">free</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span> err<span class="token punctuation">&#125;</span><span class="token keyword">type</span> wrapError <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>msg <span class="token builtin">string</span>err <span class="token builtin">error</span><span class="token punctuation">&#125;</span><span class="token keyword">func</span> <span class="token punctuation">(</span>e <span class="token operator">*</span>wrapError<span class="token punctuation">)</span> <span class="token function">Error</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">string</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> e<span class="token punctuation">.</span>msg<span class="token punctuation">&#125;</span><span class="token keyword">func</span> <span class="token punctuation">(</span>e <span class="token operator">*</span>wrapError<span class="token punctuation">)</span> <span class="token function">Unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> e<span class="token punctuation">.</span>err<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>但是在 <code>pkg/errors</code> 的源码，可以发现除了使用 <code>withMessage</code>  附加了错误信息之外还使用 <code>withStack</code>  附加了堆栈信息，这样我们在程序入口处打印日志信息的时候就可以将堆栈信息一并打出了。</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// Wrap returns an error annotating err with a stack trace</span><span class="token comment">// at the point Wrap is called, and the supplied message.</span><span class="token comment">// If err is nil, Wrap returns nil.</span><span class="token keyword">func</span> <span class="token function">Wrap</span><span class="token punctuation">(</span>err <span class="token builtin">error</span><span class="token punctuation">,</span> message <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span><span class="token keyword">if</span> err <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">&#125;</span>err <span class="token operator">=</span> <span class="token operator">&amp;</span>withMessage<span class="token punctuation">&#123;</span>cause<span class="token punctuation">:</span> err<span class="token punctuation">,</span>msg<span class="token punctuation">:</span>   message<span class="token punctuation">,</span><span class="token punctuation">&#125;</span><span class="token keyword">return</span> <span class="token operator">&amp;</span>withStack<span class="token punctuation">&#123;</span>err<span class="token punctuation">,</span><span class="token function">callers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="为什么不允许处处使用-errors-Wrap"><a href="#为什么不允许处处使用-errors-Wrap" class="headerlink" title="为什么不允许处处使用 errors.Wrap"></a>为什么不允许处处使用 errors.Wrap</h3><p>因为每一次 <code>errors.Wrap</code>  的调用都会为错误添加堆栈信息，如果处处调用那会有大量的无用堆栈<br>下面的代码中，我们只有一处 wrap</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">TestErrorWrap</span><span class="token punctuation">(</span>t <span class="token operator">*</span>testing<span class="token punctuation">.</span>T<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>     fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"err: %+v"</span><span class="token punctuation">,</span> <span class="token function">c</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">func</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span>     <span class="token keyword">return</span> errors<span class="token punctuation">.</span><span class="token function">Wrap</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"xxx"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"test"</span><span class="token punctuation">)</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">func</span> <span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span>     <span class="token keyword">return</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">func</span> <span class="token function">c</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span>     <span class="token keyword">return</span> <span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>print的结果显示一次wrap足够打出全部的堆栈信息</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">err: xxxtestbackend&#x2F;pkg.a&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:14backend&#x2F;pkg.b&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:18backend&#x2F;pkg.c&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:22backend&#x2F;pkg.TestErrorWrap&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:10testing.tRunner&#x2F;Users&#x2F;suncle&#x2F;.gvm&#x2F;gos&#x2F;go1.18&#x2F;src&#x2F;testing&#x2F;testing.go:1439runtime.goexit&#x2F;Users&#x2F;suncle&#x2F;.gvm&#x2F;gos&#x2F;go1.18&#x2F;src&#x2F;runtime&#x2F;asm_arm64.s:1259--- PASS: TestErrorWrap (0.00s)<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>再看下多处wrap的代码</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">TestErrorWrap</span><span class="token punctuation">(</span>t <span class="token operator">*</span>testing<span class="token punctuation">.</span>T<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>     fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"err: %+v"</span><span class="token punctuation">,</span> <span class="token function">c</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">func</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span>     <span class="token keyword">return</span> errors<span class="token punctuation">.</span><span class="token function">Wrap</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"xxx"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"test"</span><span class="token punctuation">)</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">func</span> <span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span>     <span class="token keyword">return</span> errors<span class="token punctuation">.</span><span class="token function">Wrap</span><span class="token punctuation">(</span><span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"b"</span><span class="token punctuation">)</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">func</span> <span class="token function">c</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">&#123;</span>     <span class="token keyword">return</span> errors<span class="token punctuation">.</span><span class="token function">Wrap</span><span class="token punctuation">(</span><span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"c"</span><span class="token punctuation">)</span>  <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>可以看到每一处wrap都添加了一次堆栈信息</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">err: xxxtestbackend&#x2F;pkg.a&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:14backend&#x2F;pkg.b&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:18backend&#x2F;pkg.c&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:22backend&#x2F;pkg.TestErrorWrap&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:10testing.tRunner&#x2F;Users&#x2F;suncle&#x2F;.gvm&#x2F;gos&#x2F;go1.18&#x2F;src&#x2F;testing&#x2F;testing.go:1439runtime.goexit&#x2F;Users&#x2F;suncle&#x2F;.gvm&#x2F;gos&#x2F;go1.18&#x2F;src&#x2F;runtime&#x2F;asm_arm64.s:1259bbackend&#x2F;pkg.b&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:18backend&#x2F;pkg.c&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:22backend&#x2F;pkg.TestErrorWrap&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:10testing.tRunner&#x2F;Users&#x2F;suncle&#x2F;.gvm&#x2F;gos&#x2F;go1.18&#x2F;src&#x2F;testing&#x2F;testing.go:1439runtime.goexit&#x2F;Users&#x2F;suncle&#x2F;.gvm&#x2F;gos&#x2F;go1.18&#x2F;src&#x2F;runtime&#x2F;asm_arm64.s:1259cbackend&#x2F;pkg.c&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:22backend&#x2F;pkg.TestErrorWrap&#x2F;Users&#x2F;suncle&#x2F;pkg&#x2F;a_test.go:10testing.tRunner&#x2F;Users&#x2F;suncle&#x2F;.gvm&#x2F;gos&#x2F;go1.18&#x2F;src&#x2F;testing&#x2F;testing.go:1439runtime.goexit&#x2F;Users&#x2F;suncle&#x2F;.gvm&#x2F;gos&#x2F;go1.18&#x2F;src&#x2F;runtime&#x2F;asm_arm64.s:1259--- PASS: TestErrorWrap (0.00s)<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="错误判断"><a href="#错误判断" class="headerlink" title="错误判断"></a>错误判断</h2><h3 id="errors-Is"><a href="#errors-Is" class="headerlink" title="errors.Is"></a>errors.Is</h3><p>不断unwrap然后判断是否相等</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">Is</span><span class="token punctuation">(</span>err<span class="token punctuation">,</span> target <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token builtin">bool</span> <span class="token punctuation">&#123;</span><span class="token keyword">if</span> target <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> err <span class="token operator">==</span> target<span class="token punctuation">&#125;</span>isComparable <span class="token operator">:=</span> reflectlite<span class="token punctuation">.</span><span class="token function">TypeOf</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Comparable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 循环判断是否相等</span><span class="token keyword">for</span> <span class="token punctuation">&#123;</span><span class="token keyword">if</span> isComparable <span class="token operator">&amp;&amp;</span> err <span class="token operator">==</span> target <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">&#125;</span><span class="token keyword">if</span> x<span class="token punctuation">,</span> ok <span class="token operator">:=</span> err<span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token keyword">interface</span><span class="token punctuation">&#123;</span> <span class="token function">Is</span><span class="token punctuation">(</span><span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token builtin">bool</span> <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ok <span class="token operator">&amp;&amp;</span> x<span class="token punctuation">.</span><span class="token function">Is</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">&#125;</span><span class="token comment">// APIs, thereby making it easier to get away with them.</span><span class="token keyword">if</span> err <span class="token operator">=</span> <span class="token function">Unwrap</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="errors-As"><a href="#errors-As" class="headerlink" title="errors.As"></a>errors.As</h3><p>和is的逻辑类似，也是不断unwrap比较，找到一个相同的err就返回</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">As</span><span class="token punctuation">(</span>err <span class="token builtin">error</span><span class="token punctuation">,</span> target any<span class="token punctuation">)</span> <span class="token builtin">bool</span> <span class="token punctuation">&#123;</span><span class="token keyword">if</span> target <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span><span class="token function">panic</span><span class="token punctuation">(</span><span class="token string">"errors: target cannot be nil"</span><span class="token punctuation">)</span><span class="token punctuation">&#125;</span>val <span class="token operator">:=</span> reflectlite<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span>typ <span class="token operator">:=</span> val<span class="token punctuation">.</span><span class="token function">Type</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">if</span> typ<span class="token punctuation">.</span><span class="token function">Kind</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> reflectlite<span class="token punctuation">.</span>Ptr <span class="token operator">||</span> val<span class="token punctuation">.</span><span class="token function">IsNil</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token function">panic</span><span class="token punctuation">(</span><span class="token string">"errors: target must be a non-nil pointer"</span><span class="token punctuation">)</span><span class="token punctuation">&#125;</span>targetType <span class="token operator">:=</span> typ<span class="token punctuation">.</span><span class="token function">Elem</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">if</span> targetType<span class="token punctuation">.</span><span class="token function">Kind</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> reflectlite<span class="token punctuation">.</span>Interface <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>targetType<span class="token punctuation">.</span><span class="token function">Implements</span><span class="token punctuation">(</span>errorType<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token function">panic</span><span class="token punctuation">(</span><span class="token string">"errors: *target must be interface or implement error"</span><span class="token punctuation">)</span><span class="token punctuation">&#125;</span><span class="token keyword">for</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span><span class="token keyword">if</span> reflectlite<span class="token punctuation">.</span><span class="token function">TypeOf</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">AssignableTo</span><span class="token punctuation">(</span>targetType<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>val<span class="token punctuation">.</span><span class="token function">Elem</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span>reflectlite<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">&#125;</span><span class="token keyword">if</span> x<span class="token punctuation">,</span> ok <span class="token operator">:=</span> err<span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token keyword">interface</span><span class="token punctuation">&#123;</span> <span class="token function">As</span><span class="token punctuation">(</span>any<span class="token punctuation">)</span> <span class="token builtin">bool</span> <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ok <span class="token operator">&amp;&amp;</span> x<span class="token punctuation">.</span><span class="token function">As</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">&#125;</span>err <span class="token operator">=</span> <span class="token function">Unwrap</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">&#125;</span><span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="统一打印错误日志"><a href="#统一打印错误日志" class="headerlink" title="统一打印错误日志"></a>统一打印错误日志</h2><p>在http&#x2F;rpc 服务的中间件，或者是公司的kit库里面，添加上统一打印错误日志的中间<br>价，就可以统一处理了，再也不需要在代码中每一处错误返回的地方都打印日志。<br>以kratos框架为例，server的logging middleware可以写成这样:</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// Server is an server logging middleware.</span><span class="token keyword">func</span> <span class="token function">Server</span><span class="token punctuation">(</span>logger log<span class="token punctuation">.</span>Logger<span class="token punctuation">)</span> middleware<span class="token punctuation">.</span>Middleware <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token keyword">func</span><span class="token punctuation">(</span>handler middleware<span class="token punctuation">.</span>Handler<span class="token punctuation">)</span> middleware<span class="token punctuation">.</span>Handler <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token keyword">func</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">,</span> req <span class="token keyword">interface</span><span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>reply <span class="token keyword">interface</span><span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span> err <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token keyword">var</span> <span class="token punctuation">(</span>code      <span class="token builtin">int32</span> <span class="token operator">=</span> <span class="token number">200</span>reason    <span class="token builtin">string</span>kind      <span class="token builtin">string</span>operation <span class="token builtin">string</span><span class="token punctuation">)</span>startTime <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">if</span> info<span class="token punctuation">,</span> ok <span class="token operator">:=</span> transport<span class="token punctuation">.</span><span class="token function">FromServerContext</span><span class="token punctuation">(</span>ctx<span class="token punctuation">)</span><span class="token punctuation">;</span> ok <span class="token punctuation">&#123;</span>kind <span class="token operator">=</span> info<span class="token punctuation">.</span><span class="token function">Kind</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span><span class="token punctuation">)</span>operation <span class="token operator">=</span> info<span class="token punctuation">.</span><span class="token function">Operation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#125;</span>reply<span class="token punctuation">,</span> err <span class="token operator">=</span> <span class="token function">handler</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> req<span class="token punctuation">)</span><span class="token keyword">if</span> se <span class="token operator">:=</span> errors<span class="token punctuation">.</span><span class="token function">FromError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> se <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">&#123;</span>code <span class="token operator">=</span> se<span class="token punctuation">.</span>Codereason <span class="token operator">=</span> se<span class="token punctuation">.</span>Reason<span class="token punctuation">&#125;</span>level<span class="token punctuation">,</span> stack <span class="token operator">:=</span> <span class="token function">extractError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token boolean">_</span> <span class="token operator">=</span> log<span class="token punctuation">.</span><span class="token function">WithContext</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> logger<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Log</span><span class="token punctuation">(</span>level<span class="token punctuation">,</span><span class="token string">"kind"</span><span class="token punctuation">,</span> <span class="token string">"server"</span><span class="token punctuation">,</span><span class="token string">"component"</span><span class="token punctuation">,</span> kind<span class="token punctuation">,</span><span class="token string">"operation"</span><span class="token punctuation">,</span> operation<span class="token punctuation">,</span><span class="token string">"args"</span><span class="token punctuation">,</span> <span class="token function">extractArgs</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token string">"code"</span><span class="token punctuation">,</span> code<span class="token punctuation">,</span><span class="token string">"reason"</span><span class="token punctuation">,</span> reason<span class="token punctuation">,</span><span class="token string">"stack"</span><span class="token punctuation">,</span> stack<span class="token punctuation">,</span><span class="token string">"latency"</span><span class="token punctuation">,</span> time<span class="token punctuation">.</span><span class="token function">Since</span><span class="token punctuation">(</span>startTime<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Seconds</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>有了这样的middleware统一处理错误日志之后，我们就不需要再代码中疯狂打日志了，在kibana上也很方便的排查问题<br><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/picgo/20221204174017.png"></p><hr><p>参考：</p><ol><li><a href="https://coolshell.cn/articles/21140.html">GO 编程模式：错误处理</a></li><li><a href="https://github.com/pkg/errors">pkg&#x2F;errors</a></li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;最佳实践&quot;&gt;&lt;a href=&quot;#最佳实践&quot; class=&quot;headerlink&quot; title=&quot;最佳实践&quot;&gt;&lt;/a&gt;最佳实践&lt;/h2&gt;&lt;p&gt;先总结一下，在日常开发工作中，我们是怎么使用panic和error的。&lt;/p&gt;
&lt;h3 id=&quot;panic最佳实践&quot;&gt;&lt;a </summary>
      
    
    
    
    <category term="Go进阶" scheme="https://suncle.me/categories/Go%E8%BF%9B%E9%98%B6/"/>
    
    <category term="02. Go 工程化" scheme="https://suncle.me/categories/Go%E8%BF%9B%E9%98%B6/02-Go-%E5%B7%A5%E7%A8%8B%E5%8C%96/"/>
    
    
    <category term="Go" scheme="https://suncle.me/tags/Go/"/>
    
    <category term="工程化" scheme="https://suncle.me/tags/%E5%B7%A5%E7%A8%8B%E5%8C%96/"/>
    
    <category term="Error" scheme="https://suncle.me/tags/Error/"/>
    
  </entry>
  
  <entry>
    <title>“有用之用”还是“无用之用” -#4</title>
    <link href="https://suncle.me/posts/2599171020/"/>
    <id>https://suncle.me/posts/2599171020/</id>
    <published>2022-07-10T09:56:37.000Z</published>
    <updated>2023-09-03T14:31:24.461Z</updated>
    
    <content type="html"><![CDATA[<p>这是 Suncle Lab 第 4 期，我会在这里更新区块链、技术、金融、旅行和个人成长相关的内容。每期都会同步发布在我<a href="https://suncle.me/">博客</a>，欢迎你<a href="https://suncle.zhubai.love/">邮箱订阅</a>，这样就可以第一时间收到更新推送。</p><p>读书时代，我们经常会听到老师和家长告诫我们，学习一定要能学以致用，不能产生价值的不要浪费时间。</p><p>比如在高中，老师会说，一切对高考没有用处的杂事，都应该舍弃，不要看没办法直接增加分数的杂志，甚至《读者》、《意林》、《青年文摘》这类杂志都不建议看。</p><p>但是庄子在《人间世》中又这样说道：</p><blockquote><p>山木自寇也，膏火自煎也。桂可食，故伐之；漆可用，故割之。人皆知有用之用，而莫知无用之用也。</p></blockquote><p>讲人话就是：树木、油脂、桂树皮、树漆这些具有使用价值的物品，都会因使用而毁灭。反而森林中那种不能用于造船而且容易腐烂，百无一用的树种，最终能长成参天大树。</p><p>所以，本期内容主要会围绕无用之用讨论以下内容：</p><ul><li>“有用之用”还是“无用之用”？</li><li>比特币和以太坊谁更有价值？</li></ul><h2 id="“有用之用”还是“无用之用”"><a href="#“有用之用”还是“无用之用”" class="headerlink" title="“有用之用”还是“无用之用”"></a>“有用之用”还是“无用之用”</h2><p>首先，我们判断一个东西是有用还是无用，更多的是在讨论一个东西是否具有使用价值。 </p><p>生活中有使用价值的东西有很多，比如说，石油，除了作为燃油，还可以练出各种化学产品。</p><p>最常见的有塑料 、沥青、衣服、合成橡胶、制药、清洁用品等等，可以说现代文明把石油比作“工业的血液”一点也不为过。</p><p>但是，生活中很多东西是没有使用价值但又很有价值的。</p><p>阅读有没有用呢？你可能会说看书又不会立刻提升工资，也没办法让你买得起今晚上要吃的面包。</p><p>旅游有没有用呢？你去旅游领导也不会给你升职，还可能会说你不务正业，当然也不只是领导了，你的父母大概率也都会这样说的。</p><p>还记得我毕业之后的几年里面，几乎每个周末都到周边看看，差不多走遍了整个江浙沪，也被我爸妈劝诫了无数次：整天就知道出去玩，工作不好好干，以后怎么娶媳妇？怎么买房？</p><p>如果毕业的时候只是着眼于当前的生活，所有的努力都只是为了谋生，就很容易陷入绝大多数毕业生都无法逃脱的迷茫。</p><p>我们要跳出局部空间，从更长远的时间维度去思考，做自己的主人，将业余时间放在提升认知上，多去研究数学，多去深入哲学，尽可能的摒弃重复工作，多一些高屋建瓴，少一些原地打转。</p><p><strong>无用之用，是为大用。</strong></p><p>除了阅读、旅游、数学、哲学，还有一个很典型的没有使用价值但很有价值的东西就是法币。</p><p>如果说纸制的法币还能用来燃烧，那么数字化的法币就完全丢失了使用价值，只剩下和面值相等的数额价值了。</p><p>那在加密世界里面的比特币和以太坊又都有什么价值呢？</p><h2 id="比特币和以太坊谁更有价值"><a href="#比特币和以太坊谁更有价值" class="headerlink" title="比特币和以太坊谁更有价值"></a>比特币和以太坊谁更有价值</h2><p>有人说比特币是空气，除了炒作，没有任何用处。</p><p>如果从使用价值的角度看，比特币和数字化的法币确实很像，除了用于交换，你并不能用比特币干任何事情。</p><p>但是从时间和空间两个角度去考虑，比特币有两个实实在在的价值。</p><p>一个是在相同时间下，实现不同空间的价值交换。你可以从地球一端将价值传输到另一端，而不需要任何第三方中心化结算机构。</p><p>另一个是在不同时间之间，实现价值的传承。你可以将你现在的收入存储到比特币中，在将来的某一天从比特币中取出价值。</p><p>而且比特币的总量有限，一个比特币永远等于一个比特币。在法币超发的大背景下，便携、低手续费、去中心化的比特币是远胜黄金的价值存储手段。</p><p>法币越贬值，比特币币价就越高，但是币价越高，并不会给比特币系统造成任何的负面影响。</p><p>以太坊呢？以太坊诞生的目的就是为了给比特币增加使用价值，只是这个理念，和比特币是相悖的。</p><p>作为世界上最大的使用率最高的智能合约平台，以太坊生态上蓬勃发展的去中心化金融和非同质化代币项目数不胜数。比如常见的去中心化交易所uniswap，去中心化借贷aave，加密朋克NFT，无聊猿猴NFT。</p><p>以太币的价格想要上涨，就需要其内在价值越高，而内在价值取决于以太坊生态的繁荣程度。</p><p>但是以太币又是以太坊生态系统中的燃气（gas），再加上以太坊的竞价打包机制，以太币价格的升高，gas就会越高，会反过来抑制用户对以太坊的使用。</p><p>记得在去年加密货币牛市期间，以太坊的一笔交易费用高达千元人民币，几乎到了无法使用的地步，这也间接的催生了各种高性能低费率公链和侧链的诞生。</p><p>在这之后，一些项目的协议和代币发行迁移到了这些低费率的公链或者侧链上，比如Polygon，这也意味着过高的币价导致了以太坊生态的外溢。</p><p>这样的负反馈也就导致了以太坊具有与生俱来的自限性。</p><p>在我看来，随着加密行业快速发展，行业快速扩张，越来越多的用户加入，在价值存储方面，拥有自限性的以太坊自然无法和比特币相提并论。</p><h2 id="最近看了什么"><a href="#最近看了什么" class="headerlink" title="最近看了什么"></a>最近看了什么</h2><ul><li><a href="%5Bhttps://addyosmani.com/blog/software-engineering-soft-parts/%5D(https://mirror.xyz/rolex1.eth/_ruqwO5vvrZiNxr_MZBtOYgTo-gpjhaijQ7yeELPKBs)">2022上半年回顾与展望</a> - 大姨妈对2022上半年暴跌的复盘，记得顺势而为，永远不要逆周期</li><li><a href="https://www.btcstudy.org/2022/06/16/proof-of-stake-is-not-objective/"># 以太坊今天过审的可租赁NFT ERC-4907为什么这么重要？</a> - 将NFT使用权与所有权进行分离的协议</li><li><a href="%5Bhttps://guoyu.mirror.xyz/RD-xkpoxasAU7x5MIJmiCX4gll3Cs0pAd5iM258S1Ek%5D(https://ri.firesbox.com/#/cn/)">定投改变命运</a> - 李笑来文集，定投才是投资中的正派武功，改变的不仅仅是你一个人的命运，也是与你有关的几代人的命运</li></ul><hr><blockquote><p>往期推荐：</p></blockquote><ul><li><a href="https://suncle.zhubai.love/posts/2155790361623166976">打破信息茧房-我主动获取信息的方法 -#3</a></li><li><a href="https://suncle.zhubai.love/posts/2152510668208181248">如何筹备一场感人的婚礼 -#2</a></li><li><a href="https://suncle.zhubai.love/posts/2114096491819589632">追求不舒适的地方，直到整个世界都是我的家 -#1</a></li></ul><p>声明：本文出自作者，不代表任何机构或公司，亦不构成投资建议。</p><p>你可以在这里找到我：</p><ul><li><a href="https://suncle.me/">Blog</a></li><li><a href="http://twitter.com/suncle_chen">Twitter</a> @suncle_chen</li><li>公众号：<strong>职场亮哥</strong></li></ul><blockquote><p><em>这里是</em> <a href="https://suncle.me/"><em>Suncle</em></a> <em>的</em> <a href="https://suncle.zhubai.love/"><em>newsletter</em></a> <em>，欢迎</em> <a href="https://suncle.zhubai.love/"><em>订阅</em></a> <em>，如果觉得这篇文章对你有用，可以分享给好友</em></p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;这是 Suncle Lab 第 4 期，我会在这里更新区块链、技术、金融、旅行和个人成长相关的内容。每期都会同步发布在我&lt;a href=&quot;https://suncle.me/&quot;&gt;博客&lt;/a&gt;，欢迎你&lt;a href=&quot;https://suncle.zhubai.love/&quot;&gt;</summary>
      
    
    
    
    <category term="newsletter" scheme="https://suncle.me/categories/newsletter/"/>
    
    
    <category term="newletter" scheme="https://suncle.me/tags/newletter/"/>
    
  </entry>
  
  <entry>
    <title>打破信息茧房-我主动获取信息的方法 -#3</title>
    <link href="https://suncle.me/posts/2734770981/"/>
    <id>https://suncle.me/posts/2734770981/</id>
    <published>2022-07-02T06:46:37.000Z</published>
    <updated>2023-09-03T14:31:24.461Z</updated>
    
    <content type="html"><![CDATA[<p>这是 Suncle Lab 第 3 期，我会在这里更新区块链、技术、金融、旅行和个人成长相关的内容。每期都会同步发布在我<a href="https://suncle.me/">博客</a>，欢迎你<a href="https://suncle.zhubai.love/">邮箱订阅</a>，这样就可以第一时间收到更新推送。</p><p>我在之前写过两篇 blog 介绍我推荐的 mac alfred 工作流和 google chrome 插件，都是用于提高效率的。</p><p>Alfred 工作流的阅读量有 3600+，而 chrome 插件的阅读量只有 100+，这个比例和我在工作中实际的使用频率也是相符合的。</p><ul><li><a href="https://suncle.me/2020/12/09/tool-recommendation-useful-alfred-workflow/">神兵利器推荐——你一定不能错过的mac alfred工作流</a></li><li><a href="https://suncle.me/2020/12/09/tool-recommendation-chrome-browser-extension/">神兵利器推荐——你一定不能错过的chrome插件</a></li></ul><p>但是除了提高工作效率之外，我的生活中还有很大一部分是处理信息的输入，所以本期会分享一下我在繁忙的工作中，保持信息高效率获取的方法。</p><p>首先说一点，我不反对通过算法推荐来获取信息，但是我不看快速冲击的并且只有爽点的短视频，比如抖音，快手和微信短视频。</p><p>生活中除了推荐之外，应该还有一些自己去主动搜寻的信息，而这些主动获得的信息，才会对你的生活现状有指导作用，才可能对你的生活有长远的帮助。</p><p>所以，本期内容主要有这些：</p><ul><li>获取信息的渠道</li><li>分享我的信息源</li><li>主动扩展信息源的方法</li><li>获取信息的工具</li></ul><h2 id="获取信息的渠道"><a href="#获取信息的渠道" class="headerlink" title="获取信息的渠道"></a>获取信息的渠道</h2><p>目前我获取信息的渠道主要有以下 6 种：</p><ol><li>RSS 简易信息聚合 </li><li>Newsletter 通讯周刊</li><li>YouTube 最大的视频网站 </li><li>Column 专栏</li><li>Podcast 播客</li><li>EBook 电子书</li></ol><p>6 种渠道的使用频率依次递减，但是这个频率不是一成不变的，而是动态变化的。</p><p>比如之前在欢聚-Hago 工作的时候，坐地铁单程上班时间是 40 分钟左右，但是 newsletter 和 rss 我习惯是用电脑 App 阅读。因此那段时间，使用频率最高的是在手机上看 <code>得到</code> 和 <code>极客时间</code> 的专栏。</p><p>今年开始的远程工作，没有了路上的通勤时间，就可以按照自己的喜好来决定阅读顺序。</p><p>下面我会对 RSS, Newsletter, Youtube 这 3 种渠道分别做些简单的阐述。</p><p><strong>RSS 简易信息聚合</strong></p><p>RSS 是起源于互联网早期一种很基础的协议，可以轻松获取网站的文章，通过 RSS 阅读器，也很清楚的知道这些文章从哪里来的。</p><p>而且一旦发现文章对自己没有用了，就可以随时取消订阅。</p><p>简单的说，RSS 有 4 个优点。</p><p><strong>即时性</strong>，对于 RSS 的订阅者而言，可以最快的得到最新讯息以及头条新闻。而不用被动式的打开每个网站上去搜索。</p><p><strong>统一的标准</strong>，RSS 有一套统一的标准，每个有提供 RSS 的网站都会依循此标准，方便阅读器解析和迁移。</p><p><strong>隐私性</strong>，对于订阅者而言，基于拉取模式的 RSS 并不需要提供自己的电子信箱；而发行者并不能利用电子邮件重复不断的寄广告信或是垃圾信件。因此 RSS 意味着真正的隐私。</p><p>最近兴起的 RSS3，在 RSS 的基础上，实现了去中心化的内容分发协议。</p><p>基于 RSS3，用户在各种去中心化的网络中创作的数字内容，也可以实现聚合，比如以太坊和 solana 上的内容。</p><p>信息聚合在一起之后，可以作为自己的个人主页或者个人博客。</p><p><strong>Newsletter 通讯周刊</strong></p><p>这个我是今年（2022 年）才开始重点使用的信息渠道。有一次在刷推特的时候，偶然发现一篇关于加密货币的文章，文章的最后就有提醒订阅的邮箱输入框，从此打开了我的 newsletter 大门。</p><p>到现在我已经订阅了不少领域的 newsletter，而且几乎都是周刊性质的。我很少看日刊，是因为信息频率太高，就意味着质量不会那么高。</p><p>想象一下，你在工作之余，写出了一篇高质量的文章，但是日复一日的去写，绝对不可能每篇都是精品。</p><p>比如我知道的那些在公众号做到日更的作者，除了极少数高产，其他的都是接受读者投稿，或者掺杂水文和广告。</p><p>因此我觉得写作的频率，周更是最合适的，多数 newsletter 都是周更的，非常契合我的日常节奏。</p><p>而且，newsletter 读起来并不会很枯燥，除了本周的主题之外，作者还会介绍一下自己的近况以及本周读过并且推荐的文章链接。</p><p>因此从一篇 newsletter 里面获取到的信息远远不止文章本身，相当于得到了一个同频的人为你筛选好了精华文章。</p><p>随着订阅的 newsletter 数量越来越多，也不可能每一篇都精读，很多 newsletter 我都只读标题，和开篇的粗略介绍，觉得有意思才会精读。</p><p>此外订阅的 newsletter 也不仅限于技术和 crypto 领域的，也会主动订阅一些金融，音乐相关的，永远都不要把自己局限在某一个小圈子里面，很有可能在别的领域，你能得到意想不到的收获。</p><p><strong>YouTube 最大的视频网站</strong></p><p>Youtube 创作者的广告收入是所有平台里面最高的，国内没有一个平台可以与之相比，收入高，生态好，持续正循环。</p><p>而想要玩转youtube，只要学好英语就够了，教程&#x2F;测评&#x2F;全世界旅游vlog&#x2F;大学课程等各种各样的英语资源等着你去发掘。</p><p>还记得我大一时搭建的个人网站，就是在 youtube 上搜到的基于 wordpress 的搭建教程，通俗易懂，还能练习英语。</p><h2 id="我的信息源"><a href="#我的信息源" class="headerlink" title="我的信息源"></a>我的信息源</h2><ul><li>我订阅的 <a href="https://github.com/suncle1993/share/blob/main/rss/RSS-Subscriptions.opml">RSS 列表</a>  - 技术部分读的比较多，其他几乎都是 crypto 相关的（集成过<a href="https://twitter.com/nake13">潘老师</a>的一部分列表）</li><li>我订阅的 <a href="https://github.com/suncle1993/share/blob/main/newsletter/suncle-newsletter-subscription-list.md">newsletter 列表</a> - 只列了常看的，会持续更新 </li><li>我订阅的 <a href="https://github.com/suncle1993/share/blob/main/youtube/suncle-youtube-subscription-list.md">YouTube列表</a> - 全部列表都在这里了，写了个简单的 python 脚本解析的。如果只能推荐一个频道那就是 <a href="https://youtube.com//channel/UCMUnInmOkrWN4gof9KlhNmQ">老高與小茉 Mr &amp; Mrs Gao</a></li></ul><p>专栏列表和播客列表后续会继续在这篇 newsletter 更新，欢迎收藏并且订阅我的 newsletter。</p><h2 id="主动扩展信息源的方法"><a href="#主动扩展信息源的方法" class="headerlink" title="主动扩展信息源的方法"></a>主动扩展信息源的方法</h2><p>思考一下，互联网的本质是什么？</p><p>互联网本质是一个图状的网络，这个网络的节点是各个网站，而这个网络中一个节点到另一个节点的边，就是一个网站到另一个网站的外链。</p><p>因此，我们需要做的就是 2 步，第一步根据喜好找到你的种子网站，第二步通过种子网站的外链一层一层的找到那些你感兴趣的站点。</p><p>此外，现在很多的 newsletter 都会互相推荐，比如在文章最后会推荐一些其他文章，还有其他的 newsletter 作者，从这些外链可以找到一些高质量的作者，比如在上文，我分享的信息源里面就有很多优质作者。</p><p>博客同样也是如此，如果你想找到一些活跃的作者，有一个电报频道 <a href="https://t.me/FindBlog">Find Blogs</a>，是专门做优质博客推荐的，可以作为参考。</p><p>当然除了拓展新的信息源，也要时常更新已有的，那些质量降低的就直接取消订阅就好了。</p><h2 id="获取信息的工具"><a href="#获取信息的工具" class="headerlink" title="获取信息的工具"></a>获取信息的工具</h2><p>下面到了我最喜欢的推荐环节，按照信息渠道依次推荐对应的工具。</p><p><strong>RSS：NetNewsWire</strong></p><p>对比使用了 Reeder, InoReader, Feedly 和 NetNewsWire 之后，只推荐你使用 NetNewsWire 作为阅读器，最看重它的 3 大优势：完全开源，零广告，免费。</p><p><strong>Newsletter： Spark 邮箱客户端</strong></p><p>用了 Spark 之后再也不想用 Apple Mail。优异的速度，隐式的自动重连，阅后即归档的理念，彻底告别信息焦虑，这个世界上信息不是太少，而是太多，阅读完了就不要继续挂念了。</p><p><strong>专栏：得到 App ｜ 极客时间</strong></p><p>中文世界里，通识教育专栏就去得到 App，技术提升专栏就选极客时间。</p><p><strong>播客：Apple Podcast ｜ 小宇宙</strong></p><p>很多大佬不愿意写文章，但是愿意接受访谈。国外的大佬基本都在 Apple Podcast 或者是 Spotify，国内的大佬基本都在小宇宙。</p><p>所以，我的睡前时间，经常是贡献给播客的。</p><h2 id="最近看了什么"><a href="#最近看了什么" class="headerlink" title="最近看了什么"></a>最近看了什么</h2><ul><li><a href="https://addyosmani.com/blog/software-engineering-soft-parts/">Software Engineering - The Soft Parts</a> - Addy 在 Google Chrome 10 年软件开发工作的软技能总结</li><li><a href="https://www.btcstudy.org/2022/06/16/proof-of-stake-is-not-objective/">PoS 不具备客观性</a> - 这也是我不看好以太坊转 POS 的原因之一，还有一个原因是 POS 存在的循环证明问题</li><li><a href="https://guoyu.mirror.xyz/RD-xkpoxasAU7x5MIJmiCX4gll3Cs0pAd5iM258S1Ek">Web3 DApp 最佳编程实践指南</a> - @guoyu DApp 开发全周期的经验分享，twitter上也有答疑的Space，非常推荐有意向转 Web3 的同学精读</li></ul><hr><blockquote><p>往期推荐：</p></blockquote><ul><li><a href="https://suncle.zhubai.love/posts/2152510668208181248">如何筹备一场感人的婚礼 -#2</a></li><li><a href="https://suncle.zhubai.love/posts/2114096491819589632">追求不舒适的地方，直到整个世界都是我的家 -#1</a></li></ul><p>声明：本文出自作者，不代表任何机构或公司，亦不构成投资建议。</p><p>你可以在这里找到我：</p><ul><li><a href="https://suncle.me/">Blog</a></li><li><a href="http://twitter.com/suncle_chen">Twitter</a> @suncle_chen</li><li>公众号：<strong>职场亮哥</strong></li></ul><blockquote><p><em>这里是</em> <a href="https://suncle.me/"><em>Suncle</em></a> <em>的</em> <a href="https://suncle.zhubai.love/"><em>newsletter</em></a> <em>，欢迎</em> <a href="https://suncle.zhubai.love/"><em>订阅</em></a> <em>，如果觉得这篇文章对你有用，可以分享给好友</em></p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;这是 Suncle Lab 第 3 期，我会在这里更新区块链、技术、金融、旅行和个人成长相关的内容。每期都会同步发布在我&lt;a href=&quot;https://suncle.me/&quot;&gt;博客&lt;/a&gt;，欢迎你&lt;a href=&quot;https://suncle.zhubai.love/&quot;&gt;</summary>
      
    
    
    
    <category term="newsletter" scheme="https://suncle.me/categories/newsletter/"/>
    
    
    <category term="newletter" scheme="https://suncle.me/tags/newletter/"/>
    
  </entry>
  
  <entry>
    <title>如何筹备一场感人的婚礼 -#2</title>
    <link href="https://suncle.me/posts/4048622437/"/>
    <id>https://suncle.me/posts/4048622437/</id>
    <published>2022-06-24T15:57:37.000Z</published>
    <updated>2023-09-03T14:31:24.461Z</updated>
    
    <content type="html"><![CDATA[<p>3月底拍婚纱照，然后开始正式筹备，5月1号开始陆续接待客人，5月2号婚前双方亲人的晚宴，5月3号婚礼最重要的日子。</p><p>一直到今天（6月25号），才完成婚礼视频的上传，整个婚礼圆满结束。</p><p>我问了参加婚礼的亲戚朋友对这场婚礼的评价，他们的回答都是“这是一场非常感人的婚礼”。</p><p>从结婚当天他们眼睛的泪水，我也能知道，这不是客套话。</p><p>今天刚好是周末，记录一下这场婚礼的过程，和我们的筹备细节。</p><p>这也意味着拖更了很久的第二期newsletter终于动笔了。（婚礼结束，后续还是会尽力保证更新了）</p><h2 id="克服恐婚心理"><a href="#克服恐婚心理" class="headerlink" title="克服恐婚心理"></a>克服恐婚心理</h2><p>讲细节之前，先谈一下我认为很重要的一点：克服你内心的恐婚情绪。</p><p>要做到这一点，你需要想明白两个问题。</p><p><strong>第一个问题她是不是你认为最适合的伴侣</strong>。</p><p>这个问题每个人有自己的判断，你是否足够喜欢她，你能否欣赏她的优点，你能否接受她的缺点，她是不是一个践行终生学习的人。</p><p>除了这些之外，对我来说还有一点很重要，她是否支持你内心最深处的梦想。</p><p>如果有一天你要去追梦了，她是否愿意陪着你一起东奔西跑，爱和不爱，只有在这种时刻才会展现的淋漓尽致。</p><p><strong>第二个问题是你对你们的未来是否有足够的信心</strong>。</p><p>倘若在五年前，甚至是三年前，如果说我不恐婚，我自己都不相信。</p><p>刚出社会的时候，是很迷茫的，不只是你我，绝大多数大学生都是如此。</p><p>就算过了很多年，很多人也没办法找到迷茫的根治方法。</p><p>前两年公众号改名为<strong>职场亮哥</strong>，其实就是想写写我是怎么一步一步破除迷茫的。</p><p>后来因缘际会接触了加密行业，便全力投入到这个行业，帮大学生破除迷茫的大理想就搁下了。</p><p>如果你单身的时候就是内心迷茫的，信心不足的，对未来不坚定的。</p><p>再加上一个伴侣，再以后可能加上一个孩子，又怎么可能会有充足信心呢？</p><p>信心来源于什么呢？来源于你的人生阅历。</p><p>至于怎么增进对未来的信心，老实说，我也没什么好的解决方法给你。</p><p>只能劝你多出去走走，多看看世界，了解不同人的生活方式，认识到自己的普通，也多参加几场婚礼。</p><p>趁早想明白这辈子自己想成为什么样的人，想做什么。</p><p>王阳明临终时指着心说的“此心光明，亦复何言”，我希望你现在就可以说。</p><p>但我又知道，从普鲁士教育诞生的我们，本质上是很难想透彻这些问题的。</p><p>你看，我就是很矛盾的，既很理想，又很现实。但我并不觉得有什么不适应。</p><p>从我的亲身经历来看，想明白这些的最快捷的方法是去仙本那玩一次“潜水”。</p><p>清澈见底水深20米的碧绿海平面上，从小船自由落体直下，如果你不会游泳就更有效了，能更快的想明白。</p><p>当然这是我自己经历的有生命危险的馊主意了。简单的方法是假设自己在生命的尽头，想明白有什么是你觉得这这辈子必须要实现的。</p><p>人啊，一旦大彻大悟，真的无敌。</p><h2 id="感人婚礼的筹备"><a href="#感人婚礼的筹备" class="headerlink" title="感人婚礼的筹备"></a>感人婚礼的筹备</h2><p>如果你很确定，你不恐婚了，然后遇到对的人想结婚了，那就开始筹备吧。</p><p>多预留点时间，最少最少2个月，半年应该比较合适。</p><p>但是筹备婚礼也是讲究方法论的，整个筹备过程的时间比较长，很多事情需要反复确认，反复比较。</p><p>清晰有条理的记录和追踪是很重要的。</p><p>下面展示下我用notion做的完整的婚礼计划，相应的模板再notion模板库里面是可以找到的，按需定制即可。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/image-20220625213315736-1656164003485.png" alt="wedding-planning"></p><p><strong>名单类的信息</strong>，天然是可以用表格来记录的，比如晚宴名单，宾客名单等。</p><p>以宾客名单为例，大概会包含的信息有这些：姓名，是否参加，大人数量，小孩数量，到达日期，是否住酒店，房型，备注。</p><p>大概数了下，至少9个字段，用notion制作成表，就可以看到下面的记录了（截图不全）。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/image-20220625214656158-1656164821614.png" alt="The guest list"></p><p>记录清楚之后，就很方便统计酒席的桌数，以及预定住宿房间的数量和房型。</p><p>如果有些人不能立马确定怎么办呢？只能一遍一遍的去问去催了。</p><p>但是有清晰的记录，就不用每次家里几个人坐在一起掰着手指头数数了。</p><p>晚宴酒店的选择，这个没有太多好说的，多打听多去现场考察。</p><p>想知道酒席的饭好不好吃很简单，亲自去吃一顿就好了。</p><p>如果你确实想知道，又没时间去，就让朋友去，或者花钱请人去试吃也都是可以的。</p><p>婚礼酒店，看的时候一定要带上另一半，只是自己决定了是没有用的，必须要两个人都满意才可以。</p><p>以我为例，我妈选的婚礼酒店，最后就被我和小太阳一起临时换掉了，但也只是因为疫情期间，酒店闲置较多，婚礼场地还是建议提前半年选定。</p><p>此外，整个婚礼过程中，每个地方都是需要花钱的。对一些主要花费做一些记录，知道钱大概花哪里去了。</p><p>过于零碎的记录可以直接忽略。初步统计，我的整个婚礼花费11万左右。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/image-20220625215414312-1656171334514.png" alt="bills"></p><p>婚礼上的一些细节，比如音乐，仪式流程，仪式细节，都是可以自行设计，自行添加，自行修改的。</p><p>再加上一些小灵感，亲手设计的婚礼才会感动自己，感动了自己才有可能感动别人。</p><p>特别提醒一下，如果有灵感，一定要立刻记录下来，然后尽自己努力让这些灵感出现在你的婚礼上。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/image-20220625222851114-1656167334972.png" alt="inspiration"></p><p>有朋友国庆结婚，就贴一下我们婚礼当天的大概流程供参考。</p><blockquote><ol><li>主持人登场</li><li>大姑爹唱歌暖场</li><li>新郎登场</li><li>新娘出场（父亲陪伴)</li><li>新郎迎接新娘（和父亲交接）</li><li>父亲退场休息</li><li>新人一起登场</li><li>新郎新娘誓言环节（伴娘送誓词卡）</li><li>婚戒（伴娘送戒指）</li><li>拥吻</li><li>主婚证婚（只要一个人，小舅）</li><li>父母登场</li><li>新郎感言（自己提前想一段）</li><li>改口茶</li><li>男方父亲致辞（用主持人的）</li><li>女方父亲致辞（自己写的）</li><li>抛捧花</li><li>同伴郎伴娘互动</li><li>礼成</li><li>纸飞机</li></ol></blockquote><h2 id="留住回忆"><a href="#留住回忆" class="headerlink" title="留住回忆"></a>留住回忆</h2><p>我参加过的婚礼不多，应该不超过五次。</p><p>除了我自己的婚礼之外，最打动我的就是2017年国庆在河南商丘参加<strong>大个</strong>的婚礼。</p><p>除了亲自布置的精美现场，就是他们的结婚誓词了，当时作为伴郎的我，站在新郎的旁边，第一次感觉到，爱情的结局原来也可以如此美妙。</p><p>在这里，记录一下我认为最重要的，也是最能打动人的誓词。</p><p>当初我和小太阳一致决定要亲自写婚礼现场的誓词，绝对不使用主持人提供的模板照着读。</p><p>最终，婚礼现场亲人们的泪水，小太阳的泪水以及我的泪水都让我们觉得，写这两份誓词熬夜的几个晚上，没有白费。</p><p>贴一下我的誓词</p><blockquote><p>致我的爱人朋友小太阳：</p><p>在正式的场合，我还是喜欢叫你小太阳。</p><p>前天晚上，我还在为这篇誓词苦思冥想，写了删，删了写。</p><p>如果婚姻是一场考试，第一道题就把我难倒了。</p><p>可明明是我给你写的第一封信，那封有九页纸的信，你才答应和我在一起。</p><p>但是一想到要在众人面前讲话我像是丧失了语言功能，怎么写都觉得很矫情。</p><p>和给外界的印象截然相反的是，在我们在一起的快三年的时间里，我都是吐露心声比较多的那个。</p><p>而你总是听的很认真，当然也不总是我说你听。</p><p>我们之间能聊的可太多了，旅行，文学、职业，还有那些关乎人生底色的议题，我们都有幸达成共识。</p><p>希望往后的日子里我们可以一直这么坦诚的沟通。</p><p>以前常常会想象我未来的另一半是什么样的人。</p><p>小太阳，你是一个善良敏锐，有正义感，有同情心，充满阳光的人</p><p>就像你的昵称小太阳一样，你会关怀身边，甚至于社会上的人，还常常听别人的遭遇愤愤不平。</p><p>看到拉萨山区里啃着冻土豆的老奶奶，会心里难过很久很久，然后捐款捐物。</p><p>我喜欢你的优点也常常被你所感染，希望我们能一直保持初心，一直生活下去。</p><p>我们有一只11斤的肥猫，旺财，他已经陪伴利们两年半了，俨然成为我们生命中的一部分。</p><p>今天在这里，我答应你，无论以后我们是去迪拜，去日本是去泰国，无论在哪里工作，我都会带上它，让它永远陪着你。</p><p>因为我知道在你心底里除了爸爸妈妈，最舍不得的就是它。</p><p>不管是什么事情，也无论有多么麻烦，只要你愿意，我都会努力去实现。</p><p>你和我说过有一段时间特别不喜欢特别害怕谈恋爱，因为你害怕两个人在一起就会很容易迷失了自己。</p><p>希望这几年过来你有感受到我对你的尊重。</p><p>就算今天我们更进一步走进婚姻，关系变得更密切，你还是能保持备你的尊严。</p><p>虽然这么说，其实我知道你也为了我们的相处付出了很多，改变了很多。</p><p>也谢谢小太阳的家人，因为你们的支持和包容，我们每一步都走得很坚定。</p><p>人生路上虽有暗夜，但有你亦如清晨！</p><p>如果明天看不见太阳，在最后一刻我会紧紧抱住你。</p><p>我们是爱侣一般的朋友，朋友一般的爱侣。</p><p>你是爱人，是馈赠</p><p>谢谢！ </p></blockquote><p>suncle誓词手稿链接：</p><ul><li><p><a href="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/IMG_20220611_204005_edit_24228973680677-1656169017810.jpg">suncle誓词手稿第一页</a></p></li><li><p><a href="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/IMG_20220611_204015_edit_24386830716590-1656169555877.jpg">suncle誓词手稿第二页</a></p></li></ul><p>再贴一下小太阳的誓词</p><blockquote><p>嗨～亲爱的陈先生</p><p>到今天为止，我们已经在一起度过了926天。</p><p>我觉得我是一个很幸运的人。</p><p>老天让我们共赴一场山海时，成为无话不谈的挚友。</p><p>在拉萨雪埋，死神叩问时，你几乎一夜未眠给我打了无数电话。</p><p>在过去两年里你陪伴在我身边，经历了我最无助的时刻。</p><p>我很感谢你看懂了我，却还一直陪伴在我身边度过这些挑战。</p><p>谢谢你总是迁就我，迁就我的工作和生活，从上海来到广州。</p><p>迁就我的小脾气，包容我的任性。</p><p>在遇到你之前我总觉得结婚这件事离我很遥远。</p><p>可是你的努力担当，你的善良果敢让我想要与你共度余生。</p><p>有幸与你相爱，</p><p>朝暮与年岁并往，</p><p>与你一起共至光年。</p><p>未来的路我们一起打卡金世界。</p><p>你好！我的老公。</p><p>周雨佳</p><p>2022.5.1</p></blockquote><p>小太阳誓词手稿链接：</p><ul><li><a href="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/IMG_20220611_204037_edit_24404787491067-1656169033781.jpg">小太阳誓词手稿第一页</a></li><li><a href="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/IMG_20220611_204041_edit_24413694007211-1656169442416.jpg">小太阳誓词手稿第二页</a></li></ul><p>除了誓词之外，还有由摄像师剪辑的婚礼视频，已经上传到西瓜视频，可以点击下方链接观看。</p><ul><li><a href="https://www.ixigua.com/7112067348714291742">婚礼花絮</a></li><li><a href="https://www.ixigua.com/7112457460094337544">接亲视频</a></li><li><a href="https://www.ixigua.com/7112217835320902158">婚礼现场视频</a></li></ul><p>如果你对筹备婚礼不知所措，或者对婚礼的一些细节不清楚，可以随时找我聊聊。</p><hr><blockquote><p><em>这里是</em> <a href="https://suncle.me/"><em>Suncle</em></a> <em>的</em> <a href="https://suncle.zhubai.love/"><em>newsletter</em></a><em>，欢迎</em><a href="https://suncle.zhubai.love/"><em>订阅</em></a><em>，如果觉得这篇文章对你有用，可以分享给好友</em></p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;3月底拍婚纱照，然后开始正式筹备，5月1号开始陆续接待客人，5月2号婚前双方亲人的晚宴，5月3号婚礼最重要的日子。&lt;/p&gt;
&lt;p&gt;一直到今天（6月25号），才完成婚礼视频的上传，整个婚礼圆满结束。&lt;/p&gt;
&lt;p&gt;我问了参加婚礼的亲戚朋友对这场婚礼的评价，他们的回答都是“这是</summary>
      
    
    
    
    <category term="newsletter" scheme="https://suncle.me/categories/newsletter/"/>
    
    
    <category term="newletter" scheme="https://suncle.me/tags/newletter/"/>
    
  </entry>
  
  <entry>
    <title>追求不舒适的地方，直到整个世界都是我的家 -#1</title>
    <link href="https://suncle.me/posts/1172265047/"/>
    <id>https://suncle.me/posts/1172265047/</id>
    <published>2022-03-11T15:57:37.000Z</published>
    <updated>2023-09-03T14:31:24.461Z</updated>
    
    <content type="html"><![CDATA[<p>这是 Suncle Lab 第一期，我会在这里更新 区块链、技术、金融、旅行相关的内容。每期都会同步发布在我<a href="https://suncle.me/">博客</a>，欢迎你<a href="https://suncle.zhubai.love/">邮箱订阅</a>，这样就可以第一时间收到更新推送。</p><p>我有个独立<a href="https://suncle.me/">博客</a>（可能需要科学上网），已经维护了五年了，但是更新频率并不高，不过陆陆续续还是有人在看，阅读数也在缓慢增长，这个博客主要是用来记录技术知识点，偶尔也会有一些所思所想。</p><p>对我自己来说，也会看一些独立博客，优质博客的rss也都有订阅，比如<a href="https://lxkaka.wang/">拉总的博客</a>，<a href="https://liriansu.com/">子岳的博客</a>。除了博客，日常会逛逛<strong>得到</strong>，<strong>极客时间</strong>这种知识付费型的app，学学专业知识，增长增长见识。</p><p>最近一年沉迷于区块链以及数字货币，在分布式内容创作平台<a href="https://mirror.xyz/">mirror</a>上也找到了很多优质的内容。慢慢的也就接触到了海外很火的newletter这种知识分享的方式。</p><p><strong>什么是newletter呢？</strong>先摘录一些维基百科的定义</p><blockquote><p>Newsletter（电子报）是社群或商家根据邮件列表发送给其相关对象的电子通讯刊物。</p></blockquote><p>对我来说，Newsletter 是一封准时抵达的信件，基于自己的兴趣和爱好。</p><h2 id="开始写newletter"><a href="#开始写newletter" class="headerlink" title="开始写newletter"></a>开始写newletter</h2><p>我陆陆续续在微信公众号上写过一些内容，也经常停更半年，甚至停更一年。不是不想更新，只是每写一篇文章都需要先深思熟虑彻底搞懂，然后提炼观点，像写一篇论文一样翻找资料，引经据典。这样的结果就是花费了大量的时间，写的知识都是已经完全内化了的，自己很难有成长，最后难以坚持。</p><p>接触到newsletter的时候，我大受震撼。原来世界上还有这样原始又有效的知识输出方式。我不再需要每一篇只写一个中心思想。我可以写我这一周学了什么，看了什么，听了什么，再从一周的生活里面提炼出一个核心观点，更真实，更时效。</p><p>以一周为单位来划分自己的时间，以一周为单位去总结自己的成长，吸引同频的人，也就够了。</p><p>对比了大量的newsletter平台，最终我选择了国内的一款产品<a href="https://suncle.zhubai.com/">竹白</a>，不需要科学上网，支持邮件和微信订阅。我还是倾向于大家使用邮件订阅的方式，如果哪一天中心化的竹白和微信都挂了，我还可以用最去中心化的邮件给你们发消息。</p><p>介绍一下我的newletter定位。只写自己感兴趣的，可以长期更新下去的内容，比如 <strong>区块链、技术、金融、旅行</strong>。每周一更，周五发布，每篇文章里面会分享最近一周看的一些文章链接还有听的播客链接。</p><p>今天是2022年3月11号，我从hago离职了，我的newsletter从今天开始诞生了，欢迎大家随缘订阅。</p><p>第一篇就结合这几天看的一篇文章写写我无限折腾的原因吧。</p><h2 id="全世界都是我的家"><a href="#全世界都是我的家" class="headerlink" title="全世界都是我的家"></a>全世界都是我的家</h2><blockquote><p>我强迫自己离开美国。离开我的舒适区。我考虑在美国境内寻找新的视角，生活在德克萨斯、路易斯安那、南卡罗来纳、新墨西哥和阿拉斯加。</p><p>但为什么是人为划定的边界？</p><p>为什么不是土耳其、尼日利亚、芬兰、印度尼西亚、以色列、中国和巴西？我想了解这些不同的生活方式。我希望它们也能有家的感觉。 所以我带着这个目标，向世界出发了。</p><p>搬到一个陌生的地方，直到感觉像家一样。不断地学习和成长。然后再做一次，追求不舒适的地方，直到整个世界都是我的家。</p></blockquote><p>这段话摘自于这周刚看的我很喜欢的作者<a href="https://sive.rs/">Derek Sivers</a>d的一篇文章<a href="https://sive.rs/left">Why I left America</a>。看这篇文章的时候，我想了很多。</p><p>我很庆幸高考报志愿的时候选择了计算机行业，工作了五年，虽然还远远没有暴富，但是这五年来，得益于IT行业的较高收入，工薪阶层的同龄人在为生活发愁省吃俭用的时候，我走遍了中国大多数地方。</p><p>很多人会说，你只是出去走马观花式的旅游，又能增长多少见识呢，还不如把钱省下来，留着以后买房买车娶老婆，周末宅在住处打打游戏多开心。这其中也包括我的爸妈。</p><p>同年毕业一起进税友的同事们，五年多的时间里没有出过远门，无论是见识还是视野，我想应该都会局限一些吧。</p><p>很多长辈的思想也和HR一样，劝你不要才干两三年就跳槽。换个角度想想，如果你打算一辈子为别人工作，那就找一个稳定职位待下去吧。</p><p>而我只有一个想法，<strong>找一个自己感兴趣的有高附加值的行业深耕下去</strong>。</p><p>这句话请再仔细逐字读一遍。<strong>找</strong>不是简单的投投简历，是要经过不断试错不断实践的，<strong>感兴趣</strong>不是说随便找一个大厂养老，而是自己想给这个世界创造点什么。<strong>高附加值</strong>意味着在这个行业干下去你会有一个美好的未来，即使你离开了某个特定的公司，但是只要还在行业内，显然再惠和Hago所在的行业并不符合这一条。<strong>深耕</strong>不是浅尝辄止而是专精和深度。</p><p>毕业五年，从西安到杭州，再到上海，之后来了广州，下一段旅程是迪拜。搬到一个陌生的地方，都像家一样的生活。</p><p>半年前（2021年9月5号）进入一个星球会员群的时候，发了一个自我介绍，给自己定了一个短期目标：进入区块链行业。半年之后就实现了。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/image-20220311233607722-1647012967843-1647013649384.png" alt="image-20220311233607722"></p><p>毕业五年多了，你的未来在哪里？你还迷茫嘛？你该去向何方？</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/bigfish-1647011167822.jpg" alt="电影《大鱼》中的场景"></p><p><em>此图出自电影《<a href="https://en.wikipedia.org/wiki/Big_Fish">大鱼</a>》，主人公的旅程一开始，他找到了一个天堂，所有人都求他留下来，但他说：“对不起，我可能永远没办法再找到这么美好的地方，但我需要走出去到这个世界去冒险。”）</em></p><h1 id="最近看了什么"><a href="#最近看了什么" class="headerlink" title="最近看了什么"></a>最近看了什么</h1><ul><li><a href="https://sive.rs/plaintext">Write plain text files</a>- 用纯文本做效率软件，现在的笔记软件很多，问题也很多，何不回归最原始的方式呢</li><li><a href="https://mirror.xyz/0xc19be75B8B9152d884987e1B58b3F18A94875396/cd7dO7shcNZJmeniaTPoqotkI13mMGCuOCMBrQY-Gew">毁灭的技术｜预言家周报#160</a>- 动荡的世界，crypto才是不可毁灭的</li><li><a href="https://ecn.mirror.xyz/m7p1QQwT_0jRcfud0IY_FKvskFMQ4Hyk8fHXITEo1_E">到底什么是零知识证明 (ZKP)？</a>- 零知识证明在web3的作用</li><li><a href="https://mirror.xyz/jayjiang.eth/yyUkSV1Ih6ffuHTIFlpf6jr-HugGrCRsJq-mjIMWIqU">Web3 社交思考</a></li><li><a href="https://mirror.xyz/paulfinneyx.eth/jFt3EmRcGqNsEGynj5LnTXjo8Fa1epRUoPCD5VSIxrc">How to be an early user of Web3?</a>- 在web3的世界你可以做些什么</li><li><a href="https://www.preethikasireddy.com/post/a-normies-guide-to-rollups">A normie’s guide to rollups</a>- 一直在说以太坊rollup扩容，rollup到底是什么</li><li><a href="https://www.huxiu.com/article/492646.html">互联网为什么让我们越来越不开心？</a>- 一个让世界更好的互联网，不同寻常的创业思想</li><li><a href="https://judyhevenly.com/predictions/">2022 Predictions</a>- 朱迪海文利的2022预测</li><li><a href="https://www.btcstudy.org/2022/02/21/pow-cheapest/">没什么比 PoW 更便宜</a>- 费电吗？省电</li></ul><hr><p>借用方可成的这段 [描述](<a href="https://newslab2020.github.io/Collection/%E5%AA%92%E4%BD%93%E9%A3%9F%E8%B0%B1/[%E6%96%B0%E9%97%BB%E5%AE%9E%E9%AA%8C%E5%AE%A4]">https://newslab2020.github.io/Collection/媒体食谱/[新闻实验室]</a> - 2017-05-10 把你的邮箱调教成最好的新闻阅读器｜媒体食谱07.html)结束本篇：</p><blockquote><p>他们总是准时抵达，安静地躺在我的收件箱里，不搞标题党，不插入影响阅读的表情包，只以最朴素的方式把我需要的内容传递给我。在这个信息过载、垃圾泛滥的年代，最古老的电子邮件成了最好的避难所。</p></blockquote><p>希望<a href="https://suncle.zhubai.com/">Suncle Lab</a>能成为你万千订阅的newsletter之一。</p><blockquote><p><em>这里是</em> <a href="https://suncle.me/"><em>Suncle</em></a> <em>的</em> <a href="https://suncle.zhubai.love/"><em>newsletter</em></a><em>，欢迎</em><a href="https://suncle.zhubai.love/"><em>订阅</em></a><em>，如果觉得这篇文章对你有用，可以分享给好友</em></p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;这是 Suncle Lab 第一期，我会在这里更新 区块链、技术、金融、旅行相关的内容。每期都会同步发布在我&lt;a href=&quot;https://suncle.me/&quot;&gt;博客&lt;/a&gt;，欢迎你&lt;a href=&quot;https://suncle.zhubai.love/&quot;&gt;邮箱订阅&lt;/</summary>
      
    
    
    
    <category term="newsletter" scheme="https://suncle.me/categories/newsletter/"/>
    
    
    <category term="newletter" scheme="https://suncle.me/tags/newletter/"/>
    
  </entry>
  
  <entry>
    <title>智能合约案例-众筹</title>
    <link href="https://suncle.me/posts/3775562041/"/>
    <id>https://suncle.me/posts/3775562041/</id>
    <published>2021-08-21T16:12:37.000Z</published>
    <updated>2023-09-03T14:31:24.469Z</updated>
    
    <content type="html"><![CDATA[<p>一个众筹的智能合约示例，来源于 <a href="https://learnxinyminutes.com/docs/solidity/">learnxinyminutes</a></p><p>主要是用来展示智能合约状态机（State machines）和函数修饰器（modifier）的使用</p><figure><div class="code-wrapper"><pre class="line-numbers language-solidity" data-language="solidity"><code class="language-solidity"><span class="token comment">// CrowdFunder.sol</span><span class="token keyword">pragma</span> <span class="token keyword">solidity</span> <span class="token operator">^</span><span class="token version number">0.6.6</span><span class="token punctuation">;</span><span class="token comment">/// @title CrowdFunder</span><span class="token comment">/// @author nemild</span><span class="token keyword">contract</span> <span class="token class-name">CrowdFunder</span> <span class="token punctuation">&#123;</span>    <span class="token comment">// Variables set on create by creator</span>    <span class="token builtin">address</span> <span class="token keyword">public</span> creator<span class="token punctuation">;</span>    <span class="token builtin">address</span> <span class="token keyword">payable</span> <span class="token keyword">public</span> fundRecipient<span class="token punctuation">;</span> <span class="token comment">// creator may be different than recipient, and must be payable</span>    <span class="token builtin">uint</span> <span class="token keyword">public</span> minimumToRaise<span class="token punctuation">;</span> <span class="token comment">// required to tip, else everyone gets refund</span>    <span class="token builtin">string</span> campaignUrl<span class="token punctuation">;</span>    <span class="token builtin">byte</span> version <span class="token operator">=</span> <span class="token string">"1"</span><span class="token punctuation">;</span>    <span class="token comment">// Data structures</span>    <span class="token keyword">enum</span> <span class="token class-name">State</span> <span class="token punctuation">&#123;</span>        Fundraising<span class="token punctuation">,</span>        ExpiredRefund<span class="token punctuation">,</span>        Successful    <span class="token punctuation">&#125;</span>    <span class="token keyword">struct</span> <span class="token class-name">Contribution</span> <span class="token punctuation">&#123;</span>        <span class="token builtin">uint</span> amount<span class="token punctuation">;</span>        <span class="token builtin">address</span> <span class="token keyword">payable</span> contributor<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token comment">// State variables</span>    State <span class="token keyword">public</span> state <span class="token operator">=</span> State<span class="token punctuation">.</span>Fundraising<span class="token punctuation">;</span> <span class="token comment">// initialize on create</span>    <span class="token builtin">uint</span> <span class="token keyword">public</span> totalRaised<span class="token punctuation">;</span>    <span class="token builtin">uint</span> <span class="token keyword">public</span> raiseBy<span class="token punctuation">;</span>    <span class="token builtin">uint</span> <span class="token keyword">public</span> completeAt<span class="token punctuation">;</span>    Contribution<span class="token punctuation">[</span><span class="token punctuation">]</span> contributions<span class="token punctuation">;</span>    <span class="token keyword">event</span> <span class="token function">LogFundingReceived</span><span class="token punctuation">(</span><span class="token builtin">address</span> addr<span class="token punctuation">,</span> <span class="token builtin">uint</span> amount<span class="token punctuation">,</span> <span class="token builtin">uint</span> currentTotal<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">event</span> <span class="token function">LogWinnerPaid</span><span class="token punctuation">(</span><span class="token builtin">address</span> winnerAddress<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">modifier</span> <span class="token function">inState</span><span class="token punctuation">(</span>State _state<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token keyword">require</span><span class="token punctuation">(</span>state <span class="token operator">==</span> _state<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">_</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">modifier</span> <span class="token function">isCreator</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token keyword">require</span><span class="token punctuation">(</span>msg<span class="token punctuation">.</span>sender <span class="token operator">==</span> creator<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">_</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token comment">// Wait 24 weeks after final contract state before allowing contract destruction</span>    <span class="token keyword">modifier</span> <span class="token function">atEndOfLifecycle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">require</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>state <span class="token operator">==</span> State<span class="token punctuation">.</span>ExpiredRefund <span class="token operator">||</span> state <span class="token operator">==</span> State<span class="token punctuation">.</span>Successful<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span>        completeAt <span class="token operator">+</span> <span class="token number">24</span> weeks <span class="token operator">&lt;</span> now<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">_</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">function</span> <span class="token function">crowdFund</span><span class="token punctuation">(</span>        <span class="token builtin">uint</span> timeInHoursForFundraising<span class="token punctuation">,</span>        <span class="token builtin">string</span> <span class="token keyword">memory</span> _campaignUrl<span class="token punctuation">,</span>        <span class="token builtin">address</span> <span class="token keyword">payable</span> _fundRecipient<span class="token punctuation">,</span>        <span class="token builtin">uint</span> _minimumToRaise<span class="token punctuation">)</span>        <span class="token keyword">public</span>    <span class="token punctuation">&#123;</span>        creator <span class="token operator">=</span> msg<span class="token punctuation">.</span>sender<span class="token punctuation">;</span>        fundRecipient <span class="token operator">=</span> _fundRecipient<span class="token punctuation">;</span>        campaignUrl <span class="token operator">=</span> _campaignUrl<span class="token punctuation">;</span>        minimumToRaise <span class="token operator">=</span> _minimumToRaise<span class="token punctuation">;</span>        raiseBy <span class="token operator">=</span> now <span class="token operator">+</span> <span class="token punctuation">(</span>timeInHoursForFundraising <span class="token operator">*</span> <span class="token number">1</span> hours<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">function</span> <span class="token function">contribute</span><span class="token punctuation">(</span><span class="token punctuation">)</span>    <span class="token keyword">public</span>    <span class="token keyword">payable</span>    <span class="token function">inState</span><span class="token punctuation">(</span>State<span class="token punctuation">.</span>Fundraising<span class="token punctuation">)</span>    <span class="token keyword">returns</span><span class="token punctuation">(</span><span class="token builtin">uint256</span> id<span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        contributions<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>            <span class="token function">Contribution</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>                amount<span class="token punctuation">:</span> msg<span class="token punctuation">.</span>value<span class="token punctuation">,</span>                contributor<span class="token punctuation">:</span> msg<span class="token punctuation">.</span>sender            <span class="token punctuation">&#125;</span><span class="token punctuation">)</span> <span class="token comment">// use array, so can iterate</span>        <span class="token punctuation">)</span><span class="token punctuation">;</span>        totalRaised <span class="token operator">+=</span> msg<span class="token punctuation">.</span>value<span class="token punctuation">;</span>        <span class="token keyword">emit</span> <span class="token function">LogFundingReceived</span><span class="token punctuation">(</span>msg<span class="token punctuation">.</span>sender<span class="token punctuation">,</span> msg<span class="token punctuation">.</span>value<span class="token punctuation">,</span> totalRaised<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">checkIfFundingCompleteOrExpired</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> contributions<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// return id</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">function</span> <span class="token function">checkIfFundingCompleteOrExpired</span><span class="token punctuation">(</span><span class="token punctuation">)</span>    <span class="token keyword">public</span>    <span class="token punctuation">&#123;</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span>totalRaised <span class="token operator">></span> minimumToRaise<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>            state <span class="token operator">=</span> State<span class="token punctuation">.</span>Successful<span class="token punctuation">;</span>            <span class="token function">payOut</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token comment">// could incentivize sender who initiated state change here</span>        <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> now <span class="token operator">></span> raiseBy <span class="token punctuation">)</span>  <span class="token punctuation">&#123;</span>            state <span class="token operator">=</span> State<span class="token punctuation">.</span>ExpiredRefund<span class="token punctuation">;</span> <span class="token comment">// backers can now collect refunds by calling getRefund(id)</span>        <span class="token punctuation">&#125;</span>        completeAt <span class="token operator">=</span> now<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">function</span> <span class="token function">payOut</span><span class="token punctuation">(</span><span class="token punctuation">)</span>    <span class="token keyword">public</span>    <span class="token function">inState</span><span class="token punctuation">(</span>State<span class="token punctuation">.</span>Successful<span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        fundRecipient<span class="token punctuation">.</span><span class="token function">transfer</span><span class="token punctuation">(</span><span class="token builtin">address</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span>balance<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">LogWinnerPaid</span><span class="token punctuation">(</span>fundRecipient<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">function</span> <span class="token function">getRefund</span><span class="token punctuation">(</span><span class="token builtin">uint256</span> id<span class="token punctuation">)</span>    <span class="token function">inState</span><span class="token punctuation">(</span>State<span class="token punctuation">.</span>ExpiredRefund<span class="token punctuation">)</span>    <span class="token keyword">public</span>    <span class="token keyword">returns</span><span class="token punctuation">(</span><span class="token builtin">bool</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        <span class="token keyword">require</span><span class="token punctuation">(</span>contributions<span class="token punctuation">.</span>length <span class="token operator">></span> id <span class="token operator">&amp;&amp;</span> id <span class="token operator">>=</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> contributions<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">.</span>amount <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token builtin">uint256</span> amountToRefund <span class="token operator">=</span> contributions<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">.</span>amount<span class="token punctuation">;</span>        contributions<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">.</span>amount <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>        contributions<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">.</span>contributor<span class="token punctuation">.</span><span class="token function">transfer</span><span class="token punctuation">(</span>amountToRefund<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">function</span> <span class="token function">removeContract</span><span class="token punctuation">(</span><span class="token punctuation">)</span>    <span class="token keyword">public</span>    <span class="token function">isCreator</span><span class="token punctuation">(</span><span class="token punctuation">)</span>    <span class="token function">atEndOfLifecycle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        <span class="token keyword">selfdestruct</span><span class="token punctuation">(</span>msg<span class="token punctuation">.</span>sender<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// creator gets all money that hasn't be claimed</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><span id="more"></span><h2 id="修饰器"><a href="#修饰器" class="headerlink" title="修饰器"></a>修饰器</h2><p>函数修饰器(Modifiers)可以用来改变一个函数的行为。比如用于在函数执行前检查某种前置条件。这个和python的修饰器(Decorators)的作用很类似，在python中，我们也经常使用装饰器对函数执行前后增加一些逻辑。下面是solidity修饰器的简单使用，在众筹支付前需要检查合约状态是否已完成</p><figure><div class="code-wrapper"><pre class="line-numbers language-solidity" data-language="solidity"><code class="language-solidity"><span class="token keyword">function</span> <span class="token function">payOut</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token function">inState</span><span class="token punctuation">(</span>State<span class="token punctuation">.</span>Successful<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token keyword">_</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>python中一个函数可以有多个装饰器，solidity中的函数也是可以有多个修饰器的。</p><p>如果同一个函数有多个修饰器，他们之间以空格隔开，修饰器会依次检查执行。</p><h2 id="状态机"><a href="#状态机" class="headerlink" title="状态机"></a>状态机</h2><p>状态State在合约中本质是一个合约的全局变量，在实际合约中，状态会有很多种，各个合约方法也会对State做修改，并且根据State执行不同的逻辑。对于多个State可以通过枚举管理</p><figure><div class="code-wrapper"><pre class="line-numbers language-solidity" data-language="solidity"><code class="language-solidity"><span class="token keyword">enum</span> <span class="token class-name">State</span> <span class="token punctuation">&#123;</span>    Fundraising<span class="token punctuation">,</span><span class="token comment">// 筹款中</span>    ExpiredRefund<span class="token punctuation">,</span><span class="token comment">// 过期退款</span>    Successful<span class="token comment">// 众筹成功</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;一个众筹的智能合约示例，来源于 &lt;a href=&quot;https://learnxinyminutes.com/docs/solidity/&quot;&gt;learnxinyminutes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;主要是用来展示智能合约状态机（State machines）和函数修饰器（modifier）的使用&lt;/p&gt;
&lt;pre class=&quot;line-numbers language-solidity&quot; data-language=&quot;solidity&quot;&gt;&lt;code class=&quot;language-solidity&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// CrowdFunder.sol&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pragma&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;solidity&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;token version number&quot;&gt;0.6.6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/// @title CrowdFunder&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// @author nemild&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;contract&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CrowdFunder&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Variables set on create by creator&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; creator&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;payable&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; fundRecipient&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// creator may be different than recipient, and must be payable&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; minimumToRaise&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// required to tip, else everyone gets refund&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; campaignUrl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;byte&lt;/span&gt; version &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Data structures&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
        Fundraising&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        ExpiredRefund&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        Successful
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Contribution&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;token builtin&quot;&gt;uint&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token builtin&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;payable&lt;/span&gt; contributor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// State variables&lt;/span&gt;
    State &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; State&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Fundraising&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// initialize on create&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; totalRaised&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; raiseBy&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; completeAt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    Contribution&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; contributions&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;LogFundingReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;address&lt;/span&gt; addr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;uint&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;uint&lt;/span&gt; currentTotal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;LogWinnerPaid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;address&lt;/span&gt; winnerAddress&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;modifier&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;inState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;State _state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; _state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;modifier&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isCreator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sender &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; creator&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Wait 24 weeks after final contract state before allowing contract destruction&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;modifier&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;atEndOfLifecycle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; State&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ExpiredRefund &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; State&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Successful&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        completeAt &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt; weeks &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; now&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;crowdFund&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token builtin&quot;&gt;uint&lt;/span&gt; timeInHoursForFundraising&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;memory&lt;/span&gt; _campaignUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token builtin&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;payable&lt;/span&gt; _fundRecipient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token builtin&quot;&gt;uint&lt;/span&gt; _minimumToRaise&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
        creator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sender&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        fundRecipient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; _fundRecipient&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        campaignUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; _campaignUrl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        minimumToRaise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; _minimumToRaise&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        raiseBy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; now &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeInHoursForFundraising &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; hours&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;contribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;payable&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;inState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;State&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Fundraising&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;uint256&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
        contributions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Contribution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
                amount&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                contributor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sender
            &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// use array, so can iterate&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        totalRaised &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;emit&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;LogFundingReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sender&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; totalRaised&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;checkIfFundingCompleteOrExpired&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; contributions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// return id&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkIfFundingCompleteOrExpired&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;totalRaised &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; minimumToRaise&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
            state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; State&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Successful&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;payOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// could incentivize sender who initiated state change here&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; now &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; raiseBy &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
            state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; State&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ExpiredRefund&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// backers can now collect refunds by calling getRefund(id)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;
        completeAt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; now&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;payOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;inState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;State&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Successful&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
        fundRecipient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transfer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;balance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;LogWinnerPaid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fundRecipient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getRefund&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;uint256&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;inState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;State&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ExpiredRefund&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contributions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; contributions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;amount &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token builtin&quot;&gt;uint256&lt;/span&gt; amountToRefund &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; contributions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;amount&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        contributions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;amount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        contributions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contributor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transfer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;amountToRefund&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;removeContract&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;isCreator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;atEndOfLifecycle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;selfdestruct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sender&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// creator gets all money that hasn&#39;t be claimed&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;line-numbers-rows&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</summary>
    
    
    
    <category term="区块链" scheme="https://suncle.me/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
    
    <category term="Solidity" scheme="https://suncle.me/tags/Solidity/"/>
    
  </entry>
  
  <entry>
    <title>Golang Concurrent Write Problem</title>
    <link href="https://suncle.me/posts/502431314/"/>
    <id>https://suncle.me/posts/502431314/</id>
    <published>2021-08-16T19:34:34.000Z</published>
    <updated>2023-09-03T14:31:24.461Z</updated>
    
    <content type="html"><![CDATA[<p>分享几个golang并发写入的坑</p><h2 id="并发读写map"><a href="#并发读写map" class="headerlink" title="并发读写map"></a>并发读写map</h2><p>在golang的实际项目中经常需要并发写数据，并且将数据塞到一个map中作为一个整体返回。</p><p>分为2种情况：这两种情况都会造成panic</p><ol><li>并发读写map</li><li>并发写map</li></ol><p>并发读写map的示例代码：</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> util<span class="token keyword">import</span> <span class="token string">"testing"</span><span class="token keyword">func</span> <span class="token function">TestMap</span><span class="token punctuation">(</span>t <span class="token operator">*</span>testing<span class="token punctuation">.</span>T<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>m <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">int</span><span class="token punctuation">]</span><span class="token builtin">int</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token comment">// 不停地对map进行写入</span><span class="token keyword">for</span> <span class="token punctuation">&#123;</span>m<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token comment">// 不停地对map进行读取</span><span class="token keyword">for</span> <span class="token punctuation">&#123;</span><span class="token boolean">_</span> <span class="token operator">=</span> m<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token keyword">select</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>运行代码之后会报错：<code>fatal error: concurrent map read and map write</code></p><span id="more"></span><p>错误信息显示，并发的 map 读和 map 写，也就是说使用了两个并发函数不断地对 map 进行读和写而发生了竞态问题，map 内部会对这种并发操作进行检查并提前发现。</p><p>并发写map的示例代码：</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">TestMap</span><span class="token punctuation">(</span>t <span class="token operator">*</span>testing<span class="token punctuation">.</span>T<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>m <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">int</span><span class="token punctuation">]</span><span class="token builtin">int</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token comment">// 不停地对map进行写入</span><span class="token keyword">for</span> <span class="token punctuation">&#123;</span>m<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token comment">// 不停地对map进行写入</span><span class="token keyword">for</span> <span class="token punctuation">&#123;</span>m<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 无限循环, 让并发程序在后台执行</span><span class="token keyword">select</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>运行代码之后会报错：<code>fatal error: concurrent map writes</code></p><p>错误信息显示，并发的 map 写，也就是说使用了两个并发函数不断地对 map 进行写而发生了竞态问题</p><p>需要并发读写或者并发写时，一般的做法是加锁，但这样性能并不高，Go语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map，sync.Map 和 map 不同，不是以语言原生形态提供，而是在 sync 包下的特殊结构。</p><p>sync.Map 有以下特性：</p><ul><li>无须初始化，直接声明即可。</li><li>sync.Map 不能使用 map 的方式进行取值和设置等操作，而是使用 sync.Map 的方法进行调用，Store 表示存储，Load 表示获取，Delete 表示删除。</li><li>使用 Range 配合一个回调函数进行遍历操作，通过回调函数返回内部遍历出来的值，Range 参数中回调函数的返回值在需要继续迭代遍历时，返回 true，终止迭代遍历时，返回 false。</li></ul><p>并发安全的 sync.Map 演示代码如下：</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">TestSyncMap</span><span class="token punctuation">(</span>t <span class="token operator">*</span>testing<span class="token punctuation">.</span>T<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token keyword">var</span> m sync<span class="token punctuation">.</span>Map<span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token comment">// 不停地对map进行写入</span><span class="token keyword">for</span> <span class="token punctuation">&#123;</span>m<span class="token punctuation">.</span><span class="token function">Store</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token comment">// 不停地对map进行读取</span><span class="token keyword">for</span> <span class="token punctuation">&#123;</span><span class="token boolean">_</span><span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> m<span class="token punctuation">.</span><span class="token function">Load</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 无限循环, 让并发程序在后台执行</span><span class="token keyword">select</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>这段代码会无限循环，并且不会有并发读写的错误</p><p>sync.Map 没有提供获取 map 数量的方法，替代方法是在获取 sync.Map 时遍历自行计算数量，sync.Map 为了保证并发安全有一些性能损失，因此在非并发情况下，使用 map 相比使用 sync.Map 会有更好的性能。</p><h2 id="并发写slice"><a href="#并发写slice" class="headerlink" title="并发写slice"></a>并发写slice</h2><p>一般不太会有并发写slice的，因为slice和map不同，对加入的先后顺序是敏感的，因此目前的实际应用场景没有使用到并发读写slice</p><h2 id="并发写string"><a href="#并发写string" class="headerlink" title="并发写string"></a>并发写string</h2><p>string是Go的内建类型，但对它的读写操作并非线程安全的，原因在于它的内部实际上是通过struct存储的，我们可以在runtime&#x2F;string.go里面看到它的内部定义。</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">type</span> stringStruct <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>str unsafe<span class="token punctuation">.</span>Pointer<span class="token builtin">len</span> <span class="token builtin">int</span><span class="token punctuation">&#125;</span><span class="token keyword">func</span> <span class="token function">stringStructOf</span><span class="token punctuation">(</span>sp <span class="token operator">*</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">*</span>stringStruct <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token operator">*</span>stringStruct<span class="token punctuation">)</span><span class="token punctuation">(</span>unsafe<span class="token punctuation">.</span><span class="token function">Pointer</span><span class="token punctuation">(</span>sp<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>对于这样一个 struct ，go 无法保证原子性地完成赋值，因此可能会出现goroutine 1 刚修改完指针（str）、还没来得及修改长度（len），goroutine 2 就读取了这个string 的情况。</p><p>我们可以通过一个测试代码发现并发读写string的问题：</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">TestString</span><span class="token punctuation">(</span>t <span class="token operator">*</span>testing<span class="token punctuation">.</span>T<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>s <span class="token operator">:=</span> <span class="token string">"0"</span>ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>i <span class="token operator">:=</span> <span class="token number">1</span><span class="token keyword">for</span> <span class="token punctuation">&#123;</span><span class="token keyword">if</span> i<span class="token operator">%</span><span class="token number">2</span> <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">&#123;</span>s <span class="token operator">=</span> <span class="token string">"0"</span><span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>s <span class="token operator">=</span> <span class="token string">"aa"</span><span class="token punctuation">&#125;</span>i<span class="token operator">++</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Microsecond<span class="token punctuation">)</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token keyword">for</span> <span class="token punctuation">&#123;</span>b <span class="token operator">:=</span> s<span class="token keyword">if</span> b <span class="token operator">!=</span> <span class="token string">"0"</span> <span class="token operator">&amp;&amp;</span> b <span class="token operator">!=</span> <span class="token string">"aa"</span> <span class="token punctuation">&#123;</span>ch <span class="token operator">&lt;-</span> b<span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">&#123;</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Got strange string: "</span><span class="token punctuation">,</span> <span class="token operator">&lt;-</span>ch<span class="token punctuation">)</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>执行这个代码得到下面的输出结果：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">&#x3D;&#x3D;&#x3D; RUN   TestStringGot strange string:  aGot strange string:  aGot strange string:  aGot strange string:  01Got strange string:  aGot strange string:  aGot strange string:  01Got strange string:  aGot strange string:  aGot strange string:  a--- PASS: TestString (0.01s)<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>通过<code>go tool compile -S</code>查看执行的汇编代码，可以发现，string的写入是分为写入长度和写入指针2个部分的。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/htZm5E-1629119549723.png"></p><p>因此在频繁的写入操作中，可能会出现写入了一部分数据就被读取出去了，自然就会读取到脏数据</p><p>仔细看上述示例代码，会发现在写入协程中有一个多余的sleep操作，如果把这个sleep去掉，运行的结果是永远读不到脏数据，这是为什么呢？原因在于编译器的优化。编译器优化之后会直接改写频繁赋值的逻辑，而不是持续写入长度和指针</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/image-20210816212544508-1629120344614-1629120356605.png" alt="image-20210816212544508"></p><h2 id="并发写interface"><a href="#并发写interface" class="headerlink" title="并发写interface"></a>并发写interface</h2><p>将上述并发写string代码中的类型改为interface就可以复现并发写interface的问题。</p><hr><p>了解上面4中并发读写会造成panic或者脏读的情况之后，在后续的日常开发中，需要十分注意这样的情况</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;分享几个golang并发写入的坑&lt;/p&gt;
&lt;h2 id=&quot;并发读写map&quot;&gt;&lt;a href=&quot;#并发读写map&quot; class=&quot;headerlink&quot; title=&quot;并发读写map&quot;&gt;&lt;/a&gt;并发读写map&lt;/h2&gt;&lt;p&gt;在golang的实际项目中经常需要并发写数据，并且将数据塞到一个map中作为一个整体返回。&lt;/p&gt;
&lt;p&gt;分为2种情况：这两种情况都会造成panic&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;并发读写map&lt;/li&gt;
&lt;li&gt;并发写map&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;并发读写map的示例代码：&lt;/p&gt;
&lt;pre class=&quot;line-numbers language-go&quot; data-language=&quot;go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; util

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;testing&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TestMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;testing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
	m &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 不停地对map进行写入&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
			m&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 不停地对map进行读取&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;
			&lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  
	&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;line-numbers-rows&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;运行代码之后会报错：&lt;code&gt;fatal error: concurrent map read and map write&lt;/code&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="golang" scheme="https://suncle.me/categories/golang/"/>
    
    
    <category term="并发" scheme="https://suncle.me/tags/%E5%B9%B6%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>仿MybatisGenerator：根据sql生成go struct</title>
    <link href="https://suncle.me/posts/1939519946/"/>
    <id>https://suncle.me/posts/1939519946/</id>
    <published>2021-07-27T15:36:52.000Z</published>
    <updated>2023-09-03T14:31:24.461Z</updated>
    
    <content type="html"><![CDATA[<h1 id="genstruct"><a href="#genstruct" class="headerlink" title="genstruct"></a>genstruct</h1><p>项目地址：<a href="https://github.com/suncle1993/genstruct">https://github.com/suncle1993/genstruct</a></p><p>根据mysql schema生成go struct，适用于习惯先写sql后写struct的同学</p><p>根据 <a href="https://github.com/fifsky/genstruct">https://github.com/fifsky/genstruct</a> 项目做了一些修改，更适用于目前的hago项目。在原版的基础上添加了以下功能：</p><ol><li>schema的生成（信奉sql和model放在一起的人喜欢这种方式），便于自动建表之内的操作</li><li>暴露的变量和方法注释的添加</li><li>通过表注释添加为struct的注释</li><li>对于生成的struct字段进行首字母缩写词的转换，总共38种，完全符合golint的检查规则</li></ol><span id="more"></span><h2 id="命令行版本"><a href="#命令行版本" class="headerlink" title="命令行版本"></a>命令行版本</h2><p>安装：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">go get github.com&#x2F;suncle1993&#x2F;genstruct<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>使用方法：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">genstruct -h 127.0.0.1 -u root -P 123456 -p 3306<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li><code>-h</code> default <code>localhost</code></li><li><code>-u</code> default <code>root</code></li><li><code>-p</code> default <code>3306</code></li></ul><p>演示见下面的 aciinema svg：</p><p><a href="https://asciinema.org/a/X5sk7TqrTTjF8AhN764K0Fc6m"><img src="https://asciinema.org/a/X5sk7TqrTTjF8AhN764K0Fc6m.svg" alt="asciicast"></a></p><h2 id="线上版本"><a href="#线上版本" class="headerlink" title="线上版本"></a>线上版本</h2><p>页面地址：<a href="https://genstruct.suncle.me/">https://genstruct.suncle.me/</a></p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/DV5XD3-1627372059061.png"></p><h2 id="接口版本"><a href="#接口版本" class="headerlink" title="接口版本"></a>接口版本</h2><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">--location</span> <span class="token parameter variable">--request</span> GET <span class="token string">'https://genstructapi.herokuapp.com/api/struct/generate'</span> <span class="token punctuation">\</span><span class="token parameter variable">--header</span> <span class="token string">'Content-Type: application/json'</span> <span class="token punctuation">\</span>--data-raw <span class="token string">'&#123;    "tags": ["db", "json"],    "table": "create table user_mine_info( id bigint(20) NOT NULL AUTO_INCREMENT, uid bigint(20) NOT NULL DEFAULT '</span><span class="token punctuation">\</span>'<span class="token string">'0'</span><span class="token punctuation">\</span>'<span class="token string">' COMMENT '</span><span class="token punctuation">\</span>'<span class="token string">'用户uid'</span><span class="token punctuation">\</span>'<span class="token string">', mined_cnt bigint(20) NOT NULL COMMENT '</span><span class="token punctuation">\</span>'<span class="token string">'剩余挖矿次数'</span><span class="token punctuation">\</span>'<span class="token string">', un_exchange_diamond bigint(20) NOT NULL COMMENT '</span><span class="token punctuation">\</span>'<span class="token string">'未兑换为挖矿次数的钻石'</span><span class="token punctuation">\</span>'<span class="token string">', created_at bigint(20) NOT NULL COMMENT '</span><span class="token punctuation">\</span>'<span class="token string">'创建时间'</span><span class="token punctuation">\</span>'<span class="token string">', updated_at bigint(20) NOT NULL COMMENT '</span><span class="token punctuation">\</span>'<span class="token string">'更新时间'</span><span class="token punctuation">\</span>'<span class="token string">', PRIMARY KEY (id), UNIQUE KEY uk_uid (uid) USING BTREE) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '</span><span class="token punctuation">\</span>'<span class="token string">'用户挖矿剩余次数记录'</span><span class="token punctuation">\</span>'<span class="token string">';"&#125;'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="示例模型"><a href="#示例模型" class="headerlink" title="示例模型"></a>示例模型</h2><p>建表数据</p><figure><div class="code-wrapper"><pre class="line-numbers language-mysql" data-language="mysql"><code class="language-mysql">CREATE TABLE user_mine_info(  id bigint(20) NOT NULL AUTO_INCREMENT,   UID bigint(20) NOT NULL DEFAULT &#39;0&#39; COMMENT &#39;用户uid&#39;,   mined_cnt bigint(20) NOT NULL COMMENT &#39;剩余挖矿次数&#39;,   un_exchange_diamond bigint(20) NOT NULL COMMENT &#39;未兑换为挖矿次数的钻石&#39;,   created_at bigint(20) NOT NULL COMMENT &#39;创建时间&#39;,   updated_at bigint(20) NOT NULL COMMENT &#39;更新时间&#39;,   PRIMARY KEY (id),   UNIQUE KEY uk_uid (UID) USING BTREE) ENGINE &#x3D; InnoDB DEFAULT CHARSET &#x3D; utf8mb4 COMMENT &#x3D; &#39;用户挖矿剩余次数记录&#39;;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>生成的模型：</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> user_mine_info<span class="token comment">// UserMineInfo 用户挖矿剩余次数记录</span><span class="token keyword">type</span> UserMineInfo <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>ID                <span class="token builtin">int64</span> <span class="token string">`db:"id" json:"id" `</span>UID               <span class="token builtin">int64</span> <span class="token string">`db:"uid" json:"uid" `</span>                                 <span class="token comment">// 用户uid</span>MinedCnt          <span class="token builtin">int64</span> <span class="token string">`db:"mined_cnt" json:"mined_cnt" `</span>                     <span class="token comment">// 剩余挖矿次数</span>UnExchangeDiamond <span class="token builtin">int64</span> <span class="token string">`db:"un_exchange_diamond" json:"un_exchange_diamond" `</span> <span class="token comment">// 未兑换为挖矿次数的钻石</span>CreatedAt         <span class="token builtin">int64</span> <span class="token string">`db:"created_at" json:"created_at" `</span>                   <span class="token comment">// 创建时间</span>UpdatedAt         <span class="token builtin">int64</span> <span class="token string">`db:"updated_at" json:"updated_at" `</span>                   <span class="token comment">// 更新时间</span><span class="token punctuation">&#125;</span><span class="token comment">// TableName ...</span><span class="token keyword">func</span> <span class="token punctuation">(</span>u <span class="token operator">*</span>UserMineInfo<span class="token punctuation">)</span> <span class="token function">TableName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">string</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token string">"user_mine_info"</span> <span class="token comment">// TODO: 如果分表需要修改</span><span class="token punctuation">&#125;</span><span class="token comment">// PK ...</span><span class="token keyword">func</span> <span class="token punctuation">(</span>u <span class="token operator">*</span>UserMineInfo<span class="token punctuation">)</span> <span class="token function">PK</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">string</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token string">"id"</span><span class="token punctuation">&#125;</span><span class="token comment">// Schema ...</span><span class="token keyword">func</span> <span class="token punctuation">(</span>u <span class="token operator">*</span>UserMineInfo<span class="token punctuation">)</span> <span class="token function">Schema</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">string</span> <span class="token punctuation">&#123;</span><span class="token keyword">return</span> <span class="token string">`(  id bigint NOT NULL AUTO_INCREMENT,  uid bigint NOT NULL DEFAULT '0' COMMENT '用户uid',  mined_cnt bigint NOT NULL COMMENT '剩余挖矿次数',  un_exchange_diamond bigint NOT NULL COMMENT '未兑换为挖矿次数的钻石',  created_at bigint NOT NULL COMMENT '创建时间',  updated_at bigint NOT NULL COMMENT '更新时间',  PRIMARY KEY (id),  UNIQUE KEY uk_uid (uid) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户挖矿剩余次数记录'`</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
    
    
    <summary type="html">&lt;h1 id=&quot;genstruct&quot;&gt;&lt;a href=&quot;#genstruct&quot; class=&quot;headerlink&quot; title=&quot;genstruct&quot;&gt;&lt;/a&gt;genstruct&lt;/h1&gt;&lt;p&gt;项目地址：&lt;a href=&quot;https://github.com/suncle1993/genstruct&quot;&gt;https://github.com/suncle1993/genstruct&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;根据mysql schema生成go struct，适用于习惯先写sql后写struct的同学&lt;/p&gt;
&lt;p&gt;根据 &lt;a href=&quot;https://github.com/fifsky/genstruct&quot;&gt;https://github.com/fifsky/genstruct&lt;/a&gt; 项目做了一些修改，更适用于目前的hago项目。在原版的基础上添加了以下功能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;schema的生成（信奉sql和model放在一起的人喜欢这种方式），便于自动建表之内的操作&lt;/li&gt;
&lt;li&gt;暴露的变量和方法注释的添加&lt;/li&gt;
&lt;li&gt;通过表注释添加为struct的注释&lt;/li&gt;
&lt;li&gt;对于生成的struct字段进行首字母缩写词的转换，总共38种，完全符合golint的检查规则&lt;/li&gt;
&lt;/ol&gt;</summary>
    
    
    
    <category term="golang" scheme="https://suncle.me/categories/golang/"/>
    
    
    <category term="genstruct" scheme="https://suncle.me/tags/genstruct/"/>
    
    <category term="generator" scheme="https://suncle.me/tags/generator/"/>
    
  </entry>
  
  <entry>
    <title>redis统计送礼人数</title>
    <link href="https://suncle.me/posts/1014846652/"/>
    <id>https://suncle.me/posts/1014846652/</id>
    <published>2021-07-27T14:56:47.000Z</published>
    <updated>2023-09-03T14:31:24.473Z</updated>
    
    <content type="html"><![CDATA[<p>在Hago的营收活动中， 我们经常要记录的一个数据是送礼用户数，作为活动对于用户的吸引程度的一个关键指标。</p><p>本文将介绍3种使用 Redis 对用户数量进行记录的方案， 这些方案虽然都可以对送礼用户的数量进行统计， 但每个方案都有一些自己特有的操作， 并且各个方案的性能特征以及资源消耗也各有不同。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/T3LYCH-1627956415425.png"></p><span id="more"></span><h2 id="方案-1-：使用集合"><a href="#方案-1-：使用集合" class="headerlink" title="方案 1 ：使用集合"></a>方案 1 ：使用集合</h2><p>如果产品需求除了需要记录送礼用户数量，还需要记录送礼用户的名单， 那么可以使用集合来达成这个目标。</p><p>可以执行以下 <a href="http://redisdoc.com/set/sadd.html">SADD</a> 命令， 将用户添加到送礼用户名单当中：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">SADD &quot;users&quot; &lt;uid&gt;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>通过使用 <a href="http://redisdoc.com/set/sismember.html">SISMEMBER</a> 命令， 我们可以检查一个指定的用户是否送过礼：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">SISMEMBER &quot;users&quot; &lt;uid&gt;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>而统计送礼用户则可以通过执行 <a href="http://redisdoc.com/set/scard.html">SCARD</a> 命令来完成：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">SCARD &quot;users&quot;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>通过集合运算操作， 我们可以像有序集合方案一样， 对不同时间段或者日期的送礼用户名单进行聚合计算。 比如说， 通过 <a href="http://redisdoc.com/set/sinter.html">SINTER</a> 或者 <a href="http://redisdoc.com/set/sinterstore.html">SINTERSTORE</a> 命令， 我们可以计算出一周之内连续送礼的用户：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">SINTER &quot;day_1_users&quot; &quot;day_2_users&quot; ... &quot;day_7_users&quot;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>此外， 通过 <a href="http://redisdoc.com/set/sunion.html">SUNION</a> 命令或者 <a href="http://redisdoc.com/set/sunionstore.html">SUNIONSTORE</a> 命令， 我们可以计算出一周内连续送礼的用户总数量：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">SUNION &quot;day_1_users&quot; &quot;day_2_users&quot; ... &quot;day_7_users&quot;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>而通过执行 <a href="http://redisdoc.com/set/sdiff.html">SDIFF</a> 命令或者 <a href="http://redisdoc.com/set/sdiffstore.html">SDIFFSTORE</a> 命令， 我们可以知道哪些用户今天送礼了， 但是昨天没有送礼：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">SDIFF &quot;today_users&quot; &quot;yesterday_users&quot;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>又或者工作日送礼了， 但是假日没有送礼：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none"># 计算工作日送礼名单SINTERSTORE &quot;weekday_users&quot; &quot;monday_users&quot; &quot;tuesday_users&quot; ... &quot;friday_users&quot;# 计算假日送礼名单SINTERSTORE &quot;holiday_users&quot; &quot;saturday_users&quot; &quot;sunday_users&quot;# 计算工作日送礼但是假日未送礼的名单SDIFF &quot;weekday_users&quot; &quot;holiday_users&quot;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>etc.</p><h2 id="方案-2-：使用-HyperLogLog"><a href="#方案-2-：使用-HyperLogLog" class="headerlink" title="方案 2 ：使用 HyperLogLog"></a>方案 2 ：使用 HyperLogLog</h2><p>虽然集合能够很好的记录活动人数， 但以上这两个方案都有一个明显的缺点， 那就是， 这两个方案耗费的内存会随着被统计用户数量的增多而增多： 如果你的用户数量比较多， 又或者你需要记录多天&#x2F;多个时段的送礼用户名单并进行聚合计算， 那么这两个方案可能会消耗你大量内存。</p><p>另一方面， 在有些情况下， 我们只想要知道送礼用户的人数， 而不需要知道具体的送礼用户名单， 这时集合储存的信息就会显得多余了。</p><p>在需要尽可能地节约内存并且只需要知道送礼用户数量的情况下， 我们可以使用 HyperLogLog 来对送礼用户进行统计： HyperLogLog 是一个概率算法， 它可以对元素的基数进行估算， 并且每个 HyperLogLog 只需要耗费 12 KB 内存， 对于用户数量非常多但是内存却非常紧张的系统， 这一方案无疑是最佳之选。</p><p>在这一方案下， 我们使用 <a href="http://redisdoc.com/hyperloglog/pfadd.html">PFADD</a> 命令去记录在线的用户：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">PFADD &quot;users&quot; &lt;uid&gt;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>使用 <a href="http://redisdoc.com/hyperloglog/pfcount.html">PFCOUNT</a> 命令获取在线人数：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">PFCOUNT &quot;users&quot;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>因为 HyperLogLog 也提供了计算交集的 <a href="http://redisdoc.com/hyperloglog/pfmerge.html">PFMERGE</a> 命令， 所以我们也可以用这个命令计算出多个给定时间段或日期之内， 上线的总人数：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none"># 统计 7 天之内总共有多少人上线了PFMERGE &quot;7_days_both_users&quot; &quot;day_1_users&quot; &quot;day_2_users&quot; ... &quot;day_7_users&quot;PFCOUNT &quot;7_days_both_users&quot;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="方案-3-：使用位图（bitmap）"><a href="#方案-3-：使用位图（bitmap）" class="headerlink" title="方案 3 ：使用位图（bitmap）"></a>方案 3 ：使用位图（bitmap）</h2><p>回顾上面介绍的2个方案， 我们可以得出以上结论：</p><ul><li>使用集合能够储存具体的送礼用户名单， 但是却需要消耗大量的内存；</li><li>而使用 HyperLogLog 虽然能够有效地减少统计送礼用户所需的内存， 但是它却没办法准确地记录具体的送礼用户名单。</li></ul><p>那么是否存在一种既能够获得送礼用户名单， 又可以尽量减少内存消耗的方法存在呢？ 这种方法的确存在 —— 使用 Redis 的位图就可以办到。</p><p>Redis 的位图就是一个由二进制位组成的数组， 通过将数组中的每个二进制位与用户 ID 进行一一对应， 我们可以使用位图去记录每个用户是否在线。</p><p>当一个用户上线时， 我们就使用 <a href="http://redisdoc.com/string/setbit.html">SETBIT</a> 命令， 将这个用户对应的二进制位设置为 1 ：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none"># 此处的 uid 必须为数字，因为它会被用作索引SETBIT &quot;users&quot; &lt;uid&gt; 1<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><p>通过使用 <a href="http://redisdoc.com/string/getbit.html">GETBIT</a> 命令去检查一个二进制位的值是否为 1 ， 我们可以知道指定的用户是否送过礼：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">GETBIT &quot;users&quot; &lt;uid&gt;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>而通过 <a href="http://redisdoc.com/string/bitcount.html">BITCOUNT</a> 命令， 我们可以统计出位图中有多少个二进制位被设置成了 1 ， 也即是有多少个用户送过礼：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none">BITCOUNT &quot;users&quot;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>跟集合一样， 用户也能够对多个位图进行聚合计算 —— 通过 <a href="http://redisdoc.com/string/bitop.html">BITOP</a> 命令， 用户可以对一个或多个位图执行逻辑并、逻辑或、逻辑异或或者逻辑非操作：</p><figure><div class="code-wrapper"><pre class="line-numbers language-none"><code class="language-none"># 计算出 7 天都送礼的用户BITOP &quot;AND&quot; &quot;7_days_both_users&quot; &quot;day_1_users&quot; &quot;day_2_users&quot; ... &quot;day_7_users&quot;# 计算出 7 天的送礼用户总人数BITOP &quot;OR&quot; &quot;7_days_total_users&quot; &quot;day_1_users&quot; &quot;day_2_users&quot; ... &quot;day_7_users&quot;# 计算出两天当中只有其中一天送礼的用户BITOP &quot;XOR&quot; &quot;only_one_day_sent&quot; &quot;day_1_users&quot; &quot;day_2_users&quot;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>位图方案记录一个用户是否在线需要花费 1 个二进制位， 对于用户数为 100 万的网站来说， 使用这一方案只需要耗费 125 KB 内存， 而对于用户数为 1000 万的网站来说， 使用这一方案也只需要花费 1.25 MB 内存。</p><p>虽然位图节约内存的效果不及 HyperLogLog 那么显著， 但是使用位图可以准确地判断一个用户是否上线， 并且能够像集合和有序集合一样， 对送礼用户名单进行聚合计算。 因此对于想要尽量节约内存， 但又需要准确地知道用户是否在线， 又或者需要对用户的在线名单进行聚合计算的应用来说， 使用位图可以说是最佳之选。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>以下表格总结了以上3个方案的特点：</p><table><thead><tr><th align="left">方案</th><th align="left">特点</th></tr></thead><tbody><tr><td align="left">集合</td><td align="left">能够储存送礼用户的名单，也能够执行聚合计算，消耗的内存比有序集合少，但是跟有序集合一样，这个方案消耗的内存也会随着用户数量的增多而增多。</td></tr><tr><td align="left">HyperLogLog</td><td align="left">无论需要统计的用户有多少，只需要耗费 12 KB 内存，但由于概率算法的特性，只能给出在线人数的估算值，并且也无法获取准确的送礼用户名单。</td></tr><tr><td align="left">位图</td><td align="left">在尽可能节约内存的情况下，记录送礼用户的名单，并且能够对这些名单执行聚合操作。</td></tr></tbody></table>]]></content>
    
    
    <summary type="html">&lt;p&gt;在Hago的营收活动中， 我们经常要记录的一个数据是送礼用户数，作为活动对于用户的吸引程度的一个关键指标。&lt;/p&gt;
&lt;p&gt;本文将介绍3种使用 Redis 对用户数量进行记录的方案， 这些方案虽然都可以对送礼用户的数量进行统计， 但每个方案都有一些自己特有的操作， 并且各个方案的性能特征以及资源消耗也各有不同。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://suncle-public.oss-cn-shenzhen.aliyuncs.com/uPic/T3LYCH-1627956415425.png&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="工程实践" scheme="https://suncle.me/categories/%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/"/>
    
    
    <category term="redis" scheme="https://suncle.me/tags/redis/"/>
    
    <category term="HyperLogLog" scheme="https://suncle.me/tags/HyperLogLog/"/>
    
  </entry>
  
  <entry>
    <title>2020 Annual Report</title>
    <link href="https://suncle.me/posts/2646187180/"/>
    <id>https://suncle.me/posts/2646187180/</id>
    <published>2020-12-14T19:52:15.000Z</published>
    <updated>2023-09-03T14:31:24.473Z</updated>
    
    <content type="html"><![CDATA[<p>先通过一组数据看下2020年我到底都做了写什么：</p><ul><li>公众号写了15篇</li><li>博客写了7篇</li><li>专栏学了10个</li><li>听书13本</li><li>看书2本</li><li>居家隔离70天</li><li>旅行一个半月</li><li>综合投资收益率35%</li></ul><p>今年的总结，我想少聊一些细节，多聊一些关键问题的思考。</p><h2 id="1-又是一年的工作"><a href="#1-又是一年的工作" class="headerlink" title="1. 又是一年的工作"></a>1. 又是一年的工作</h2><p>今年工作了9个月，旅行加休息差不多3个月。</p><p>以辞职为分水岭，  1到4月都在上海再惠，主要做一些数据仓库相关的杂事。8到12月在广州YY，主要做海外营收相关的业务。</p><p>疫情期间在湖北远程工作两个月，算是体验了一下远程办公的快感。不用挤地铁，零通勤时间，幸福感猛增。这样的工作也就成为了我的一个奋斗目标。</p><p>我妈甚至对我说：要不以后就不去上海在家工作得了。</p><p>乍一看，这样的工作节奏似乎很好，但仔细想想并不完美。因为人生不只是工作，人还得有充足又有效的社交活动，也需要竞争激烈的环境，只有这样才能成长的更快，更早的实现人生的意义。</p><p>今年就不聊具体的工作内容了，我们聊一下理想中的工作生活模式。</p><h2 id="2-你喜欢组合式工作法吗？"><a href="#2-你喜欢组合式工作法吗？" class="headerlink" title="2. 你喜欢组合式工作法吗？"></a>2. 你喜欢组合式工作法吗？</h2><p>欧洲一位叫<strong>查尔斯·汉迪</strong>的管理大师发明的<code>组合式工作</code>的方法，是我个人非常喜欢，也非常期待拥有的 。组合式工作有4个组成部分：</p><ol><li>有薪工作：就是能产生收益的工作，可以是为别人的公司工作，也可以是为自己的公司工作</li><li>学习工作：用于提升自己，增长见识，持续升级</li><li>家庭工作：把陪伴家人当成一项工作，并且作为日常提前安排，做到不被其他工作占据</li><li>义务工作：往大了说是反哺社会，往小了说是回馈他人</li></ol><p>4种工作的大致组成是一年中6个月时间上班，3个月时间学习，2个月时间陪家人，剩下的1个月时间从事公益。</p><p>这些时间都是连续的，而不是分散的。连续的时间能保证效率，也能很容易分隔开不同的工作，确保不会互相挤占。</p><p>这样的工作看着很理想，其实也挺常见。比如今年五月份在敦煌旅游的时候，看到疫情逐渐好转，旅游慢慢恢复，差一点就留在那里开客栈了。</p><p>如果我成为了一名住在敦煌的客栈老板，那么从每年的11月份到第二年的4月份，是敦煌的旅游淡季，几乎没有游客，也就不需要工作了。那么6个月时间就可以合理的分配给组合式工作法中的另外3项工作了。</p><h2 id="3-今年你去了哪里？"><a href="#3-今年你去了哪里？" class="headerlink" title="3. 今年你去了哪里？"></a>3. 今年你去了哪里？</h2><p>原以为疫情的一年会收心，禁足在家，结果却比以前绝大多数年份都玩的多，足足玩了一个半月的时间。美中不足的是没法出国去心心念念的伊朗。</p><ol><li><p>元旦在重庆，和大学室友聚会，喝了一年唯一的一次白酒，也给小太阳拍了《少年的你》</p></li><li><p>清明节，提前去看一下未来的城市广州。仔细想想，沙面公园真的很美很时尚。在广州也大半年了，除了上下班路上、沙面公园、白云山之外，也没有去过其他的地方。自从打算把摄影当成一个普通的爱好之后，出门放风对我就没有那么大的吸引力了。果然人的行为受心态的影响是最大的。</p></li><li><p>五一假期，逛了一下杭州，曾经待了两年的城市，后续没机会经常回去看看了。广州不像上海，和杭州离得远，可以三天两头的去耍一圈。</p></li><li><p>5月8号从再惠辞职之后，玩转了上海、乌镇。在乌镇吃了一个星期的东坡肉和酱鸭</p></li><li><p>5月15号-5月22号，从成都走318一路玩到稻城亚丁，天不高，云也不淡。反倒是又浓密又深蓝。</p></li><li><p>5月23，西安一日游，毕业四年第一次回西安，居然是路过。疫情还是很严重，大唐不夜城依旧人山人海。</p></li><li><p>5月24-6月6号，陪小太阳去了一次西北大环线，在敦煌这个我喜欢的城市待了3天，不慌不忙的观赏敦煌古城，风沙扬起，天地昏暗，美的是那么的壮丽。</p></li><li><p>兰州-天水-麦积山石窟，看了莫高窟和龙门石窟再来看麦积山石窟，矗立于麦积山顶，依山而建。最大的大佛俯视着葱葱郁郁的麦积山。危楼高百尺，手可摘星辰，描述的就是这番景象吧。</p></li><li><p>深圳大芬油画村，大鹏古城。第一眼见到洛阳的时候沉迷其静谧古老的城中绿道。第一眼见到深圳的时候，这座城市干净整洁得就像刚被雨洗过一样，满是东南亚风情。车行驶在南山的路上，一瞬间仿佛回到了18年，那一段从岘港到灵姑湾的旅行。</p></li></ol><blockquote><p> 敦煌古城吹起了远古的风沙</p></blockquote><h2 id="4-你真的需要去那么多地方吗？"><a href="#4-你真的需要去那么多地方吗？" class="headerlink" title="4. 你真的需要去那么多地方吗？"></a>4. 你真的需要去那么多地方吗？</h2><p>虽然去了这么多地方，但我还是想告诉你，一个真正的心灵旅行者并不需要每个假期都外出。</p><p>除旅行之外，每个人都可以有一些更高雅的爱好，学习一些专业之外的知识。</p><p>比如说，天文学就是一个很不错的选择。对这个世界了解的越多，对于宇宙，对于个体，对于自己目前的位置就会了解的更清楚，更透彻。</p><p>类似的还有天体物理学，宇宙学。都值得去了解。先认识到自己有多渺小，生活中才能过的有多伟大。假如你没有时间深入研究，也有很多优秀的科普剧科普频道都是可以看的，比如《走近科学》，比如BBC的一些关于科学方面的内容，还有NASA的各种报道。</p><p>这一年，在天文学上话费的大量时间，让我更深刻的认识到人间值得这四个字的含义。更清晰的认识到自己为什么活着，活着的意义是什么，继而内心坚定、有条不紊的向着目标前进。</p><h2 id="5-为什么是广州？"><a href="#5-为什么是广州？" class="headerlink" title="5. 为什么是广州？"></a>5. 为什么是广州？</h2><p>给年轻人的一点忠告，这是人一辈子会有两个家，一个家是自己的老家，另外一个家就是自己的心酱，这个家你到了哪儿就是家。</p><p>有些人在一线城市生活，哪怕过得再苦也不愿意离开；因为他们很清楚，相比在大城市里生活，小地方出身的孩子有一个很难弥补的短板，就是见识不足，目光不够长远，以至于走不出贫困的怪圈。所以为了孩子，自己再苦也要熬下去。这是一个很现实的情况。</p><h2 id="6-你将到哪里去？"><a href="#6-你将到哪里去？" class="headerlink" title="6. 你将到哪里去？"></a>6. 你将到哪里去？</h2><p>年初疫情肆虐，线上办公和知识付费如火如荼。我也一样，趁有空上了几门付费的课程。有写作课，有个体创业课，还有教你做课程的课。</p><p>姑且不论这些付费课程质量是否足够高，对于技能的增长是否足够有效。但是有一点，在不同的社群里面，你能看到各行各业不同的人都在自己的领域深入探索，或者都在寻找一些突破自我的机会。</p><p>如果非要说上了那些课，自己的最大的收获是什么。那么应该是更深刻的认识到自身的优势以及未来的定位。</p><p>你爱独居，有书有花，有笔有茶，生活简单却又自我充盈，这是一种生活方式；你爱工作，详细每一天每小时的工作计划，在文件与沟通中迸发活力，这是一种生活方式；你爱旅行，看远山淡影，桥上的游人，古街的小吃摊，这是一种生活方式；你爱冒险，去三山五岳，去黄沙大漠，登悬崖峭壁，摘星揽月，这也是一种生活方式。</p><p>一百个人有一百种生活方式。我们经历重重尝试后，总能找到最适合自己的那种。但问题是，找到以后，真正的考验也许才刚刚开始，面对种种艰难，你是否能坚持？</p><h2 id="7-20年赚到你需要的钱了吗"><a href="#7-20年赚到你需要的钱了吗" class="headerlink" title="7. 20年赚到你需要的钱了吗"></a>7. 20年赚到你需要的钱了吗</h2><p>投资</p><p>股票和基金的收益率</p><p>、过去的2020惊心动魄又曲折振奋。国家、民族和A股都经历了从“实鼠不易”到“牛转乾坤”的转变。在连续两年的高回报率下，投资管理人更需要的是谨慎自己的表现是否更多源于随机性贡献，而潜伏的风险是否被踊跃的市场低估。在二分法的市场里，投资管理人往往容易将源自于运气、随机、可能、假说、理论、巧合的结果归功于自身的技能、决定、知识、现实、法则。在牛市的环境下，我们也许只是一个通过了生存偏差的幸运傻瓜，却往往将市场的优异表现幻想成自己的专业投资水平。<br>2、我们意识到投资的最大风险不是短期回撤而是长期持有资产不能带来盈利，即损失了时间，也再无法回到过去。”<br>3、通过对上市公司地区风土人情的不断实地拜访，决定了我们的投资从不过山海关到退守长江南，目前则基本限于南宋的地盘。由于基金管理公司的平台优势，我们能够更多的和企业管理人交流，将对单纯对市值充满诉求、对模式涛涛不绝、对各种机会都能把握、对赌性极其坚强的公司小心翼翼而不怀遗憾的划在自身能力圈范围之外。<br>“投资的最大风险不是短期回撤而是长期持有资产不能带来盈利，即损失了时间，也再无法回到过去。”非常欣赏这句话。所有的投资策略和投资风格，最终都还是应该以长期能否带来盈利为评价的核心依据。无论个体差异多大，我们在本质追求是一样的：在合理控制风险前提下获得尽可能高的收益。宁可错过也不犯错的逻辑适用于期货等博弈类工具，但不适用于长期来说螺旋式上升的权益类资产。我们始终强调，在估值合理或偏低的大背景下，拉长周期来看，踏空风险远大于套牢风险。错过就是一种巨大的犯错，不存在不犯错这一说法。<br>毕竟，错过的不仅是收益，还有那再也无法回去的青春时光。</p><h2 id="8-婚姻是什么？"><a href="#8-婚姻是什么？" class="headerlink" title="8. 婚姻是什么？"></a>8. 婚姻是什么？</h2><p>看尽了时间的繁华之后，才发现，婚姻不再恐惧的让人心碎，而是美好的让人心醉</p><hr><p>今年事情太多，忙不过来，连新年音乐会这样的老传统都忘在了脑后<br>罗曼罗兰说：「世界上只有一种英雄主义，就是看清生活的真相之后依然热爱生活」。</p><p>人生只要有一次你发自灵魂深处感觉到快乐和幸福，你的人生就值了，剩下全是痛苦都没关系，只要感受过一次就可以，这样的人生你一定希望再来一次，所以不管 是怎样的人生，你要做的事情并不是在意那些痛苦，而是去寻找内心的快乐和喜悦</p><p>我在《大学之路》中讲，不用担心起跑线上的那一点差距，因为人生的许多事其实是马拉松，跑到后来你会发现，身边的同行的人不会很多，因为更多的人在过程中就已经止步了。</p><p>在河内还剑湖旁边摆地摊卖旅游照片的背包客，我现在也能理解了</p><p>这一年，我看清了世界，也看清了自己。但还需要看清的是众生</p><p>愿你尝遍烟火，依然相信，人间值得</p><p>最后，我从来不觉得霍皮人传说中白色的岛是遥不可及的神仙居所，在我心里，那只是终将路过的一隅。</p><p>乌斯怀亚 世界</p><p>在这个小点上，每个你爱的人、每个你认识的人、每个你曾经听过的人，以及每个曾经存在的人，都在那里过完一生。这里集合了一切的欢喜与苦难，数千个自信的宗教、意识形态以及经济学说，每个猎人和搜寻者、每个英雄和懦夫、每个文明的创造者与毁灭者、每个国王与农夫、每对相恋中的年轻爱侣、每个充满希望的孩子、每对父母、发明家和探险家，每个教授道德的老师、每个贪污政客、每个超级巨星、每个至高无上的领袖、每个人类历史上的圣人与罪人，都住在这里 —— 一粒悬浮在阳光下的微尘。</p><!-- flag of hidden posts -->]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;先通过一组数据看下2020年我到底都做了写什么：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;公众号写了15篇&lt;/li&gt;
&lt;li&gt;博客写了7篇&lt;/li&gt;
&lt;li&gt;专栏学了10个&lt;/li&gt;
&lt;li&gt;听书13本&lt;/li&gt;
&lt;li&gt;看书2本&lt;/li&gt;
&lt;li&gt;居家隔离70天&lt;/li&gt;
&lt;li&gt;旅行</summary>
      
    
    
    
    <category term="年度总结" scheme="https://suncle.me/categories/%E5%B9%B4%E5%BA%A6%E6%80%BB%E7%BB%93/"/>
    
    
  </entry>
  
  <entry>
    <title>坚持住啊，还在屎山中爬行的同事们</title>
    <link href="https://suncle.me/posts/2194094500/"/>
    <id>https://suncle.me/posts/2194094500/</id>
    <published>2020-12-14T19:46:46.000Z</published>
    <updated>2023-09-03T14:31:24.473Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>“There are only two hard things in Computer Science: cache invalidation and naming things.”</p><p> — <a href="https://martinfowler.com/bliki/TwoHardThings.html">Phil Karlton</a></p></blockquote><p>在计算机领域只有两件艰难的事情：缓存失效和对象命名。</p><p>这还真不是一个笑话。写代码是比较容易的事情，但是阅读别人的代码，那就因人而异了。</p><p>好的工程师写出来的代码可读性很高，比如我上家公司的同事旭总。一般的工程师写出来的代码就像是一坨屎，比如之前某某几位同事。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/best-practice-shit-code/%E4%BD%A0%E7%8C%9C%E6%88%91%E7%9A%84%E6%9E%AA%E9%87%8C%E6%9C%89%E6%B2%A1%E6%9C%89%E5%AD%90%E5%BC%B9.jpg"></p><p>所以我会经常去格式化他们的代码。如果不幸轮到你继续在屎代码上面开发，那就是屎上堆屎了。心疼你。</p><p>当然有时候工期紧张，我自己也会写一些屎代码<code>shit code</code>，但是每次提交的时候都有一种强烈的愧疚感。希望这些代码最多存活一个月就消失，不要被人踩到了。</p><span id="more"></span><h2 id="屎代码是怎么产生的？"><a href="#屎代码是怎么产生的？" class="headerlink" title="屎代码是怎么产生的？"></a>屎代码是怎么产生的？</h2><p>要说怎么写屎代码，这个我就很拿手了。下面随便列举一些常见的屎代码产生方式：</p><ol><li>看不懂的命名</li><li>过长的类|函数</li><li>大段重复的代码</li><li>没有注释的Magic number</li><li>100多个参数的函数</li><li>一堆没有注释的if-else嵌套</li><li>业务过度耦合：支付订单和点餐订单能耦合在一起？谁重构谁痛苦</li><li>代码和文档分离：几年前的业务完全不知道是个啥</li><li>…</li></ol><p>很不幸，大多数人的项目中，这些常见的屎代码产生方式是随处可见的。</p><p>毕竟，几百个人写屎代码，就像几百个人堆积木。堆得歪歪扭扭，摇摇晃晃，乱七八糟，你千万不能抽里面的积木，指不定抽了一块就塌了。只能看见哪里觉得不牢靠不停的往那边填积木。只要不倒就好了。这也是大部分程序员的追求了。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/best-practice-shit-code/%E6%8B%89%E5%B1%8E.jpg"></p><h2 id="不写屎代码从命名规范开始"><a href="#不写屎代码从命名规范开始" class="headerlink" title="不写屎代码从命名规范开始"></a>不写屎代码从命名规范开始</h2><p>如果想解决代码高耦合这坨屎，需要有比较好的顶层解耦设计，划分清楚各自服务的边界。</p><p>如果你一直都只是代码搬运工，传说中的cv工程师，那么到这一步还有一些内功需要你去修炼的。</p><p>但是好的命名规范确实每个人都可以做到的。不同的语言都有各自的规范，如果所有人都能正确理解那些规范，并且严格遵守，同时强制使用ci校验，就可以保证代码外表上是美观的。</p><p>这里就以Go语言的命名规范为例讲一下怎么写出人人都想闻的香代码。</p><p><code>go fmt</code>可以统一不同人的编码规范，却没有办法格式化出一个好的命名。但是在Go社区中其实一直都存在着一些成文的或者不成文的命名规则，比如：</p><ol><li>某个名称在包外是否可见，就取决于其首个字符是否为大写字母</li><li>使用驼峰命名而不是下划线</li><li>单个方法的接口名称应该是<code>InterfaceName = MethodName + er</code></li><li><code>Getter</code>方法的命名不需要包含<code>Get</code>，比如cat.Owner()方法不需要命名成cat.GetOwner()</li><li>首字母缩写词应该保持原有格式：应该使用userID而不是userId，应该使用userAPI而不是userApi</li><li>变量名需要尽可能的简单但是又能描述清楚</li><li>…</li></ol><p>总之，规则是有的。只是很多程序员选择直接忽视。比如前面的五条。</p><p>还有一些规则是被过渡滥用了。比如第6条，很多同事的命名沿用以前的老风格，在一个struct中大量使用单字符的的变量或者随心所欲的缩写。这样的代码是完全没有办法阅读的。所谓有追求的程序员，还是得追求一下代码的品味。</p><p>悲观的说，即使做到了这些也只是你一个人的代码是优雅的，但是你怎么能保证所有人都有这样的追求呢？作为个人，除了提建议之外，其实是没有太多有效的办法的。</p><h2 id="怎么才能根治屎上堆屎？"><a href="#怎么才能根治屎上堆屎？" class="headerlink" title="怎么才能根治屎上堆屎？"></a>怎么才能根治屎上堆屎？</h2><p>想根治这个问题，只靠某个程序员一直保持优雅代码是没什么用的。</p><p>你写10句优雅的代码，其他10个同事每个人都写10句屎代码。这样算起来，优雅代码的比例最多只有十分之一。</p><p>如果一个团队想要彻底解决屎上堆屎这个老大难问题，就需要贯彻执行下面两点方法：</p><ol><li>招高质量的程序员：code sense很重要，每个程序员都需要懂得奥卡姆剃刀原理：若无必要，勿增实体。</li><li>管理层需要有长远的视野而不仅仅是短期目标。</li></ol><p>深度悲观的说，这两个方法真正执行起来的时候也是难度重重，基本不可能完成。</p><p>高质量无论是不是在互联网行业，都意味着价格昂贵，但又有几家公司能给得雇佣的起这么多昂贵的程序员呢？</p><p>而长远的目标在资本的压力之下，也显得一文不值。代码规范提升10%的重要性和“明天上线”这个命令比起来，也是低到尘埃的的。然后日复一日，明天又将是明天，规范性最终消失殆尽，屎山越来越高。</p><p>说到底，这些都是钱的问题，也是最无解的问题，最终在某一天，屎山崩溃，一切回到原点。</p><p>最后，大胆猜测一下，昨天的Google服务崩溃也是因为屎太多了吧。</p>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;“There are only two hard things in Computer Science: cache invalidation and naming things.”&lt;/p&gt;
&lt;p&gt; — &lt;a href=&quot;https://martinfowler.com/bliki/TwoHardThings.html&quot;&gt;Phil Karlton&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在计算机领域只有两件艰难的事情：缓存失效和对象命名。&lt;/p&gt;
&lt;p&gt;这还真不是一个笑话。写代码是比较容易的事情，但是阅读别人的代码，那就因人而异了。&lt;/p&gt;
&lt;p&gt;好的工程师写出来的代码可读性很高，比如我上家公司的同事旭总。一般的工程师写出来的代码就像是一坨屎，比如之前某某几位同事。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/best-practice-shit-code/%E4%BD%A0%E7%8C%9C%E6%88%91%E7%9A%84%E6%9E%AA%E9%87%8C%E6%9C%89%E6%B2%A1%E6%9C%89%E5%AD%90%E5%BC%B9.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;所以我会经常去格式化他们的代码。如果不幸轮到你继续在屎代码上面开发，那就是屎上堆屎了。心疼你。&lt;/p&gt;
&lt;p&gt;当然有时候工期紧张，我自己也会写一些屎代码&lt;code&gt;shit code&lt;/code&gt;，但是每次提交的时候都有一种强烈的愧疚感。希望这些代码最多存活一个月就消失，不要被人踩到了。&lt;/p&gt;</summary>
    
    
    
    <category term="工程实践" scheme="https://suncle.me/categories/%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/"/>
    
    
    <category term="shit code" scheme="https://suncle.me/tags/shit-code/"/>
    
    <category term="规范性" scheme="https://suncle.me/tags/%E8%A7%84%E8%8C%83%E6%80%A7/"/>
    
  </entry>
  
  <entry>
    <title>神兵利器推荐——你一定不能错过的mac alfred工作流</title>
    <link href="https://suncle.me/posts/441900336/"/>
    <id>https://suncle.me/posts/441900336/</id>
    <published>2020-12-09T12:44:56.000Z</published>
    <updated>2023-09-03T14:31:24.473Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/alfred4.jpg"></p><p>上一篇介绍了我日常高频使用的chrome插件，那就顺便介绍一下mac上最最最能提升效率的Alfred工作流吧。</p><span id="more"></span><p>以下是我常用的 Alfred workflow 列表：（几乎每天都会用到）</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/all-workflow.jpg" alt="自用 Alfred Workfolw"></p><p>不同的插件，可能有不同作者开发的版本，下面会给出我一直在用的版本，但是你完全可以去选择更适合自己的版本。比如像有道翻译，可以找到很多版本，选择自己喜欢的即可。</p><p>下面按照顺序一款一款的介绍下。</p><h2 id="CodeVar"><a href="#CodeVar" class="headerlink" title="CodeVar"></a>CodeVar</h2><blockquote><p>生成变量名，支持大小驼峰、常量、下划线，开发者必备的工作流</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/code-var.jpg"></p><p>下载地址：<a href="https://github.com/xudaolong/CodeVar">https://github.com/xudaolong/CodeVar</a></p><h2 id="Encode-x2F-Decode"><a href="#Encode-x2F-Decode" class="headerlink" title="Encode &#x2F; Decode"></a>Encode &#x2F; Decode</h2><blockquote><p>Base64 编解码</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/encode.jpg"></p><p>下载地址：<a href="https://github.com/willfarrell/alfred-encode-decode-workflow">https://github.com/willfarrell/alfred-encode-decode-workflow</a></p><h2 id="Fakeum"><a href="#Fakeum" class="headerlink" title="Fakeum"></a>Fakeum</h2><blockquote><p>生成各种假数据，比如姓名，城市，银行卡号等等</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/fake.jpg"></p><p>下载地址：<a href="https://github.com/deanishe/alfred-fakeum">https://github.com/deanishe/alfred-fakeum</a></p><h2 id="Github"><a href="#Github" class="headerlink" title="Github"></a>Github</h2><blockquote><p>快速查找项目&#x2F;用户</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/gh.jpg"></p><p>下载地址：<a href="https://github.com/gharlan/alfred-github-workflow">https://github.com/gharlan/alfred-github-workflow</a></p><h2 id="Hash"><a href="#Hash" class="headerlink" title="Hash"></a>Hash</h2><blockquote><p>支持各种hash算法的计算</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/hash.jpg"></p><p>下载地址：<a href="https://github.com/willfarrell/alfred-hash-workflow">https://github.com/willfarrell/alfred-hash-workflow</a></p><h2 id="Http-Status-Codes"><a href="#Http-Status-Codes" class="headerlink" title="Http Status Codes"></a>Http Status Codes</h2><blockquote><p>快速显示 http 状态码含义</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/http.jpg"></p><p>下载地址：<a href="https://www.packal.org/workflow/http-status-codes">https://www.packal.org/workflow/http-status-codes</a></p><h2 id="IP-Address"><a href="#IP-Address" class="headerlink" title="IP Address"></a>IP Address</h2><blockquote><p>快速查询本地ip和公网出口ip，再也不用到ipip.net上去查询了</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/ip.jpg"></p><p>下载地址：<a href="https://github.com/zenorocha/alfred-workflows/raw/master/ip-address/ip-address.alfredworkflow">https://github.com/zenorocha/alfred-workflows/raw/master/ip-address/ip-address.alfredworkflow</a></p><h2 id="Kill-Process"><a href="#Kill-Process" class="headerlink" title="Kill Process"></a>Kill Process</h2><blockquote><p>杀进程，不知道怎么处理卡死的程序？那就用kill process吧</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/kill.jpg"></p><p>下载地址：<a href="https://github.com/nathangreenstein/alfred-process-killer">https://github.com/nathangreenstein/alfred-process-killer</a></p><h2 id="Microsoft-ToDo"><a href="#Microsoft-ToDo" class="headerlink" title="Microsoft ToDo"></a>Microsoft ToDo</h2><blockquote><p>奇妙清单的升级版本就是Microsoft ToDo了，这个工作量可以快速记录todo事项</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/ms-todo.jpg"></p><p>下载地址：<a href="https://github.com/johandebeurs/alfred-mstodo-workflow">https://github.com/johandebeurs/alfred-mstodo-workflow</a></p><h2 id="Password-Generator"><a href="#Password-Generator" class="headerlink" title="Password Generator"></a>Password Generator</h2><blockquote><p>快速生成各种复杂度的密码，总有一款密码适合你</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/pwgen.jpg"></p><p>下载地址：<a href="https://github.com/deanishe/alfred-pwgen">https://github.com/deanishe/alfred-pwgen</a></p><h2 id="Terminal-Finder"><a href="#Terminal-Finder" class="headerlink" title="Terminal Finder"></a>Terminal Finder</h2><blockquote><p>mac上终端的目录并不像windows那么好找到，经常使用终端的开发者需要快速打开当前路径所在的finder目录</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/terminal-finder.jpg"></p><p>下载地址：<a href="https://github.com/LeEnno/alfred-terminalfinder">https://github.com/LeEnno/alfred-terminalfinder</a></p><h2 id="Wechat-Plugin"><a href="#Wechat-Plugin" class="headerlink" title="Wechat Plugin"></a>Wechat Plugin</h2><blockquote><p>mac版本的微信客户端插件，支持多种配色方案。支持微信多开以及小程序查看，还有消息防撤回功能。</p><p>微信插件分享就不贴图了，自行探索</p></blockquote><h2 id="Youdao-Translate"><a href="#Youdao-Translate" class="headerlink" title="Youdao Translate"></a>Youdao Translate</h2><blockquote><p>快速有道翻译，再也不用打开网页去查翻译结果了</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/yd.jpg"></p><p>下载地址：<a href="https://github.com/wensonsmith/YoudaoTranslate">https://github.com/wensonsmith/YoudaoTranslate</a></p><h2 id="Lock"><a href="#Lock" class="headerlink" title="Lock"></a>Lock</h2><blockquote><p>锁定屏幕，<code>alfred</code> 自带的功能，不需要付费即可使用</p></blockquote><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/lock.jpg"></p><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>做一个懒人吧。善于使用各种工具，简化工作流，减少在重复的事情上浪费时间，使效率最大化。</p><p>附上一个Alfred workflow合集：<a href="https://github.com/zenorocha/alfred-workflows">https://github.com/zenorocha/alfred-workflows</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-alfred-workflow/alfred4.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;上一篇介绍了我日常高频使用的chrome插件，那就顺便介绍一下mac上最最最能提升效率的Alfred工作流吧。&lt;/p&gt;</summary>
    
    
    
    <category term="效率" scheme="https://suncle.me/categories/%E6%95%88%E7%8E%87/"/>
    
    
    <category term="工具" scheme="https://suncle.me/tags/%E5%B7%A5%E5%85%B7/"/>
    
    <category term="chrome" scheme="https://suncle.me/tags/chrome/"/>
    
  </entry>
  
  <entry>
    <title>神兵利器推荐——你一定不能错过的chrome插件</title>
    <link href="https://suncle.me/posts/2096485371/"/>
    <id>https://suncle.me/posts/2096485371/</id>
    <published>2020-12-09T11:06:53.000Z</published>
    <updated>2023-09-03T14:31:24.473Z</updated>
    
    <content type="html"><![CDATA[<p>最近有朋友问我，有没有什么可以提升效率的chrome插件推荐一下。我看了一下我的插件库，好用的还真不少。既然要推荐给一个人，还不如整理出来，分享给所有需要的人。毕竟，极致利他才能最终利己嘛。</p><p>话不多说，下面一个一个的推荐，并且附上效果截图。（注意，一定要看完，图中隐藏着很多人梦寐以求的大杀器！！！）</p><h2 id="技术文章一键分发：openWrite助手"><a href="#技术文章一键分发：openWrite助手" class="headerlink" title="技术文章一键分发：openWrite助手"></a>技术文章一键分发：openWrite助手</h2><p>安全认证简书、博客园、知乎、开源中国、掘金、SegmentFault等平台，一键群发文章。</p><p>免费版只有有限的分发次数，但是为了效率，这点费用还是很值得的。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/open-write.jpg"></p><span id="more"></span><h2 id="公众号排版助手：壹伴"><a href="#公众号排版助手：壹伴" class="headerlink" title="公众号排版助手：壹伴"></a>公众号排版助手：壹伴</h2><p>简单好用的公众号效率工具。类似工具有秀米和135编辑器，可以按需使用。</p><p>只要配置好公众号的样式，之后所有的文章都可以一键排版，从而节省大量的时间。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/yiban.jpg"></p><h2 id="去掉烦人的youtube广告：Adblock-for-Youtube"><a href="#去掉烦人的youtube广告：Adblock-for-Youtube" class="headerlink" title="去掉烦人的youtube广告：Adblock for Youtube"></a>去掉烦人的youtube广告：Adblock for Youtube</h2><p>作为youtube重度用户，日常所有的看视频需求都会上youtube。虽然youtube秉承了google的不作恶原则，可以手动跳过大段的广告，观看5秒钟就可以。但是这5秒钟也非常影响观看体验。而这款插件就可以完美的解决这个痛点。</p><p>在下图的红框中可以看到有一段很不显眼的小字：”cleaned by Adblock for Youtube™ “。恭喜你，进入到没有广告的世界。</p><p>顺便推荐一下，我最最最喜欢的youtube频道<code>老高与小沫</code></p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/adblock-for-youtube.jpg"></p><h2 id="一键清除浏览器缓存：Clear-Cache"><a href="#一键清除浏览器缓存：Clear-Cache" class="headerlink" title="一键清除浏览器缓存：Clear Cache"></a>一键清除浏览器缓存：Clear Cache</h2><p>一键清除缓存和浏览器数据，再也不用按照<code>inspect -&gt; Application -&gt; Cache -&gt; delete</code>这样一条路走下来了。</p><p>对于经常需要清空浏览器缓存的前端同学来说，这个简直就是调试的春天。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/clear-cache.jpg"></p><h2 id="chrome标签页增强：Earth-View-from-Google-Earth"><a href="#chrome标签页增强：Earth-View-from-Google-Earth" class="headerlink" title="chrome标签页增强：Earth View from Google Earth"></a>chrome标签页增强：Earth View from Google Earth</h2><p>默认的chrome浏览器标签页实在是太普通，看多了就很腻。如果这时候来一个Google 地球的天空视角背景图，想来也会很开心吧，比如今天无意之间看到的这张图，West Lyons River的河流冲击区。看完之后，立马产生了去澳洲看看的念头。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/google-earth-view.jpeg"></p><h2 id="前端开发助手：FeHelper"><a href="#前端开发助手：FeHelper" class="headerlink" title="前端开发助手：FeHelper"></a>前端开发助手：FeHelper</h2><p>前端开发中使用到的各种工具的大合集，非常实用。比如常见的json美化，编解码，时间相关的等等。大部分后端也是用的上的。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/fe-helper.jpg"></p><h2 id="chrome插件分享利器：Get-CRX"><a href="#chrome插件分享利器：Get-CRX" class="headerlink" title="chrome插件分享利器：Get CRX"></a>chrome插件分享利器：Get CRX</h2><p>chrome插件商店里面安装的插件是很难找到存放位置的。比如上面安装的这些，在页面上没有保存为crx文件的按钮。这时候如果你有个朋友没法访问<code>chrome web store</code>，但是又有需要用的插件找你帮忙。这时候就只能想办法保存为离线的crx文件了。<code>Get CRX</code>这个工具就能解决你的这个问题，对着安装好的插件页面点击右键，就可以下载到你需要的crx文件了，非常好用。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/get-crx.jpg"></p><h2 id="网页Json格式自动美化：JSON-Formatter"><a href="#网页Json格式自动美化：JSON-Formatter" class="headerlink" title="网页Json格式自动美化：JSON Formatter"></a>网页Json格式自动美化：JSON Formatter</h2><p>某些网页或者接口返回的json数据，没有美化，就是扁平的字符串。这样会眼睛看着不是很方便。比如下面这个gist文件。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/json-formatter-example-raw.jpg"></p><p>装了这个插件之后，会自动将原始的json串进行pretty操作，就转换成了美化的，如下图</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/json-formatter-example-pretty.jpg"></p><h2 id="网页版以太坊钱包：MetaMask"><a href="#网页版以太坊钱包：MetaMask" class="headerlink" title="网页版以太坊钱包：MetaMask"></a>网页版以太坊钱包：MetaMask</h2><p>以太坊转账，或者基于以太坊的一些通证转账都是可以通过MetaMask而不需要登上以太坊交易所。此外，MetaMask的界面非常简洁，使用体验很好。</p><p>不过不管用什么工具，请记得保管好你的账号短语phrase。要不然像我一样，攒了很久的以太坊找不回来了。否则按照现在的虚拟货币的价格，可能已经发财了吧</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/metamask.jpg"></p><h2 id="多引擎以图搜图神器：NooBox（二箱）"><a href="#多引擎以图搜图神器：NooBox（二箱）" class="headerlink" title="多引擎以图搜图神器：NooBox（二箱）"></a>多引擎以图搜图神器：NooBox（二箱）</h2><p>以图搜图的功能能做什么就不多说了，从快播的远古时代生存到这个年代的老司机，肯定都是懂的。这款插件支持多种引擎，除了常用的baidu和google识图，还有俄罗斯的yandex和微软的必应。功能无比强大。不过，记得选好关键词<code>for teen</code>，少儿不宜的少看。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/NooBox.jpg"></p><h2 id="一键获取图片文本：One-click-Image-Reader-OCR"><a href="#一键获取图片文本：One-click-Image-Reader-OCR" class="headerlink" title="一键获取图片文本：One-click Image Reader (OCR)"></a>一键获取图片文本：One-click Image Reader (OCR)</h2><p>经常看网页pdf的打工人们，通常有一个困惑，就是很多pdf都不是文件版本的，而是扫描版本的。因此没有办法复制你需要的文字。这个工具就是为了解决这个问题。截取你需要转换成文字的区域，就可以自动转换成文字版本，而且支持一键复制。</p><p>除了网页pdf之外，还支持图片，视频。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/one-click-ocr.jpg"></p><h2 id="截取网页全屏：ScreenShot-Capture-amp-Editor-Tool"><a href="#截取网页全屏：ScreenShot-Capture-amp-Editor-Tool" class="headerlink" title="截取网页全屏：ScreenShot Capture &amp; Editor Tool"></a>截取网页全屏：ScreenShot Capture &amp; Editor Tool</h2><p>找一个短点的截图展示下效果，反正用过的都说好。就不多说了。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/screenshot-carbon-now-sh-1607436737137.png"></p><h2 id="增强github浏览体验：Sourcegraph"><a href="#增强github浏览体验：Sourcegraph" class="headerlink" title="增强github浏览体验：Sourcegraph"></a>增强github浏览体验：Sourcegraph</h2><p>经常逛github的肯定有一个体验就是，绝大多数项目我只是看，又不想clone到本地，但是github的浏览体验挺不咋地的，这时候就需要插件辅助了。Sourcegraph就是其中美美的一个。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/sourcegraph.jpg"></p><h2 id="Tampermonkey"><a href="#Tampermonkey" class="headerlink" title="Tampermonkey"></a>Tampermonkey</h2><p>一千个司机眼中有一千个油猴。油🐵的存在给了你的浏览器无限种可能。不过需要去找一些好用的脚本，或者自行折腾。</p><h2 id="键盘代替鼠标的神器：Vimium"><a href="#键盘代替鼠标的神器：Vimium" class="headerlink" title="键盘代替鼠标的神器：Vimium"></a>键盘代替鼠标的神器：Vimium</h2><p>假如我的鼠标坏了，我还是想用浏览器，那怎么办呢？Vimium本着Vim的精神为导航和控制提供键盘快捷键。</p><p>进入vim模式，按f键就可以给每一个按钮加一个键盘快捷键。这样就可以使用键盘进行跳转了。</p><p>除此之外，还有很多导航相关的快捷键可以使用。记住几个常用的，效率就会极大的提升。</p><p>不过，可惜的是，元旦我的成都之旅，大概率要凉凉了。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/vimium.jpg"></p><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>还有一些也不错，但是因为我的使用频率并没有那么高，所以没有在上面的列表中列举出来。</p><p>比如<code>Exstension Manager</code>，<code>Evernote Web Clipper</code>，<code>方片收集</code> 等等插件，都是能够提升效率的。</p><p>总之，善于使用浏览器的插件，会极大的提升效率、提升幸福感。希望大家都能享受这些插件带来的快感。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近有朋友问我，有没有什么可以提升效率的chrome插件推荐一下。我看了一下我的插件库，好用的还真不少。既然要推荐给一个人，还不如整理出来，分享给所有需要的人。毕竟，极致利他才能最终利己嘛。&lt;/p&gt;
&lt;p&gt;话不多说，下面一个一个的推荐，并且附上效果截图。（注意，一定要看完，图中隐藏着很多人梦寐以求的大杀器！！！）&lt;/p&gt;
&lt;h2 id=&quot;技术文章一键分发：openWrite助手&quot;&gt;&lt;a href=&quot;#技术文章一键分发：openWrite助手&quot; class=&quot;headerlink&quot; title=&quot;技术文章一键分发：openWrite助手&quot;&gt;&lt;/a&gt;技术文章一键分发：openWrite助手&lt;/h2&gt;&lt;p&gt;安全认证简书、博客园、知乎、开源中国、掘金、SegmentFault等平台，一键群发文章。&lt;/p&gt;
&lt;p&gt;免费版只有有限的分发次数，但是为了效率，这点费用还是很值得的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/efficiency-tools-chrome-extension/open-write.jpg&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="效率" scheme="https://suncle.me/categories/%E6%95%88%E7%8E%87/"/>
    
    
    <category term="工具" scheme="https://suncle.me/tags/%E5%B7%A5%E5%85%B7/"/>
    
    <category term="chrome" scheme="https://suncle.me/tags/chrome/"/>
    
  </entry>
  
  <entry>
    <title>元旦去峨眉山吧，人间值得</title>
    <link href="https://suncle.me/posts/2372450090/"/>
    <id>https://suncle.me/posts/2372450090/</id>
    <published>2020-10-26T19:30:14.000Z</published>
    <updated>2023-09-03T14:31:24.481Z</updated>
    
    <content type="html"><![CDATA[<p>每年到了10月底11月初的时候，就会开始计划一年一度的大学室友聚会。</p><p>前几年关于目的地是没什么争议的，因为其中一个舍友，人称装逼王的<code>旭云</code>在成都电子科技大学读研究生，所以每年都是在成都以及周边聚聚，比如重庆，都江堰之类的。</p><p>今年比较特殊，装逼王要毕业了，时间很充裕，去哪儿都行。四人小团队第一次遇到在哪个城市聚会的问题。可能很多小伙伴元旦也开始计划元旦出行，但是又不知道去哪儿。那可以看下我们的决策方法，应该有一些借鉴意义。</p><h3 id="定决策方案"><a href="#定决策方案" class="headerlink" title="定决策方案"></a>定决策方案</h3><p>既然不知道干什么那就roll一下</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-1.jpg"></p><span id="more"></span><h3 id="定聚会目的"><a href="#定聚会目的" class="headerlink" title="定聚会目的"></a>定聚会目的</h3><p>一年没见了，检查下大家伙有没有啥变化</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-2.jpg"></p><h3 id="定目的省份"><a href="#定目的省份" class="headerlink" title="定目的省份"></a>定目的省份</h3><p>每人报3个想去的省份，然后从里面再roll。说出每个人的想法之后才知道还可以这么好玩？打猎的都有，牛逼普拉斯。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-3.jpg"></p><p>每个人都报完了之后，就有了以下这些城市了，吉林有两个人说了，所以就算2票</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-4.jpg"></p><p>见证奇迹的时刻到了，什么？是新疆？</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-5.jpg"></p><p>不对啊，喀什不是疫情很严重嘛，哥儿几个作为爱惜生命立志为国家工作50年的N好青年，那肯定不能身陷险境啊。那么就排除掉疫情高风险区，重新roll，这就出结果了。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-6.jpg"></p><p>吉林，不对啊，吉林好像也是刚刚结束疫情，再考虑考虑。不过roll出来了还有效力的，得让大家表态了。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-7.jpg"></p><p>好，果断决定不去了，又得重新roll。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-8.jpg"></p><p>恩，是四川，一个熟悉的城市，虽然已经去过多次，但还是全票通过。</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-9.jpg"></p><p>毕竟，四川吃的好，喝的好，景色也好。确实没啥理由不去。</p><p>到这里，省份就定了，那么接下来需要定城市了。</p><h3 id="定目的城市"><a href="#定目的城市" class="headerlink" title="定目的城市"></a>定目的城市</h3><p>成都和都江堰群之前一次聚会去过，那么剩下可以选的有乐山（峨眉山）、色达、九寨沟，先看下距离</p><p><img src="https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-10.jpg"></p><p>元旦3天小假，在四川那样的地形情况下，直线距离200公里以上的都不太现实，用在路上的时间太久。那就选了乐山-峨眉山这条线。前段时间看乐山有不少特色美食，峨眉山也可以去爬一爬。</p><p>那就这么愉快的决定了。</p><hr><p>最后，目标真的能决定一切，自从将以后的方向定位成职场方向之后，对于摄影、户外的兴趣就相对应的下降了。来广州的几个月里面，没有去过一次白云山，也就不奇怪了，更多的喜欢休闲的方式。比如这次的聚会目的地也都是自带休闲属性。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;每年到了10月底11月初的时候，就会开始计划一年一度的大学室友聚会。&lt;/p&gt;
&lt;p&gt;前几年关于目的地是没什么争议的，因为其中一个舍友，人称装逼王的&lt;code&gt;旭云&lt;/code&gt;在成都电子科技大学读研究生，所以每年都是在成都以及周边聚聚，比如重庆，都江堰之类的。&lt;/p&gt;
&lt;p&gt;今年比较特殊，装逼王要毕业了，时间很充裕，去哪儿都行。四人小团队第一次遇到在哪个城市聚会的问题。可能很多小伙伴元旦也开始计划元旦出行，但是又不知道去哪儿。那可以看下我们的决策方法，应该有一些借鉴意义。&lt;/p&gt;
&lt;h3 id=&quot;定决策方案&quot;&gt;&lt;a href=&quot;#定决策方案&quot; class=&quot;headerlink&quot; title=&quot;定决策方案&quot;&gt;&lt;/a&gt;定决策方案&lt;/h3&gt;&lt;p&gt;既然不知道干什么那就roll一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://suncle-public.oss-cn-shenzhen.aliyuncs.com/pics/article/2021-new-year-day/leshan-planning-1.jpg&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="随笔" scheme="https://suncle.me/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="旅游" scheme="https://suncle.me/tags/%E6%97%85%E6%B8%B8/"/>
    
  </entry>
  
  <entry>
    <title>git禁止在master分支push和commit</title>
    <link href="https://suncle.me/posts/3750596276/"/>
    <id>https://suncle.me/posts/3750596276/</id>
    <published>2020-09-25T13:25:35.000Z</published>
    <updated>2023-09-03T14:31:24.473Z</updated>
    
    <content type="html"><![CDATA[<p>作为管理者，在远端将master分支设为保护分支，可以从根源上杜绝直接推送到master的问题。dev分支同理。</p><p>作为开发者，在本地的git hook中加配置可以做到在commit和push操作时做对应的检查</p><span id="more"></span><h3 id="禁止在master分支上Commit"><a href="#禁止在master分支上Commit" class="headerlink" title="禁止在master分支上Commit"></a>禁止在master分支上Commit</h3><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token assign-left variable">protected_branch</span><span class="token operator">=</span><span class="token string">'master'</span><span class="token assign-left variable">current_branch</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token function">git</span> rev-parse <span class="token parameter variable">--symbolic</span> --abbrev-ref HEAD<span class="token variable">)</span></span><span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$protected_branch</span>"</span> <span class="token operator">==</span> <span class="token string">"<span class="token variable">$current_branch</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>  <span class="token builtin class-name">echo</span> <span class="token string">".git/hooks: Do not commit to <span class="token variable">$current_branch</span> branch"</span>  <span class="token builtin class-name">exit</span> <span class="token number">1</span><span class="token keyword">fi</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="在master分支上Commit时提示"><a href="#在master分支上Commit时提示" class="headerlink" title="在master分支上Commit时提示"></a>在master分支上Commit时提示</h3><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token assign-left variable">protected_branch</span><span class="token operator">=</span><span class="token string">'master'</span><span class="token assign-left variable">current_branch</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token function">git</span> rev-parse <span class="token parameter variable">--symbolic</span> --abbrev-ref HEAD<span class="token variable">)</span></span><span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$protected_branch</span>"</span> <span class="token operator">==</span> <span class="token string">"<span class="token variable">$current_branch</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>  <span class="token builtin class-name">read</span> <span class="token parameter variable">-p</span> <span class="token string">"You're about to commit to master, is that what you intended? [y|n] "</span> <span class="token parameter variable">-n</span> <span class="token number">1</span> <span class="token parameter variable">-r</span> <span class="token operator">&lt;</span>/dev/tty  <span class="token builtin class-name">echo</span>  <span class="token keyword">if</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token environment constant">$REPLY</span>"</span> <span class="token operator">|</span> <span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">'^[Yy]$'</span> <span class="token operator">></span>/dev/null<span class="token punctuation">;</span> <span class="token keyword">then</span>    <span class="token builtin class-name">exit</span> <span class="token number">0</span> <span class="token comment"># commit will execute</span>  <span class="token keyword">fi</span>  <span class="token builtin class-name">exit</span> <span class="token number">1</span> <span class="token comment"># commit will not execute</span><span class="token keyword">fi</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="禁止推送到master分支"><a href="#禁止推送到master分支" class="headerlink" title="禁止推送到master分支"></a>禁止推送到master分支</h3><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token assign-left variable">protected_branch</span><span class="token operator">=</span><span class="token string">'master'</span><span class="token assign-left variable">remote_branch_prefix</span><span class="token operator">=</span><span class="token string">"refs/heads/"</span><span class="token assign-left variable">protected_remote_branch</span><span class="token operator">=</span><span class="token variable">$remote_branch_prefix</span><span class="token variable">$protected_branch</span><span class="token keyword">while</span> <span class="token builtin class-name">read</span> local_ref local_sha remote_ref remote_sha<span class="token keyword">do</span><span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$protected_remote_branch</span>"</span> <span class="token operator">==</span> <span class="token string">"<span class="token variable">$remote_ref</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span><span class="token builtin class-name">echo</span> <span class="token string">".git/hooks: Do not commit to <span class="token variable">$protected_branch</span> branch"</span>  <span class="token builtin class-name">exit</span> <span class="token number">1</span><span class="token keyword">fi</span><span class="token keyword">done</span><span class="token builtin class-name">exit</span> <span class="token number">0</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="推送到master分支时提示"><a href="#推送到master分支时提示" class="headerlink" title="推送到master分支时提示"></a>推送到master分支时提示</h3><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token assign-left variable">protected_branch</span><span class="token operator">=</span><span class="token string">'master'</span><span class="token assign-left variable">remote_branch_prefix</span><span class="token operator">=</span><span class="token string">"refs/heads/"</span><span class="token assign-left variable">protected_remote_branch</span><span class="token operator">=</span><span class="token variable">$remote_branch_prefix</span><span class="token variable">$protected_branch</span><span class="token keyword">while</span> <span class="token builtin class-name">read</span> local_ref local_sha remote_ref remote_sha<span class="token keyword">do</span><span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$protected_remote_branch</span>"</span> <span class="token operator">==</span> <span class="token string">"<span class="token variable">$remote_ref</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span><span class="token builtin class-name">read</span> <span class="token parameter variable">-p</span> <span class="token string">"You're about to push master, is that what you intended? [y|n] "</span> <span class="token parameter variable">-n</span> <span class="token number">1</span> <span class="token parameter variable">-r</span> <span class="token operator">&lt;</span> /dev/tty    <span class="token builtin class-name">echo</span>    <span class="token keyword">if</span> <span class="token builtin class-name">echo</span> <span class="token environment constant">$REPLY</span> <span class="token operator">|</span> <span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">'^[Yy]$'</span> <span class="token operator">></span> /dev/null    <span class="token keyword">then</span>        <span class="token builtin class-name">exit</span> <span class="token number">0</span> <span class="token comment"># push will execute</span>    <span class="token keyword">fi</span>    <span class="token builtin class-name">exit</span> <span class="token number">1</span> <span class="token comment"># push will not execute</span><span class="token keyword">fi</span><span class="token keyword">done</span><span class="token builtin class-name">exit</span> <span class="token number">0</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><blockquote><p>为什么需要循环读取？因为git一次可以push多个分支</p></blockquote><h3 id="推送时如果commit消息包含WIP则禁止推送"><a href="#推送时如果commit消息包含WIP则禁止推送" class="headerlink" title="推送时如果commit消息包含WIP则禁止推送"></a>推送时如果commit消息包含WIP则禁止推送</h3><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token assign-left variable">z40</span><span class="token operator">=</span>0000000000000000000000000000000000000000<span class="token keyword">while</span> <span class="token builtin class-name">read</span> local_ref local_sha remote_ref remote_sha<span class="token punctuation">;</span> <span class="token keyword">do</span>  <span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$local_sha</span>"</span> <span class="token operator">=</span> <span class="token variable">$z40</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>    <span class="token comment"># Handle delete</span>    <span class="token builtin class-name">:</span>  <span class="token keyword">else</span>    <span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$remote_sha</span>"</span> <span class="token operator">=</span> <span class="token variable">$z40</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>      <span class="token comment"># New branch, examine all commits</span>      <span class="token assign-left variable">range</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$local_sha</span>"</span>    <span class="token keyword">else</span>      <span class="token comment"># Update to existing branch, examine new commits</span>      <span class="token assign-left variable">range</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$remote_sha</span>..<span class="token variable">$local_sha</span>"</span>    <span class="token keyword">fi</span>    <span class="token comment"># Check for WIP commit</span>    <span class="token assign-left variable">commit</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token function">git</span> rev-list <span class="token parameter variable">-n</span> <span class="token number">1</span> <span class="token parameter variable">--grep</span> <span class="token string">'^feat: WIP'</span> <span class="token string">"<span class="token variable">$range</span>"</span><span class="token variable">)</span></span>    <span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token parameter variable">-n</span> <span class="token string">"<span class="token variable">$commit</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>      <span class="token builtin class-name">echo</span> <span class="token operator">></span><span class="token file-descriptor important">&amp;2</span> <span class="token string">"Found WIP commit in <span class="token variable">$local_ref</span>, not pushing"</span>      <span class="token builtin class-name">exit</span> <span class="token number">1</span>    <span class="token keyword">fi</span>  <span class="token keyword">fi</span><span class="token keyword">done</span><span class="token builtin class-name">exit</span> <span class="token number">0</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>这时候，你可能会发现，你每一次clone项目之后都需要手动把commit和push的hook文件丢在<code>.git/hooks</code>目录下，是不是觉得不方便？别着急，有办法，我们可以让所有项目的hook操作统一到一个自定义目录中。</p><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">mkdir</span> ~/.git-hooks<span class="token comment"># 创建一个存放hook的自定义目录</span><span class="token function">git</span> config <span class="token parameter variable">--global</span> core.hookspath ~/.git-hooks<span class="token comment"># 更改git配置指定hook目录到自定义，先别着急执行，往后看</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><p>这样就可以实现统一管理所有项目的hooks操作了</p><blockquote><p>core.hookspath配置需要git版本在v2.9以上才行</p></blockquote><p>然后，你会觉得全局统一管理也太霸道了吧，比如说，公司的项目可以统一一套hooks操作，但是我不想把这一套hooks应用于个人github的项目啊。也就是说你需要在不同的目录下面执行不同的hooks操作，那么该怎么办呢？还是有办法：git配置是可以根据不同目录使用不同配置的</p><p>比如我只想统一管理<code>~/yy</code>目录下的所有项目，那就修改<code>~/.gitconfig</code>文件加入以下内容</p><figure><div class="code-wrapper"><pre class="line-numbers language-ini" data-language="ini"><code class="language-ini"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">includeIf "gitdir:~/yy/"</span><span class="token punctuation">]</span></span>    <span class="token key attr-name">path</span> <span class="token punctuation">=</span> <span class="token value attr-value">.gitconfig-yy</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><p>然后增加一个<code>~/.gitconfig-yy</code>文件，在这个文件中加入yy目录下面的独有配置</p><figure><div class="code-wrapper"><pre class="line-numbers language-ini" data-language="ini"><code class="language-ini"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">core</span><span class="token punctuation">]</span></span>    <span class="token key attr-name">hookspath</span> <span class="token punctuation">=</span> <span class="token value attr-value">~/.git-hooks</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><hr><p>参考：</p><ol><li><p><a href="https://stackoverflow.com/questions/42455506/in-pre-push-hook-get-git-push-command-full-content">https://stackoverflow.com/questions/42455506/in-pre-push-hook-get-git-push-command-full-content</a></p></li><li><p><a href="https://www.geek-share.com/detail/2776108340.html">https://www.geek-share.com/detail/2776108340.html</a></p></li></ol><p>我的博客即将同步至腾讯云+社区，邀请大家一同入驻：<a href="https://cloud.tencent.com/developer/support-plan?invite_code=38qhpnqeksg0g">https://cloud.tencent.com/developer/support-plan?invite_code=38qhpnqeksg0g</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;作为管理者，在远端将master分支设为保护分支，可以从根源上杜绝直接推送到master的问题。dev分支同理。&lt;/p&gt;
&lt;p&gt;作为开发者，在本地的git hook中加配置可以做到在commit和push操作时做对应的检查&lt;/p&gt;</summary>
    
    
    
    <category term="效率" scheme="https://suncle.me/categories/%E6%95%88%E7%8E%87/"/>
    
    
    <category term="hook" scheme="https://suncle.me/tags/hook/"/>
    
    <category term="git" scheme="https://suncle.me/tags/git/"/>
    
  </entry>
  
  <entry>
    <title>你压力都这么大了，怎么还睡不好？</title>
    <link href="https://suncle.me/posts/535377457/"/>
    <id>https://suncle.me/posts/535377457/</id>
    <published>2020-09-07T02:22:53.000Z</published>
    <updated>2023-09-03T14:31:24.481Z</updated>
    
    <content type="html"><![CDATA[<p>在这个社会里面，每一个人压力都不小。特别是在大城市的小伙伴们，加班是常态。</p><p>面对着高消费和高房价这两座大山，随之而来的，就是焦虑，进而失眠。按照这个逻辑，应该是压力越大才会越容易失眠啊。</p><p>但我却不这么认为。你压力都这么大了，怎么还睡不好？难道不是一回到家倒头就睡的嘛，睡眠效率是100%才对啊，应该是远高于普通人的90%啊。</p><p>可是你为什么还是失眠了呢？</p><span id="more"></span><p>而我只有一种情况会失眠，那就是窗户没关，蚊子进来了，而我却以为关了窗户，但其实我只是拉了窗帘。</p><p>其他所有的时间我都能睡得很香，效率也很高，第二天也不会感觉到困。</p><p>记得今年5月走318线的时候，在新都桥露营营地的小河边扎下帐篷，海拔3300米，被子也不太够，其他人也都没有睡着，我还是睡得很香，即使外面河水哗啦啦的响。</p><h1 id="你越在乎睡眠，反倒越容易失眠"><a href="#你越在乎睡眠，反倒越容易失眠" class="headerlink" title="你越在乎睡眠，反倒越容易失眠"></a>你越在乎睡眠，反倒越容易失眠</h1><p>不要老是想今晚没睡够怎么办，躺下睡不着，我是不是身体出什么问题了？</p><p>别人都能睡八九个小时，我自己只能睡五六个小时，怎么办？</p><p>自己每天都睡不够，影响工作、学习怎么办？今天早上3点就醒过来睡不着了，怎么办？</p><p>这听起来有些反常理： 我对睡眠有要求，反而让我睡不好？</p><p>你仔细想想，是不是这样。</p><p>你心里面顾虑的事情太多， 顾虑工作，顾虑学习，顾虑考试，顾虑和别人的约会。</p><p>每次到了考试前，拼命的让自己睡觉，却失眠了，考试结果一塌糊涂。</p><p>然后就陷入了死循环。越担心睡不好结果越睡不好，越睡不好越担心。</p><p>要解开这个死结，需要降低自己对睡眠的短期预期。</p><p>就好像财富一样，不要老想着去挣钱，要去理解财富创造的规律，做正确的事，财富自然会发生。</p><h1 id="破除3个睡眠误区"><a href="#破除3个睡眠误区" class="headerlink" title="破除3个睡眠误区"></a>破除3个睡眠误区</h1><p><strong>误区一：每天必须睡够8小时。</strong> 高质量的睡眠不需要每晚睡够8小时，很多人觉得自己没睡好，只是没睡到自己设定的8小时而已。</p><p>有时候早上6点多醒了，好像也没有很困，但是算了下时间，离平时起床还有一个小时，这时候就很担心，这提前醒了可咋办啊，今天还有这么多事情要忙，又没睡好，好烦啊。</p><p>不过大可不必有这样的担心，8小时只是人这个物种每天晚上睡眠的一个平均数而已。</p><p>每个人睡眠时间的长短都是不一样的，没必要非得达到8小时，有自己的睡眠节奏就行了。</p><p><strong>误区二：喝酒能改善睡眠。</strong> 酒精虽然能助眠但是并不能提升睡眠效率，即使你喝了酒睡着了，第二天精神就真的会变好？多数情况下也并没有。</p><p>去年有一段时间，晚上下班我经常会去买两瓶RIO，有水蜜桃味白兰地的，也有蓝玫瑰味威士忌的。</p><p>但是我喝酒只是享受微醺的感觉，并不是为了助眠。喝完之后入睡确实很快。</p><p>第二天我并没有觉得精神比不喝酒更好，甚至有一些下降，白天出现了一丝丝困。</p><p>其实酒精会打断你的深度睡眠，酒喝多了，第二天会觉得全身疲乏，精神会更差。</p><p><strong>误区三：晚上没睡好中午还有午睡。</strong> 你越是睡不好越是不能午睡。在互联网公司上班，绝大多数同事都是有睡午觉的习惯的。可能是因为前一晚没睡好，需要午睡补一补。</p><p>也可能是因为下午还要工作，强迫自己休息，进而提高效率。</p><p>如果是为了提高下午的工作效率，那中午可以眯个十分钟。</p><p>但是如果是前一晚没睡好，那么并不建议中午休息。因为中午休息了，晚上又容易睡不着了，这样就进入了恶循环。</p><p>另外午睡不能超过30分钟，超过30分钟就容易进入深度睡眠。这时候被闹铃叫醒，只会觉得更困乏。</p><h1 id="提高睡眠效率的实用方法"><a href="#提高睡眠效率的实用方法" class="headerlink" title="提高睡眠效率的实用方法"></a>提高睡眠效率的实用方法</h1><p><strong>了解自己大概每天需要睡几个小时。</strong> 每个人每天需要的睡眠时长不一样。撒切尔夫人一天只需要睡四五个小时，而网球名将费德勒每天需要睡10个小时。</p><p>那么怎么找到自己的睡眠时长呢？有一个非常简单的方法。</p><blockquote><p>你可以找一个周天，完全不需要工作，按照喜欢的方式安排一定的事情，比如爬山、约会、看电影。到晚上觉得困的时候就睡觉，并且记录一下时间，第二天睡到自然醒，就可以知道自己需要的睡眠时间是几个小时了，也知道了几点开始睡觉，几点起床了。</p></blockquote><p>按照这个方法，我自己每天需要睡6个半小时，第二天就可以精力充沛了。</p><p>知道自己的睡眠时长和规律之后就按照这个节奏去实践吧。</p><p>而且早上一定要在同一时间点起床，不能赖床，立马跳到地上。晚上也是，不困就不上床，困了才去床上，去了就立马睡。</p><p>另外每天工作再忙，都找点时间出去晒晒太阳，整天待在恒温大楼里面，只能接触到人造光，蓝色光的成分太少。</p><p>我的IT同事们，愿意吃完饭去溜一圈的人寥寥无几。</p><p>中午尽量不睡觉，把困意留到晚上，就这样坚持下去，一定能收获好的睡眠。</p><p>当然最最最重要的一点是，睡眠，真的不需要太刻意。</p><p>好吧，到这里，你离好的睡眠应该更近了一步。那就慢慢改善吧，像我一样，到哪儿都能倒头就睡，睡眠效率100%。</p><p>毕竟，人生的失控，始于逐步推迟的睡眠。而人生的进步，始于高效率的睡眠。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;在这个社会里面，每一个人压力都不小。特别是在大城市的小伙伴们，加班是常态。&lt;/p&gt;
&lt;p&gt;面对着高消费和高房价这两座大山，随之而来的，就是焦虑，进而失眠。按照这个逻辑，应该是压力越大才会越容易失眠啊。&lt;/p&gt;
&lt;p&gt;但我却不这么认为。你压力都这么大了，怎么还睡不好？难道不是一回到家倒头就睡的嘛，睡眠效率是100%才对啊，应该是远高于普通人的90%啊。&lt;/p&gt;
&lt;p&gt;可是你为什么还是失眠了呢？&lt;/p&gt;</summary>
    
    
    
    <category term="随笔" scheme="https://suncle.me/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="职场" scheme="https://suncle.me/tags/%E8%81%8C%E5%9C%BA/"/>
    
    <category term="睡眠" scheme="https://suncle.me/tags/%E7%9D%A1%E7%9C%A0/"/>
    
  </entry>
  
  <entry>
    <title>程序员小哥教你秋招拿大厂offer</title>
    <link href="https://suncle.me/posts/4045852110/"/>
    <id>https://suncle.me/posts/4045852110/</id>
    <published>2020-08-30T16:35:30.000Z</published>
    <updated>2023-09-03T14:31:24.481Z</updated>
    
    <content type="html"><![CDATA[<p>快要到秋招了，对于应届生来说，秋招是一个特别重要的机会。对于社招同学来说，金九银十也是一个很好的跳槽窗口。</p><p>而我呢，因为是从上海到广州工作，就没有提前先把工作定下来。刚好也趁这个机会出去旅游了两个月。</p><p>旅游结束，等到要开始找工作的时候，发现很多知识点也都忘记的差不多了。说是从0开始准备面试似乎也不是那么过分。</p><p>但最终，大概花了3周时间，最终面试通过找到了一个薪资还不错的工作，入职广州仅有的几家大厂之一的YY欢聚时代，任职高级后端开发工程师。</p><span id="more"></span><h1 id="没有时间规划就没有offer"><a href="#没有时间规划就没有offer" class="headerlink" title="没有时间规划就没有offer"></a>没有时间规划就没有offer</h1><p>时间规划特别重要，前期你需要做的就是需要指定一个时间计划，我给自己定的面试准备时间是两周，充分准备之后才会投递简历。</p><p>一般来说，整个面试准备过程，主要分为4个部分，分别是准备简历，回顾曾经做过的项目，复习各种计算机相关理论和技术栈，还有刷算法题。</p><p>那我是怎么规划时间，将各个部分复习到位的呢？</p><p>首先，你要明白，你这次找工作是面向面试编程的。因此你得有一个意识，你不需要搞明白所有的东西，你只需要从繁杂的技术点里面剥离出面试中最可能被问到的每一个小点。</p><p>比如很多同学会花费大量时间，单独去复习自己日常使用的计算机语言。比如Python或者是Java，也可能是其他的。但其实这样性价比并没有多高。</p><p>一方面是因为你一定会刷算法题的，那么在刷算法题的时候语言相关的就顺带过一下就好了。另一方面是如果你真的要复习语言，那么只需要复习大概率会被问到的知识点就好了，并不需要单独时间。比如只需要复习Python的装饰器应用，Java的并发库实现，还有各个语言的垃圾回收机制等等。</p><p>这一步就是在做减法，给你的面试过程减负，有了这个意识，你就会觉得整个过程很轻松。</p><p>然后你需要合理的分配时间，我采用的是3+4+7的比例来分配这14天时间的。</p><p>3就是3天，用来准备简历还有回顾曾经的项目。简历要写得数据化而不是泛化，数据给面试官的冲击远远比一大堆笼统话语来得更直接。</p><p>4就是4天，用来复习各种计算机相关理论和技术栈。大后端的范畴内，考的知识点很泛，需要熟悉的方方面面很多。</p><p>比如运维开发相关，以docker+k8s为例来复习持续集成自动化部署相关。</p><p>比如大数据相关，搞清楚Hadoop生态圈主要组件的工作原理以及流程，当然也不要仅仅局限在这个圈。</p><p>此外，还有常用消息队列、关系型数据库、非关系型数据库、分布式设计的各种理论，熔断、限流、降级、秒杀，CAP理论等等。</p><p>最后的7就是7天，用来刷算法题。大厂面试必问算法，这是所有技术人员逃不过的关卡，也是大多数同学的最头疼的事情。</p><p>那么怎样才能高效顺利的攻破算法这一关呢？</p><h1 id="高效刷算法题的秘诀"><a href="#高效刷算法题的秘诀" class="headerlink" title="高效刷算法题的秘诀"></a>高效刷算法题的秘诀</h1><p>我上家公司的领导是传说中有着拉之微笑的拉总。毕竟是再惠公司唯一后端专家，技术能力没得说，广度如宇宙，深度似海洋，后端技术栈就没有拉总不知道的。</p><p>但我也经常看到拉总在LeetCode上刷题保持手感，最终拉总凭借自身强大的技术能力，再加上不断刷题保持算法的手感，成功入职哔哩哔哩。</p><p>大牛都需要刷题，更何况普通程序员。</p><p>现在大家基本都是在LeetCode上刷题，我们也只需要用好LeetCode这一个平台就够了。</p><p>开始之前，我们还是厘清我们的目的，我们是为了面试刷算法题，而不是刷着玩，也不会为了刷算法题在LeetCode上打榜争排名。</p><p>那么最高效的刷题方式还是分类刷题最好。你可能会问了，为什么不是把LeetCode一题一题的刷完呢？</p><p>首先，你并没有这么多的时间，LeetCode目前有1700多道算法题，要刷完需要大量的时间，但面试中并不会都问啊，那么我们就需要有针对性的去刷题。</p><p>此外，1700多道题目中有大量重复类型的题目。同类题目中只需要彻底搞清楚一稿题目的，举一反三就能解决这一类型的全部问题。</p><p>你有可能会问，这么多题目，我怎么知道哪些题目是一类的呢？别着急，已经有大牛为我们分门别类的整理好了LeetCode的刷题指南。</p><p>我自己刷过并且感觉有效的是github上的一个LeetCode题解仓库：</p><ul><li><a href="https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md">https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md</a></li></ul><p>然后按照分类，我用了一周的时间，针对性的刷了40多道各个类型的题目。这个仓库给出的是Java语言的题解。我最熟悉的是Python，自然用Python刷题了。</p><p>附上我的Python解法：公众号回复【刷算法题】，可以获得Python解法的代码包。</p><h1 id="不会不要慌，大胆给出你的猜测"><a href="#不会不要慌，大胆给出你的猜测" class="headerlink" title="不会不要慌，大胆给出你的猜测"></a>不会不要慌，大胆给出你的猜测</h1><p>分门别类的刷完题目之后，你的心里应该就更有底了，但是面试嘛，总有一些你没有准备到的技术点，总会被问到一些你完全不知道的角落。</p><p>遇到这种情况完全不用慌，大胆的告诉面试官：我不会，但是我猜是xxx这样的。</p><p>而且，只要你大胆的给出自己的猜测，只要是基于你的理解，基于你所掌握的基础理论，我相信你是可以猜到八九不离十的。</p><p>而且即使真的错了，面试官也可以从中看到你的自信，也能了解到你面向未知问题的分析思路。而这些也正是未来职场上真正需要的能力。</p><p>从我以前作为面试官的经历来看，我不喜欢面试者什么问题都能答得很流利，我更喜欢有一些关于未知的探讨，这样才能考察出面试者真正的底层能力。</p><p>从我这次作为面试者的经历来看，显然，我的大胆猜测给了面试官很好的印象。</p><hr><p>最后，这篇文章是从个人号Suncle迁移到职场亮哥这个企业号的第一篇文章，感谢之前的朋友的不离不弃，迁移过程中没有一个人取关，谢谢大家。</p><p>后续会有一篇文章解释为什么会去做这样的迁移，背后究竟经历了怎样的深思熟虑，又是否有职业规划上的方向性改变呢？公众号的定位又是什么呢？尽情期待吧！</p><p>还有新鲜出炉的留言功能，作为即将成长起来的头部大号的第一批用户的你们，请尽情的留言吧，哈哈哈！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;快要到秋招了，对于应届生来说，秋招是一个特别重要的机会。对于社招同学来说，金九银十也是一个很好的跳槽窗口。&lt;/p&gt;
&lt;p&gt;而我呢，因为是从上海到广州工作，就没有提前先把工作定下来。刚好也趁这个机会出去旅游了两个月。&lt;/p&gt;
&lt;p&gt;旅游结束，等到要开始找工作的时候，发现很多知识点也都忘记的差不多了。说是从0开始准备面试似乎也不是那么过分。&lt;/p&gt;
&lt;p&gt;但最终，大概花了3周时间，最终面试通过找到了一个薪资还不错的工作，入职广州仅有的几家大厂之一的YY欢聚时代，任职高级后端开发工程师。&lt;/p&gt;</summary>
    
    
    
    <category term="随笔" scheme="https://suncle.me/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="面试" scheme="https://suncle.me/tags/%E9%9D%A2%E8%AF%95/"/>
    
    <category term="offer" scheme="https://suncle.me/tags/offer/"/>
    
  </entry>
  
</feed>
