最近工作繁忙,公众号许久没有更新。
前段时间被组长约谈,我谈到了我目前对编程的一些看法,主要聊了「面向崩溃编程」这种想法,本篇文章简单聊一下我的浅显看法。
Let it crash
「凡是可能出现问题的都会出现问题」- 墨菲定律
墨菲定律在编程中翻译成大白话就是,Bug一定会有,随着时间的积累,程序最终都会出现不同的问题,那关键的点在于「如何处理没有预料到的程序Bug」。
一年多以前,我并不知道还有面向崩溃编程这种说法,然后我接触到了Erlang,作为一个Python写手,突然从面向对象的写法转到面向函数的写法,一时没有适应过来,Bug横飞,当时我经常叫我旁边的哥们帮我查Bug。
后面渐渐开始了解Erlang的思想,也可以说是它的一种哲学。每个语言都有自己的哲学,Python的话大家应该都比较熟悉,直接 importthis
就好了。
这里提一下Erlang中「Let it crash」的思想,直译就是让它崩溃吧。
为什么要让程序崩溃?这不是让业务中断吗?
先解释一下Let it crash的想法。
很多时候,代码在运行,遇到异常会被catch住(Python中就是except),然后打个log,然后再继续运行,这个时候程序并没有崩溃,但运行状态已经不是正常状态了,但依旧苟活着,当不正常状态越来越多时,最终会导致整个程序崩溃,此时你去看崩溃原因,你会无法理解为啥这个地方会报这个错误。
此时好的方式就是让程序崩溃报错退出,这样去排错是很容易发现问题的,而不是等不正常状态累积到一定状态后,再崩溃,其结果让人懵逼。
但这并不是说try...catch毫无用处,正确的处理方式应该是catch住自己能处理的类型错误,那些不能处理的类型错误就让它过去,让它报错,让它将程序搞崩溃,这样你就可以快速去定位问题并解决。
不隐藏任何不能处理的Bug,构建出「崩溃后 --> 快速排错 --> 修复」的流程,从而让程序可以越来越健壮。
这其实有个疑惑。程序崩溃虽然可以快速知道问题,但业务也中断了,此时catch的目的是为了让业务继续运行,catch到错误后,打印错误日志以及当前运行状态的内存堆栈,然后再发错误日志警报,此时在业务没有中断的情况下,也可以知道错误,从而实现快速排错以及修复的过程。
如果可以保证每个异常log都有人会看,并且看完后都会找时间去处理并重启服务,那确实是更好的方法,但做得到吗?「其实一个大型系统,人,才是最终的Bug。」
我自问做不到,很多时候,手里都有工作,报警后,看了一下,程序还在跑,就先放一下,先做手头的工作,放多了,就忘了。
那业务中断肯定会造成经济损失,这个怎么办?
请回顾墨菲定律,业务总是会中断的,现在做的各种负载、解耦等措施只是在降低中断的概率,要让业务稳定运行应该依赖于多服务(负载)并降低服务间的关联性(即微服务),而不应该依赖于放过异常状态让程序继续运行。
在多服务情况下,就算单点断了,其他节点依旧会让整个业务继续运行,这与让程序崩溃本身没有直接的冲突。
这并不是银弹
当程序发生异常时,让其崩溃的目的只是为了更快的定位出问题并加以改进,但这种思想并不适合于所有的系统。
思考一下Erlang的特性就可以理解Erlang为什么提倡让其崩溃这种做法。Erlang中都是进程,每个进程之间相互独立,一个进程出现异常情况,让其崩溃并不会影响整体,这是很多语言不具有的特性(Golang可以尝试尝试)。
但很多项目的模块并没有很好的独立开,此时让一个模块崩溃会直接影响整个系统,此时让其崩溃并不是好的方式,其成本较高。直接写日志,然后依赖于让人主动去处理可能更好一些,但正因为人很难主动去处理还可以运行的程序,导致很多大的项目难以维护(原因之一吧)。
微服务化的概念大家都知道,其目的就是减少系统间的耦合,此时单模块的崩溃并不会影响整个系统,最后实现在整个系统具有良好模块化的基础上,每个模块只有正常提供功能以及停止运行这两种状态。
异常是不可控
要清晰的明白,异常是不可控。
你能想到的所有状态、可能出现的所有错误都只是“操作错误”,此时程序是可以正常处理,这并不能算一个Bug。
比如一个web系统,你不允许用户输入密码小于6位,但用户在注册时只给你小于6位的密码,这种情况你已经意识到了,你会给用户返回一个错误,但这并不是程序错误,即不是一个Bug。
真正让系统出现问题是你无法意识到的点,这个点才会造成系统的异常。你根本没有意识到这个点,却通过try...catch将其包裹然后打个报警日志,让程序以非正常状态运行,这并不是可取的做法。
关于工作
在约谈的过程中,除了聊上面的内容,组长还指出了我个人在工作中的一些问题并给出了相应的建议,比如太过依赖于测试、代码规范性不强、边界条件考虑不完全。
感谢组长的建议,知道了自己的问题后,要做的当然就是debug自己,将这些问题修复。
最后如果你觉得文章有帮助,点击「在看」支持二两,后面会抽时间将APScheduler源码分析系列文章补完的。
参考文章
[Why Do Computers Stop and What Can Be Done About It?] [Error Handling in Node.js] [应该如何理解 Erlang 的「任其崩溃」思想?]