【整理】Erlang 中的 supervisor

【概况】

supervisor behaviour 是用来实现监控其他子进程的 supervisor 进程的模块; 
子进程可以是另一个 supervisor 进程,也可以是一个 worker 进程; 
worker 进程一般使用 gen_event,gen_fsm 或 gen_server behaviour 来实现 ; 
一个使用 supervisor behaviour 实现的 supervisor 有一个接口方法的标准集,包括跟踪和错误报告的功能; 
supervisor 机制用来构建一个分层进程结构,称为 supervision tree,这是组织一个容错系统的好方式。 

【原则】

supervisor 负责启动、停止和监控它的子进程; 
supervisor 在必要时,通过重启它的子进程来使其保持活着; 
supervisor 的子进程由称作子进程规范的列表进行定义; 
当 supervisor 启动时,子进程按子进程规范列表中内容从左至右的顺序启动; 
当 supervisor 终止时,会按子进程启动顺序的反顺序终止相应子进程。 

【重启策略】

one_for_one 
如果一个子进程停止,则只重启该子进程; 
one_for_all 
如果一个子进程停止,则所有其他子进程也停止,然后所有子进程重启; 
rest_for_one 
如果一个子进程停止,则启动顺序中在它之后的所有其他子进程也停止,然后停止的这些子进程重启; 
simple_one_for_one  
一个简化的 one_for_one ,所有的子进程都是同样进程类型并且是动态添加的实例; 

【最大重启频率】

supervisor 有一个自带的机制来限制给定时间内重启的次数; 
如果在最近的 MaxT 秒之内有超过 MaxR 次数的重启,则 supervisor 停止它本身和它所有的子进程; 
当 supervisor 停止后,下一个更高级别的 supervisor 将进行下一步动作,重启上述停止的 supervisor 或者终止本身; 
重启机制的意图是防止一个进程由于某些原因重复性的死掉; 

【子规范】

?


1

{Id, StartFunc, Restart, Shutdown, Type, Modules}

Id 用于 supervisor 内部识别子规范的名字; 

StartFunc 定义了用来启动子进程的的方法,由三元组 {M, F, A} 来确定,其一般情况下调用的是 supervisor:start_link 、 gen_server:start_link 、gen_fsm:start_link 或 gen_event:start_link ,或对上述函数的相应封装。  

Restart 定义了子进程什么时候重启: 

  • permanent 表示子进程始终重启;
  • temporary 表示子进程决不重启;
  • transient 表示只有在子进程异常终止时才重启,即除了 normal 以外的终止原因;

Shutdown 定义了子进程怎样终止: 

  • brutal_kill 表示子进程使用 exit(Child, kill) 来无条件的终止;
  • 一个整数 timeout 值表示 supervisor 通过调用 exit(Child, shutdown) 告诉子进程请终止自己,然后等待子进程返回退出信号;如果没有在指定的时间内接收到(来自子进程的) 退出信号,则使用 exit(Child, kill) 无条件终止子进程;
  • 如果子进程是另一个 supervisor,则应该设置为 infinity 来给子树足够的时间来终止;

Type 指定子进程是一个 supervisor 还是一个 worker; 

Modules 应该是一个 list,含有一个元素 [Module]。 
如果子进程是一个 supervisor,或者子进程是 woker 且采用 gen_server 或 gen_fsm 行为模式实现,则 Modules 是 callback 模块的名字; 
如果子进程是 worker 且采用 gen_event 行为模式实现,则 Modules 应该为 dynamic ,该信息用来在升级和降级时供 release handler 使用; 

【参数选择原则】

按照子进程规范启动的子进程,若是要求该子进程为任何时候均可访问的注册进程,则一般使用参数 permanent ; 
按照子进程规范启动的子进程,若是要求该子进程终止之前不需要做任何清理工作,则不需要设置 timeout 值,可以设置为 brutal_kill; 
若是要求其终止之前做相应资源清理,则需要设置 timeout 超时值。  

【启动 supervisor】

?


1

supervisor:start_link(CallbackModuleName, InitCallbackParamList).

启动一个新的 supervisor 进程并链接到 OTP 系统的监督树中; 

  • 第一个参数 callback 模块的名字,是 init callback 方法所在 module 的名字;
  • 第二个参数 init callback 方法的参数列表;

supervisor 的执行流程如下: 
supervisor:start_link  --> init --> 根据指定的子规范的入口来启动它的所有子进程 

注意: supervisor:start_link 是同步的,当所有子进程启动之后才会返回 。 

【动态添加子进程】

?


1

supervisor:start_child(Sup, ChildSpec)

Sup 是 supervisor 的 pid 或名字,ChildSpec 是子进程规范; 
使用 supervisor:start_child/2 添加的子进程会表现出像其他子进程一样的行为,除了这点,如果 supervisor 死掉然后重启,则所有动态添加的子进程都将丢失; 

【停止一个子进程】

任何子进程,不管静态的还是动态的,都可以使用 shutdown 规范来停止。 

?


1

supervisor:terminate_child(Sup, Id)

停止的子进程的子规范使用如下调用来删除。 

?


1

supervisor:delete_child(Sup, Id)

Sup 是 supervisor 的 pid 或 name,Id 是子规范里指定的 id 。 
就像动态添加的子进程一样,如果 supervisor 本身重启,那么删除静态子进程的效果会丢失。 

【simple_one_for_one】

采用 simple_one_for_one 重启策略的 supervisor 是一个简化的 one_for_one supervisor,所有的子进程都是动态添加的同一类型子进程的实例。 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

-module(simple_sup).

-behaviour(supervisor).

 

-export([start_link/0]).

-export([init/1]).

 

start_link() ->

  supervisor:start_link(simple_sup, []).

 

init(_Args) ->

  {ok, {{simple_one_for_one, 0, 1},

    [{call, {call, start_link, []},

      temporary, brutal_kill, worker, [call]}]}}.

当 supervisor 通过 supervisor:start_link/2 启动后,将不会启动任何子进程,而是通过调用如下代码来动态添加子进程: 

?


1

supervisor:start_child(Sup, List)

Sup 是 supervisor 的 pid 或 name,List 是一个任意的 term 列表,该列表值将会被动态添加到子规范的参数列表里; 
如果启动方法指定为 {M, F, A},则子进程的启动是通过调用 apply(M, F, A++List) 来完成的。  

【supervisor 的终止】

既然 supervisor 是 supervision tree 的一部分,则其将自动被它自身的 supervisor 所终止;当终止时,它会按启动的反顺序根据相应的 shutdown 规范来自动终止它所有的子进程,然后终止本身。 

时间: 2024-08-30 19:18:12

【整理】Erlang 中的 supervisor的相关文章

Erlang中的映射组Map详细介绍_Erlang

主要是遇到 Map匹配的问题,所以顺便回忆一下 Erlang 中的映射组 Map,在其它语言中被称作 Hash 哈希或者 Dict 字典. Erlang 从 R17 版本开始支持映射组 创建映射组 Erlang 中的映射组用结构 #{} 表示,创建一个映射组可以这样 复制代码 代码如下: % 不管你怎么排序,最终结果都是按键的字典顺序排列的 #{ name => "wittyfox", age => 19 }. % => #{age => 20,name =&g

Erlang中的socket编程简单例子_Erlang

Erlang 中gen_tcp 用于编写TCP程序,gen_udp用于编写UDP程序.一个简单的TCP服务器echo示例: 复制代码 代码如下: Start_echo_server()->          {ok,Listen}= gen_tcp:listen(1234,[binary,{packet,4},{reuseaddr,true},{active,true}]),          {ok,socket}=get_tcp:accept(Listen),          gen_tc

Erlang中的模块与模式匹配介绍_Erlang

模块是Erlang的基本代码单元,erl文件编译后以.beam作为扩展名,采用UTF8字符集,.erl文件示意如下: -module(模块名,与存放模块的文件名相同) -export([方法名/输入参数的个数]) 复制代码 代码如下: Method1( {a,b,c})->a*b*c; Mehtod2({d,e})->d-e. 模块属性有两种类型:预定义型和用户定义型.   Erlang中用于代表函数的数据类型被称为fun,相当于python中的lambda,一般用于 1)      对列表里

Erlang中的注册进程使用实例_Erlang

之前看到 Erlang 中的注册进程时,对注册并不理解,主要是不理解注册的原子的作用域.刚才突然想明白了: 复制代码 代码如下: 注册进程关联的原子具有全局作用域 也就是说关联了注册进程之后的原子可以全局被使用 Erlang 中的并发机制是通过消息邮箱实现的,进程间进行通讯的方式只有消息邮箱,而进程间通讯需要知道进程的进程号,而使用 spawn 产生新进程时会返回新进程的进程号供使用. 一个最简单的进程间通信的程序如下 复制代码 代码如下: -module(test). start() ->  

Erlang中的并发程序简介_Erlang

Erlang中基本的并发函数 1)  Pid =spwan(Mod,Func,Args) 创建一个新的进程来执行apply(Mod,Func,Args),与调用进程并列运行,会使用最新的代码定义模块. 2)  Pid!Message 向Pid进程异步发送Message,!为发送操作符 3)  Receive - end 接收消息 复制代码 代码如下:  receive            Pattern1[when Guard1]-> Expression1;            Patte

Erlang中的匹配模式总结_Erlang

一.赋值时匹配 原子匹配 复制代码 代码如下: atom    = atom                        % atom another = another                     % another atom    = another                     % exception error 变量匹配 复制代码 代码如下: Var = 2.                              % 2 Var = 3 - 1.       

Erlang中的基本元素操作小结_Erlang

Erlang shell中,用句号加空格.tab或回车来结束表达式,%表示注释的起点,;隔离子句.模块是.erl 文件,库的头文件.hrl, shell中的编译时c(),外编译命令时erlc, 退出shell用q(),或erlang:halt(). 变量以大写字母开头,且不能重新绑定变量,只能一次性赋值,具有不可变状态.原子是全局的,不需要宏定义或包含文件,以小写字母开头,还可放在单引号内,是极简表达式.   元组(tuple)是一些数量固定的项目归组成单一实体{,}, 由于是匿名的,通常在第一

Erlang中的函数与流程控制介绍_Erlang

一:函数 1:在Erlang中,[名字相同但参数数目不同]的两个函数是完全不同的函数. 2:其他模块内的函数用完全限定名称 被调用: 复制代码 代码如下: -module(sort1). -export([reverse_sort/1, sort/1]). reverse_sort(L) ->         lists1:reverse(sort(L)). sort(L) ->         lists:sort(L). 3:子句间以分号[;]分隔,在最后的结尾处以[.]结尾. 4:每个函

收集整理项目中常用到的正则表达式

最近做的一个内部系统项目,涉及大量的文本校验,里面用到了一些常用的正则表达式,收集不易,先记录在此,以备后用. 匹配中文字符的正则表达式: [\u4e00-\u9fa5] 匹配双字节字符(包括汉字在内):[^\x00-\xff]  可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1) 匹配空白行的正则表达式:\n\s*\r  可以用来删除空白行 匹配HTML标记的正则表达式:< (\S*?)[^>]*>.*?|< .*? /> 这个仅仅能匹配部分,对于复杂的