前几天在知乎上看到《你目前写出的最大的Bug是怎样的》这个问答(点阅读原文可以看看)还挺有意思的,作为程序员,写出bug几乎是不可避免的,无非是造成的严重性和bug的程度不同(高级bug、低级bug),简单来聊下我自己在这块的印象。
常见的造成故障的问题
就我自己来说,之前写的东西造成过不少次挺严重的故障,但可能因为都发生在公司业务还没那么大影响力的时候,所以造成的故障的影响面相对还好,就会显得没那么严重,不过这么些年,眼见和处理过不少故障,写几个自己有印象的故障类型,多数严重故障其实也并不是什么很复杂的原因造成的,只是业务本身的影响力不太一样而已....
- 常见的自增长集合对象造成的内存溢出
很多的语言里的集合类型的数据结构都是自增长的,看到过太多次,由于bug或业务增长,造成集合大小远超当时写代码时候的想象,最终造成内存溢出的问题。
我建议是只要是动态的集合对象,都要给个大小的限制,避免未知的情况出现导致问题,所以一直希望语言本身能支持好,否则在并发类型的场景要专门处理下这个事情也挺折腾的。 - 常见的线程池误用造成的问题
对于并发场景而言,线程池是经常会要用到的,语言层面通常会给些更易用的API,但这些里面通常要么等待队列是不限长度的,要么是线程池大小其实是不限的,最终也很容易造成类似上面自增长集合对象的问题。
和1的问题一样,对资源的使用我建议是要有边界控制的,对一些在边界超出会导致进程退出的,其实在集群类的场景可能还好点,但例如对于Java,内存用多了没造成进程退出,但造成了频繁的GC,最终出现业务响应慢这种更严重的问题。 - 常见的批量API未限制造成的问题
代码里通常会提供一些用来做批量操作的API,但其实通常并不会希望这个批量操作是没边界的,例如可能希望最多一次操作1k,但在代码实现里面并没有去限定,而只是在API文档里写了下,这其实是没什么用的。
同样看到过好多次故障是这原因造成的,所以作为API的编写者,一定要保护好自己,不能因为用的人误用什么就造成严重问题。 - 高级的并发中的活锁问题
并发通常是比较复杂的问题,串行是容易理解的,但一旦并行化,有些时候人很难想清楚,再加上现代的程序通常是多人协作完成的,就更难了。
并发里面一种超级复杂的是活锁问题,有些忘了具体的case,只记得我们当年在内核层面碰到了一个这样的问题,然后有个妹子竟然自己在脑袋里运行模拟出了这个问题的出现,被一众人惊为天人,反正后来她解释这个问题出现的原因,我听了半天都没听懂... - 系统设计问题
如果是系统设计问题造成的故障,通常处理起来就会非常麻烦,在我5年前写的《我在系统设计上犯过的14个错》里面有不少case...
由于大型的系统设计涉及到非常多的集群、子系统、模块,会非常复杂,必须在设计时确保能抓住重心,就像前几天看采访鲁肃的文章里,鲁肃说CTO很大的责任是为CEO扫清障碍和风险,系统设计师很类似,系统设计师的责任是在设计系统时避免系统上线后出现或造成重大风险,并且确保最核心的做系统的目标达成。
印象深刻的历史上的严重Bug
除了这些看过或处理过的外,历史上发生过的严重bug里,我自己印象深刻的始终是1999年火星气候探测者号的故障,摘述下百度百科上关于此次故障原因的描述:
"火星气候探测者号任务失败的主要原因是人为因素,因为火星气候探测者号上的飞行系统软件使用英制单位磅力计算推进器动力,而地面人员输入的方向校正量和推进器参数则使用公制单位牛顿,导致探测器进入大气层的高度有误,最终瓦解碎裂。NASA在此后的所有任务中小心地避免这个因计量单位混淆所造成的错误。”
再摘述下关于这次任务的成本:
“火星气候探测者号总成本是3亿2,760万美元(包含轨道卫星及着陆卫星),花费1亿9,310万美元来研发,花费9170万美元来发射,花费4,280万美元来进行探测任务。”
相当于这个bug造成了这么多钱的损失,如果再加上时间成本那就损失更大了。
之所以对这个故障印象深刻,是因为这个故障其实也是我们在写代码过程中最容易犯的错,就是会默认调用的API是怎么实现的,上面的故障里就是使用API的团队默认参数的单位是公制单位牛顿,但其实API的实现里对这个参数的单位是按照英制单位磅来计算的,这些问题是很难靠什么API文档来避免的,也千万不要靠文档,毕竟执行的时候是只有代码在执行,必须在API上有非常明确的限定和约束(例如在这个故障里的这种是可以要求API上传入单位的),或者就是在代码实现上有明确的约束,高质量的代码之所以难写,很多时候是因为作为API的编写者,需要防范各种误调用造成的超出意期的状况,同时对于要去调用的API最好也能非常明确的知道它的实现逻辑,就像对于运维人员来说,每敲的一行命令,都应该知道按了回车后会发生什么,会造成什么影响。
Talk is cheap, show me the code
最后还是那句话,优秀的工程师,从代码上就是能一眼看出来的,代码的鲁棒性是靠着很多看起来没用的一行一行的代码来做到的。
特别推荐本文作者老毕,19 年获得国家技术发明二等奖、20 年获得国家计算机协会颁发的“ CCF 杰出工程师奖”,沉浮阿里 14 载,江湖人称“毕大师”,这就是前阿里 P10 ——毕玄。
他接受了极客时间的采访,做了一个《超级访谈:对话毕玄》的专栏。
这是他第一次以内部视角揭秘阿里的技术演进、并且“深度复盘”自己这 20 年来作为架构师、项目负责人的踩坑经验。上线第一时间我就去看了,不由感慨大佬不愧是大佬。
这个专栏是以对谈的形式展现,看的时候,仿佛老毕就坐在对面,跟我对话。通过 100 多个问题,他不仅讲了自己从非专业到阿里 P10 的职业成长经历,还提供了不少方法论,比如技术敏感度的培养以及如何做好管理和架构等等。当然,还揭秘了阿里在这 14 年来内部技术演进的一些“不为人知”的故事,比如:为什么 HSF 在淘宝上线第一天就被“回滚”了?跟多隆一起共同参与的“淘宝消防队”到底是做什么的?等等。