首页 > 代码库 > Erlang:RabbitMQ源码分析 3. supervisor和supervisor2深入分析

Erlang:RabbitMQ源码分析 3. supervisor和supervisor2深入分析

supervisor也是Erlang/OTP里一个常用的behavior,用于构建supervisor tree实现进程监控,故障恢复。

而RabbitMQ实现了一个supervisor2,我们从源码角度分析二者的实现和区别。


先介绍一些supervisor的基本概念,假设node_manager_sup是一个supervisor,它的init函数会定义supervisor的一些参数和它的children。

参数:

          1. Restart Strategy:

                  Strategy必须是simple_one_for_one,one_for_one, one_for_all, rest_for_one 中的一种

                    simple_one_for_one是指supervisor启动时并不启动children,children个数不限,但只能是同一个类型的child,共享一份代码

                    one_for_one是指supervisor启动时就启动所有children,一旦一个child挂了,supervisor只去重启这一个child process,不影响其他children

                    one_for_all是指supervisor启动时就启动所有children,一旦一个child挂了,supervisor重启所有children processes

                    one_for_rest是指supervisor启动时就启动所有children,一旦一个child挂了,supervisor重启在这个child之后声明的所有children


         2. intensity and period:如果在period时间内重启超过intensity 次,supervisor就把所有children连同自己一起kill掉


Children的参数:

         1. StartFun,child启动函数,必须返回{ok,ChildPid} or {ok,ChildPid,Info}

         2. Restart, 本child的重启策略, permanent代表一直重启,temporary代表绝不重启,transient代表只有当error退出时才重启

         3. ShutDown, 本child process的shutdown策略,brutal_kill代表立刻强行kill掉, 正整数代表timeout,即发送一个kill的request,如果timeout时间还没收到response,就强行kill掉,infinity代表只是发送一个kill的request过去,不强行kill,一般用于当child也是一个supervisor的时候。


supervisor也是一个gen_server

supervisor2并没有使用gen_server2而是使用了原版的gen_server


既然是gen_server,supervisor的入口start_link,其实也就是内部的init函数:

1. 检查supervisor的所有参数

2. 如果不是simple_one_for_one, 就启动所有children。


当有child process exit 时,根据gen_server的behavior,会由handle_info({‘EXIT‘, Pid, Reason}, State)来处理,根据此child的Restart类型进行重启。

重启之前会更新重启次数,如果发现在period时间内重启超过intensity 次,就把所有children连同自己一起kill掉

如果重启失败会再次重启,再次重启的请求由handle_cast处理


最后再看supervisor的几个export函数:

start_child, 是一个gen_server:call,

1. 如果是simple_one_for_one,就从children里随便拿一个启动,因为simple_one_for_one的children都一样,而且都没随supervisor启动

2. 如果不是simple_one_for_one,start_child就传入一个child,启动这个child

restart_child,delete_child, terminate_child,which_children,which_children也都是gen_server:call


supervisor2:

supervisor2并没有使用RabbitMQ自己的gen_server2而是使用gen_server,原因supervisor接收的request比较少(都是重启,启动,终止之类),也没有用到hibernate。

supervisor2对supervisor的改动并不算多:

1. intrinsic, child的Restart增加了intrinsic类型,和transient很像,不同的是,transient如果child非正常退出,就将其删除,supervisor照常运行;但intrinsic如果child非正常退出,supervisor也会退出并删除其他所有children。

2. Delay, 如上所述,在period时间内重启超过intensity 次,supervisor就把所有children连同自己一起kill掉supervisor2里,child的Restart可以写{permanent, Delay} | {transient, Delay} | {intrinsic, Delay},这样在period时间内重启超过intensity 次后,supervisor并不会kill所有,而是等待Delay时间再尝试重启该child。

Erlang:RabbitMQ源码分析 3. supervisor和supervisor2深入分析