<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>西西弗</title>
  
  
  <link href="https://noname007.github.io/atom.xml" rel="self"/>
  
  <link href="https://noname007.github.io/"/>
  <updated>2025-06-08T16:20:37.788Z</updated>
  <id>https://noname007.github.io/</id>
  
  <author>
    <name>soul11201</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>RBAC 权限系统落地经验要点总结 - 1</title>
    <link href="https://noname007.github.io/2025/06/08/rbac.html"/>
    <id>https://noname007.github.io/2025/06/08/rbac.html</id>
    <published>2025-06-08T15:21:14.000Z</published>
    <updated>2025-06-08T16:20:37.788Z</updated>
    
    <content type="html"><![CDATA[<h2 id="一些基础概念">一些基础概念</h2><p>SSD 静态分离（会计、出纳设计两个角色） DSD动态分离（一个人(账号)即使被同时分配了出纳、会计两个角色，但一个会话期间只能使用一个角色）</p><figure><img src="/assets/imgs/2025060903.png" alt="alt text" /><figcaption aria-hidden="true">alt text</figcaption></figure><p>Attr-RBAC: 基于属性的 RBAC(业务员只能查看其维护的客户信息，业务员组长能看所有组员维护的客户信息)</p><p>Rule-RBAC: 基于规则的 RBAC (系统只能在早上 9:00- 18:00才允许业务员进行业务操作)</p><h2 id="一些重要的点">一些重要的点</h2><h3 id="能看到就有对应的权限">能看到就有对应的权限</h3><p>能看到页面索引连接，就能读取索引下面的数据不同的人同一个页面看到的可能数据不一样能看到操作按钮，就有对应操作的权限</p><h3 id="关注点分离">关注点分离</h3><p>产研团队要注意在系统设计落地过程中各个角色的职责边界范围</p><figure><img src="/assets/imgs/2025060902.png" alt="alt text" /><figcaption aria-hidden="true">alt text</figcaption></figure><p>RD、FE 关注点的差异 FE(Frontend): 关注权限 对 页面显示的影响和控制RD(Backend): 关注权限 对 数据流的影响和控制</p><h3 id="角色说明表角色---权限矩阵表权限-资源表">角色说明表、角色 -权限矩阵表、权限-资源表</h3><p>从 DDD的角度来说，每个角色、权限、资源点都是统一语言，可以让不同角色在不同层分别有清晰、一致的语义理解。角色 - 权限矩阵表：GUI 系统可以细分为页面、操作、数据三个维度的权限</p><figure><img src="/assets/imgs/2025060901.png" alt="alt text" /><figcaption aria-hidden="true">alt text</figcaption></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;一些基础概念&quot;&gt;一些基础概念&lt;/h2&gt;
&lt;p&gt;SSD 静态分离（会计、出纳设计两个角色） DSD
动态分离（一个人(账号)即使被同时分配了出纳、会计两个角色，但一个会话期间只能使用一个角色）&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/assets/imgs/</summary>
      
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="RBAC" scheme="https://noname007.github.io/tags/RBAC/"/>
    
  </entry>
  
  <entry>
    <title>如何为 Git 设置代理连接 GitHub</title>
    <link href="https://noname007.github.io/2025/06/04/git-proxy.html"/>
    <id>https://noname007.github.io/2025/06/04/git-proxy.html</id>
    <published>2025-06-04T09:00:33.000Z</published>
    <updated>2025-06-08T09:22:03.018Z</updated>
    
    <content type="html"><![CDATA[<p>嗨，朋友们！用Git和GitHub的时候，是不是老遇到连不上或者速度特别慢的问题？别担心，这多半是网络搞的鬼！简单来说，给Git设置个代理就能帮你轻松搞定这些烦恼，就像给你的网络找了个专属“司机”一样，特别方便！为啥要给Git设代理呢？</p><p>原因嘛，就那么几个，但都挺有用：</p><pre><code class="hljs">轻松突破限制！ 有些公司或学校的网络可能会限制你访问GitHub这些网站。代理呢，就像一条“秘密通道”，能帮你绕过这些限制。速度快起来！ 有时候直接连接网络可能不怎么给力，通过代理服务器，你的连接会更快、更稳定。多一份安全！ 代理还能给你的网络多加一层保护，让你用起来更安心一些。</code></pre><p>Git代理设置，几种方法随便你选！</p><p>你可以选择让所有Git操作都通过代理走（这叫全局设置），或者只让某个特定的仓库用代理（这叫局部设置）。1. 所有HTTP/HTTPS操作都走代理？没问题！</p><p>如果你希望所有的Git操作都通过代理进行，就像给它开了个“通用通道”：</p><h1 id="如果是http代理就这样写">如果是HTTP代理，就这样写！</h1><p>git config --global http.proxy http://<proxy_host>:<proxy_port></p><h1 id="如果是https代理也这么做">如果是HTTPS代理，也这么做！</h1><p>git config --global https.proxy https://<proxy_host>:<proxy_port></p><p>举个例子，给你看看：</p><p>git config --global http.proxy http://192.168.1.100:8080 git config--global https.proxy https://192.168.1.100:8080</p><p>如果你的代理需要用户名和密码，直接把它们写进URL里就行，很简单！ 2.不想用代理了？分分钟就能取消！</p><p>如果你不再需要代理了，随时可以把它关掉：</p><p>git config --global --unset http.proxy git config --global --unsethttps.proxy</p><ol start="3" type="1"><li>只给GitHub开“小灶”？当然可以！</li></ol><p>如果你不想所有的网络流量都走代理，只希望连接GitHub的时候才用代理，那也没问题！你可以这样设置：</p><p>git config --global url."https://github.com/".proxyhttps://<proxy_host>:<proxy_port></p><p>再给你个例子：</p><p>git config --global url."https://github.com/".proxyhttps://192.168.1.100:8080</p><p>这样一来，就只会在你访问https://github.com/的时候才走代理，是不是很方便？4. SSH连接GitHub也要代理？得另外设哦！</p><p>如果你的Git仓库是通过SSH方式连接GitHub的（就是那种git@github.com:user/repo.git格式的链接），那上面的HTTP/HTTPS代理可就不管用了！你得去改一下SSH的配置文件，通常在~/.ssh/config里：</p><p>Host github.com Hostname ssh.github.com Port 443 ProxyCommand connect-H <proxy_host>:<proxy_port> %h %p</p><p>比如这样（Sock 5 代理： -X 5 ）：</p><p>Host github.com Hostname ssh.github.com Port 443 ProxyCommand nc -X 5-x proxy_host :<proxy_port> %h %p</p><p>小提醒： nc 使用 openbsd-netcat。 gnu-netcat 可能无法正常工作</p><p>代理到底设好了没？来，咱们检查一下！</p><p>想知道代理到底有没有设置成功？来，敲下这个命令，一看就知道了：</p><p>git config --global -l</p><p>要是看到里面有http.proxy或者url."https://github.com/".proxy这些信息，那就恭喜你，代理已经上线啦！</p><p>要是SSH代理，就试试这个命令：</p><p>ssh -T git@github.com</p><p>如果弹出来“Hi <你的用户名>! You've successfullyauthenticated...”这样的消息，那就恭喜你，搞定啦！ 超棒的！如果ssh有问题，命令后面加上 -v 参数，可以根据输出的 debug信息,查看工作流程，并进行简单的问题排查</p><p>遇到点小麻烦？别慌，都是些小问题！</p><pre><code class="hljs">代理信息不对？ 朋友，仔细看看你填的代理服务器地址、端口和账号密码是不是正确的，别弄错了哦！HTTP和SSH搞混了？ 记住，HTTPS代理是管HTTPS连接的，SSH代理是管SSH连接的，这两种方式可不能混着用！防火墙捣乱？ 哎呀，是不是你的电脑防火墙太“尽职”了，把代理连接给拦住了？检查一下防火墙设置！Git版本太旧？ 有时候Git的版本太老也会出问题，更新到最新版本试试，说不定就好了。工具没装好？ 如果你是用SSH代理，记得确保你需要的那些代理工具（比如connect、netcat）都已经安装好了，而且能正常运行！</code></pre><p>只要你按照这些步骤来，相信很快就能把Git代理设置好，然后和GitHub顺畅地“交流”起来！祝你编码愉快，没有烦恼！</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;嗨，朋友们！用Git和GitHub的时候，是不是老遇到连不上或者速度特别慢的问题？别担心，这多半是网络搞的鬼！简单来说，给Git设置个代理就能帮你轻松搞定这些烦恼，就像给你的网络找了个专属“司机”一样，特别方便！
为啥要给Git设代理呢？&lt;/p&gt;
&lt;p&gt;原因嘛，就那么几个，</summary>
      
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="git" scheme="https://noname007.github.io/tags/git/"/>
    
  </entry>
  
  <entry>
    <title>项目管理-如何做复杂的协调工作</title>
    <link href="https://noname007.github.io/2025/05/20/project.html"/>
    <id>https://noname007.github.io/2025/05/20/project.html</id>
    <published>2025-05-20T09:22:37.000Z</published>
    <updated>2025-06-08T09:30:00.149Z</updated>
    
    <content type="html"><![CDATA[<p>《<ahref="https://mp.weixin.qq.com/s/GTninvY4fPZiXp3lTvc-mw">公众号首发位置</a>》</p><p>PMP 五大过程组(规划、启动、执行、监控、收尾)、十大知识领域(整合、范围、进度、质量、成本、资源、风险、干系人、沟通、采购)</p><p>49个子流程中，没有协调这个单独的模块，被分散开了。</p><h2 id="协调是什么呢">协调是什么呢？</h2><p>法约尔在从职能角度对管理进行定义时，从职能观的角度将，管理拆分为计划、组织、协调、指挥、控制若干部分。现在的一些管理学书籍将协调、指挥合并，叫领导。当前职场环境很少是平衡性矩阵组织结构，也是很多人说的有责无权，传声筒，也有些人叫微权力下的项目管理，其实就是把指挥的权利给下掉了😁。</p><h2 id="微权力情境下如何推进进展">微权力情境下如何推进进展？</h2><p>这个视频讲的还挺有意思。看这个视频还有些高效能人事七个习惯中提到的一些原则。以终为始，积极主动等</p><ol type="1"><li><p>明确目标倒排任务。原则太多就是没原则，关窍一定是简洁的，世界已经很复杂了，再弄一个更复杂的的方案关窍有什么意义呢？</p></li><li><p>广泛的收集整合前沿行业信息，做事最大的成本是信任成本，善于借势，人微不言轻</p></li><li><p>行动起来，万事已具备，行动的自己就是东风</p></li><li><p>梳理人际网络，找准关键事项关键节点上的关键干系人</p></li><li><p>千斤拨四两，不要犹豫徘徊，确定的做的事情，狮虎搏兔</p></li><li><p>龙卷风 把握主动权</p></li><li><p>不要本本主义，结果价值导向</p></li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;《&lt;a
href=&quot;https://mp.weixin.qq.com/s/GTninvY4fPZiXp3lTvc-mw&quot;&gt;公众号首发位置&lt;/a&gt;》&lt;/p&gt;
&lt;p&gt;PMP 五大过程组
(规划、启动、执行、监控、收尾)、十大知识领域(整合、范围、进度、质量、成本、资源、风险、干</summary>
      
    
    
    
    <category term="项目管理" scheme="https://noname007.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
    
    
    <category term="项目管理" scheme="https://noname007.github.io/tags/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>使用 AI 写技术总结文章</title>
    <link href="https://noname007.github.io/2025/03/16/yii2-redis-queue-ai-summary.html"/>
    <id>https://noname007.github.io/2025/03/16/yii2-redis-queue-ai-summary.html</id>
    <published>2025-03-16T09:03:07.000Z</published>
    <updated>2025-06-08T09:21:53.396Z</updated>
    
    <content type="html"><![CDATA[<p>分析了队列的原理机制，最后说先前的实现有个 Bug 。去年提交了一个 PR,samdark让我补充单元测试，当时没有想好怎么构造样例，就一直停摆在那里了。前段时间突然想到怎么构造样例，模拟实际场景了，在AI 的帮助下快速推进 PR合并到了主线。下面用百度文心一言最新模型写了一个技术文章，确实很惊艳。</p><p>技术博客：确保Redis队列消息至少被消费一次——Yii2队列组件的可靠性增强</p><p>去年，我在Yii2的队列组件库yiisoft/yii2-queue中发起了一个PR（#516），旨在增强Redis驱动在进程崩溃时的消息可靠性，确保每条消息至少被消费一次。在这篇博客中，我将详细介绍这个问题的背景、解决方案以及实现过程中的关键讨论。背景介绍</p><p>在使用Redis作为队列驱动时，我们可能会遇到进程崩溃的情况。如果进程在处理消息的过程中意外终止，而消息尚未从队列中移除，那么这条消息可能会永久丢失，导致数据不一致或服务中断。为了解决这个问题，我们需要一种机制来确保即使进程崩溃，消息也能在恢复后被重新消费。解决方案</p><p>我的解决方案是通过模拟进程崩溃的场景，验证Redis队列在崩溃后能否正确恢复并重新消费消息。具体步骤如下：</p><pre><code class="hljs">模拟延迟任务：首先，我向队列中推送一个延迟任务，并等待其过期。触发Redis崩溃：在任务过期后，模拟Redis在尝试将任务从延迟队列移动到等待队列时崩溃（通过拦截rpush命令）。验证消息恢复：在Redis恢复后，运行队列处理器，验证任务能否被成功消费。</code></pre><p>为了实现这个解决方案，我编写了一个单元测试，通过MockRedis客户端来模拟崩溃场景，并验证消息的恢复机制。 技术实现</p><p>在单元测试testConsumeDelayedMessageAtLeastOnce中，我完成了以下关键步骤：</p><pre><code class="hljs">创建延迟任务：使用createSimpleJob方法创建一个简单的延迟任务，并将其推送到队列中。模拟Redis崩溃：通过配置Redis客户端，使其在收到rpush命令时崩溃。这模拟了进程在尝试将任务从延迟队列移动到等待队列时崩溃的场景。运行队列处理器：在Redis恢复后，运行队列处理器，并验证任务能否被成功消费。断言消息计数：通过断言消息计数来验证任务是否仅被消费一次，确保没有重复消费或消息丢失的情况。</code></pre><p>关键讨论</p><p>在PR的审查过程中，维护者samdark提出了一些宝贵的改进建议：</p><pre><code class="hljs">测试方法命名：建议将测试方法名从testConsumeMsgAtLeastOnce改为testConsumeDelayedMessageAtLeastOnce，以更明确地表达测试的是延迟消息场景。变量命名和注释：指出变量名和注释中的一些拼写错误和不清晰的地方，建议进行修正以提升代码可读性。异常处理：建议在catch块中添加注释，说明忽略异常的原因，以便其他开发者更容易理解测试逻辑。时间一致性：指出sleep(2)与注释中提到的“1秒”不一致，需要确认是否为预期行为。</code></pre><p>这些建议帮助我进一步完善了测试代码，提升了其健壮性和可读性。未来展望</p><p>目前，这个PR已经根据社区反馈进行了多次迭代，测试代码更加健壮和清晰。这个增强将使得Yii2的Redis队列驱动在进程崩溃时更加可靠，为开发者提供更加稳定的服务。</p><p>通过这个PR，我不仅解决了 Redis队列在进程崩溃时可能丢失消息的问题，还学会了如何编写更加健壮和可读的单元测试。同时，我也深刻体会到了社区合作的重要性，以及通过不断迭代和完善来提升代码质量的过程。</p><p>如果你对这个PR感兴趣，欢迎在https://github.com/yiisoft/yii2-queue/pull/516上查看完整的讨论和代码实现。期待与大家一起交流和学习！</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;分析了队列的原理机制，最后说先前的实现有个 Bug 。去年提交了一个 PR,
samdark让我补充单元测试，当时没有想好怎么构造样例，就一直停摆在那里了。前段时间突然想到怎么构造样例，模拟实际场景了，在
AI 的帮助下快速推进 PR
合并到了主线。下面用百度文心一言最新模</summary>
      
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="ai" scheme="https://noname007.github.io/tags/ai/"/>
    
    <category term="yii2" scheme="https://noname007.github.io/tags/yii2/"/>
    
    <category term="redis" scheme="https://noname007.github.io/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>本质复杂度</title>
    <link href="https://noname007.github.io/2025/03/14/complexity-e.html"/>
    <id>https://noname007.github.io/2025/03/14/complexity-e.html</id>
    <published>2025-03-14T09:15:41.000Z</published>
    <updated>2025-06-08T09:20:32.533Z</updated>
    
    <content type="html"><![CDATA[<p>读《<ahref="https://mp.weixin.qq.com/s?__biz=Mzg2Nzc0MDM3Nw%3D%3D&amp;mid=2248432026&amp;idx=1&amp;sn=14be2d09d05d50413867d52df09b4a6c&amp;scene=45#wechat_redirect">最新一批被AI逼转行的人，已经出现</a>》时，人与科技的之间到底是什么关系？卢德主义?共存？服务？问题一直盘桓脑中。</p><p>想到了王小波的我的师承、《读库2501 -英语走出中世纪》这些文章，还有日常碰到的问题《<ahref="https://mp.weixin.qq.com/s?__biz=MzIzNDQ3OTMwOQ==&amp;mid=2247484130&amp;idx=1&amp;sn=b12107cec7d556dfc2bd1a21fcec090f&amp;scene=21#wechat_redirect">专业术语翻译混乱一例</a>》，这些内容互相印证，让我越来越坚信，每个领域都有其本质复杂度(人月神话-Brooks)，这些也才是引起人需求的内因。</p><p>正如王小波所说，语言是用来读的，不是用来写的，否则噪音很大。这种具有美感、触达心灵引起共鸣的文字，以及“小赔钱货”这种有很深语境的词汇所传达的情感，目前是机器难以企及的。</p><p>如果 ai 再多训练一些语料是否也能做达，但梅拉宾法则说，语言仅占沟通的7%。多模态有没有可能突破这个问题呢？额，那可能还需要一个大容量存储。但要这么多电子记忆干什么？……</p><p>逻辑梳理到最后，不论为何。但最本质问题依旧不好回答，人真的愿意做一个如《玩具总动员》中听从机器支配的胖子吗？真的会无欲无求了吗？</p><p><ahref="https://mp.weixin.qq.com/s?__biz=MzA3MTg1NjMwNg==&amp;mid=2650921822&amp;idx=2&amp;sn=ce87af0432f12c6160ccd54b43222bfe&amp;scene=21#wechat_redirect">译论||王小波谈“师承”：要读好文字就要读译著，最好的作者在搞翻译</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;读《&lt;a
href=&quot;https://mp.weixin.qq.com/s?__biz=Mzg2Nzc0MDM3Nw%3D%3D&amp;amp;mid=2248432026&amp;amp;idx=1&amp;amp;sn=14be2d09d05d50413867d52df09b4a6c&amp;amp</summary>
      
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="翻译" scheme="https://noname007.github.io/tags/%E7%BF%BB%E8%AF%91/"/>
    
  </entry>
  
  <entry>
    <title>分布式锁 - Redlock</title>
    <link href="https://noname007.github.io/2024/11/27/redlock.html"/>
    <id>https://noname007.github.io/2024/11/27/redlock.html</id>
    <published>2024-11-27T15:38:49.000Z</published>
    <updated>2025-05-25T17:31:42.840Z</updated>
    
    <content type="html"><![CDATA[<h2 id="redlock">Redlock</h2><h3 id="锁所需要具有的属性">锁所需要具有的属性</h3><ol type="1"><li>独享 - 在任意一个时刻，只有一个客户端持有锁</li><li>无死锁 - 持锁客户端失联（崩溃、网络分区），锁仍然可以被自动释放</li><li>容错 - 只要大部分Redis节点都活着，客户端就可以获取和释放锁</li></ol><h3 id="面临的问题">面临的问题</h3><ol type="1"><li>系统层 - 网络分区、崩溃、重启等问题</li><li>Redis层 - Redis节点的崩溃、重启等问题</li><li>客户端层 - 客户端的崩溃、重启等问题</li><li>重入 - 同一个客户端多次获取锁</li><li>锁续期 - 锁的有效期到了，但是业务还没有执行完，需要续期</li><li>释放 - 释放锁的时候，锁已经被其他客户端获取</li><li>时钟漂移 - 不同的Redis节点的时间不一致</li><li>时钟回拨 - 时钟回拨导致锁的有效期不准确</li><li>误删 - 误删其他客户端的锁</li><li></li></ol><h3 id="解决方法">解决方法</h3><ol type="1"><li>互斥性 - 通过SETNX命令实现</li><li>无死锁 - 通过设置锁的过期时间实现</li><li>容错性 - 通过大多数原则实现</li><li>重入 - 通过客户端ID实现</li><li>锁续期 - 通过锁的过期时间实现</li><li>释放 - 通过客户端ID实现</li><li>时钟漂移 - 通过设置锁的过期时间实现</li><li>时钟回拨 - 通过设置锁的过期时间实现</li><li>误删 - 通过客户端ID实现</li></ol><h3 id="算法">算法</h3><h4 id="单机算法">单机算法</h4><ol type="1"><li><p>获取锁</p><p><code>SET resource_name my_random_value NX PX 30000</code></p></li><li><p>释放锁</p></li></ol><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs lua"><span class="hljs-keyword">if</span> redis.call(<span class="hljs-string">&quot;get&quot;</span>,KEYS[<span class="hljs-number">1</span>]) == ARGV[<span class="hljs-number">1</span>] <span class="hljs-keyword">then</span> <br>  <span class="hljs-keyword">return</span> redis.call(<span class="hljs-string">&quot;del&quot;</span>,KEYS[<span class="hljs-number">1</span>]) <br><span class="hljs-keyword">else</span><br>  <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <br><span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure><h4 id="分布式算法">分布式算法</h4><ol type="1"><li>获取锁<ol type="1"><li>生成一个随机的客户端ID</li><li>依次向N个Redis节点获取锁</li><li>如果大多数Redis节点都获取到了锁，那么认为获取锁成功</li><li>如果获取锁失败，那么向所有Redis节点释放锁</li></ol></li></ol><h2 id="yii2-redis-lock-代码实现">Yii2 Redis lock 代码实现</h2><figure><img src="/assets/imgs/2024-11-23_13-54.png" alt="类图" /><figcaption aria-hidden="true">类图</figcaption></figure><ol type="1"><li>随机数获取实现<code>Yii::$app-&gt;security-&gt;generateRandomString</code> 底层依赖https://www.php.net/manual/en/function.random-bytes.php</li></ol><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><code class="hljs php"> <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * Acquires a lock by name.</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> string $name of the lock to be acquired. Must be unique.</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> int $timeout time (in seconds) to wait for lock to be released. Defaults to `0` meaning that method will return</span><br><span class="hljs-comment">     * false immediately in case lock was already acquired.</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> bool lock acquiring result.</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">acquireLock</span>(<span class="hljs-params"><span class="hljs-variable">$name</span>, <span class="hljs-variable">$timeout</span> = <span class="hljs-number">0</span></span>)</span><br><span class="hljs-function">    </span>&#123;<br>        <span class="hljs-variable">$key</span> = <span class="hljs-variable language_">$this</span>-&gt;<span class="hljs-title function_ invoke__">calculateKey</span>(<span class="hljs-variable">$name</span>);<br>        <span class="hljs-variable">$value</span> = <span class="hljs-title class_">Yii</span>::<span class="hljs-variable">$app</span>-&gt;security-&gt;<span class="hljs-title function_ invoke__">generateRandomString</span>(<span class="hljs-number">20</span>);<br><br>        <span class="hljs-variable">$result</span> = <span class="hljs-variable language_">$this</span>-&gt;<span class="hljs-title function_ invoke__">retryAcquire</span>(<span class="hljs-variable">$timeout</span>, function () <span class="hljs-keyword">use</span> ($<span class="hljs-title">key</span>, $<span class="hljs-title">value</span>) &#123;<br>            <span class="hljs-title">return</span> $<span class="hljs-title">this</span>-&gt;<span class="hljs-title">redis</span>-&gt;<span class="hljs-title">executeCommand</span>(&#x27;<span class="hljs-title">SET</span>&#x27;, [$<span class="hljs-title">key</span>, $<span class="hljs-title">value</span>, &#x27;<span class="hljs-title">NX</span>&#x27;, &#x27;<span class="hljs-title">PX</span>&#x27;, (<span class="hljs-title">int</span>) ($<span class="hljs-title">this</span>-&gt;<span class="hljs-title">expire</span> * 1000)]);<br>        &#125;);<br><br>        <span class="hljs-keyword">if</span> (<span class="hljs-variable">$result</span>) &#123;<br>            <span class="hljs-variable language_">$this</span>-&gt;_lockValues[<span class="hljs-variable">$name</span>] = <span class="hljs-variable">$value</span>;<br>        &#125;<br>        <span class="hljs-keyword">return</span> <span class="hljs-variable">$result</span>;<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * Releases acquired lock. This method will return `false` in case the lock was not found or Redis command failed.</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> string $name of the lock to be released. This lock must already exist.</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> bool lock release result: `false` in case named lock was not found or Redis command failed.</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">releaseLock</span>(<span class="hljs-params"><span class="hljs-variable">$name</span></span>)</span><br><span class="hljs-function">    </span>&#123;<br>        <span class="hljs-built_in">static</span> <span class="hljs-variable">$releaseLuaScript</span> = <span class="hljs-string">&lt;&lt;&lt;LUA</span><br><span class="hljs-string">if redis.call(&quot;GET&quot;,KEYS[1])==ARGV[1] then</span><br><span class="hljs-string">    return redis.call(&quot;DEL&quot;,KEYS[1])</span><br><span class="hljs-string">else</span><br><span class="hljs-string">    return 0</span><br><span class="hljs-string">end</span><br><span class="hljs-string">LUA</span>;<br>        <span class="hljs-keyword">if</span> (<br>            !<span class="hljs-keyword">isset</span>(<span class="hljs-variable language_">$this</span>-&gt;_lockValues[<span class="hljs-variable">$name</span>])<br>            || !<span class="hljs-variable language_">$this</span>-&gt;redis-&gt;<span class="hljs-title function_ invoke__">executeCommand</span>(<span class="hljs-string">&#x27;EVAL&#x27;</span>, [<br>                <span class="hljs-variable">$releaseLuaScript</span>,<br>                <span class="hljs-number">1</span>,<br>                <span class="hljs-variable">$this</span>-&gt;<span class="hljs-title function_ invoke__">calculateKey</span>(<span class="hljs-variable">$name</span>),<br>                <span class="hljs-variable">$this</span>-&gt;_lockValues[<span class="hljs-variable">$name</span>],<br>            ])<br>        ) &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br>        &#125;<br><br>        <span class="hljs-keyword">unset</span>(<span class="hljs-variable language_">$this</span>-&gt;_lockValues[<span class="hljs-variable">$name</span>]);<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * Generates a unique key used for storing the mutex in Redis.</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> string $name mutex name.</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> string a safe cache key associated with the mutex name.</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateKey</span>(<span class="hljs-params"><span class="hljs-variable">$name</span></span>)</span><br><span class="hljs-function">    </span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">$this</span>-&gt;keyPrefix . <span class="hljs-title function_ invoke__">md5</span>(<span class="hljs-title function_ invoke__">json_encode</span>([<span class="hljs-keyword">__CLASS__</span>, <span class="hljs-variable">$name</span>]));<br>    &#125;<br></code></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;redlock&quot;&gt;Redlock&lt;/h2&gt;
&lt;h3 id=&quot;锁所需要具有的属性&quot;&gt;锁所需要具有的属性&lt;/h3&gt;
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;独享 - 在任意一个时刻，只有一个客户端持有锁&lt;/li&gt;
&lt;li&gt;无死锁 - 持锁客户端失联（崩溃、网络分区），锁仍</summary>
      
    
    
    
    
    <category term="PHP" scheme="https://noname007.github.io/tags/PHP/"/>
    
    <category term="Yii2" scheme="https://noname007.github.io/tags/Yii2/"/>
    
    <category term="并发" scheme="https://noname007.github.io/tags/%E5%B9%B6%E5%8F%91/"/>
    
    <category term="Redis" scheme="https://noname007.github.io/tags/Redis/"/>
    
    <category term="分布式" scheme="https://noname007.github.io/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
    
  </entry>
  
  <entry>
    <title>Golang - N个协程交替打印数字</title>
    <link href="https://noname007.github.io/2024/07/02/goroutin-print-number.html"/>
    <id>https://noname007.github.io/2024/07/02/goroutin-print-number.html</id>
    <published>2024-07-02T12:39:36.000Z</published>
    <updated>2025-05-25T13:16:37.564Z</updated>
    
    <content type="html"><![CDATA[<div class="note note-info">            <p>N个协程交替打印数字，到达一个数值后终止</p>          </div><h2 id="网友解法">网友解法</h2><p>网上搜索的解法大概如下，网友错别字地方已经修复</p><blockquote><p>启动N个协程，共用一个外部变量计数器，计数器范围是1到100</p><p>开启N个<code>无</code>缓冲chan，chans[i]塞入数据代表协程i可以进行打印了，打印的数字就是计数器的数</p><p>协程i一直阻塞，直到chan[i]通道有数据可以拉，才打印</p></blockquote><h2 id="分析">分析</h2><p>大思路没有问题，但<ahref="https://gist.github.com/noname007/6fd0a23920fa115618dbef2504511727?permalink_comment_id=5108543#gistcomment-5108543">具体实现</a>的小细节处理上存在两个问题。</p><ul><li>N 为 1 的时候存在死锁问题</li><li>程序运行完后，协程没有回收，这个比较严重。<ul><li>首先，破坏了<code>谁申请、谁释放</code>资源管理的基本设计原则</li><li>其次，常驻内存运行时如果经常被其他协程调用，应用有 OOM 的风险。</li></ul></li></ul><p>只能作为一次性脚本调用，运行结束后整个进程被操作系统回收。针对两个问题做如下修正:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br>    <span class="hljs-string">&quot;fmt&quot;</span><br>    <span class="hljs-string">&quot;sync&quot;</span><br>)<br><br><br><br><span class="hljs-keyword">var</span> counter = <span class="hljs-number">0</span><br><br><span class="hljs-keyword">const</span> CounterMaxValue = <span class="hljs-number">100</span><br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">DoJob</span><span class="hljs-params">(GoRoutineNum <span class="hljs-type">int</span>)</span></span> &#123;<br><br>    <span class="hljs-comment">//用于控制业务逻辑</span><br>    sigChannel := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>&#123;&#125;, GoRoutineNum)<br>    <span class="hljs-comment">//用于控制协成是否终结</span><br>    quitSigChannel := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>&#123;&#125;, GoRoutineNum)<br><br>    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; (GoRoutineNum); i++ &#123;<br>        sigChannel[i] = <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>&#123;&#125;)<br>        quitSigChannel[i] = <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>&#123;&#125;)<br>    &#125;<br><br>    mainRoutine := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>&#123;&#125;)<br><br>    wg := sync.WaitGroup&#123;&#125;<br><br>    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; GoRoutineNum; i++ &#123;<br>        wg.Add(<span class="hljs-number">1</span>)<br>        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(idx <span class="hljs-type">int</span>)</span></span> &#123;<br>            <span class="hljs-keyword">defer</span> wg.Done()<br>            <span class="hljs-comment">//FOR:</span><br>            <span class="hljs-keyword">for</span> &#123;<br>                <span class="hljs-keyword">select</span> &#123;<br>                <span class="hljs-keyword">case</span> &lt;-quitSigChannel[idx]:<br>                    <span class="hljs-comment">//break FOR</span><br>                    <span class="hljs-keyword">goto</span> END<br>                <span class="hljs-keyword">case</span> &lt;-sigChannel[idx]:<br>                    fmt.Printf(<span class="hljs-string">&quot;goroutine-num:%d %d\n&quot;</span>, idx, counter)<br>                    counter += <span class="hljs-number">1</span><br>                    mainRoutine &lt;- <span class="hljs-keyword">struct</span>&#123;&#125;&#123;&#125;<br>                &#125;<br><br>            &#125;<br>        END:<br>            fmt.Printf(<span class="hljs-string">&quot;goroutine-num:%d end\n&quot;</span>, idx)<br>        &#125;(i)<br>    &#125;<br><br>    wg.Add(<span class="hljs-number">1</span>)<br><br>    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br>        wg.Done()<br><br>        <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; CounterMaxValue; i++ &#123;<br>            <span class="hljs-comment">//&lt;-mainRoutine</span><br>            sigChannel[i%GoRoutineNum] &lt;- <span class="hljs-keyword">struct</span>&#123;&#125;&#123;&#125;<br>            &lt;-mainRoutine<br>        &#125;<br><br>        <span class="hljs-comment">////使最后一个任务处理协成，进入信号等待状态</span><br>        <span class="hljs-comment">//&lt;-mainRoutine</span><br><br>        <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; GoRoutineNum; i++ &#123;<br>            quitSigChannel[i] &lt;- <span class="hljs-keyword">struct</span>&#123;&#125;&#123;&#125;<br>        &#125;<br>    &#125;()<br><br>    <span class="hljs-comment">//mainRoutine &lt;- struct&#123;&#125;&#123;&#125;</span><br>    wg.Wait()<br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> &#123;<br>    DoJob(<span class="hljs-number">10</span>)<br>&#125;<br></code></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">🤝 协同交替打印数字</summary>
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="并发" scheme="https://noname007.github.io/tags/%E5%B9%B6%E5%8F%91/"/>
    
    <category term="Golang" scheme="https://noname007.github.io/tags/Golang/"/>
    
  </entry>
  
  <entry>
    <title>volatile 实战 - 使用信号实现 sleep 函数</title>
    <link href="https://noname007.github.io/2024/06/17/volatile.html"/>
    <id>https://noname007.github.io/2024/06/17/volatile.html</id>
    <published>2024-06-17T11:51:26.000Z</published>
    <updated>2025-05-25T13:16:48.658Z</updated>
    
    <content type="html"><![CDATA[<h2 id="源码">源码</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&lt;signal.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&lt;unistd.h&gt;</span></span><br><br><span class="hljs-type">static</span> <span class="hljs-type">int</span> sleepFlag = <span class="hljs-number">1</span>;<br><br><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title function_">alrm_handler</span><span class="hljs-params">(<span class="hljs-type">int</span> signo)</span>&#123;<br>    sleepFlag = <span class="hljs-number">0</span>;<br>&#125;<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">mySleep</span><span class="hljs-params">(<span class="hljs-type">int</span> sec)</span><br>&#123;<br>    signal(SIGALRM,alrm_handler);<br>    alarm(sec);<br>    <br>    <span class="hljs-keyword">while</span>(sleepFlag);<br>    <br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>开启优化选项后，程序变成了死循环。从下面的汇编代码，不难发现编译器优化了sleepFlag 的读取，导致了死循环。</p><figure><img src="/assets/2024-06-17-O0.png" title="不开启优化"alt="不开启优化" /><figcaption aria-hidden="true">不开启优化</figcaption></figure><figure><img src="/assets/2024-06-17-O3.png" title="O3 优化" alt="O3 优化" /><figcaption aria-hidden="true">O3 优化</figcaption></figure><h2 id="volatile-使用场景总结-1">volatile 使用场景总结<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="[https://blog.csdn.net/tigerjibo/article/details/7427366](https://blog.csdn.net/tigerjibo/article/details/7427366)">[1]</span></a></sup></h2><h3id="在中断服务程序中进行修改的共享变量">在中断服务程序中进行修改的共享变量</h3><p>上面中 <code>sleepFlag</code> 就是一个很好的例子。</p><h3 id="多线程间共享的变量">多线程间共享的变量</h3><h3id="对硬件寄存器2进行映射的变量">对硬件寄存器<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="[https://www.runoob.com/w3cnote/c-volatile-keyword.html](https://www.runoob.com/w3cnote/c-volatile-keyword.html)">[2]</span></a></sup>进行映射的变量</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-type">int</span>  *output = (<span class="hljs-type">unsigned</span>  <span class="hljs-type">int</span> *)<span class="hljs-number">0xff800000</span>;<span class="hljs-comment">//定义一个IO端口；</span><br><span class="hljs-type">int</span>   <span class="hljs-title function_">init</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span><br>&#123;<br>    <span class="hljs-type">int</span> i;<br>    <span class="hljs-comment">//会被优化为 *output = 9</span><br>    <span class="hljs-keyword">for</span>(i=<span class="hljs-number">0</span>;i&lt; <span class="hljs-number">10</span>;i++)&#123;<br>        *output = i;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="参考">参考</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><ahref="https://blog.csdn.net/tigerjibo/article/details/7427366">https://blog.csdn.net/tigerjibo/article/details/7427366</a><a href="#fnref:1" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><ahref="https://www.runoob.com/w3cnote/c-volatile-keyword.html">https://www.runoob.com/w3cnote/c-volatile-keyword.html</a><a href="#fnref:2" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:3" class="footnote-text"><span><ahref="https://zh.wikipedia.org/wiki/%E7%A1%AC%E9%AB%94%E6%9A%AB%E5%AD%98%E5%99%A8">硬件寄存器</a><a href="#fnref:3" rev="footnote" class="footnote-backref">↩︎</a></span></span></li></ol></div></section>]]></content>
    
    
    <summary type="html">volatile 与 编译优化</summary>
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="C" scheme="https://noname007.github.io/tags/C/"/>
    
    <category term="系统编程" scheme="https://noname007.github.io/tags/%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Gitlab CI/CD 改造回顾总结 ♻️</title>
    <link href="https://noname007.github.io/2024/06/04/xhs-gitlab-ci.html"/>
    <id>https://noname007.github.io/2024/06/04/xhs-gitlab-ci.html</id>
    <published>2024-06-04T07:23:30.000Z</published>
    <updated>2025-05-25T17:06:41.935Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/assets/imgs/2025-05-25_211849_851.png" /></p><h2 id="背景">背景</h2><p>我是22年入职的公司。</p><p>在熟悉环境的过程中发现：公司对 Go的支持人力投入有限，构建过程缺少规范、没有指导文档。比我入职早的一位同事，靠自己东找找西找找，缝合了一个Gitlab 的 <code>yml</code> 文件。</p><p>我当时觉得这是一个很奇葩的事情，就拉上中间件团队、研效、基础设施团队的同事组了一个公司的Go 技术交流群。沟通过程中发现，构建 Go 基础镜像的<code>Dockerfile</code> 文件也丢失了，这下可好一件事变成了两件事。</p><p>针对 <code>Gitlab 的编排流程</code>和<code>Go基础镜像的构建</code>，结合自己的项目实际情况进行了<code>造轮子</code>。去年9月，研效团队把<code>Go 基础镜像的构建</code>的也接过手去了，对这件事算是画上了一个相对完满的句号。下面对造轮子的过程进行一个简要的回顾总结。</p><h2 id="研发流程">研发流程</h2><p>先简单的说下 CI 流程</p><figure><img src="/assets/2024-06-04-xhs-ci-cd-flow.png"title="代码集成、构建、发版流程" alt="集成、发版流程" /><figcaption aria-hidden="true">集成、发版流程</figcaption></figure><ol type="1"><li>开发人员提交代码到 Gitlab</li><li>Gitlab CI/CD 自动构建</li><li>构建成功后，镜像推送到 Harbor</li><li>研发人员去发版系统选择构建好的镜像进行发布到测试、正式环境</li></ol><h2 id="改造">改造</h2><p><img src="/assets/imgs/2024-06-04-gitlab-ci-workflow.drawio.png"title="Gitlab CI/CD 工作流程" /></p><h3 id="改造-golang-基础镜像">改造 Golang 基础镜像</h3><h4 id="反解旧镜像dockerfile">反解旧镜像<code>Dockerfile</code></h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage $&#123;PRIVATE_DOCKER_REPO&#125;/base/xhs-golang:1.15 <br></code></pre></td></tr></table></figure><p>从输出结果来看主要需要做了如下几件事情 1. 基础镜像使用<code>debian:buster-20200803</code> 2. copy go 1.15 的包到镜像内 3. 更改apt 镜像源 4. 修改时区、语言设置、环境变量 5.安装一些依赖使用的软件包</p><h4 id="重新封装-golang-基础镜像-dockerfile">重新封装 Golang 基础镜像Dockerfile</h4><p>基于反解后的信息，和同事先前做的 CI 脚本重新封装了一个 Golang的基础镜像。</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs Dockerfile"><span class="hljs-keyword">FROM</span> golang:<span class="hljs-number">1.18</span>-bullseye<br><br><span class="hljs-keyword">RUN</span><span class="language-bash">  <span class="hljs-built_in">cat</span> /etc/apt/sources.list &amp;&amp; \</span><br><span class="language-bash">     sed -i <span class="hljs-string">&quot;s/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g&quot;</span> /etc/apt/sources.list &amp;&amp; \</span><br><span class="language-bash">     sed -i <span class="hljs-string">&quot;s/security.debian.org/mirrors.tuna.tsinghua.edu.cn/g&quot;</span> /etc/apt/sources.list &amp;&amp; \</span><br><span class="language-bash">     <span class="hljs-built_in">cat</span> /etc/apt/sources.list &amp;&amp; \</span><br><span class="language-bash">     apt update &amp;&amp;  \</span><br><span class="language-bash">     apt install upx -y &amp;&amp; \</span><br><span class="language-bash">     <span class="hljs-built_in">rm</span> -rf /var/lib/apt/lists/*</span><br><br><span class="hljs-keyword">RUN</span><span class="language-bash"> <span class="hljs-built_in">cp</span> /usr/share/zoneinfo/Asia/Shanghai /etc/localtime  \</span><br><span class="language-bash">         &amp;&amp; <span class="hljs-built_in">echo</span> <span class="hljs-string">&#x27;Asia/Shanghai&#x27;</span> &gt; /etc/timezone  \</span><br><span class="language-bash">         &amp;&amp; <span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;alias ll=&#x27;ls -l --color&#x27;&quot;</span> &gt;&gt; /root/.bashrc</span><br><br><span class="hljs-keyword">ENV</span>  GO111MODULE=on GOPROXY=https://goproxy.cn GOPRIVATE=code.devops.xiaohongshu.com<br><br><span class="hljs-keyword">RUN</span><span class="language-bash"> git config --global url.<span class="hljs-string">&quot;git@code.devops.xiaohongshu.com:&quot;</span>.insteadof <span class="hljs-string">&quot;https://code.devops.xiaohongshu.com/&quot;</span> &amp;&amp; \</span><br><span class="language-bash">    git config --global --add url.<span class="hljs-string">&quot;git@code.devops.xiaohongshu.com:&quot;</span>.insteadof <span class="hljs-string">&quot;http://code.devops.xiaohongshu.com/&quot;</span></span><br><br><span class="hljs-keyword">COPY</span><span class="language-bash"> id_rsa /root/.ssh/</span><br><span class="hljs-keyword">COPY</span><span class="language-bash"> id_rsa.pub /root/.ssh/</span><br><br><span class="hljs-keyword">RUN</span><span class="language-bash">  ssh-keyscan code.devops.xiaohongshu.com &gt; /root/.ssh/known_hosts &amp;&amp; \</span><br><span class="language-bash">      <span class="hljs-built_in">chmod</span> 600 ~/.ssh/id_rsa</span><br></code></pre></td></tr></table></figure><ol type="1"><li>基于 Go 官方提供的最新镜像<code>golang:1.18-bullseye</code>进行构建</li><li>修改镜像源、安装需要的用到的软件包、清理 apt 包缓存、更改时区</li><li>配置 Go 私有仓库、共有仓库代理。</li><li>配置密钥，防止 gitlab有些仓库因为权限问题无法拉取。密钥可以不配置，放在主机的某个文件路径下面，使用卷挂载的方式使进行共享。</li></ol><h3 id="改造-golang-应用程序运行时镜像">改造 Golang应用程序运行时镜像</h3><h4 id="反解旧镜像dockerfile-1">反解旧镜像<code>Dockerfile</code></h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">docker run -v /var/run/docker.sock:/var/run/docker.sock alpine/dfimage  $&#123;PRIVATE_DOCKER_REPO&#125;/base/xhs-debian:10<br></code></pre></td></tr></table></figure><p>基础镜像的构建过程主要做了几件事： 1. 选定基础镜像： <ahref="https://dso.docker.com/images/debian/digests/sha256:a44ab0cca6cd9411032d180bc396f19bc98f71972d2398d50460145cab81c5ab">debian:buster-20200803</a>（xhs-debian构建时的Debian latest 版本） 2. 本地化设置：更改时区、设置语言字符集、更改镜像源3. 安装了一些非所有场景都需要的软件</p><h4 id="重新封装-golang-应用程序运行时镜像-dockerfile">重新封装 Golang应用程序运行时镜像 Dockerfile</h4><p>原理弄清楚，问题就很容易解决。下面对应用程序的基础镜像每一步做如下的优化升级：</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs Dockerfile"><span class="hljs-keyword">FROM</span> debian:stable-slim<br><br><span class="hljs-keyword">RUN</span><span class="language-bash">  <span class="hljs-built_in">cat</span> /etc/apt/sources.list &amp;&amp; \</span><br><span class="language-bash">     sed -i <span class="hljs-string">&quot;s/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g&quot;</span> /etc/apt/sources.list &amp;&amp; \</span><br><span class="language-bash">     sed -i <span class="hljs-string">&quot;s/security.debian.org/mirrors.tuna.tsinghua.edu.cn/g&quot;</span> /etc/apt/sources.list &amp;&amp; \</span><br><span class="language-bash">     <span class="hljs-built_in">cat</span> /etc/apt/sources.list</span><br><br><span class="hljs-keyword">RUN</span><span class="language-bash"> <span class="hljs-built_in">cp</span> /usr/share/zoneinfo/Asia/Shanghai /etc/localtime  \</span><br><span class="language-bash">         &amp;&amp; <span class="hljs-built_in">echo</span> <span class="hljs-string">&#x27;Asia/Shanghai&#x27;</span> &gt; /etc/timezone  \</span><br><span class="language-bash">         &amp;&amp; <span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;alias ll=&#x27;ls -l --color&#x27;&quot;</span> &gt;&gt; /root/.bashrc</span><br><span class="hljs-keyword">ENV</span> LANG=C.UTF-<span class="hljs-number">8</span><br></code></pre></td></tr></table></figure><ol type="1"><li>换一个比较精巧的OS ，最后选了 Debian Slim (↘️<ahref="https://groups.google.com/g/golang-nuts/c/15TLaxqUpA0">alpine vsslim</a>)<ol type="1"><li>镜像相对来说也比较小：删除了很多运行时用不到的东西</li><li>glibc vs musl<ol type="1"><li>↘️<ahref="https://wiki.musl-libc.org/functional-differences-from-glibc.html">Functionaldifferences from glibc</a></li><li>musl 跑纯 Go 的项目一般问题不大，但是碰到需要使用依赖 glibc的项目就很麻烦</li></ol></li><li>包管理器，线上出现问题时，还能使用包管理器安装 Deb 包 排查问题</li><li>运行时用不到的软件就不装了，除了浪费空间、增加攻击面暂时也没发现其他用处~</li></ol></li><li>镜像源、语言、时区，照抄原来镜像的设置</li></ol><h4 id="效果">效果</h4><ol type="1"><li>镜像大小从 440M → 110M （alpine 能降到 36M ）</li><li>285个中、高、致命漏洞 降低为 23 个低级风险问题</li></ol><p><imgsrc="/assets/imgs/2024-06-04-old-golang-runtime-docker-image.png"title="旧运行时镜像" /></p><p><imgsrc="/assets/imgs/2024-06-04-new-golang-runtime-container-image.png"title="新运行时镜像" /></p><h3 id="改造-.gitlab-ci.yml">改造 <code>.gitlab-ci.yml</code></h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">set</span> -x<br><span class="hljs-comment"># 基于 Dockerfile 构建镜像，并将构建后的镜像推送到私有镜像仓库</span><br><span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">docker_build</span></span>() &#123;<br>    <span class="hljs-built_in">local</span> app_id=<span class="hljs-variable">$1</span><br>    <span class="hljs-built_in">local</span> docker_tags=<span class="hljs-variable">$2</span><br>    docker build .  -t <span class="hljs-variable">$&#123;PRIVATE_DOCKER_REPO&#125;</span>/qa/<span class="hljs-string">&quot;<span class="hljs-variable">$app_id</span>&quot;</span>:<span class="hljs-string">&quot;<span class="hljs-variable">$docker_tags</span>&quot;</span><br>    docker login -u username -p passwordk <span class="hljs-variable">$&#123;PRIVATE_DOCKER_REPO&#125;</span><br>    docker push <span class="hljs-variable">$&#123;PRIVATE_DOCKER_REPO&#125;</span>/qa/<span class="hljs-string">&quot;<span class="hljs-variable">$app_id</span>&quot;</span>:<span class="hljs-string">&quot;<span class="hljs-variable">$docker_tags</span>&quot;</span><br>&#125;<br><br><br><br><span class="hljs-comment"># 更改时区，不然 date 函数时间会计算错误</span><br>sed -i <span class="hljs-string">&#x27;s/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g&#x27;</span> /etc/apk/repositories<br>apk add -U tzdata<br><span class="hljs-built_in">cp</span> /usr/share/zoneinfo/Asia/Shanghai /etc/localtime<br>CURRENT_DATETIME=<span class="hljs-string">&quot;<span class="hljs-subst">$(date +%Y%m%d_%H%M)</span>&quot;</span><br><br><br>tags=<span class="hljs-variable">$1</span><br>bin=<span class="hljs-variable">$2</span><br><span class="hljs-built_in">cp</span> <span class="hljs-string">&quot;<span class="hljs-variable">$&#123;bin&#125;</span>&quot;</span> ./app<br>docker_build <span class="hljs-variable">$&#123;APP_ID&#125;</span> <span class="hljs-string">&quot;<span class="hljs-variable">$&#123;tags&#125;</span>_<span class="hljs-variable">$&#123;CURRENT_DATETIME&#125;</span>&quot;</span><br></code></pre></td></tr></table></figure><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-comment"># 此镜像用于执行 docker 运行时镜像的构建</span><br><span class="hljs-attr">image:</span> <span class="hljs-string">$&#123;PRIVATE_DOCKER_REPO&#125;/library/docker:v1.0</span><br><br><span class="hljs-comment"># gitlab 关键字，分几个阶段</span><br><span class="hljs-attr">stages:</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-string">compile</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-string">publish</span><br><br><span class="hljs-comment"># 代码编译阶段</span><br><span class="hljs-attr">compile:</span><br>  <span class="hljs-attr">stage:</span> <span class="hljs-string">compile</span><br>  <span class="hljs-attr">image:</span> <span class="hljs-string">$&#123;PRIVATE_DOCKER_REPO&#125;/qa/itworkspace:go_1.21_latest</span><br><br>  <span class="hljs-attr">before_script:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">go</span> <span class="hljs-string">version</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">ls</span> <span class="hljs-string">-alh</span><br>  <span class="hljs-attr">script:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">go</span> <span class="hljs-string">mod</span> <span class="hljs-string">tidy</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">go</span> <span class="hljs-string">build</span> <span class="hljs-string">-o</span> <span class="hljs-string">./app_dir/http</span> <span class="hljs-string">./cmd/http.go</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">upx</span> <span class="hljs-string">./app_dir/http</span><br>  <br>  <span class="hljs-comment"># 编译成功后，将产物共享给下一个阶段</span><br>  <span class="hljs-attr">artifacts:</span><br>    <span class="hljs-attr">paths:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">&quot;./app_dir&quot;</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">&quot;./conf&quot;</span><br>    <span class="hljs-attr">untracked:</span> <span class="hljs-literal">false</span><br>    <span class="hljs-comment"># 编译成功后，才进行产物缓存</span><br>    <span class="hljs-attr">when:</span> <span class="hljs-string">on_success</span><br><br><span class="hljs-comment"># 镜像构建阶段</span><br><span class="hljs-attr">publish_http:</span><br>  <span class="hljs-attr">stage:</span> <span class="hljs-string">publish</span><br>  <span class="hljs-attr">script:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">cp</span> <span class="hljs-string">./conf/app.sit.yaml</span> <span class="hljs-string">./conf/app.yaml</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">/bin/sh</span> <span class="hljs-string">./build.gitlab.sh</span> <span class="hljs-string">sit_http</span> <span class="hljs-string">./app_dir/http</span><br></code></pre></td></tr></table></figure><h2 id="参考资料">参考资料</h2><ol type="1"><li><div class="douban-card-block">    <a class="douban-card" href="https://book.douban.com/subject/26285268">        <div bg-lazy class="douban-card-bgimg" style="background-image: url('https://images.weserv.nl/?url=https://img1.doubanio.com/view/subject/s/public/s27967469.jpg');">        </div>        <div class="douban-card-left">            <div bg-lazy class="douban-card-img" style="background-image: url('https://images.weserv.nl/?url=https://img1.doubanio.com/view/subject/s/public/s27967469.jpg');">            </div>            <div class="douban-card-status">见字如晤</div>        </div>        <div class="douban-card-right" style="line-height: 1.7;">            <div class="douban-card-item"><span>书名: </span><strong>第一本Docker书</strong></div>            <div class="douban-card-item"><span>作者: </span><span>[澳]詹姆斯•特恩布尔（JamesTurnbull）</span></div>            <div class="douban-card-item"><span>出版年份: </span><span>2014-12</span></div>            <div class="douban-card-item"><span>评分: </span><span>7.3</span></div>        </div>    </a></div></li></ol>]]></content>
    
    
    <summary type="html">💪 改造公司基础设施</summary>
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="构建" scheme="https://noname007.github.io/tags/%E6%9E%84%E5%BB%BA/"/>
    
    <category term="部署" scheme="https://noname007.github.io/tags/%E9%83%A8%E7%BD%B2/"/>
    
    <category term="Docker" scheme="https://noname007.github.io/tags/Docker/"/>
    
    <category term="Golang" scheme="https://noname007.github.io/tags/Golang/"/>
    
  </entry>
  
  <entry>
    <title>使用命令行批量删除 Github 仓库</title>
    <link href="https://noname007.github.io/2024/05/28/delete-github-repo-use-gh-xargs.html"/>
    <id>https://noname007.github.io/2024/05/28/delete-github-repo-use-gh-xargs.html</id>
    <published>2024-05-28T09:46:58.000Z</published>
    <updated>2025-05-25T13:21:09.280Z</updated>
    
    <content type="html"><![CDATA[<h2 id="安装-gh">安装 gh</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">gh auth login<br>gh auth refresh -h github.com -s delete_repo<br></code></pre></td></tr></table></figure><h2 id="批量删除仓库">批量删除仓库</h2><p>待删除的仓库批量放入 <code>github.repo.txt</code>文本文件中，每行一个仓库名。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">cat</span> github.repo.txt | xargs -I &#123;&#125;  -n 1  gh repo delete &#123;&#125; --<span class="hljs-built_in">yes</span><br></code></pre></td></tr></table></figure><figure><img src="/assets/20240528.png" title="Image Title" alt="alt text" /><figcaption aria-hidden="true">alt text</figcaption></figure>]]></content>
    
    
    <summary type="html">使用命令行批量删除 Github 仓库</summary>
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="软件工具" scheme="https://noname007.github.io/tags/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>Thrift  一种 协议-工具 设计方法</title>
    <link href="https://noname007.github.io/2022/04/10/a-protocol-design-method.html"/>
    <id>https://noname007.github.io/2022/04/10/a-protocol-design-method.html</id>
    <published>2022-04-10T03:46:12.000Z</published>
    <updated>2025-05-25T16:58:53.679Z</updated>
    
    <content type="html"><![CDATA[<p><strong>双边通信是个复杂的问题</strong></p><h3 id="请求---响应">请求 - 响应</h3><p>双边通信本质来说是<code>请求-响应</code>模型，主动方发出一个服务请求，被动方给一个服务结果应答。事很简单，但实现起来确很费劲。</p><p>传输层协议提供信道传输模型-如TCP，应用层协议提供信息标准格式封装模型-如HTTP,RPC。</p><p>Thrift的设计方式对应用层协议设计有很好的参考意义</p><h2 id="thrift">thrift</h2><figure class="highlight thrift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs thrift"><span class="hljs-class"><span class="hljs-keyword">service</span> <span class="hljs-title">HelloWorldService</span> </span>&#123;<br>  <span class="hljs-type">string</span> say(<span class="hljs-number">1</span>: <span class="hljs-type">string</span> username)<br>&#125;<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SimpleServer</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-type">ServerSocket</span> <span class="hljs-variable">serverSocket</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ServerSocket</span>(ServerConfig.SERVER_PORT);<br>        <span class="hljs-type">TServerSocket</span> <span class="hljs-variable">serverTransport</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">TServerSocket</span>(serverSocket);<br>        HelloWorldService.<span class="hljs-type">Processor</span> <span class="hljs-variable">processor</span> <span class="hljs-operator">=</span><br>                <span class="hljs-keyword">new</span> <span class="hljs-title class_">HelloWorldService</span>.Processor&lt;HelloWorldService.Iface&gt;(<span class="hljs-keyword">new</span> <span class="hljs-title class_">HelloWorldServiceImpl</span>());<br><br>        TBinaryProtocol.<span class="hljs-type">Factory</span> <span class="hljs-variable">protocolFactory</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">TBinaryProtocol</span>.Factory();<br>        TSimpleServer.<span class="hljs-type">Args</span> <span class="hljs-variable">tArgs</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">TSimpleServer</span>.Args(serverTransport);<br>        tArgs.processor(processor);<br>        tArgs.protocolFactory(protocolFactory);<br><br>        <span class="hljs-comment">// 简单的单线程服务模型 一般用于测试</span><br>        <span class="hljs-type">TServer</span> <span class="hljs-variable">tServer</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">TSimpleServer</span>(tArgs);<br>        System.out.println(<span class="hljs-string">&quot;Running Simple Server&quot;</span>);<br>        tServer.serve();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>结合代码，从架构上看拆分了四层<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="https://juejin.cn/post/6844903622380093447">[1]</span></a></sup></p><ul><li>服务层 <code>HelloWorldServiceImpl</code></li><li>处理层 <code>HelloWorldService.Processor</code></li><li>协议层 <code>TBinaryProtocol.Factory</code></li><li>传输层 <code>TServerSocket</code></li></ul><p><img src="/assets/imgs/thrift-arch-2022-04.png"title="https://www.cnblogs.com/mymelody/p/9474300.html" /></p><h2 id="分析">分析</h2><h3 id="为什么是-4-层">为什么是 4 层</h3><p>正常来说一个系统会考虑这么几个最近本的问题：具体功能、吞吐量、作业时间,显然thrift考虑到了下面几个问题</p><ol type="1"><li><p>传输信道是什么？系统吞吐量如何？</p></li><li><p>协议封装格式是什么？ 协议封装格式是否合法？</p></li><li><p>一个系统不可能只有一个服务，不同服务请求的服务如何进行 路由分发？</p></li><li><p>具体服务响应是什么，以何种封装格式返回？</p></li></ol><h3 id="设计者-使用者">设计者-使用者</h3><p>一个系统永远可以从设计者与使用者两个视角去看。thrift也不例外，暂时叫做<code>协议框架设计者</code>与<code>协议使用者</code>，处理层是两者的边界，利用<code>钩子（注入）</code>进行关注点分离。</p><ul><li><p>协议框架设计者</p><p>关注的整体的效率吞吐量、idl compiler工具，</p></li><li><p>协议使用者</p><p>关注的是工具易用性、接入成本。</p></li></ul><h3 id="标准---工具">标准 - 工具</h3><p>相比HTTP,thrift在模型上来说改进上进步不大，都在应用层。但通过对idl增加约束，引入工具，在工程上前进了一步，形成接口描述统一语言，减少沟通歧义。</p><h3 id="设计工具留意的其他点">设计工具留意的其他点</h3><ul><li><p>学习成本</p><p>带来额外的通信模型与工具学习成本</p></li><li><p>桥接</p><p>以开头代码为例，idl生成的代码从一个项目角度来说是在接口层，接口层有一套自己的设计模型理念，服务层使用的工具有一套工具理念。这层代码的桥接，本质是两种理念的差异弥合</p></li><li><p>兼容性</p><p>刨除标准版本升级导致的前向不兼容外，工具本身一定依赖其他工具，工具集合融入到另外的工具集合时就会遇到版本冲突问题。</p></li></ul><h2 id="参考资料">参考资料</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1"class="footnote-text"><span>https://juejin.cn/post/6844903622380093447<a href="#fnref:1" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:2"class="footnote-text"><span>https://www.cnblogs.com/mymelody/p/9474300.html<a href="#fnref:2" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:3"class="footnote-text"><span>https://www.bilibili.com/video/BV1aL4y157s7<a href="#fnref:3" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:4"class="footnote-text"><span>https://www.bilibili.com/video/BV1j44y1q7fy<a href="#fnref:4" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:5" class="footnote-text"><span>https://thrift.apache.org/<a href="#fnref:5" rev="footnote" class="footnote-backref">↩︎</a></span></span></li></ol></div></section>]]></content>
    
    
    <summary type="html">著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处</summary>
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="Thrift" scheme="https://noname007.github.io/tags/Thrift/"/>
    
  </entry>
  
  <entry>
    <title>迁回 Hexo</title>
    <link href="https://noname007.github.io/2022/03/26/blog-migration.html"/>
    <id>https://noname007.github.io/2022/03/26/blog-migration.html</id>
    <published>2022-03-26T11:18:34.000Z</published>
    <updated>2024-07-02T14:27:27.607Z</updated>
    
    <content type="html"><![CDATA[<h2 id="hexo-2-jekyll">Hexo 2 Jekyll</h2><p>Hexo是最早使用的工具，样式精美，主题切换也是很方，便部署工具友好。年轻总爱折腾，16年切换到了Jekyll。Jekeyll主题很多，但切换麻烦。本地构建预览，每隔一段时间总是莫名的出一些问题，小问题不死人但很恶心人。</p><h2 id="jekyll-2-org">Jekyll 2 Org</h2><p>有段时间用 Emacs 很上头， 感觉 Markdown 好难用，折腾了一段时间Org。DIY 友好，运维部署稍微有点麻烦。</p><h2 id="hexo">Hexo</h2><p>对于工具简单前段时间就想着牵回来了，这么多年了，牵回来也每个头绪，心烦意乱，往后拖延~</p><p>最近看 Manjaro 电报群 的劝退文https://blog.zhullyb.top/2021/01/01/Why-I-dont-recommend-Manjaro/发现这个Hexo主题不错，Fluid<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="https://hexo.fluid-dev.com/docs/guide/#slogan-%E6%89%93%E5%AD%97%E6%9C%BA">[3]</span></a></sup>文档也不错，瞬间感觉有点世外桃源人了，趁着有点心气，赶紧折腾下，切换回来。</p><p>老二最好的策略就是模仿，恩，开干，Hexo + Fluid + Waline</p><p>Hexo 升级<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="https://coldnew.github.io/hexo-org-example/2017/03/05/getting-started-with-hexo-and-org-mode/">[2]</span></a></sup><sup id="fnref:4" class="footnote-ref"><a href="#fn:4" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="https://leimingshan.com/posts/d9017f30/">[4]</span></a></sup></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs sh">// 配置镜像源<br>&gt; npm config <span class="hljs-built_in">set</span> registry https://registry.npmmirror.com<br><br>//以下指令均在Hexo目录下操作，先定位到Hexo目录<br>//查看当前版本，判断是否需要升级<br>&gt; hexo version<br><br>//全局升级hexo-cli<br>&gt; npm i hexo-cli -g<br><br>//再次查看版本，看hexo-cli是否升级成功<br>&gt; hexo version<br><br>//安装npm-check，若已安装可以跳过<br>&gt; npm install -g npm-check<br><br>//检查系统插件是否需要升级<br>&gt; npm-check<br><br>//安装npm-upgrade，若已安装可以跳过<br>&gt; npm install -g npm-upgrade<br><br>//更新package.json<br>&gt; npm-upgrade<br><br>//更新全局插件<br>&gt; npm update -g<br><br>//更新系统插件<br>&gt; npm update --save<br><br>//再次查看版本，判断是否升级成功<br>&gt; hexo version<br><br>// 安装 hexo 依赖<br>&gt; npm i -d<br><br>// 更新 hexo 依赖<br>&gt; npm update -d<br></code></pre></td></tr></table></figure><p>Hexo + Fluid + Waline + Emacs + Markdown就是以后的写字基本工具了。</p><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1"class="footnote-text"><span>http://mpwang.github.io/2019/02/13/how-to-write-hexo-blog-with-org-mode/<a href="#fnref:1" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:2"class="footnote-text"><span>https://coldnew.github.io/hexo-org-example/2017/03/05/getting-started-with-hexo-and-org-mode/<a href="#fnref:2" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:3"class="footnote-text"><span>https://hexo.fluid-dev.com/docs/guide/#slogan-%E6%89%93%E5%AD%97%E6%9C%BA<a href="#fnref:3" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:4"class="footnote-text"><span>https://leimingshan.com/posts/d9017f30/<a href="#fnref:4" rev="footnote" class="footnote-backref">↩︎</a></span></span></li></ol></div></section>]]></content>
    
    
    <summary type="html">👌</summary>
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="hexo" scheme="https://noname007.github.io/tags/hexo/"/>
    
    <category term="jekyll" scheme="https://noname007.github.io/tags/jekyll/"/>
    
    <category term="emacs" scheme="https://noname007.github.io/tags/emacs/"/>
    
    <category term="waline" scheme="https://noname007.github.io/tags/waline/"/>
    
    <category term="org" scheme="https://noname007.github.io/tags/org/"/>
    
  </entry>
  
  <entry>
    <title>工作居住证</title>
    <link href="https://noname007.github.io/2022/03/25/gongzuojuzhuzheng.html"/>
    <id>https://noname007.github.io/2022/03/25/gongzuojuzhuzheng.html</id>
    <published>2022-03-25T05:09:03.000Z</published>
    <updated>2024-11-21T09:17:56.604Z</updated>
    
    <content type="html"><![CDATA[<p><ahref="https://fw.bjrcgz.gov.cn/person-platform/#/person-platform/overview">人才业务办理平台</a></p><h2 id="系统老旧升级缓慢">系统老旧，升级缓慢</h2><p>20年9月份，公司给员工统一办理工作居住证，这项业务已暂停办理有两年左右了。分批办理，每两周一批。本批次没有凑集够材料的，下批次继续。公司投入人力巨大，自己前前后后弄了有将近一个月的时间才把材料凑全，唯一的感觉时政务信息系统不是一般的难用。</p><p>收集的材料基本全是税收、社保、居住、学历学位、婚姻、职称等相关的材料，有些系统当时比较老旧只支持IE 8，搞得我还装了个虚拟机，在里装了一个已经废弃好多年不用的 XP系统跑IE。</p><p>前段时间换了工作，工作居住证需要办理交接，按照公司的行政同事的指南操作，发现系统改进不少。首先系统升级了，登录方式也便捷不少，支持医保卡、北京通、微信、支付宝等多种常见的方式。但是进来后发现原先的资料都丢了，最后从同事处得知，21年7月份系统升级的时候，此前所有材料全部丢失，需要自行补充，<code>囧</code>。</p><h2 id="事虽小侮辱性大">事虽小，侮辱性大</h2><p>时间拖的一般非常长，社保、税收公司交替，前面做完公司关联，后面等一个月后再补充材料，拖拖拉拉，毫无效率，前面看的指导文档，后面估计还需要再看一边。从总耗费时间上来，大概需要16-24小时。我的同事就因为这玩意复杂，换工作后就一直没弄。每次需要准备材料，电子版的先打印、找人盖章、再扫描电子化，这个操作也跟吃屎一样蠢。经过多次折磨都快患上政务办理PTSD了。</p><h2 id="信息系统基础建设问题多">信息系统基础建设问题多</h2><p>从正常人的理解，税收、社保、居住、学历学位、婚姻、职称、征信、房产、医疗、行程流调这些个人信息资料其实都在国家的数据系统中，为啥办理事情的时候效率总是这么低呢？</p><p>基本就是信息系统建设的缺失。</p><p>首先这些数据在政府内部其实是不互通的，从每次办理事情都需要提供户口本首页、本人页面，结婚证信息，可以看到信息时跨职能部门不通的，医疗信息，你会发现各家公立医院是根本不互通的，有很多重复检查的问题<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="2022年3月1日起看病换医院不需重复检查 https://www.thepaper.cn/newsDetail_forward_16905493">[1]</span></a></sup>。政府部门跨地域，跨职能数据并没有很有效的打通，导致办事效率很低。当然也有些改进的地方，流调，交通违章信息基本上打通了，身份证可以异地办理了，但瑜不掩瑕。</p><p>其次，这些数据如何被开放使用，以及如何使用做的并不好。使用的过程中明显能感觉出来，他们之间有<code>信息流鸿沟</code>。而填平鸿沟的方式就是每一个人在不同的系统之间来回腾挪，浪费时间，在一个系统查询、下载、打印、扫描电子化，在另外一个系统提交、审核、入系统。</p><p>系统间信息流合法授权使用有时候说简单确实也很简单，只需要做好两件事情- 请求获取数据的人是有权获取的 - 提交数据的人数据就是他本人的数据</p><p>剩下的靠信息系统之间打通就可以了，人在信息系统中所做的事情其实只是授权给信息系统代理而已。</p><p>平衡好授权使用与安全问题很难。方式可能也有很多方式去解决，但肯定不是现在这么愚蠢的方式。</p><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1"class="footnote-text"><span>2022年3月1日起看病换医院不需重复检查https://www.thepaper.cn/newsDetail_forward_16905493<a href="#fnref:1" rev="footnote" class="footnote-backref">↩︎</a></span></span></li></ol></div></section>]]></content>
    
    
    <summary type="html">😮‍💨 垃圾的政务信息系统</summary>
    
    
    
    <category term="随笔" scheme="https://noname007.github.io/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
  </entry>
  
  <entry>
    <title>manjaro 使用攻略</title>
    <link href="https://noname007.github.io/2022/03/18/manjaro.html"/>
    <id>https://noname007.github.io/2022/03/18/manjaro.html</id>
    <published>2022-03-18T13:02:56.000Z</published>
    <updated>2025-05-25T14:02:14.519Z</updated>
    
    <content type="html"><![CDATA[<h2 id="yay">yay</h2><h3 id="pkg-构建目录">pkg 构建目录</h3><pre><code class="hljs">`~/.cache/yay/pkg/`</code></pre><p>安装过程有时会出现问题，手工介入，解决速度会快不少。先前装drawio的时候经常下载一般旧挂了，浏览器下载完后copy到目录 重新yay 可以继续往后执行。</p><h3 id="安装过程修改-pkgbuild">安装过程修改 PKGBUILD</h3><p>需要联公司VPN，本来琢磨 openconnect应该能解决问题，没想到某服自己单独玩了一套。安装EeasyConnect时，官方提供的最新的包版本号没有变，但是md5 发生了变化，不知道他们在搞什么鬼。</p><p>着急用，需要修改PKGBUILD 尝试了两个没有效果的解决方案<code>yay --editmenu --save</code><code>yay -G &lt;pkgname&gt;</code>，直接进入构建目录后，手动修改构建脚本快，改完后编译、安装一分钟不到解决问题</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">cd .cache/yay/easyconnect<br>md5sum <br>makepkg -si<br></code></pre></td></tr></table></figure><h3 id="清缓存">清缓存</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sh">yay -Sc<br><br>pacman -Qqtd| <span class="hljs-built_in">sudo</span> pacman -Rs -<br><br>paru -Sc<br></code></pre></td></tr></table></figure><p>跑了有一年多了前段时间看硬盘不足二十个G了，清清缓存节约了 80G</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs log">[2020-11-04T19:24:07+0800] [ALPM] running &#x27;update-mime-database.hook&#x27;...<br>[2020-11-04T19:28:49+0800] [PACMAN] Running &#x27;pacman -Syyu&#x27;<br></code></pre></td></tr></table></figure><h2 id="backup">backup</h2><h3 id="系统引导修复">系统引导修复</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs shell">lsblk -f<br>vim /etc/fstab<br>mkinitcpio -P<br><br>grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --recheck --force<br>update-grub<br></code></pre></td></tr></table></figure><h2 id="virtualbox">VirtualBox</h2><h3 id="virtualbox-cannot-register-the-hard-disk-解决办法-1">VirtualBox– Cannot register the hard disk 解决办法<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="https://stackoverflow.com/questions/44114854/virtualbox-cannot-register-the-hard-disk-already-exists">[1]</span></a></sup></h3><pre><code class="hljs">File -&gt; Virtual Media Manager -&gt; Removed existing images (note, I removed them only from the registry).</code></pre><h3 id="压缩-vdi-磁盘大小">压缩 vdi 磁盘大小</h3><p>vmdk 格式不支持，vdi 压缩前，需要先进入虚拟机中把没有使用的磁盘位置填充 0</p><p>windows工具：<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="https://www.appinn.com/sdelete-for-windows/">[2]</span></a></sup></p><pre><code class="hljs">https://docs.microsoft.com/en-us/sysinternals/downloads/sdelete</code></pre><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">sdelete64.exe -z c:  <span class="hljs-comment"># 对 C 盘剩余空间写零，适合虚拟磁盘优化</span><br></code></pre></td></tr></table></figure><p>Linux 工具：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh"><span class="hljs-built_in">dd</span> <span class="hljs-keyword">if</span>=/dev/zero of=./1.img bs=102400 status=progress <br></code></pre></td></tr></table></figure><p>Manjaro 宿主机中执行如下压缩指令</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">VBoxManage modifymedium  docs.vdi --compact<br></code></pre></td></tr></table></figure><h2 id="vmware">vmware</h2><h2 id="wayland">Wayland</h2><p>wayland<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="https://bbs.archlinuxcn.org/viewtopic.php?id=11294">[3]</span></a></sup><sup id="fnref:4" class="footnote-ref"><a href="#fn:4" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="https://fcitx-im.org/wiki/Setup_Fcitx_5">[4]</span></a></sup><sup id="fnref:5" class="footnote-ref"><a href="#fn:5" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="https://wiki.archlinux.org/title/wayland#Qt">[5]</span></a></sup><sup id="fnref:6" class="footnote-ref"><a href="#fn:6" rel="footnote"><spanclass="hint--top hint--rounded"aria-label="https://batsov.com/articles/2021/12/19/building-emacs-from-source-with-pgtk/">[6]</span></a></sup></p><h3 id="fcitx5">fcitx5</h3><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs routeros"><span class="hljs-comment">#/etc/profile</span><br><span class="hljs-built_in">export</span> <span class="hljs-attribute">GTK_IM_MODULE</span>=fcitx5<br><span class="hljs-built_in">export</span> <span class="hljs-attribute">QT_IM_MODULE</span>=fcitx5<br><span class="hljs-built_in">export</span> <span class="hljs-attribute">XMODIFIERS</span>=<span class="hljs-string">&quot;@im=fcitx5&quot;</span><br></code></pre></td></tr></table></figure><h3 id="firefox-thunderbird">Firefox thunderbird</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">MOZ_ENABLE_WAYLAND=1<br></code></pre></td></tr></table></figure><h3 id="chromium">chromium</h3><pre><code class="hljs">--ozone-platform-hint=autovivaldi-stable --ozone-platform-hint=autochrome://flags 裏面就可以開啓wayland</code></pre><h3 id="qt">QT</h3><pre><code class="hljs"> QT_QPA_PLATFORM=&quot;wayland;xcb&quot;</code></pre><h3 id="emacs">Emacs</h3><pre><code class="hljs">./configure --enable-largefile --with-all --with-json --with-imagemagick --with-threads --with-pgtk --with-native-compilation --with-native-image-api</code></pre><h2 id="ref">Ref</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1"class="footnote-text"><span>https://stackoverflow.com/questions/44114854/virtualbox-cannot-register-the-hard-disk-already-exists<a href="#fnref:1" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:2"class="footnote-text"><span>https://www.appinn.com/sdelete-for-windows/<a href="#fnref:2" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:3"class="footnote-text"><span>https://bbs.archlinuxcn.org/viewtopic.php?id=11294<a href="#fnref:3" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:4"class="footnote-text"><span>https://fcitx-im.org/wiki/Setup_Fcitx_5<a href="#fnref:4" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:5"class="footnote-text"><span>https://wiki.archlinux.org/title/wayland#Qt<a href="#fnref:5" rev="footnote" class="footnote-backref">↩︎</a></span></span></li><li><span id="fn:6"class="footnote-text"><span>https://batsov.com/articles/2021/12/19/building-emacs-from-source-with-pgtk/<a href="#fnref:6" rev="footnote" class="footnote-backref">↩︎</a></span></span></li></ol></div></section>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;yay&quot;&gt;yay&lt;/h2&gt;
&lt;h3 id=&quot;pkg-构建目录&quot;&gt;pkg 构建目录&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;`~/.cache/yay/pkg/`&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装过程有时会出现问题，手工介入，解决速度会快不少。</summary>
      
    
    
    
    
    <category term="Linux" scheme="https://noname007.github.io/tags/Linux/"/>
    
    <category term="软件工具" scheme="https://noname007.github.io/tags/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>Go 语言学习笔记</title>
    <link href="https://noname007.github.io/2022/01/14/Go-Notes.html"/>
    <id>https://noname007.github.io/2022/01/14/Go-Notes.html</id>
    <published>2022-01-14T04:36:17.000Z</published>
    <updated>2025-05-25T17:06:14.687Z</updated>
    
    <content type="html"><![CDATA[<h2 id="工具链">工具链</h2><h3 id="gopath">GOPATH</h3><p><code>go env|grep GOPATH</code></p><h3 id="go-mod">go mod</h3><figure><img src="/assets/imgs/2022-04-09_17-24.png" title="a"alt="go help modules" /><figcaption aria-hidden="true">go help modules</figcaption></figure><h3 id="go-mod-代理设置">go mod 代理设置</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">go <span class="hljs-built_in">env</span> -w GO111MODULE=on<br>go <span class="hljs-built_in">env</span> -w GOPROXY=https://goproxy.cn,direct<br>go <span class="hljs-built_in">env</span> -w GOPRIVATE=code.devops.xiaohongshu.com <br>git config --global url.<span class="hljs-string">&quot;git@code.devops.xiaohongshu.com:&quot;</span>.insteadof <span class="hljs-string">&quot;http://code.devops.xiaohongshu.com/&quot;</span><br>git config --global url.<span class="hljs-string">&quot;git@code.devops.xiaohongshu.com:&quot;</span>.insteadof <span class="hljs-string">&quot;https://code.devops.xiaohongshu.com/&quot;</span><br></code></pre></td></tr></table></figure><h3 id="goland">Goland</h3><h4 id="对-go-get-引入的包不能自动提示">对 go get引入的包不能自动提示</h4><p>有说需要清理缓存重新启动，有说需要 go mod vendor， 但都不起效，加上<code>-u</code> 选项就起效了 <code>go get -u -x -v pkg_url</code></p><h2 id="语言">语言</h2><h3 id="channel">channel</h3><h4 id="读写操作">读写操作</h4><figure><img src="/assets/imgs/2024-07-02-go-channel.png"alt="read、write、close" /><figcaption aria-hidden="true">read、write、close</figcaption></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;工具链&quot;&gt;工具链&lt;/h2&gt;
&lt;h3 id=&quot;gopath&quot;&gt;GOPATH&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;go env|grep GOPATH&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;go-mod&quot;&gt;go mod&lt;/h3&gt;
&lt;figure&gt;
&lt;img src=&quot;/ass</summary>
      
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="Golang" scheme="https://noname007.github.io/tags/Golang/"/>
    
  </entry>
  
  <entry>
    <title>yii2-queue Redis 源码分析</title>
    <link href="https://noname007.github.io/2021/12/27/yii2-queue.html"/>
    <id>https://noname007.github.io/2021/12/27/yii2-queue.html</id>
    <published>2021-12-27T06:28:34.000Z</published>
    <updated>2025-05-25T13:15:41.518Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简介">简介</h2><p>一个流行的 Yii2 队列扩展，支持驱动支持DB,Redis,RabbitMQ,Bestalk,German等十多种技术架构</p><p>源码地址: https://github.com/yiisoft/yii2-queue</p><p>下面对以 Redis 为驱动的实现源码进行简要分析</p><h2 id="特性">特性</h2><ol type="1"><li>延迟消息</li><li>重试机制</li><li>消息主题</li><li>消费者组</li><li>消费确认</li></ol><h2 id="数据结构">数据结构</h2><table><thead><tr><th>名称</th><th>redis 数据类型</th><th>作用</th></tr></thead><tbody><tr><td>$(channel).moving_lock</td><td>string</td><td>分布式锁，部分队列操作，需要进行加锁处理</td></tr><tr><td>$(channel).waiting</td><td>list</td><td>待处理消息队列</td></tr><tr><td>$(channel).reserved</td><td>zset</td><td>重试队列，用于业务处理失败情况下，进行下次重试</td></tr><tr><td>$(channel).delayed</td><td>zset</td><td>延迟队列，对消息进行延迟处理</td></tr><tr><td>$(channel).attempts</td><td>hash</td><td>存存每条消息重试处理次数</td></tr><tr><td>$(channel).messages</td><td>hash</td><td>完整的消息</td></tr></tbody></table><h2 id="流程">流程</h2><p><img src="/assets/imgs/2024-07-01-yii2-queue-redis-3.png"title="主流程" alt="alt text" /> <imgsrc="/assets/imgs/2024-07-01-yii2-queue-redis.png" title="reserve"alt="alt text" /> <imgsrc="/assets/imgs/2024-07-01-yii2-queue-redis-2.png" title="moveExpire"alt="alt text" /></p><h2 id="pr">PR</h2><p><ahref="https://github.com/yiisoft/yii2-queue/pull/516/files">yii2-queue#516</a></p>]]></content>
    
    
    <summary type="html">源码解析</summary>
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="PHP" scheme="https://noname007.github.io/tags/PHP/"/>
    
    <category term="Yii2" scheme="https://noname007.github.io/tags/Yii2/"/>
    
    <category term="消息队列" scheme="https://noname007.github.io/tags/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/"/>
    
    <category term="Redis" scheme="https://noname007.github.io/tags/Redis/"/>
    
  </entry>
  
  <entry>
    <title>Go Sarama Kafka 数据丢失分析</title>
    <link href="https://noname007.github.io/2021/12/23/go-invalid-mem-address.html"/>
    <id>https://noname007.github.io/2021/12/23/go-invalid-mem-address.html</id>
    <published>2021-12-23T08:56:36.000Z</published>
    <updated>2025-05-25T14:08:29.266Z</updated>
    
    <content type="html"><![CDATA[<h2 id="背景与过程">背景与过程</h2><p>维护的一个线上工单同步项目，业务方反馈工单不能同步。线上排查过程简单总结一下。</p><p>查看定时任务，没什么问题，每分钟会检测进程是否存活，挂掉的会拉起来，并且每天凌晨也会重启一下进程。</p><p>查看进程状态也是在<code>S</code>态，运行时间也无异常</p><p>日志中有一条如下记录：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">invalid memory address or nil pointer dereference<br>exit....<br></code></pre></td></tr></table></figure><p>很奇怪，日志记录 exit，但是进程却没有退出。</p><p>排查源码，结构逻辑类似下面代码样例</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br><span class="hljs-string">&quot;fmt&quot;</span><br><span class="hljs-string">&quot;github.com/Shopify/sarama&quot;</span><br><span class="hljs-string">&quot;github.com/astaxie/beego/logs&quot;</span><br>cluster <span class="hljs-string">&quot;github.com/bsm/sarama-cluster&quot;</span><br><span class="hljs-string">&quot;os&quot;</span><br><span class="hljs-string">&quot;os/signal&quot;</span><br><span class="hljs-string">&quot;runtime/debug&quot;</span><br><span class="hljs-string">&quot;syscall&quot;</span><br>)<br><br><span class="hljs-keyword">var</span> (<br>cfg <span class="hljs-keyword">struct</span> &#123;<br>Kafka <span class="hljs-keyword">struct</span> &#123;<br>Addrs  []<span class="hljs-type">string</span><br>Group  <span class="hljs-type">string</span><br>Topics []<span class="hljs-type">string</span><br>&#125;<br>&#125;<br>)<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">bussiness_logic</span><span class="hljs-params">(message *sarama.ConsumerMessage)</span></span> &#123;<br><span class="hljs-built_in">println</span>(<span class="hljs-string">&quot;pass&quot;</span>)<br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> &#123;<br><br><span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br><br><span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">if</span> err := <span class="hljs-built_in">recover</span>(); err != <span class="hljs-literal">nil</span> &#123;<br>fmt.Println(err)<br>fmt.Println(<span class="hljs-string">&quot;exit....&quot;</span>)<br>&#125;<br>&#125;()<br><br><span class="hljs-keyword">var</span> (<br>kafkaConfig   = cluster.NewConfig()<br>KafkaConsumer *cluster.Consumer<br>err           <span class="hljs-type">error</span><br>)<br><br>kafkaConfig.Consumer.Return.Errors = <span class="hljs-literal">true</span><br>kafkaConfig.Group.Return.Notifications = <span class="hljs-literal">true</span><br><br><span class="hljs-keyword">if</span> KafkaConsumer, err = cluster.NewConsumer(cfg.Kafka.Addrs, cfg.Kafka.Group, cfg.Kafka.Topics, kafkaConfig); err != <span class="hljs-literal">nil</span> &#123;<br><span class="hljs-built_in">panic</span>(err)<br>&#125;<br><br><span class="hljs-keyword">defer</span> KafkaConsumer.Close()<br><br><span class="hljs-keyword">var</span> (<br>message      *sarama.ConsumerMessage<br>notification *cluster.Notification<br>)<br><br><span class="hljs-keyword">for</span> &#123;<br><span class="hljs-keyword">select</span> &#123;<br><span class="hljs-keyword">case</span> message = &lt;-KafkaConsumer.Messages():<br>bussiness_logic(message)<br><span class="hljs-keyword">case</span> notification = &lt;-KafkaConsumer.Notifications():<br>logs.Info(<span class="hljs-string">&quot;kafka notification:%v&quot;</span>, notification)<br><span class="hljs-keyword">case</span> err = &lt;-KafkaConsumer.Errors():<br>logs.Info(<span class="hljs-string">&quot;kafka error:%v&quot;</span>, err)<br>&#125;<br>&#125;<br>&#125;()<br><br>c := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> os.Signal, <span class="hljs-number">1</span>)<br>signal.Notify(c)<br>signal.Ignore(syscall.SIGPIPE, syscall.SIGWINCH, syscall.SIGHUP, syscall.SIGURG)<br><br>s := &lt;-c<br>fmt.Println(s)<br>debug.PrintStack()<br>&#125;<br><br></code></pre></td></tr></table></figure><p>bussiness_logic 出现指针访问异常后，go 协程会被回收，执行<code>defer</code>语句。问题出在了这里，记录完日志后，没调用<code>os.Exit(1)</code></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs go">      <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">if</span> err := <span class="hljs-built_in">recover</span>(); err != <span class="hljs-literal">nil</span> &#123;<br>fmt.Println(err)<br>fmt.Println(<span class="hljs-string">&quot;exit....&quot;</span>)<br>&#125;<br>&#125;()<br></code></pre></td></tr></table></figure><h2 id="消息为什么会丢失">消息为什么会丢失？</h2><p><img src="/assets/go-sarama.consumer-3.png" /></p><ul><li>协程 hbloop： 间隔一段时间向broker发送心跳的</li><li>协程 parseResponse,responseFeeder：会将获取到的 kafka批量消息存到内存 <code>[]*ConsumerMmessage&#123;&#125;</code> 中,并通过管道<code>chan *ConsumerMmessage</code>发送出去，应用程序从而能一条一条的消费</li></ul><h3 id="原因">原因</h3><p>应用程序在消费其中一条数据时，业务处理逻辑处理出现内存异常，协程退出，系统回收。内存中剩余未消费的消息旧在哪里一直不会被消费，等到进程通过信号终止时，这些消息被丢弃</p><h2 id="同类问题-demo">同类问题 demo</h2><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br><span class="hljs-string">&quot;fmt&quot;</span><br><span class="hljs-string">&quot;sync&quot;</span><br><span class="hljs-string">&quot;time&quot;</span><br>)<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> &#123;<br><br><span class="hljs-keyword">var</span> f <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span><br><br>c := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-type">int</span>)<br>d := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-type">int</span>)<br>wg := sync.WaitGroup&#123;&#125;<br>wg.Add(<span class="hljs-number">1</span>)<br>    <br>f = <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">if</span> err := <span class="hljs-built_in">recover</span>(); err != <span class="hljs-literal">nil</span> &#123;<br>fmt.Println(err)<br>fmt.Println(<span class="hljs-string">&quot;exit....&quot;</span>)<br>&#125;<br>&#125;()<br><br><span class="hljs-keyword">for</span> data := <span class="hljs-keyword">range</span> d &#123;<br>fmt.Printf(<span class="hljs-string">&quot;cosume %d\n &quot;</span>, data)<br><br><span class="hljs-keyword">var</span> i *<span class="hljs-type">int</span><br>*i = <span class="hljs-number">1</span><br>fmt.Println(i, &amp;i, *i)<br>&#125;<br><br>&#125;<br><br><span class="hljs-keyword">go</span> f()<br><br><span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">for</span> &#123;<br><span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;-\n&quot;</span>)<br><span class="hljs-keyword">select</span> &#123;<br><span class="hljs-keyword">case</span> x, _ := &lt;-c:<br>d &lt;- x<br>&#125;<br>&#125;<br>&#125;()<br><br><span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++ &#123;<br>fmt.Printf(<span class="hljs-string">&quot;producing %d:\n&quot;</span>, i)<br>c &lt;- i<br>&#125;<br>&#125;()<br>    <br>    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br>t := time.Tick(<span class="hljs-number">1</span> * time.Second)<br><br><span class="hljs-keyword">for</span> &#123;<br><span class="hljs-keyword">select</span> &#123;<br><span class="hljs-keyword">case</span> &lt;-t:<br><span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;--\n&quot;</span>)<br><br>&#125;<br>&#125;<br>&#125;()<br><br>time.Sleep(<span class="hljs-number">1</span> * time.Second)<br><br>wg.Wait()<br><br>&#125;<br></code></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">线上故障排查笔记</summary>
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="Golang" scheme="https://noname007.github.io/tags/Golang/"/>
    
    <category term="Kafka" scheme="https://noname007.github.io/tags/Kafka/"/>
    
    <category term="消息队列" scheme="https://noname007.github.io/tags/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/"/>
    
  </entry>
  
  <entry>
    <title>50岁后继续干程序员</title>
    <link href="https://noname007.github.io/2021/11/30/dairy.html"/>
    <id>https://noname007.github.io/2021/11/30/dairy.html</id>
    <published>2021-11-30T12:48:52.000Z</published>
    <updated>2025-05-25T14:07:35.199Z</updated>
    
    <content type="html"><![CDATA[<p>这个问题困扰我很久，也很难想明白，想明白太难，先写点文字</p><p>35岁后还能干的动吗？总不能一辈子干大头兵吧？总得往上走一走吧，转管理、带项目、干产品、考公务员...？</p><p>感觉都很焦虑，随着岁数增长体力不支，家庭负担过大精力不够，和年轻全员工比会被公司抛弃，搞得好像行业呆的太久，就会被“体制化”无法融入社会再就业。</p><p>干的事情杂七杂八，没有目标方向感，挣扎在生死的边界线上。</p><p>但是仔细想想这就是现实吗，转行干哪一行不都面临这些问题？</p><p>//--------------------------------------------------------------程序员行业从大的方向来说是信息产业，行业前景很宽阔。</p><p>为什么不反过来想想我们做这些事情的目的是什么呢？社会价值是什么呢？互联网难到真是人类历史的一条弯路吗？</p><p>都说市场是一个无形的手，优化资产配置效率，但是不受管束的资本会把人推向深渊。</p><p>匹夫无罪，怀璧其罪，</p><h3 id="继续干程序员">继续干程序员 ？</h3><h3 id="转行">转行</h3><h4 id="转-管理">转 管理</h4><h4 id="转-pm">转 PM</h4><p><ahref="https://mp.weixin.qq.com/s/BLuViOaJdceYHzTuuPr1DQ">《读书》新刊 |王路：我的逻辑启蒙老师</a></p><p><ahref="https://www.douban.com/note/271659319/">怎样把书读得“四通八达”？（文/王路）</a></p><p>** 工程学方法 抓大方小，逐步求精 分而治之 编程随想单一信息源不可靠</p><p>这俩人同名</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;这个问题困扰我很久，也很难想明白，想明白太难，先写点文字&lt;/p&gt;
&lt;p&gt;35
岁后还能干的动吗？总不能一辈子干大头兵吧？总得往上走一走吧，转管理、带项目、干产品、考公务员...？&lt;/p&gt;
&lt;p&gt;感觉都很焦虑，随着岁数增长体力不支，家庭负担过大精力不够，和年轻全员工比会被公司</summary>
      
    
    
    
    <category term="职场" scheme="https://noname007.github.io/categories/%E8%81%8C%E5%9C%BA/"/>
    
    
  </entry>
  
  <entry>
    <title>jekyll 使用中遇到的问题</title>
    <link href="https://noname007.github.io/2021/11/30/jekyll-qa.html"/>
    <id>https://noname007.github.io/2021/11/30/jekyll-qa.html</id>
    <published>2021-11-30T11:59:05.000Z</published>
    <updated>2024-07-02T14:47:00.946Z</updated>
    
    <content type="html"><![CDATA[<p>系统用的 Manjaro用的软件库都比较新，过一段时间就跑不动了，记录下问题。</p><h2 id="liquid-syntax-error-line-52-unexpected-character-in">Liquidsyntax error (line 52): Unexpected character $ in</h2><pre><code class="hljs">https://github.com/jekyll/jekyll/issues/5663</code></pre><figure class="highlight handlebars"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs handlebars"><span class="hljs-template-variable">&#123;&#123; <span class="hljs-name">ppp</span> &#125;&#125;</span><br></code></pre></td></tr></table></figure><h2id="libffi.so.7-cannot-open-shared-object-file-no-such-file-or-directory">libffi.so.7:cannot open shared object file: No such file or directory</h2><p>开始用软链接的方式，以前碰到过类似问题，能过去，但这次没不去。重新安装ffi gems 会重新编译一遍依赖的ffi，问题解决。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs shell">➜  jekyll s<br>&lt;internal:/usr/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb&gt;:85:in `require&#x27;: libffi.so.7: cannot open shared object file: No such file or directory - <br>...<br>...<br>...<br><br>➜  sudo ln /usr/lib/libffi.so -s /usr/lib/libffi.so.7<br><br>➜  jekyll s<br>&lt;internal:/usr/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb&gt;:85:in `require&#x27;: /usr/lib/libffi.so.7: version `LIBFFI_CLOSURE_7.0&#x27; not found (required by /home/<br>...<br>...<br>...<br><br><br>➜   gem install ffi<br></code></pre></td></tr></table></figure><p>顺带学习了几个命令挺好 <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">➜ pacman -Si libffi<br>➜ man ffi<br></code></pre></td></tr></table></figure></p><p>https://bbs.archlinux.org/viewtopic.php?id=270587</p><p>https://github.com/libffi/libffi/blob/master/include/ffi.h.inhttps://github.com/libffi/libffi/blob/master/src/x86/ffi.c</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;系统用的 Manjaro
用的软件库都比较新，过一段时间就跑不动了，记录下问题。&lt;/p&gt;
&lt;h2 id=&quot;liquid-syntax-error-line-52-unexpected-character-in&quot;&gt;Liquid
syntax error (line 52): </summary>
      
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="jekyll" scheme="https://noname007.github.io/tags/jekyll/"/>
    
  </entry>
  
  <entry>
    <title>使用 Python 多线程导入美餐就餐数据</title>
    <link href="https://noname007.github.io/2021/11/05/python-thread.html"/>
    <id>https://noname007.github.io/2021/11/05/python-thread.html</id>
    <published>2021-11-05T12:13:32.000Z</published>
    <updated>2025-05-25T12:32:40.888Z</updated>
    
    <content type="html"><![CDATA[<p>美餐每天发一个用Excel汇总的就餐数据，我们把它导入到数据库后，行政办公服务用它和公司内的就餐数据进行比对查重。</p><p>初始实现是单线程，和<code>import_records</code>去掉多线程后的部分差不多。</p><p><code>读取Excel数据</code> ---&gt;<code>发送到行政服务接口</code></p><p>安全起见线上操作放在了晚上进行。运行时发现每条数据导入消耗<code>1s</code>多，晚上十点开始跑这几千条数据想想都让人崩溃。</p><p>等着也是干等，下楼转两圈透透气，屋里龌龊的空气让人昏昏沉沉，寒冷让人清醒不少，突然想到为什么不用多线程呢？</p><p>第一版多线程和处理业务的程序糅合在了一起，跟屎一样难读。后面两天又抽了点时间重构了几个版本，分离出来一个线程池、迭代器和<code>import_records</code>。</p><p>清晰不少，但是迭代器被暴露了出来，需要<code>import_records</code>调用一下判断当前任务是否给当前线程处理，类似协程的思路。</p><p>暴露有好有坏，但已基本满足日常使用，可以往一边先放放了。读读书、看看电影，不亦乐乎：）。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> threading<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">task_pool</span>(<span class="hljs-params">thread_num, task_fn</span>):<br><br>  <span class="hljs-keyword">if</span> thread_num &lt;= <span class="hljs-number">0</span> :<br>      <span class="hljs-keyword">raise</span> ValueError<br><br>  threads = []<br><br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">gen_thread_checker</span>(<span class="hljs-params">thread_id, step</span>):<br><br>      base = <span class="hljs-number">1</span><br>      i = <span class="hljs-number">0</span><br><br>      <span class="hljs-keyword">def</span> <span class="hljs-title function_">thread_checker</span>():<br>          <span class="hljs-keyword">nonlocal</span> i<br><br>          i += <span class="hljs-number">1</span><br>          <span class="hljs-comment"># print((thread_id,i,step, i &lt; base or (i - base) % step != thread_id))</span><br><br>          <span class="hljs-keyword">if</span> i &lt; base <span class="hljs-keyword">or</span> (i - base) % step != thread_id:<br>              <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span><br><br>          <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span><br><br>      <span class="hljs-keyword">return</span> thread_checker<br><br><br>  <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">0</span>, thread_num):<br>    threads.append(threading.Thread(target=task_fn, args=(x,thread_num, gen_thread_checker(x, thread_num))))<br><br>  <span class="hljs-comment"># 启动所有线程</span><br>  <span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> threads:<br>    t.start()<br>  <span class="hljs-comment"># 主线程中等待所有子线程退出</span><br>  <span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> threads:<br>    t.join()<br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> argparse<br><span class="hljs-keyword">import</span> re<br><br><span class="hljs-keyword">import</span> requests<br><span class="hljs-keyword">from</span> openpyxl <span class="hljs-keyword">import</span> load_workbook<br><span class="hljs-keyword">from</span> requests <span class="hljs-keyword">import</span> RequestException<br><br><span class="hljs-keyword">import</span> myThread<br><br>parser = argparse.ArgumentParser(description=<span class="hljs-string">&#x27;美餐到店交易数据导入&#x27;</span>)<br>parser.add_argument(<span class="hljs-string">&#x27;--filename&#x27;</span>, <span class="hljs-string">&#x27;-f&#x27;</span>, <span class="hljs-built_in">help</span>=<span class="hljs-string">&#x27;美餐到店交易数据 .xlsx 文件路径&#x27;</span>, required=<span class="hljs-literal">True</span>)<br>parser.add_argument(<span class="hljs-string">&#x27;--thread_num&#x27;</span>, <span class="hljs-string">&#x27;-t&#x27;</span>, <span class="hljs-built_in">help</span>=<span class="hljs-string">&#x27;线程数量&#x27;</span>, default= <span class="hljs-number">100</span>, required=<span class="hljs-literal">False</span>)<br>parser.add_argument(<span class="hljs-string">&#x27;--debug&#x27;</span>, <span class="hljs-string">&#x27;-d&#x27;</span>, <span class="hljs-built_in">help</span>=<span class="hljs-string">&#x27;调试模式&#x27;</span>, default= <span class="hljs-number">0</span>, required=<span class="hljs-literal">False</span>)<br>args = parser.parse_args()<br><br>filename = args.filename<br>thread_num = <span class="hljs-built_in">int</span>(args.thread_num)<br>debug = args.debug<br><br><span class="hljs-keyword">if</span> debug:<br>    <span class="hljs-built_in">print</span>((filename,thread_num,debug))<br><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">add_meican_meal_record</span>(<span class="hljs-params">data</span>):<br>   <span class="hljs-keyword">pass</span><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">import_records</span>(<span class="hljs-params">thread_id, thread_number, thread_checker</span>):<br>    wb = load_workbook(filename=filename)<br>    ws = wb.active<br><br>    <span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> ws:<br>        <span class="hljs-comment">#------------------------------------------</span><br>        <span class="hljs-keyword">if</span> row[<span class="hljs-number">0</span>].value <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:<br>            <span class="hljs-keyword">break</span><br><br>        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> thread_checker():<br>            <span class="hljs-keyword">continue</span><br>        <span class="hljs-comment">#------------------------------------------</span><br><br>        <span class="hljs-keyword">if</span> row[<span class="hljs-number">0</span>].value == <span class="hljs-string">&#x27;日期&#x27;</span> <span class="hljs-keyword">or</span> row[<span class="hljs-number">0</span>].value == <span class="hljs-string">&#x27;总计&#x27;</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> re.findall(<span class="hljs-string">&#x27;^\d&#123;4&#125;-\d&#123;1,2&#125;-\d&#123;1,2&#125;$&#x27;</span>, row[<span class="hljs-number">0</span>].value):<br>            <span class="hljs-keyword">continue</span><br>        <span class="hljs-keyword">else</span>:<br><br>            date = <span class="hljs-built_in">str</span>.replace(row[<span class="hljs-number">0</span>].value,<span class="hljs-string">&#x27;-&#x27;</span>, <span class="hljs-string">&#x27;&#x27;</span>)<br><br>            order_id = row[<span class="hljs-number">3</span>].value<br>            restaurant_name = row[<span class="hljs-number">5</span>].value<br>            meal_plan_name = row[<span class="hljs-number">6</span>].value<br>            meal_staffid = row[<span class="hljs-number">10</span>].value<br>            identify = row[<span class="hljs-number">11</span>].value<br>    <br>            add_meican_meal_record(&#123;<br>                <span class="hljs-string">&#x27;orderId&#x27;</span>:order_id,<br>                <span class="hljs-string">&#x27;date&#x27;</span>: date,<br>                <span class="hljs-string">&#x27;meal_plan_name&#x27;</span>:meal_plan_name,<br>                <span class="hljs-string">&#x27;meal_staffid&#x27;</span>:meal_staffid,<br>                <span class="hljs-string">&#x27;identify&#x27;</span>:identify,<br>                <span class="hljs-string">&#x27;restaurant_name&#x27;</span>:restaurant_name<br>            &#125;)<br><br>myThread.task_pool(thread_num,import_records)<br></code></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;美餐每天发一个用Excel汇总的就餐数据，我们把它导入到数据库后，行政办公服务用它和公司内的就餐数据进行比对查重。&lt;/p&gt;
&lt;p&gt;初始实现是单线程，和&lt;code&gt;import_records&lt;/code&gt;去掉多线程后的部分差不多。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;读取Excel数</summary>
      
    
    
    
    <category term="技术" scheme="https://noname007.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="系统编程" scheme="https://noname007.github.io/tags/%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B/"/>
    
    <category term="Python" scheme="https://noname007.github.io/tags/Python/"/>
    
    <category term="并发" scheme="https://noname007.github.io/tags/%E5%B9%B6%E5%8F%91/"/>
    
  </entry>
  
</feed>
