专栏名称: 程序人生
十年漫漫程序人生,打过各种杂,也做过让我骄傲的软件;管理过数十人的团队,还带领一班兄弟姐妹创过业,目前在硅谷一家创业公司担任 VP。关注程序人生,了解程序猿,学做程序猿,做好程序猿,让我们的程序人生精彩满满。
目录
相关文章推荐
码农翻身  ·  今年后端这薪资是认真的吗? ·  18 小时前  
稀土掘金技术社区  ·  用dayjs解析时间戳,我被提了bug ·  2 天前  
蚂蚁技术AntTech  ·  论文秀Live#21 ICSE 2025 ... ·  2 天前  
程序员的那些事  ·  阿里云核心域名竟遭劫持,博客园等众多网站瘫痪 ... ·  2 天前  
51好读  ›  专栏  ›  程序人生

如何阅读一份代码?

程序人生  · 公众号  · 程序员  · 2017-04-07 07:51

正文

请到「今天看啥」查看全文


这时,你不得不像 CSI 中的警探一样,顺着一点蛛丝马迹,开始剖析代码,试图从迷雾中还原真相。你会抛开一切纷扰和杂音,集中精力,带着线索,循着问题,读且只读和解决问题直接相关的代码。这种状态,我管它叫「猎手模式」—— 我们有如非洲草原上追逐离群斑马的狮子,把身上燃起的小宇宙集中于一点,眼睛紧盯猎物奔走的方向,腿如疾风,势如闪电,心中不断地盘算着雷霆一击究竟用锁喉,打脸抑或拉后腿胜算更大。道路上的石子划了脚,痛;飞奔时撞上了幺蛾子,烦,但这都不是事儿。就算远远的乞力马扎罗山上难得地挂上了两道彩虹,煞是美丽,自拍后发朋友圈定能破百赞,你也无暇顾及这些并不重要的细节。

专注,集中力量攻击且仅攻击一点是这样场景下阅读代码的主要方式。

拿我遇到的 nginx cache 的问题来举个栗子。一年前,当我接手 Tubi TV 的性能较低且很难维护的 API 系统后,虽打定主意日后重写,但摆在面前的,刻不容缓的问题是提高性能。应用层可以施展的空间不大(数据已经在 redis 里),所以只能在 web 层打主意。在 HAProxy 和 nginx cache 之间,我选择了后者, 因为 nginx 已经在当时的生产环境下大量使用。我虽然没用过 nginx cache,但启用 nginx cache 并不是难事,照着文档设置好 cache 的路径和大小等参数后,在需要使用 cache 的 location 下,设置 cache key 并使能即可,我本地的简单的测试运行正常。然而,在生产环境中,本该命中的请求却一直处在 miss 的状态。我一筹莫展,尝试了网上搜到的各种方案无果。最终,我决定自己编译一个打开 DEBUG 开关的 nginx 版本( --with-debug ),记录更多的日志,然后对着源码找问题。

nginx cache 及 upstream 里和 cache 相关的代码量并不算多,几千行,我快速过了一下,然后就着日志上的内容寻找相关的处理流程,并在几个大的 bailout 分支猜想可能出现的情景。由于 nginx debug log 还是不够详细到满足我的需求,我在这些没有被顾及到的分支上各自加了调试代码,重新编译,运行。

这个过程中,「猜」起到了很大的作用。我记得本科时的数学老师 —— 一个可爱的小老头 —— 点名提问对方答不上来时,常常挂在嘴边的口头禅是:你猜一下嘛。他总说连蒙带猜也是解题的一种思路,伟大的数学家同时也是伟大的猜想家。

我们读代码时,猜文件名,函数名,变量名的意图,猜某个分支的意图,猜某段代码的意图,最终结合运行的结果,打印出来的调试信息来印证我们的猜测。这是读者和作者间有趣的猫鼠游戏。读得越多,猜得越多,印证得越多,形成一个有效的 feedback loop( read - guess - verify ),你下次猜测成功的几率就越大。

最终,问题被我定位出来 —— 它是两三处 configuration 未正确配置的问题。stackoverflow 上的答案是部分正确的,它解决了绝大多数人的问题 —— 没有 ignore cache control 相关的 header 几乎被每个初次使用者忽略了,它也是我的配置问题之一。但之所以这个答案没能解决我的问题,是因为我们生产环境中的 nginx 有个不起眼的配置 disable 了 proxy buffer,从而导致 nginx 直接跳过 cache。

从以上的过程中,我们抽象一下,看看为了破案而阅读代码要注意什么:

  1. 带着线索,从一堆代码中找出和问题相关的代码。nginx cache 的例子中,线索是 proxy 的 upstream,cache 总不能命中,所以出问题的代码和 cache,proxy,upstream 有关。源码目录里一翻,就能挑出需要看的文件。由于问题在 cache 上,在挑出的文件中,具体看 cache 相关的函数名,宏名,以及代码。

  2. 专注阅读挑出来的内容,忽略不相干的噪音。在阅读的过程中,着力寻找潜在的触发问题的路径,然后动用「我猜我猜我猜猜猜」大法,加调试信息。

  3. 编译运行修改过的代码,复现问题,分析调试信息,然后,bingo,恭喜你答对了!请进入第 5 步。

  4. 没答对,请回第 1 步。别急,这不是高考,在老板忍无可忍炒你鱿鱼之前,你一直拥有再来一瓶的机会。

还有个关键的第 5 步,我单拎出来说。很多时候我们轮回数次,终于在第三步 bingo 后快乐地像是刚刚 K.O. 了对手的春丽,夹着腿跳将起来,左右手在空中一齐比划着二,得意忘形,以至于忘记了执行第 5 步。

喜悦是短暂的,记忆也是短暂的。整个过程你的目标是如此清晰,执行力无比强大,为达到目的「不择手段」。三天后老板问你,小程啊,你很棒啊。你用了什么手段征服了这个无比难缠的八阿哥?这时你拼命追忆,却像拿筛子盛水,忙乱半天一无所获。你开始怀疑人生:三天前的我和现在的我究竟是不是一个人?

所以关键的第 5 步是: 复盘 。解决问题后,别着急接受同事们的致谢和女(男)神的秋波。趁着那坨记忆还热气腾腾,抄起 evernote(或者 xxx),把整个过程用最简洁的方式记录下来 —— 关键代码,关键路径,到达终点的整个猜测过程,以及那些日志验证了猜测是对的,哪些日志验证了猜测走不通(恭喜你 —— console 或者 terminal 在这个时候应该还没关),总之,你在不择手段的过程中用过的一切手段,都应该像记流水账一样记录下来。最后分析总结:







请到「今天看啥」查看全文