erlang学习笔记-supervisor 行为模式
这一篇以开源项目cowboy中的顶层监督者cowboy_sup为例子解释supervisor中的一些细节
有些内容收集自网上,具体出处我也忘了,如有雷同,纯属我抄你…
cowboy_sup.erl
1 | -module(cowboy_sup). |
这个模块就是otp中一大行为模式—— supervisor (监督者)的实现,看下 supervisor 的简单介绍(摘抄自Erlang设计原则):
督程负责启动、停止和监视它的子进程。督程的基本思想是它要保持它的子进程有效,必要的时候可以重启他们。
要启动和监视的子进程由一个 子进程规格 的列表来指定。子进程按照在这个列表中的顺序启动,并且按照相反的顺序终止。
当我们调用 supervisor:start_link({local, ?MODULE}, ?MODULE, []). 这个进程会回调自己的 init/1 方法进行进程的初始化,init/1 需要返回一个元组作为初始化的参数,这里对返回的元组中一些字段解释一下:
1 | %% 返回元组格式 |
SupFlags 进程策略:
Strategy 重启模式
- one_for_one 如果子进程终止,只有这个进程会被重启
- one_for_all 如果子进程终止,所有子进程都会被重启
- rest_for_one 如果子进程终止,在这个进程重启后,其他所有子进程会被重启
- simple_one_for_one 简化one_for_one,所有子进程都动态添加同一种进程的实例
- one_for_one 和 simple_one_for_one 最大的区别在于:one_for_one 用一个列表储存了所有要启动的子进程,并按顺序启动。而simple_one_for_one 用一个进程字典定义所有子进程。所以当一个监督者拥有很多子进程时,遇到进程崩溃,simple_one_for_one 效率会快很多
Intensity 最多允许重启次数 0表示不重启
Period 限定重启时间,单位:s(秒)
结合起来看就是,在多长时间内(Period)最多允许重启几次(Intensity),重启的方式为(Strategy),Period 和 Intensity 的设定意义在于限制最大重启频率,这是为了避免反复重启进入死循环。
Procs 子进程相关:
Id 是督程内部用于标识子进程规范的名称。
StartFunc 定义了用于启动子进程的很难书调用。它是一个模块.函数.参数的元组,与 apply(M, F, A) 用的一样。
Restart定义了一个被终止的子进程要在何时被重启:
- permanent 子进程总会被重启。
- temporary 子进程从不会被重启。
- transient 子进程只有当其被异常终止时才会被重启,即,退出理由不是 normal 。
Shutdown定义了一个子进程应如何被终止。
- brutal_kill 表示子进程应使用 exit(Child, kill) 进行无条件终止。
- integer() >=0 一个整数超时值表示督程先通过调用 exit(Child, shutdown) 告诉子进程要终止了,然后等待其返回退出信号。如果在指定的事件内没有接受到任何退出信号,那么使用 exit(Child, kill) 无条件终止子进程。
- infinity 如果子进程是另外一个督程,那么应该设置为 infinity 以给予子树足够的时间关闭。
Type 指定子进程是督程还是佣程。
Modules 应该为只有一个元素的列表 [Module],其中 Module 是回调模块的名称,如果子进程是督程、gen_server或者gen_fsm。如果子进程是一个gen_event,那么 Modules 应为 dynamic 。 在升级和降级过程中发布处理器将用到这个信息。
结合上面的解释,我们就可以理解 cowboy_sup.erl 中 init/1 方法中代码的意思了:
1 | Procs = [{cowboy_clock, {cowboy_clock, start_link, []}, |
表示这个cowboy_sup监控树下,会启动一个 cowboy_clock 模块,启动的模式是 permanent 即总会重启,强制结束的等待时间设置为5000,worker表示启动的子进程是一个佣程
重启的策略是 one_for_one 一对一模式,并且在 10s 中最多允许重启 10 次
最后再说明一下Modules:
Modules 是进程依赖的模块,这个信息只有在代码热更新的时候才会被用到:标注了哪些模块需要按照什么顺序进行更新;通常这里只需要列出进程依赖的主模块.
如果子进程是supervisor gen_server gen_fsm Module名是回调模块的名称,这时Modules的值是只有一个元素的列表,元素就是回调模块的名称;
如果子进程是gen_event Modules的值是 dynamic;
关于dynamic参数余锋有一篇专门的分析: Erlang supervisor规格的dynamic行为分析
Author: Hamiel Hong
Link: https://hamielhong.github.io/2019/04/25/erlang-supervisor/
Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.