Erlang提供Supervisor的机制来管理进程,详细用法请看文档,http://erlang.org/doc/man/supervisor.htmlhttp://erlang.org/doc/design_principles/sup_princ.html

本文分享下Supervisor使用策略。

简单方案

最简单的方案,启动一个App,然后有个app_sup作为顶级的sup,下面挂着一堆permanent或者transient的常驻worker,或者子sup,重启strategy选最简单的one_for_one。但是这样的方案会有什么弊端呢?

上周末,我司的挂在app_sup的mysql poolboy worker的sup要启动子worker,由于mysql server的连接满了,启动不能,然后sup挂,poolboy worker挂,接着app挂了,只能重启。挂app可是麻烦事呀,三天两头挂,都得去手动重启,不搞死人?

挂的根本原因就是permanent或者transient的worker如果挂了,重启不能,上层的sup会将终止所有子worker,最后自己挂。这点在文档没有过多提及,当然worker挂了,能重启自然是好事,万一遇到重启不能怎么办呢。

优化方案

worker,在我看来,分三种,一种是纯函数,无副作用的内部worker,比如随机种子进程,这种估计实战上,所占比例并不多;另外一种是非常不纯的外部worker,严重依赖外部返回的,比如mysql driver之类的worker;最后一种是介于两者之间,部分依赖外部返回,比如文件内存缓存,读从内存,写直接操作文件,写操作挂,重启进程成功与否分情况。

重点讲头两种,第三种根据情况,自行选择趋向第一种还是第二种的方案。

纯函数进程

这种worker,挂了,重启多半能自己恢复过来,也是erlang的设计supervisor的初衷所在,直接大胆使用permanent或者transient即可。

依赖外部的进程

这种进程特点,自己生死掌握在别人身上,有点被动。所以项目的mysql使用,我设计成可以短暂失效的,mysql恢复之后,再将内存数据写回就好了,最理想的设计,可以临时写到其他地方去。不过暂时不考虑这么复杂化了,毕竟mysql server也没那么不靠谱,说自己挂了,就永远挂了。

这类进程就不能用permanent或者transient,万一重启不能,就全军覆没。

方案一

每个worker上级挂个transient的sup,worker挂了,sup自动退出,并不影响顶级app_sup。

方案二

worker用temporary,挂了之后,并不影响app_sup,缺点没有重启方案,如果编码防御式,其实重启也可有可无了,如果90%重启挂的,那干脆不重启了。也不是什么都可以重启解决的。

我们最后是选择了方案二,少数使用了方案一。