应该如何理解 Erlang 的「任其崩溃」思想?

关注者
1,945
被浏览
581,164

91 个回答

我看过一些很丑的代码。

比如,这里

这里

第一个例子里面,是一个全局负责连接请求包派发的 loop。在这里的连接挂了,整个应用在网络层也就挂了。用了一个 defer recover 妄想恢复,我冷冷一笑。连接层都是有状态的,发出去了哪些包,收回来了哪些包,在 channel 里面缓存中的有哪些包。在 panic 的情况下,这些状态根本不能恢复到 panic 前的状态,不是一个干净的 recover。这里的业务复杂程度也无法做好 recover。

第二个例子里面,到处都充满了 WithRecovery 的代码。场景是,一个请求会涉及到非常大的内存访问,然后需要限制住内存使用量,出问题的时候干掉这个请求,保护进程免于 OOM。所以就有了这里的许许多多 WithRecovery。

panic 是要涉及资源释放回收的。假使 panic 一次,占用的内存就不释放了,就是泄漏。或者连接各种资源就不关闭了。其实“任其崩溃”是很不负责了。

比如说,内存申请不到要 OOM 了,这个时候不 panic 还想挽回,有用么?

所以“任其崩溃”,其实是有前提条件的。这个前提条件是:

隔离。无副作用。

我们知道,这个进程挂掉了,不会影响到另一个进程的,系统还没有崩溃嘛。这就是隔离的作用。如果一个系统是由复杂的多个进程协作的,并且中间涉及很小的状态交互,那么其中某个进程任其崩溃并没太大影响。起一个进程来接替它的工作,整个系统就恢复了。

假设这些进程之间,有很复杂的交互模式,谁给谁发消息,生产和消费的怎么中,消息的中间持久化就比较难搞。如果是这些东西很少,基本上不带状态,无副作用,那“任其崩溃”了重启未尝不可。

Erlang 的 Actor 模型是 Actor 之间的交互解耦,这就是天然隔离了。

Erlang 是一门函数式语言,函数式语言本身是强调无副作用的。

这些是它能“任其崩溃”的重要前提。

没有懂得函数式语言的精华,没有深刻地理解状态,副作用这些东西,就照猫画虎学人家 “任其崩溃”的代码 ...... 都是垃圾。

利益相关:git blame 了一下,这几段代码都不是我写的哈

看到一堆说重启大法好的… 这…

说两点:

1. 可以把let it crash理解为可以“异步/不同线程/执行流”可以catch的exception,这就比普通的同线程调用栈捕捉exception就灵活了一些。但是也要注意灵活度所带来的复杂度提升问题,使用时要注意least of power的原则,不要把核弹当手雷使。

2. let it crash是说系统整体要设计成就算某线程/进程/执行流, crash了,其他部分也能通过各种合作(actor model里主要是通过定义各级supervisor actor的各种容错策略)使得crash不影响系统的正确性;或者说有些错误,有时候本地线程不具备足够的数据来处理,可以通过crash本执行流,而把错误交给非本执行流的某一个或多个其他进程/线程/甚至remote进程来处理;这样的好处是:与其把错误处理憋死在一个本地线程上,不如用灵活运用整个系统之力来处理(可以精挑细选任意适合的其他”组件/线程/进程”来更好的处理),let it crash里的it是本地的小局部,而不是整个系统重启;基本上是failure tolerence的一种思想。而不是说你在不了解系统的failure tolerence全局和具体设计的情况下,随意可以不处理和不思考如何从整体上处理异常,无脑crash,且美其名曰let it crash. 会被开的… …


Let it crash是为了利用全局信息来更好的recovery;它是磐涅后的浴火重生,而不是思想上的缴枪投降