`
uniseraph
  • 浏览: 83576 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

otp深入浅出之: supervisor的simple_one_for_one

阅读更多
    supervisor是一种很常用的erlang pattern,各种资料比较多,但是网上各种例子和说明都主要是针对one_for_one的restart strategies,simple_one_for_one的资料比较少。本文通过分析supervisor源码,说明simple_one_for_one的使用方法与运行机制。


what  is the  supervisor ?
引用
A  behaviour  module  for  implementing  a  supervisor, a process which supervises other processes called child processes.

简单的说,实现了supervisor的进程可以负责监控其他子进程,supervisor的子进程有4种重启策略,
引用
     A supervisor can have one of the following restart strategies:

         * one_for_one  -  if  one  child  process  terminates  and  should be
           restarted, only that child process is affected.

         * one_for_all -  if  one  child  process  terminates  and  should  be
           restarted,  all  other  child processes are terminated and then all
           child processes are restarted.

         * rest_for_one - if  one  child  process  terminates  and  should  be
           restarted, the 'rest' of the child processes -- i.e. the child pro-
           cesses after the terminated child process in the start order -- are
           terminated.  Then  the  terminated child process and all child pro-
           cesses after it are restarted.

         * simple_one_for_one - a simplified one_for_one supervisor, where all
           child processes are dynamically added instances of the same process
           type, i.e. running the same code.

           The functions terminate_child/2, delete_child/2 and restart_child/2
           are  invalid  for  simple_one_for_one  supervisors  and will return
           {error, simple_one_for_one} if the specified supervisor  uses  this
           restart strategy.


其中simple_one_for_one用来创建一组动态子进程,一个使用了simple_one_for_one 策略的supervisor可以类比为factory pattern,最常见的例子就是socket 服务端接受新的连接请求以后,需要创建新的socket连接进程。

假设通过supervisor,在某些情况下动态创建一个container进程,每个container behaviour(gen_server) 作为进程存在。


container.erl代码:
-module(container).

-behaviour(gen_server).


-export([start_link/1]).
-export([stop/1]).
-export([setup/0]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

-record(state, {name}).

setup()->
        supervisor:start_child(container_sup,["container1"]),
        supervisor:start_child(container_sup,["container2"]),
        supervisor:start_child(container_sup,["container3"]),
        supervisor:start_child(container_sup,["container4"]).
stop(ContainerName)->
        	gen_server:call(list_to_atom(ContainerName) , {stop } ).

start_link(Name) when is_list(Name) ->
    gen_server:start_link( {local, list_to_atom(Name)}, ?MODULE, Name, []).
    
init(Name) ->
    {ok, #state{name=Name}}.
    
handle_call( {stop } , From, State) ->
        { stop , normal , ok , State };
handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.




container_sup.erl代码:
-module(container_sup).

-behaviour(supervisor).

-export([start_link/0]).
-export([init/1]).

-define(SERVER, ?MODULE).

start_link() ->
    supervisor:start_link({local, ?SERVER}, ?MODULE, []).

init([]) ->
    ContainerSpec = {container,{container,start_link,[]},
              transient,2000,worker,[container]},
    {ok,{{simple_one_for_one,10,100}, [ContainerSpec]}}.




1 how to start the container_sup ?

1.1 start  supervisor
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).


1.2 supervisor:start_link回调gen_server:start_link
start_link(SupName, Mod, Args) ->
    gen_server:start_link(SupName, supervisor, {SupName, Mod, Args}, []).


1.3  gen_server:start_link最终回调supervisor:init(具体请参考http://uniseraph.iteye.com/blog/372838)

1.4  supervisor:init
init({SupName, Mod, Args}) ->
    process_flag(trap_exit, true),
    case Mod:init(Args) of
        {ok, {SupFlags, StartSpec}} ->
            case init_state(SupName, SupFlags, Mod, Args) of
                {ok, State} when ?is_simple(State) ->
                    init_dynamic(State, StartSpec);
                {ok, State} ->
                    init_children(State, StartSpec);
                Error ->
                    {stop, {supervisor_data, Error}}
            end;
        ignore ->
            ignore;
        Error ->
            {stop, {bad_return, {Mod, init, Error}}}
    end.

1.4.1  设置进程模式,当处理子进程退出是supervisor会受到一个{'EXIT', Pid, Reason}消息
 process_flag(trap_exit, true),

1.4.2  回调container_sup:init获得container_sup的子进程规约
1.4.3  根据不同的resart strategies调用相应的init_xxx方法
 case init_state(SupName, SupFlags, Mod, Args) of
                {ok, State} when ?is_simple(State) ->
                    init_dynamic(State, StartSpec);
                {ok, State} ->
                    init_children(State, StartSpec);
                Error ->
                    {stop, {supervisor_data, Error}}
            end;

1.4.4  init_dynamic检查子进程规约的合法性,并不真正启动子进程
init_dynamic(State, [StartSpec]) ->
    case check_startspec([StartSpec]) of
        {ok, Children} ->
            {ok, State#state{children = Children}};
        Error ->
            {stop, {start_spec, Error}}
    end;
init_dynamic(_State, StartSpec) ->
    {stop, {bad_start_spec, StartSpec}}.


2 how to add a container processor ?
2.1
 supervisor:start_child(container_sup,["container1"]),

2.2 supervisor:start_child触发一个start_child事件,在supervisor中处理
handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
    #child{mfa = {M, F, A}} = hd(State#state.children),
    Args = A ++ EArgs,
    case do_start_child_i(M, F, Args) of
        {ok, Pid} ->
            NState = State#state{dynamics =
                                 ?DICT:store(Pid, Args, State#state.dynamics)},
            {reply, {ok, Pid}, NState};
        {ok, Pid, Extra} ->
            NState = State#state{dynamics =
                                 ?DICT:store(Pid, Args, State#state.dynamics)},
            {reply, {ok, Pid, Extra}, NState};
        What ->
            {reply, What, State}
    end;


2.3 do_start_child_i根据子进程配置规约回调container:start_link方法,并传入参数"container1"

2.4 记录子进程Pid

3 how to stop a container processor ?
引用
   The functions terminate_child/2, delete_child/2 and restart_child/2
           are  invalid  for  simple_one_for_one  supervisors  and will return
           {error, simple_one_for_one} if the specified supervisor  uses  this
           restart strategy.

3.1 container进程处理一个{stop}消息
handle_call( {stop } , From, State) ->
        { stop , normal , ok , State };

3.2 container_sup受到一个{'EXIT', Pid, Reason}消息,在handle_info中处理
handle_info({'EXIT', Pid, Reason}, State) ->
    case restart_child(Pid, Reason, State) of
        {ok, State1} ->
            {noreply, State1};
        {shutdown, State1} ->
            {stop, shutdown, State1}
    end;

3.3 restart_child根据pid在state中查找并restart这个进程
restart_child(Pid, Reason, State) when ?is_simple(State) ->
    case ?DICT:find(Pid, State#state.dynamics) of
        {ok, Args} ->
            [Child] = State#state.children,
            RestartType = Child#child.restart_type,
            {M, F, _} = Child#child.mfa,
            NChild = Child#child{pid = Pid, mfa = {M, F, Args}},
            do_restart(RestartType, Reason, NChild, State);
        error ->
            {ok, State}
    end;


3.4 应用Reason=normal并且conainer是restart type是transient,所以这个container退出,不再重启。
do_restart(permanent, Reason, Child, State) ->
    report_error(child_terminated, Reason, Child, State#state.name),
    restart(Child, State);
do_restart(_, normal, Child, State) ->
    NState = state_del_child(Child, State),
    {ok, NState};
do_restart(_, shutdown, Child, State) ->
    NState = state_del_child(Child, State),
    {ok, NState};
do_restart(transient, Reason, Child, State) ->
    report_error(child_terminated, Reason, Child, State#state.name),
    restart(Child, State);
do_restart(temporary, Reason, Child, State) ->
    report_error(child_terminated, Reason, Child, State#state.name),
    NState = state_del_child(Child, State),
    {ok, NState}.



  • 大小: 6.5 KB
分享到:
评论
1 楼 mryufeng 2009-08-12  
simple_one_to_one在网络程序里面用的挺多的 主要用于比如连接什么的管理。
3个作用:

1. otp体系 完整的出错信息管理
2. tree拥有节点的信息 可以方便遍历 自己不用写这部分代码了。
3. 进程关闭什么的可以推给tree来管理。

相关推荐

    pin_code_fields:Flutter软件包,将帮助您生成具有精美设计和动画的PIN码字段。 可以用于OTP或Pin码输入:nerd_face::nerd_face:

    可用于OTP或Pin码输入 :nerd_face: :nerd_face: 特征 :green_heart: 自动将下一个字段集中在键入上,将上一个字段集中在删除上 游标支持 :high_voltage: 可以设置为任意长度。 (建议3-6个字段) 文本字段的3种...

    分布式应用Erlang:Erlang_OTP_19_win64

    在OTP中,Supervisor是核心组件之一,它负责管理和监控应用程序中的进程,当某个进程崩溃时,Supervisor可以自动重启该进程,确保系统的稳定性。此外,GenServer提供了一种状态管理机制,使得进程能够存储和操作状态...

    Erlang安装包,版本:otp_win64_24.1.7.exe

    otp_win64_24.1.7.exe

    erl:otp_src_24.0.5.tar.gz

    标题中的"erl:otp_src_24.0.5.tar.gz"指的是Erlang的源码包,其中"otp_src_24.0.5"是Erlang/OTP (Open Telecom Platform) 的24.0.5版本的源代码。Erlang是一种面向并发的、函数式的编程语言,常用于构建高可用性和...

    otp_src_23.3.tar.gz

    OTP(Open Telephony Platform)是Erlang编程语言的核心组件之一,主要负责提供分布式计算、容错和系统监控等功能。Erlang是一种并发性极强、面向进程的编程语言,常用于构建高可用性和可扩展性的系统,尤其在电信、...

    OTP.zip_OTP_OTP .nd pudn_java otp_otp java_verify

    OTP,全称One-Time Password,是一种安全的身份验证机制,用于生成一次性使用的密码,通常...通过解压并分析“OTP.zip”中的源代码,我们可以深入了解其内部实现机制,并可能学习到如何在自己的项目中实施OTP验证系统。

    otp_verify_java.rar_OTP_OTPVerify_TOTP_otp算法_一次性 口令

    OTP(One-Time Password)是一种基于时间、事件或挑战/应答机制的一次性密码技术,用于提高用户身份验证的安全性。OTP确保每个密码只能使用一次,从而降低了密码被重放攻击的风险。在Java环境中,我们可以使用不同的...

    otp_22.3_for_windows_64.rar

    【OTP 22.3 for Windows 64位】是一个为RabbitMQ提供基础支持的软件包,适用于64位Windows操作系统。OTP(Open Telecom Platform)是Ericsson开发的一个开源软件平台,主要用于构建可靠、可扩展和高度可用的分布式...

    官网otp_src_21.3.tar.gz

    在windows下解压后就是otp_src_21.3.tar.gz文件,然后拷贝到服务器上进行安装即可。RabbitMQ环境所需的Erlang软件,适用RabbitMQ的版本:3.7.25 3.7.24 3.7.23 3.7.22 3.7.21 3.7.20 3.7.19

    emqx配置和安装.docx

    EMQX 是基于 Erlang/OTP 平台开发的开源物联网 MQTT 消息服务器。它的设计目标是实现高可靠,并支持承载海量物联网终端的 MQTT 连接,支持在海量物联网设备间低延时消息路由。EMQX 可以稳定承载大规模的 MQTT 客户端...

    otp.rar_OTP .nd pudn_One Time Password_c random 函数_otp算法_一次性 口

    通过阅读和分析这些源代码,可以深入学习如何在实际应用中结合MD5和随机数生成来实现一次性口令系统,这对于信息安全领域的学习者或者开发者来说是非常有益的。同时,这也是一个很好的实例,展示了如何将理论知识...

    react-native-otp-field:React本机Otp字段

    react-native-otp-fieldReact本机OTP字段 :keycap_1: :keycap_2: :keycap_3: :keycap_4: :keycap_5:安装npm i react-native-otp-fieldRN <0.63 npm i react-native-otp-field@0.0.7一个可在android和iOS上运行的...

    thy_supervisor:实现一个基本的类似 OTP 的 Supervisor 的练习

    opts = [strategy: :one_for_one, name: ThyApp.Supervisor] Supervisor.start_link(children, opts) end end ``` 现在,我们已经创建了一个基础的Supervisor,它可以启动、管理和恢复子进程。在实践中,我们...

    Erlang OTP设计原理文档 中文版本

    Erlang OTP(Open Telephony Platform)是Erlang编程语言的一个核心部分,它提供了...通过深入理解这些文档和概念,开发者能够构建出符合OTP原则的、高度可靠的Erlang系统,有效地处理并发、故障恢复和系统扩展性问题。

    OTP.rar_MD5 password_OTP_OTP 加密_otp算法_口令认证

    首先,OTP(One-Time Password)是每次登录时生成一个仅限一次使用的密码,有效时间通常很短,如30秒至几分钟不等。这种机制大大提高了账户的安全性,因为即使攻击者截获了一个密码,也无法在下次验证时使用。OTP...

    otp_22.3_for_windows_64 和RabbitMQ-server-3.8.5安装包

    自己自学RabbitMQ整理出来的资料,windows环境下的erlang和RabbitMQ安装包,版本分别是otp_22.3_for_windows_64和rabbitmq-server-3.8.5(保证不会出现版本冲突) 在官网上下载简直是龟速,所以分享给大家便于下载,...

    otp_src_21.0.tar.gz

    标题中的"otp_src_21.0.tar.gz"是一个开源软件Erlang OTP的源代码压缩包,版本号为21.0。OTP(Open Telecommunications Platform)是Erlang编程语言的核心库,包含了用于构建可靠、分布式系统的一系列工具和库。...

    otp_win64_24.0.zip

    OTP(Open Telecom Platform)是Erlang的核心组件,提供了一整套库、设计原则和工具,旨在帮助开发者构建高效、可靠的系统。 标题"otp_win64_24.0.zip"指的是OTP的Windows 64位版本的24.0发行版的压缩文件。这个...

    otp_win64_21.0.1

    otp_win64_21.0.1otp_win64_21.0.1otp_win64_21.0.1otp_win64_21.0.1

    otp_src_25.0.2.tar.gz

    8. **源码结构**:在"otp_src_25.0.2"目录下,你可以找到Erlang OTP的源码组织结构,了解不同模块的职责和交互方式,这对于深入理解和定制OTP系统至关重要。 9. **编译和安装**:解压源码后,开发者通常会使用rebar...

Global site tag (gtag.js) - Google Analytics