某天晚上看到曹大在群里指点江山,折服。感叹为何曹大如此渊博,遂决定从头到尾研读完他所有的博文。

前后共花了一个月的时间,今天终于读完了(2020-11-24~2020-12-26),总共 118 篇。从 15 年 10 月 31 日开始的第一篇,到今天,总共写了 5 年多的时间。基本上每半个月产出一篇,非常稳定。

从最初讲具体的工作,例如将 MySQL 数据导入到 ES,到近期的《中台的末路》、《架构的腐化》、《工程师应该怎么学习》等名篇,水平一步步提高,视野也在一步步变大。

这些博文里很多内容都是从工作中提炼、总结出来的,这需要对自己所做的工作非常熟悉,并且需要做很多思考才行。这对我们而言,是有启发的。

还有一些是论文或文档的翻译,翻译它们而不是仅仅看一遍,对我们深刻理解内容是很有帮助的。连曹大都这样做了,我们有什么理由不做呢?

总的感受是,我们需要不断思考、反思、总结,并且持续不断地写出来。在所有文章里,如果只推荐一篇的话,那无疑就是《工程师应该怎么学习》这篇。其中最激励我的一段是:

人这一辈子,最重要的是能把路越走越宽。对于工程师来说,能够锻炼软技能的场合其实不是很多,但也不代表完全没有。即使没有也可以自己创造机会,例如组内、组间、部门内的技术分享都是不错的机会。

更大规模的技术分享可能因为主办方“势力眼”,在你级别不高或者影响力不大的时候,不提供给你这样的机会,但是作为一个向上的人,迟早会有走到这一步的一天。你所要做的是提前做好准备,在那一天到来的时候,在聚光灯下旁征博引,谈笑风生。

祝大家都能成为更好的自己!

我们只有保持终生学习的姿态,才有可能不被时代抛弃。

If you don’t keep moving, you’ll quickly fall behind.

下面是详细的文章内容介绍,最后有一张表格,列出了所有的文章链接和概览。因为 xargin.com 没有 archive 功能,所以这篇文章算是全网最全的、最方便的博客入口。


第 1 篇是 15 年 10 月 31 日开始的,到今天已经 5 年了,主要讲如何使用 vagrant 来搭建一套 lnmp(linux/nginx/mysql/php)开发环境,解决一些诸如只用线上才出现的 bug,以及新同学如何能快速搞定开发环境。

第 2 篇主要讲的是一致性哈希。我又查了下其他资料,总结下一致性哈希的优点:当增减 server 时,可以移动最少的 keys;因为数据是均匀分布的,所以更容易水平扩展。现实世界中,比如 Amazon’s Dynamo 数据库的分区组件、Apache Cassandra 跨集群的数据分区等等都用到了一致性哈希。

第 3 篇是将 MySQL 里的数据导入到 Solr 来满足一些特定的查询需求。

第 4 篇把 Solr 换成 ES,再来一次。

第 5 篇主要内容是说用 PHP 实现服务发现很难,不如 Java 那样方便。人家亚马逊的贝索斯在 2002 年就要求服务化,而阿里则是 2009 年则开始的。

第 6 篇主要内容是讲从 MySQL 导入大量数据时,碰到 GC 问题,导致连登陆都不行,最后通过加大 JVM 的运行时内存解决;Stop the world 机制简称 STW,即在执行垃圾收集算法时,Java 应用程序的其他所有除了垃圾收集帮助器线程之外的线程都被挂起;思想其实很朴素,用空间来换时间。

第 7 篇是关于乐观锁的内容,悲观锁用 select for 先锁定记录,然后再 update 更交换机数据;而乐观锁则比较前后的版本(例如订单,也可以比较 status)来解决并发更新时的数据覆盖问题。记住这个时间点,5 年前,曹大刚听说乐观锁^_^。

第 8 篇是一份 Redis 事务相关的文档翻译。主要内容有事务的使用、事务的错误、使用 watch 等。

第 9 篇还是一个连续剧,书接第 6 篇,在实际操作的过程中,将 MySQL 的数据导入到 ES 中遇到的导入数据有丢失的问题,主要原因是有主从延迟。处理办法就是每次都去向前多取一点:select * from [业务表] where update_time > date_sub(now(), 10 minute);。嗯,曹大会刻意总结在工作中遇到的问题及思考的解决方案,即使看起来比较简单。

第 10 篇其实也是一个续集(一致性哈希),书接第 2 篇,主要讲了 2 种缓存客户端如何生成虚拟节点的算法。并且,从这篇文章得知,PHP 是没有什么可以全局复用的全局变量的,所以每次 web 请求从 nginx 到 cgi 都会重新走各种 web 框架的 index.php。

第 11 篇主要是总结了在开发的过程中遇到的问题及解决方案,它同时也是曹大在公司做过的分享。比较重要的点有:没有填 update_time 而采用了 create_time,且由于主从同步或者时钟或者其他问题导致的“工单系统是有可能在未来创建过去一段时间的工单的”,或者说“一个时刻创建了在这个时刻之前”,这个是通过每次都向前多取 10 分钟的变更数据。还有一个点是每天低峰期删数据导致的大量删除 binlog 会对系统造成压力。另外,这时就已经用了 gin 框架了。

第 12 篇是对当时同步 MySQL 和 ES 的方案的一个总结反思,说明了优点和缺陷。嗯,估计是最后一篇了。前后也有四、五个月了。

第 13 篇曹大喷了一个 php 框架 laravel 里的一个实现:闭包套闭包在函数调用的时候类似于递归调用,也存在压栈压爆的问题。最后一段:没错,拿技术解决问题,但是不要为了炫技而炫技。(特别是你的特技可能连一个“丑陋”的解决方案都打不过。找到一篇 Go 相关的责任链文章,对照看一下。

第 14 篇是看一个用 C 写的消息队列的源码。重点在于曹大对于“如何做分布式”的总结和思考:proxy 和 smart client。

第 15 篇讲的是如何设计一个灰度发布系统。从作用到分桶/分类策略,到用哈希算法对 key 进行哈希分桶从而实现千分比灰度。最后还提了几个问题并作了解答:使用 md5 和 sha1 不能保证均匀分布怎么办?如何选取分桶?使用 md5 或者 sha1 对 CPU 消耗太高怎么办?学到了两个如何对字符串算 md5 和 sha1 的命令:

1
2
3
4
5
6
7
8
9
10
echo -n 15810321343 |openssl dgst -sha1
md5 -s 15810321343

15810321343

md5=>
05eadde36e5e5c3a00015a8f07d98d6b

sha1=>
7962e1ba260de074ef895af44c62ad353ee36c2c

第 16 篇从数据库、检索服务、日志三个方面来谈 ES 能做的事,以及优势和限制。企业内部使用的 elasticsearch 是提供垂直搜索的一种方案,内容可能是一些结构化的数据,而不像大搜索那样都是杂乱的内容。数据库层面,查询条件可以转化为 bool 查询;单表 count 也容易解决;但 ES 不能实现 Join,事务。在检索服务层面,集群便可以非常方便地进行动态扩展,数据也不容易丢失;缺点是分词不是很科学。日志方面,ELK,每天建一个索引。

第 17 篇介绍了曹大自己开发的工具:elasticsql,使用 SQL 来查询 ES,目前 629 星。工作中遇到了问题,然后就手动开发一个工具,并且写文章总结,赞!

第 18 篇讲迅雷。文件会按 16KB 进行切分,每个文件块用 sha1 算法计算一个哈希值,用作小块的校验。将一个文件的所有小块的 sha1 连接得到的字符串再进行 sha1,得到整个文件的 id。之后简单讲了 server hub、peer hub、file server 的工作原理。因为迅雷成立时,互联网上的轮子并没有那么多,所以很多都得自研,文档也不全。最难解决的问题不是 P2P 下载,而是各种乱七八糟的 case。

第 19 篇列出了实际工作过程中碰到的各种低级的做法。我自己印象深刻的且之前没碰到过的有:滥用回调,增加系统复杂性;访问数据库不做批量;树形结构的表结构设计问题;工作流系统update不判断修改前的状态;抱怨接口性能是语言问题。嬉笑怒骂,皆成文章。

第 20 篇,把 logstash/kibana/elasticsearch 之类的东西统统变成 5.0 的过程中,遇到的一些问题及解决。

第 21 篇,公司登陆验证用的 Google Authenticator 来做校验的原理。也就是将一个 key 分发给某个具体的帐户,然后服务端和客户端可以每隔 30 s 算一个 token。将 token 和帐户验证即可确定身份。

第 22 篇,翻译的一个后 GitHub 上的后端面试项目,并给出一些问题的回答。这些题基本都是比较开放的问题,很少一问一答这种。5 年前的了,不过我看原项目地址 7 个月前还有更新。

第 23 篇,HTTP 中间件的形式如何写。这里给出了一些前置、后置、耗时统计的控制的例子,不过最后给的一个例子有点问题,不是太好理解。我找到了一篇叶剑峰大佬写的比较好理解的,能直接运行的例子。可以看到,《Go 语言高级编程》中也有这篇内容,可见平时的积累是非常重要的。

第 24 篇是问题 kafka 消息重复问题的排查,其实也没有各种现场排查,最终仅通过文档的说明就发现了问题:消费者在 poll 的时候才发送心跳,那如果处理消息的时间稍长就会被判失活,导致将正在消费的 partition rebalance 给其它消费者。因此解决办法就是升级到 0.10。这也告诉我们不要太快使用新发布的软件,因为有很多问题还没有生产环境中发现。

第 25 篇又是一篇吐槽文,工作中有时总是会遇到太“笨”的合作方,人家不会转一点脑子,什么问题都来问你,本来你就已经清楚地说明了。但一旦有什么在他们预期之外的事情,他就会来麻烦你。文中给出一些应对的办法,这当然需要后端去学习一些前端知识。工程师,终身学习是基本的。

第 26 篇,讲的是 redis 的 SDS 是不是二进制安全的。简单来说,通过使用二进制安全的 SDS,而不是 C 字符串,使得 Redis 不仅可以保存文本数据,还可以保存任意格式的二进制数据。起因是看到群里有人看到 redis 里有 strlen 的调用,就怀疑 redis 的 SDS 的二进制安全是不是真的。之后,一番实验和看代码,发现真正调用 sdsnew 函数的是在内部字符串用或者是测试用。

嗯,2016 年的博客看完了,总共 26 篇,基本两周一篇的节奏。

第 27 篇,是一篇译文。Redis 客户端和 Redis 服务器使用 RESP 协议通信,RESP 是 REdis Serialization Protocol 的简称。虽然 redis 协议设计得十分对阅读友好(human readable)并且十分容易实现,但实际实现起来和二进制协议的性能依然比较接近。例如,RESP 使用了前缀的 length 来传输 bulk 数据,所以没有必要像 JSON 一类的数据结构需要通过扫描来查找特殊字符,也没必要在发送数据的时候给数据加上引号(quote the payload)之类的。

第 28 篇,在某个本来想打游戏度过的周末,听说有 B 站的分享,于是去听了,结尾感慨:本来用来打游戏的周末又学了不少东西。整篇文章就是对这次分享的一次总结,其中又结合自己平时的思考。有一点是讲业务和基础架构绑在一起的好处。

第 29 篇,发现曹大有个习惯比较好,就是经常会翻译一些比较重要的文档。因为人总是容易遗忘,即使某个东西你现在很清晰,过了一段时间之后就会忘记。我想记住的具体的知识点就是:leader 会维护一个 in-sync replica (ISR) 的集合:是和 leader 的进度完全一致的那些 follower。其他的先不深究。

第 30 篇是一篇思考,关于系统如何处理错误,直白一点是如何向用户、向研发展示错误。应该管用户一些提示,而不是直接白屏,用户在故障之后恢复时能正常使用系统,而不是像有些权限系统设计的那样缓存个几天。对研发而言,根据错误能快速定位到错误的地方是最重要的。开头整理的一些错误场景总结如下:依赖组件挂了;依赖服务挂了;依赖方超时了;调用方的参数有问题;调用方的参数无法正确地通过校验;用户的某种操作在业务逻辑上不具有合理性,不能够接着让他执行下去;程序自身出错了,比如数组越界,对字符串和数字进行加和操作,或者是把 null 当成了某种合法的数据结构,通过点或者下标来获取某种属性。

第 31 篇,吐槽 Go 包非中心化管理的一些问题。最主要的一个问题是不好和开源项目“合作”的问题。

第 32 篇,介绍了分布式如何实现。首先是用 MySQL 实现,但解决不了租期的问题;接着用 redis 的 setexnx 命令,也有一个问题是:因为 redis 的主从同步是异步行为,在主上加锁成功,数据没有同步之前 master 挂掉了,那之后就可能会有多个实例(用户进程)持有锁,这显然不是我们希望的。(这里我的理解是其他用户进程会能向新主申请到锁);然后就是 redis 的作者提出了 Redlock 的加锁方法。

第 33 篇,使用代码示例讲解如何使用 parser 包做一些“高级”的事情:基于 ast 做很多静态分析、自动化和代码生成的事情,非常酷炫。

第 34 篇,利用 MySQL 的 information_schema 表来自动生成代码,依然很酷炫。

第 35 篇,讲 awesome go 的入选标准,主要关注两方面:1. goreport(包含 gofmt/go_vet/gocyclo/golint/license/ineffassign/mispell) 的结果;2. 测试覆盖率(coveralls,或者 gocover)。

第 36 篇,17 年 6 月份,那时我快毕业了。当年如日中天的 ofo 也早已经一地鸡毛,用户的押金也得到几百年之后才能还清。想起那时的创始人何等风光,如今安在?本问主要探讨密码锁的一些方案设计。

第 37 篇,关于 pprof 和火焰图的使用,嗯,曹大少有的教程类文章。这里有个如果涉及到 http handler 的压测,profile 的时候可以有技巧地把 Write/Read 的开销除开。

第 38 篇,公司都是要盈利的,都有自己的业务,业务驱动其实是常态,老板只关心有没有实现想要的功能,你如何实现,实现的代码质量如何这些并不重要,这也是现在你看到的公司的各种垃圾代码,这个问题也没法解。但对于个人而言,多看好的开源代码、多看《重构》这样书,以及使用 test 等方法能一定程度上解决问题。

第 39 篇,一个比较常见的问题,之前我也写过一篇类似的,当时我用的一个主要工具就是:perf top

第 40 篇,回顾从 07 年入校以来的点滴,真情流露。曹大多才多艺!

第 41 篇,先是场景引入(今天刚好看到 caoz 讲的如何做分享),再层层递进。得出如果在内存中计算交集的一个方法:map[int]int。

第 42 篇,讨论阿里的一个开源库 ApsaraCache(类 redis) 所做的优化是如何实现的。主要有两点:增加对 MemCache 的支持;优化短链接。

第 43 篇,举了两个 Go 语言中 panic 无法被用户 recover 的情况:并发 map 读写;reflect.Call。结论:go 服务不用 supervisor 又不加监控就等着被开除吧。哈哈哈~

第 44 篇和第 45 篇是关于《Clean Architecture》的读书笔记,摘一句:对于商业公司的金主来说,软件系统有两方面的价值,一方面是软件的行为价值,也就是指软件的业务功能;另一方面是软件的结构,指软件的架构,易变性,可维护性,属于软性价值。软件工程师的职责是保证系统两方面的价值都能够达到最大。但是实际情况是,大多数人就只会聚焦在某一个方面,顾此失彼。

第 46 篇,宣告《Go 语言高级编程》正在搞,而且会继续搞完。周末写完了 router 一节。

第 47 篇,讨论了部门内的一个不均衡的负载均衡算法。数学证明的部分没太看懂~

第 48 篇,除了拿 debugger 来 debug。还可以用 debugger 来了解了解程序运行的机制,或者用 disass 来查看程序运行的汇编码。在查资料的时候,看到鸟窝写的文章:最近看到滴滴的工程师分享的使用debugger在调试Go程序,我觉得有必要在尝试一下这方面的技术了。估计说的就是曹大这篇。

gc 编译器产生的代码可能会包含内联的优化,这不方便调试器调试,为了禁止内联, 你可以使用 -gcflags “-N -l” 参数。

我跟着这篇讲 lldb 调试的文章走了一遍调试。设置断点有点问题,没完事做完。

这篇文章里还介绍了 dlv 的功能。由于好久没用到这些工具,暂时先不深入了。

第 49 篇,是对业务系统的接口上的思考,从功能上来说,可以抽象成 SQL。但实现起来,并且对用户友好,可能并没有那么简单。最后结论:支持什么 SQL/GraphQL 啊,thrift 大法好。

第 50 篇,这又是一篇译文(还是挺多的,所以这也是一种学习方法),关于 Go 汇编的。后来曹大在 Go 夜读上的 Go 汇编分享,这篇文章亦有贡献。

第 51 篇,关于协作式和非协作式抢占调度的特点以及关系。Go 实际上在 1.14 才真正实现了抢占式调度,使得 for 无限循环在 GC 的时候不会阻塞整个进程的执行,从而“卡死”。

第 52 篇,曹大开始进行源码阅读了,记住这个时间:2018-04-05。这篇文章开始会简单展示核心的关于 channel 的代码,包括读、写、关闭等,后面就主要是源码注释了。学到了一个在线的 markdown 编辑工具,好处是可以和 dropbox、github 绑定。

第 53 篇,启动流程分析。由于之前我已经追求一遍相关代码,不深入研究。

第 54 篇和第 50 篇来源于同一个 github 项目,但作者已经跑路了。文章是比较硬核的,作者的研究方法是从 binary asm 反推实现原理。

第 55 篇,这是曹大在 Go 夜读上所做分享的原文,牛逼。领先我们好几年。

第 56 篇,思考业务系统中的问题及解法。讨论的问题是如何收集、计算主业务流程相关的指标。开头由 DDIA 一书引入话题,然后说明场景,再谈解决办法,然后说问题,再来说方法……从这里也可以看到,曹大在 18 年就已经看完了 DDIA。我则是今年才开始看这本书,差距。

第 57 篇,G 和 P 的状态流转图,用的是“自动”生成的。相比我之前手动画的,改起来也比较容易。

第 58 篇,什么 TDD,BDD,DDD 都是浮云,事故驱动开发才是王道。不出事故,一切都好说,管你什么代码质量,有什么用?出事故,那马上得复盘、甚至罚款。结尾列出的三个案例也是值得好好看,尤其是尽可能要用 defer。

第 59 篇,喜大普奔,从 hexo 升级到 ghost,且升级了 gitalk 评论系统,直接和 github 打通。

第 60 篇,讲的《Concurrency In Go》里的一个活锁的例子,原因是没有协调好加锁的顺序,且使用的是 tryLock 的这种形式,即没有加锁成功,就先返回失败,再尝试。当然,如果不是 trylock 的话,不一致的加锁顺序会直接导致死锁。

如果已经发现了活锁导致的问题,解决手段很简单,只要规定好加锁顺序,并且大家都按同样的顺序去加锁就可以了。活锁比较麻烦的是难以发现,因为在活锁状态下的程序实际上看起来很正常,只是性能表现会稍微差一些。

另外,不得不说一句,我是今年才看的这本书。差距啊!

第 61 篇,毕业四年,列出的计划单,非常叼。我印象比较深刻的有:有个人的稳定的科学查资料方案,改天有机会当面请教一下。

第 62 篇,调度器的源码分析,由于我已经写过相关文章,不深入看,略过。

第 63 篇,《Concurrency In Go》的读书笔记,恰好我几个月前也写了一篇类似的读书笔记。不同的是,曹大这篇,代码样例为主,我的则以理解为主,代码样例则基本没有。

这有张操作 channel 的结果对照表,可以一用:

channel op

第 64 篇,timer 源码分析,不深入看。

第 65 篇,syscall 原理,以后需要的时候再来仔细研究。

第 66 篇,列举了一些英文书的出版社。如 O’Reilly,NoStarch,Manning,Apress,packt,最近我也看了 packt 出版社的《Distributed Computing With Go》,当然也是跟着曹大的书单读的。嗯,落后了 2 年。

第 67 篇,map 的源码分析,当初我写《深度解密 Go 语言之 map》时,参考了不少。

第 68 篇,select 源码分析。

第 69 篇,slice 源码分析。

第 70 篇,报告阶段性胜利,《Go 语言高级编程》初稿完成。曹大写的是关于 Go Web 的部分,字里行间可以看出他花了大力气进行总结:对于个人来说,通过这本书,把所有 web 领域相关的知识全部进行了梳理和总结(虽然有些还没有写)。可以比较自信地认为在 web 开发方面,本人已经没有任何方面的盲点和短板。要说问题,那可能也就是业务领域相关的问题了。

第 71 篇,业务遇到的问题,最终查到是 Go 语言自身的一个 bug:循环中的指针变量做 map 的 key 使用,会在 GC 时触发该 bug。在 1.9.2 上是必现的。

第 72 篇,sync 包源码分析,以后再看。

第 73 篇,semaphore 源码分析,以后再看。

2018 年的文章完了,这一年,曹大主攻 Go 语言,完成了 golang-notes,并且完成了《Go 语言高级编程》。

第 74 篇,《中台的末路》,去年在公众号“码农桃花源”上发出后,全网疯转,仅“码农桃花源”上就达到 7w+ 阅读,其他公众号、平台的阅读数就更不知多少了。

第 75 篇,又是列举了几个实际工作中碰到了案例,一个开源库对 sync.Pool 的使用,和实际场景中对开源的使用并没有发挥 sync.Pool 的作用,且在高并发场景下导致了锁竞争;第 2 个例子是 metrics 上报也会遇到锁竞争;第 3 个例子是打日志。得出结论:不可能通过看源码就能看出问题,早做压测保平安。

上面说的几个问题实际上本质都是并发场景下的 lock contention 问题,全局写锁是高并发场景下的性能杀手,一旦大量的 Goroutine 阻塞在写锁上,会导致系统的延迟飚升,直至接口超时。在开发系统时,涉及到 sync.Pool、单个 FD 的信息上报、以及写日志的场景时,应该多加注意。早做压测保平安。

第 76 篇,ghost 支持了mermaid。

第 77 篇,年终总结和年初计划。这里看到了曹大和欧神的互动,名场面。

国外的兄弟

大佬之间的交流

第 78 篇,从功能上来讲,规则引擎的基本就是一个 bool 表达式的解析和求值过程。可以直接使用 Go 的内置 parser 库完成上面一个基本规则引擎的框架。

第 79 篇,主要内容应该是曹大之前竞选 Gopher China 讲师时准备的。又是根据实际业务系统的一次精彩总结,希望以后自己也能写这样的东西。即使不能公开,也能私下总结一下。

第 80 篇,流式计算中的分布式快照的算法介绍,看不太懂。主要内容是最早的一篇论文及之后 FaceBook 出的一篇新论文的介绍。

第 81 篇,从这篇开始,是一个序列专门讲微服务相关的内容,比较精彩。本文主要是说通用语言的问题,比如高层讲战略用的是中文,但底层程序员用的是英文,这中间有一个翻译导致的信息差。这在微服务上是很难解决的,例如对快车的翻译在不同的系统中叫法是不一样的。

上面提到的 fastcar 出现在我们系统提供给别人所用的 api 的关键字段中,quickcar 出现在我们内部数据库的字段名中,kuaiche 出现在异步发送的消息中。

但我的现公司其实用的是统一的一个 idl 库,可以解决很多问题。

第 82 篇,继续说微服务的问题,进行拆分之后,各个小模块理论上可以用最适合的语言去实现,但这就造成了技术栈不统一的一些毛病,例如同一个 client 需要用各种不同的语言来实现,当遇到组织架构变动的时候,接手都很麻烦。B 站是很早就统一了技术栈的。

第 83 篇,新增功能时,可能同时需要改很多模块,从设计原则上来讲,逻辑上相同或者类似的代码应该放在一个地方来实现。这个稍微学过一点 SOLID 中的 SRP 原则就应该知道。这样可以避免逻辑本身过于分散,好处是:“一个类(模块)只会因为一个理由而发生变化”,其实就是相同的需求,尽量能够控制在单模块内完成。但职场上很多人并不是这么考虑的,做这件事是否有收益是第一要务。

第 84 篇,业界有个名词叫 dependency hell,指的是软件系统因依赖过多,或依赖无法满足时会导致软件无法运行。服务之间的循环依赖也有很多。程序员在当前的微服务架构下,将持续地被外部的垃圾 SDK 和各种莫名其妙的依赖问题所困。

第 85 篇,拆分成微服务后,一致性是个大问题。大多数公司的架构师嘴里的最终一致,依靠的都是人肉而非技术。

第 86 篇,现实中总要考虑上“政治”因素,推动事情并不容易,尤其是跨部门的协作。

如果一个公司的组织架构已经基本成型了,那么基本上设计出的系统架构和其人员组织架构必然是一致的。

之前和同事一起得到了一个在大公司内推进事情的靠谱结论,如果一件事情在一个部门内就可以解决,那可以开开心心地推动它解决。如果一件事情需要跨部门,那还需要本部门的大领导出面才能解决,哪怕这事情再小。如果一件事情需要跨两个部门,那就没治了,谁出面都不行。这种事情做不了的。而如果一件事情和你要跨的部门 KPI 有冲突,那就更别想了,把部门重组了才能解决,这是 CTO 才能干的事情。

第 87 篇,一篇回答知乎上“有哪些优秀的 Go 面试题”的答案。当初第一次看文章的时候,很多不懂,现在看很多地方懂了。

第 88 篇和 89 篇是参与 tidb talent-plan 的题解。前者是一个 merge_sort,后者是 map-reduce。

第 90 篇,尝试对接入做到完全配置化,从 SQL 语句得到启发,通过定义的元组来描述外部的数据、内部的存储。其实我没太懂,以后再研究下。

第 91 篇,讲了 Go 1.13 在 defer 上的一些优化。主要在某些情况下,可以用 deferprocStack 来代替以前的从堆上分配资源,提升性能。

第 92 篇,探索一个案例代码的优化过程。起因是看到群里有人问的一个含有 dead code 的样例代码是如何优化的,然后使用 GOSSAFUNC=main go build com.go 看了下优化过程。结论是:优化是在编译器后端做的。关于后端的优化过程,这里有一个欧神开发的在线小工具,可以很方便的查看:https://golang.design/gossa。

从词法分析到语法分析一般被称为编译器的前端(frontend),而中间代码生成和目标代码生成则是编译器后端(backend)。

第 93 篇,某团圆节(应该是中秋节吧)线上发上的真实案例,不是事故:下游系统抖动了,超时,很多 g 挂在 gopark 上,没法复用;只能创建更多的 g,这些 g 会被 append 进 allgs 数组,在 sysmon/GC 等时机会扫描 allgs;即使下游恢复了,allgs 数组也不会收缩,使得 CPU 消耗变大。只能通过重启恢复。

第 94 篇,博客用 caddy2 上 https,看着导航栏左边的小绿锁,更开心了。

第 95 篇,一个小的程序代码,只用 Rlock 的时候竟然“发现”有锁冲突,不可思议,经过排查发现:大量的 g 调度下,在 g 的执行过程中如果有调用任意的会切换 g 调度的情况下,下次回到调度该 g 的时间无法保证。还是挺有意思的。

第 96 篇,依赖反转原则,在很多地方都被人用不同的名词说过。名词不同,但本质相同。

第 97 篇,讲 ACL 的一些问题,不太能 Get 到精髓。

第 98 篇,MQ 的数据生产方、消费方、MQ 的维护者,三方各怀鬼胎,真正关心 MQ 数据的只有整个消息流的末端团队,但他控制不了生产者。很多事故的发生都是因为生产者重构导致发出有问题的消息。本问提出了 2 种可能的数据检验方案。

2019 年的文章看完了。这一年,曹大比较关注工程领域,微服务方面有不少输出。另外,排查了很多线上 Go 相关的问题。上一年读完源码,这一年就大展身手了。

第 99 篇,2020 年第一篇,是一道知乎上的题目,有些地方可能还会拿来做面试题,但其实只要运行一下就知道答案。再看看逃逸分析、SSA 优化就可以知道为什么了。文章最后一句,送给面试官:要是哪位工程师拿这个去做面试题,那就太缺德啦!

第 100 篇,线上的一个 panic case,偶现。最终排查的原因是 waitgroup 使用不当,造成并发读写 map,程序崩溃。文章里写一开始系统负责人声称一定是离职员工的锅,连代码都不愿意看。但曹大三下五除二,就还原了事实真相。

第 101 篇,举了一个打日志也可能造成锁竞争的例子,原因是最后都会加锁:fd.writeLock()。

如果提前有一些预见性,做好针对性压测,那就不会让你的用户在关键时刻靠重启续命了。

第 102 篇,主要讲了切片截取会导致内存泄露,并给出了相关的例子。

第 103 篇,《工程师应该怎么学习》,推荐所有的技术人都看看,我前前后后看了很多遍。据说是曹大跑路的时候发到内网上的。写得很赞,看书、看英文书、看博客、读论文、实践、总结、写博客、独立思考、代码库、笔记库、演讲能力,哪一个都值得我们认真学习、实战。

你所要做的是提前做好准备,在那一天到来的时候,在聚光灯下旁征博引,谈笑风生。

第 104 篇,内行的吐槽更为致命。因转到蚂蚁搞 mosn,这里就是过程中遇到的 go mod 的依赖问题,最后通过 replace 解决。

第 105 篇,因为 Go 错误处理的原因,提高 Go 项目的测试覆盖率其实比较难。

第 106 篇,又是一篇犀利的吐槽。架构师不常见,天下系统都一样。言必谈 DDD,中台,战略,一到了落地环节提不出合理的见解和建议。对于个人来说,脚踏实地,打好基础,从解决实际问题开始。

第 107 篇,首先用压测工具 wrk fasthttp,标准库,rust 写的 hello 程序,发现 fasthttp 几乎和 rust 一样快。fasthttp 快的原因:goroutine workerpool,对创建的 goroutine 进行了重用;在整个 serve 流程中,几乎所有对象全部都进行了重用。当然,因为 ctx 的重用,某些场景下会掉坑。

第 108 篇,介绍了一些 golang linter,以及怎么集成到代码 CR 的流程中。

所以如果你在维护有节操的开源项目的话,可以考虑给你的项目加个 github action 了,试用下来,感觉 reviewdog 是最简单直观的。

第 109 篇,分析了一些常见组件的连接池的具体实现。包括:http 标准库、http2、fasthttp、gRPC、thrift、redigo、go-redis/redis、database/sql。

第 110 篇,Go context 的源码实现分析。

第 111 篇,Go 如何实现自动抓取 profile,快速定位问题。这篇介绍了实现原理。

第 112 篇,如何 patch 私有函数,原理+代码实现,非常过瘾。主要原理就是找到原私有函数的代码位置,将前面的数个字节用一段跳转指令覆盖,这样就能实现“劫持”,跳转到目标函数。真牛逼。

在 mac 上跑的时候,函数名有一小点不同,修改如下:

1
2
3
4
5
6
7
8
9
func main() {
m := generateFuncName2PtrDict()

heiheiPrivate()
origin := replaceFunction(m["main.heiheiPrivate"], (uintptr)(getPtr(reflect.ValueOf(Replace))))
heiheiPrivate()
copyToLocation(m["main.heiheiPrivate"], origin)
heiheiPrivate()
}

在“劫持”完之后,再“恢复现场”,再次调用 heiheiPrivate(),又恢复原样。妙啊!

第 113 篇,介绍了 LockOSThread 的奇技淫巧,它可以“杀”死线程:在退出的时候和当前 g 绑定的线程就会直接销毁。

第 114 篇,曹大看的 youtube 上的一个吐槽微服务的视频的总结笔记,很有曹大的风格,花式吐槽。

第 115 篇,自动 dump 的实现及样例说明。这是 mosn 上的一个开源项目,有了这个项目,绩效稳了。

第 116 篇,一篇 2013 年 Google 对 packetdrill 的论文翻译。看不太懂,先跳过,以后有用到的时候再来看。

第 117 篇,介绍了一个 Google 的 subset 算法解决微服务场景下连接数量的问题。

第 118 篇,详细解释 Go 1.14 中 defer 的优化。


序号 时间 题目 内容
1 2015-10-13 从零搭建 lnmp 环境 介绍如何使用 vagrant 搭建开发 lnmp 环境
2 2015-11-01 一致性哈希详解 介绍一致性哈希原理
3 2015-11-05 Solr 5.3.1索引MySQL数据配置流程 一篇实战教程
4 2015-11-13 Elasticsearch环境搭建和river数据导入 一篇实战教程,把 Solr 换成 ES 2.0 了
5 2015-11-15 漫谈服务化、微服务(一) PHP 怎么做服务间调用
6 2015-11-16 Elasticsearch环境搭建和river数据导入(二) 接着第 4 篇,如何解决导入大表数据时虚拟机卡住的问题
7 2020-11-28 关于乐观锁 乐观锁,是一种写入、更新数据库时的逻辑特性
8 2020-11-30 [译]Redis事务详解 官方 Transactions 小节的文档翻译
9 2016-01-10 Elasticsearch环境搭建和river数据导入(三) 因为 MySQL 主从延迟导致的丢数据问题的解决
10 2016-02-01 一致性哈希-虚拟结点生成 介绍了 groupcache 和 memcached 如何生成虚拟节点
11 2016-03-05 elasticsearch服务开发总结 开发过程中遇到的问题及解决
12 2016-03-09 Elasticsearch环境搭建和river数据导入(四) 估计是 ES 最后一篇总结了
13 2016-04-11 中间件与责任链模式 laravel 框架里的一个实现
14 2016-06-26 beanstalkd 源码剖析 一个用 C 写的消息队列源码分析
15 2016-08-09 关于灰度发布 灰度发布的一些概念和实践
16 2016-08-10 谈一谈es的优势和限制 从数据库、检索服务、日志三个方面来谈 ES 能做的事,以及优势和限制
17 2016-08-28 使用sql来查询es 开发了一个 elasticsql 项目,可以用 SQL 来查 ES
18 2016-10-14 关于迅雷 简要介绍迅雷下载的原理
19 2016-10-23 初级程序员常犯错误一览 列举“初级”程序员的做法,共勉~
20 2016-11-06 es stack升级5.0了 把 logstash/kibana/elasticsearch 之类的东西统统变成 5.0
21 2016-11-12 关于我们每天都在用的token 公司登陆验证用的 Google Authenticator 来做校验的原理
22 2016-12-09 后端程序员面试题 翻译的一个后 GitHub 上的后端面试项目,并给出一些问题的回答
23 2016-12-16 重新探讨middleware Go web 中间件如何写
24 2016-12-22 一次kafka 0.9的重复消费问题排查 仅通过读文档将发现了问题
25 2016-12-25 如何与低水平web开发联调 吐槽和低水平前端联调的事,并给出了应付的办法
26 2016-12-26 sds与二进制安全 redis set 命令是否是二进制安全的实验
27 2017-01-24 [译]redis通信协议 一篇 redis client 和 server 的通信协议
28 2017-02-19 周末 听 B 站分享的一个总结
29 2017-03-11 [译]Kafka Replication 一篇 Kafka 消息 Replication 的原理的译文
30 2017-03-30 业务系统错误设计 从用户和研发的角度,来看业务系统如何处理错误的一些思考
31 2017-04-25 关于go的包管理 Go 包管理的一些问题,这时还是 GOPATH
32 2017-04-27 分布式锁 从 MySQL 到 redis 再到 redlock
33 2017-05-10 golang 和 ast 直接用源码展示如何用 parser 生成 ast,以及实际应用
34 2017-05-20 从 information_schema 到自动生成的 web dao 利用 information_schema 自动生成代码
35 2017-05-24 如何使你的 golang 项目达到 awesome go 的入选标准 关注两点:goreport、测试覆盖率(coveralls,或者 gocover)
36 2017-06-20 关于 ofo 关于 ofo 密码锁的一些方案设计
37 2017-07-11 pprof 和火焰图 一篇实例教程
38 2017-06-21 企业级应用与屎一样的代码 公司级的代码质量问题及一些解决办法
39 2017-08-31 如何定位 golang 进程 hang 死的 bug 如何定位 Go 的协作式调度的一个 bug
40 2017-09-12 十周年 回顾本科、工作
41 2017-10-11 从求交集开始 求交集的一个计法
42 2017-10-28 ApsaraCache 源码 diff 分析 分析优化如何实现
43 2017-11-30 recover 并不是无懈可击的 有些情况下加 recover 也没用,例如并发 map 读写
44 2017-12-28 clean architecture(上) 《Clean Architecture》读书笔记 1
45 2017-12-30 clean architecture(下) 《Clean Architecture》读书笔记 2
46 2018-01-07 开源书 宣告一下,《Go 语言》高级编程还在搞,而且会搞完
47 2018-01-22 你的负载均衡真的均衡么? 实验+推理说明一个均衡算法并不均衡
48 2018-01-31 使用 debugger 学习 golang lldb 和 dlv 的功能和使用
49 2018-02-09 如何在 kv 系统中支持简单的 SQL 对在线特征系统的接口的思考
50 2018-03-08 [译]go 和 plan9 汇编 一篇 Go 汇编的翻译
51 2018-03-30 协作/非协作式抢占 宏观上描述两者的运行过程
52 2018-04-05 Go 系列文章1:Channel 从使用到源码分析 channel 的使用及源码分析
53 2018-04-08 Go 系列文章2:Go 程序的启动流程 Go 程序启动流程
54 2018-04-14 [译]Go 和 interface 探究 interface 如何组装等等
55 2018-04-22 Go 系列文章3 :plan9 汇编入门 Go 汇编相关,非常好
56 2018-05-01 分布式系统中的不可靠复制问题 业务指标如何收集、计算
57 2018-05-17 goroutine 的状态切换 整理的 G 和 P 的状态切换图
58 2018-06-09 事故驱动开发 过于真实的互联网开发指南
59 2018-06-09 blog 升级了。。 喜大普奔,博客升级
60 2018-06-09 livelock 《Concurrency In Go》里的一个活锁的例子
61 2018-06-10 2018 年的几个目标 年终总结
62 2018-06-17 Go 系列文章4 : 调度器 源码分析
63 2018-06-18 concurrency in go 读书笔记 记录的一些代码样例
64 2018-06-23 Go 系列文章5 : 定时器 timer 源码分析
65 2018-06-28 Go 系列文章6: syscall syscall 原理
66 2018-07-04 packt 出版的书吐槽 一些英文书的出版社
67 2018-07-07 Go 系列文章 7: map map 源码分析
68 2018-07-16 Go 系列文章 8: select select 源码分析
69 2018-08-31 Go 系列文章 9: slice slice 源码分析
70 2018-09-01 松一口气 《Go 语言高级编程》初稿完成
71 2018-09-17 Go 1.9.2 的 bug Go 语言自身的一个 bug 复现
72 2018-10-04 Go 系列文章 10: sync sync 包源码分析
73 2018-11-24 Go 系列文章 11: semaphore semaphore 源码分析
74 2019-01-01 中台的末路 中台的困境
75 2019-01-06 几个 Go 系统可能遇到的锁问题 几个锁相关的案例
76 2019-01-24 在 ghost 中支持 mermaid 博客的优化
77 2019-02-06 2018 总结 && 2019 目标 年终总结和年初计划
78 2019-02-08 基于 Go 的内置 Parser 打造轻量级规则引擎 使用 parser 可以实现一套规则引擎
79 2019-04-13 一套实时特征系统的迭代过程 真实的业务场景的迭代升级过程,非常精彩
80 2019-04-14 流式计算中的分布式快照 最早的一篇论文及之后 FaceBook 出的一篇新论文的介绍
81 2019-05-01 微服务的灾难-通用语言 通用语言的问题,很难重构解决
82 2019-05-01 微服务的灾难-技术栈 技术栈不统一也是有问题的
83 2019-05-02 微服务的灾难-拆分 依赖设计原则划分责任是不太可能的
84 2019-05-02 微服务的灾难-依赖地狱 依赖过多、多重依赖、依赖冲突、依赖循环
85 2019-05-03 微服务的灾难-最终一致 人肉最终一致
86 2019-05-03 微服务的灾难-康威定律和 KPI 冲突 除了组织架构的问题,还需要考虑 KPI 的问题
87 2019-05-11 一些问题的答案 一篇知乎回答
88 2019-05-11 talent-plan tidb 部分个人题解-week 1 tidb 题解 1
89 2019-05-11 talent-plan tidb 部分个人题解-week 1 tidb 题解 2
90 2019-08-31 一劳永逸接入所有下游数据系统 接入做到完全配置化
91 2019-09-04 Go 1.13 defer 的变化 用 deferprocStack 提升性能
92 2019-09-22 查看 Go 的代码优化过程 探索一个样例代码的优化过程
93 2019-09-22 为什么 Go 模块在下游服务抖动恢复后,CPU 占用无法恢复 线上发生的真实案例
94 2019-10-04 从 nginx 切换到 caddy 博客系统用 caddy2 上 https
95 2019-10-13 一个和 RLock 有关的小故事 如何用 trace 查案
96 2019-10-20 依赖反转相关 很多不同的概念,但本质是一个东西
97 2019-11-02 ACL 和俄罗斯套娃 ACL 的一些问题
98 2019-11-24 MQ 正在变成臭水沟 MQ 的问题以及提出数据检验方案
99 2020-01-06 一个空 struct 的“坑” 如何看穿一些弱智面试题
100 2020-01-09 map 并发崩溃一例 waitgroup 使用不当造成的并发读写 context,导致 panic
101 2020-01-13 生于非阻塞,死于日志 写日志也可能会导致锁竞争
102 2020-01-19 slice 类型内存泄露的逻辑 切片截取会导致内存泄露
103 2020-01-26 工程师应该怎么学习 终生学习,成为更好的自己
104 2020-04-19 go mod 的智障版本选择 Go mod 的问题
105 2020-05-01 为什么提升 Go 项目的测试覆盖率有点难 因为 Go 的错误处理
106 2020-06-06 架构的腐化是必然的 业务驱动下,能上升的不是系统做的好的,而是堆业务的
107 2020-06-14 fasthttp 快在哪里 fasthttp 快的原因是几乎所有对象全部都进行了重用
108 2020-07-06 reviewdog 如何在提 RP 的时候,检测代码质量
109 2020-07-11 一些连接池相关的总结 常用组件的连接池实现
110 2020-07-11 Go context Go context 源码分析
111 2020-08-13 无人值守的自动 dump(一) 自动抓取 profile
112 2020-09-04 在 Go 语言中 Patch 非导出函数 如何 patch 私有函数,原理加代码实现
113 2020-09-18 极端情况下收缩 Go 的线程数 LockOSThread 的奇技淫巧
114 2020-10-06 10 个让微服务完全失败的 tips 老外的一个演讲,花式吐槽微服务
115 2020-11-03 无人值守的自动 dump(二) 书接第 111 篇,自动 dump 的实现及说明
116 2020-11-04 packetdrill 简介 论文翻译
117 2020-11-28 用 subsetting 限制连接池中的连接数量 介绍的一个 Google 的算法 subsetting
118 2020-12-03 open coded defer 是怎么实现的 Go 1.14 对 defer 的优化

术语

KVM:Kernel-based Virtual Machine is a virtualization module in the Linux kernel that allows the kernel to function as a hypervisor.

Consistent hashing:consistent hashing is a special kind of hashing such that when a hash table is resized, only n/m keys need to be remapped on average where n is the number of keys and m is the number of slots. In contrast, in most traditional hash tables, a change in the number of array slots causes nearly all keys to be remapped because the mapping between the keys and the slots is defined by a modular operation.

二八原则:网站开发有一个比较著名的二八原则,就是 80% 的用户其实访问的都是 20% 的数据。所以实际上你只要把这 20% 的数据缓存好,就可以让网站整体的响应和吞吐量上一个等级。

Solr:Solr is an open-source enterprise-search platform, written in Java, from the Apache Lucene project. Its major features include full-text search, hit highlighting, faceted search, real-time indexing, dynamic clustering, database integration, NoSQL features and rich document handling.