Erlang/OTP 监督者(Supervisor)

Erlang/OTP 监督者(Supervisor)的作用是负责其子进程的启动,停止和监视。监督者的基本思路是,保持其子进程能正常运行,并在必要时重新启动子进程。

一、Erlang/OTP Supervisor 基本参数

Erlang/OTP Supervisor的基本参数有进程策略,进程id,进程启动函数,进程重启,进程关闭,进程类型,进程模块。

[plain]  view plain  copy
  1. -module(test_sup).    
  2. -behaviour(supervisor).    
  3. -export([start_link/1, init/1]).    
  4.   
  5. start_link(_) ->    
  6.     io:format("test sup start link~n"),    
  7.     supervisor:start_link({local, ?MODULE}, ?MODULE, []).    
  8.   
  9. init([]) ->  
  10.   {ok,  
  11.     {{one_for_one, 1, 60},           % Strategy = {How, Max, Within}  
  12.        [{ test_handler_worker,       % Id       = internal id  
  13.           {test_server, start, []},  % StartFun = {M, F, A}  
  14.           permanent,                 % Restart  = permanent | transient | temporary  
  15.           2000,                      % Shutdown = brutal_kill | int() >= 0 | infinity  
  16.           worker,                    % Type     = worker | supervisor  
  17.           [test_server]              % Modules  = [Module] | dynamic  
  18.           }]  
  19.      }  
  20.    }.  

1、进程策略

Strategy = {How, Max, Within}

意思是在多长时间内(Within)最多允许重启了几次(Max),如何重启(HOW)。Within 和 Max 的设定意义在于限制最大重启频率,这是为了避免反复重启进入死循环。Within 以秒为单位。Max为0表示不重启。

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 效率会快很多。

2、进程id

Id  = internal id

用于监督者区分子进程,必须保证唯一

3、进程启动函数

StartFun = {M, F, A}

用于监督者启动子进程的函数。M代表模块,F代表函数,A代表参数

4、进程重启

Restart = permanent | transient | tempora

表示进程遇到错误之后是否重启

permanent 遇到任何错误导致进程终止就重启
temporary 进程永远都不重启
transient 只有进程异常终止的时候会被重启
5、进程关闭

Shutdown = brutal_kill | int() >= 0 | infinity

表示采用什么手段来关闭进程

brutal_kill 立刻强制关闭进程
int() >= 0 等待多少毫秒后强制关闭进程
infinity 当子进程也是监督者时使用,意思是给足够时间让子进程重启
6、进程类型

Type  = worker | supervisor

告诉监督者子进程是哪种类型的进程,工作进程,还是监督进程?

7、进程模块

Modules  = [Module] | dynamic

表示进程运行依赖哪些模块,仅在代码热更新时使用。使用dynamic的情况是当使用了 Erlang/OTP 发布(Release)等功能,使得Erlang/OTP 可以判断在热更新时需要哪些模块


二、Erlang/OTP Supervisor 工作流程

下面以 Erlang/OTP Application 的例子为基础,继续讨论Supervisor 的工作流程。所以这个 Application 就包含了5个结构:应用模块、监督者模块、资源文件、服务端模块、处理模块。

监督者模块(test_sup.erl):

[plain]  view plain  copy
  1. -module(test_sup).  
  2. -behaviour(supervisor).  
  3. -export([start_link/1,init/1]).  
  4.   
  5. start_link(_) ->  
  6.   io:format("test sup start link~n"),  
  7.   supervisor:start_link({local, ?MODULE}, ?MODULE, []).  
  8.   
  9.   
  10. init([]) ->  
  11.   io:format("test sup init~n"),  
  12.   {ok,{  
  13.     {one_for_all, 1, 60},  
  14.     [{  test_handler_worker,    
  15.       {  test_server, start, [test_handler] },   
  16.       permanent,   
  17.       2000,  
  18.       worker,   
  19.       [test_server,test_handler]  
  20.       }]}  
  21.   }.  
服务端模块(test_server.erl):
[plain]  view plain  copy
  1. -module(test_server).  
  2. -behaviour(gen_server).  
  3. -export([start/1, init/1, handle_call/3, handle_cast/2, handle_info/2,  
  4.   terminate/2, code_change/3]).  
  5.   
  6. start(Module) ->  
  7.   io:format("test server start ~p~n", [Module]),  
  8.   gen_server:start_link({local, Module}, ?MODULE, [], []).  
  9.   
  10. init([]) ->  
  11.   io:format("test server init~n"),  
  12.   {ok, []}.  
  13.   
  14. handle_call({test_handler, test, Name}, _From, State) ->  
  15.   io:format("test server test ~p~n", [Name]),  
  16.   {reply, ok, State};  
  17.   
  18. handle_call(_Request, _From, State) ->  
  19.   io:format("test server call nothing~n"),  
  20.   Reply = ok,  
  21.   {reply, Reply, State}.  
  22.   
  23. handle_cast(_Msg, State) ->  
  24.   {noreply, State}.  
  25.   
  26. handle_info(_Info, State) ->  
  27.   {noreply, State}.  
  28.   
  29. terminate(_Reason, _State) ->  
  30.   ok.  
  31.   
  32. code_change(_OldVsn, State, _Extra) ->  
  33.   {ok, State}.  
处理模块(test_handler.erl):
[plain]  view plain  copy
  1. -module(test_handler).  
  2. -export([test/1, stop/0]).  
  3. -define(Server, ?MODULE).  
  4.   
  5. test(Name) ->  
  6.   io:format("test handler test~n"),  
  7.   gen_server:call(?Server, {?MODULE, test, Name}).  
  8.   
  9. stop() ->  
  10.   io:format("test handler stop~n"),  
  11.   exit(whereis(?Server), kill),  
  12.   io:format("test handler is killed~n").  

资源文件(test.app) :

[plain]  view plain  copy
  1. {application,test,  
  2.   [{description,"Test application"},  
  3.     {vsn,"1.0.0"},  
  4.     {modules,[test_app,test_handler,test_server,test_sup]},  
  5.     {registered,[test_app]},  
  6.     {mod,{test_app,[]}},  
  7.     {env,[]},  
  8.     {applications,[kernel,stdlib]}]}.  

erl编译这个Application后,执行命令启动:

[plain]  view plain  copy
  1. 1> application:start(test).  
  2. test app start  
  3. test sup start link  
  4. test sup init  
  5. test server start test_handler  
  6. test server init  
  7. ok  
  8.   
  9. %% 调用处理模块函数:  
  10. 2> test_handler:test(helloworld).  
  11. test handler test  
  12. test server test helloworld  
  13. ok  
  14. 3> test_handler:stop().  
  15. test handler stop  
  16. test handler is killed  
  17. test server start test_handler  
  18. ok  
  19. test server init  
  20.   
  21. %% 如果在60秒内重复执行以下命令,进程将被彻底关闭:  
  22. 4> test_handler:stop().  
  23. test handler stop  
  24. test handler is killed  
  25. ok  
  26.   
  27. =INFO REPORT==== 14-Oct-2013::19:25:49 ===  
  28.     application: test  
  29.     exited: shutdown  
  30.     type: temporary  

由此可见,到了监督者模块(test_sup),监督者模块启动了一个监督者进程,作为监督树的一部分。监督者进程会回调监督者模块的 init/1 函数,根据这个函数的返回值来启动子进程。

[plain]  view plain  copy
  1. init([]) ->  
  2.   io:format("test sup init~n"),  
  3.   {ok,{  
  4.     {one_for_all, 1, 60},  
  5.     [{  test_handler_worker,    
  6.       {  test_server, start, [test_handler] },   
  7.       permanent,   
  8.       2000,  
  9.       worker,   
  10.       [test_server,test_handler]  
  11.       }]}  
  12.   }.  

以上参数告诉监督者调用 test_server:start(test_handler) 来启动工作进程,采用 one_for_all 的重启策略,如果60秒内重启超过1次就不再重启子进程,否则就会守护进程的正常运行。


文章完整例子下载  http://download.csdn.net/detail/cwqcwk1/6398337

猜你喜欢

转载自blog.csdn.net/boiled_water123/article/details/80643660