10

私はOTPに非常に慣れていません。スーパーバイザーの動作を理解するための簡単な例を作成しようとしています:

これが単純なインクリメントサーバーです

-module( inc_serv ).
-behaviour( gen_server ).
-export( [ start/0, inc/1, stop/0 ] ).
-export( [ init/1, handle_call/3, terminate/2 ] ).

start() ->
        gen_server:start_link( { local, ?MODULE }, ?MODULE, no_args, [] ).

stop() ->
        gen_server:call( ?MODULE, stop ).

inc( Num ) ->
        gen_server:call( ?MODULE, { num, Num } ).

init( no_args ) ->
        io:format( "~p~n", [ "Increment server started :)" ] ),
        { ok, no_state }.

handle_call( { num, Num }, _From, no_state ) ->
        { reply, Num + 1, no_state };
handle_call( stop, _From, no_state ) ->
        { stop, normal, ok, no_state }.

terminate( Reason, no_state ) ->
        io:format( "~p~n", [ "Increment server stopped" ] ).

そして、このモジュールによって監視されるようにしたいと思います:

-module( supervisor_inc ).
-behaviour( supervisor ).

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

start() ->
        supervisor:start_link( { local, ?MODULE }, ?MODULE, no_args ).

init( no_args ) ->
        process_flag( trap_exit, true ),
        Supervisor_Spec = { one_for_one, 1, 1 },
        IncServ_Spec = {
                inc_serv,
                { inc_serv, start, [] },
                permanent, 2000, worker, [ inc_serv ] },
        { ok, { Supervisor_Spec, [ IncServ_Spec ] } }.

その後、次の手順を erlang シェルで実行しました。

1> 
1> c(inc_serv).
{ok,inc_serv}
2> 
2> c(supervisor_inc).
{ok,supervisor_inc}
3> 
3> supervisor_inc:start().
"Increment server started :)"
{ok,<0.43.0>}
4> 
4> inc_serv:inc( 7 ).
8
5> inc_serv:inc( 8 ).
9

この後、次に試しました(予想どおり、エラーが発生しました):

6> inc_serv:inc( bad_arg ).
"Increment server stopped"
"Increment server started :)"

=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server inc_serv terminating 
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination == 
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
              {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
              {proc_lib,init_p_do_apply,3,
                        [{file,"proc_lib.erl"},{line,227}]}]}

=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server supervisor_inc terminating 
** Last message in was {'EXIT',<0.31.0>,
                           {{{badarith,
                                 [{inc_serv,handle_call,3,
                                      [{file,"inc_serv.erl"},{line,22}]},
                                  {gen_server,handle_msg,5,
                                      [{file,"gen_server.erl"},{line,588}]},
                                  {proc_lib,init_p_do_apply,3,
                                      [{file,"proc_lib.erl"},{line,227}]}]},
                             {gen_server,call,[inc_serv,{num,bad_arg}]}},
                            [{gen_server,call,2,
                                 [{file,"gen_server.erl"},{line,180}]},
                             {erl_eval,do_apply,6,
                                 [{file,"erl_eval.erl"},{line,576}]},
                             {shell,exprs,7,[{file,"shell.erl"},{line,668}]},
                             {shell,eval_exprs,7,
                                 [{file,"shell.erl"},{line,623}]},
                             {shell,eval_loop,3,
                                 [{file,"shell.erl"},{line,608}]}]}}
** When Server state == {state,
                            {local,supervisor_inc},
                            one_for_one,
                            [{child,<0.48.0>,inc_serv,
                                 {inc_serv,start,[]},
                                 permanent,2000,worker,
                                 [inc_serv]}],
                            undefined,1,1,
                            [{1345,739526,107495}],
                            supervisor_inc,no_args}
** Reason for termination == 
** {{{badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
                {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
                {proc_lib,init_p_do_apply,3,
                          [{file,"proc_lib.erl"},{line,227}]}]},
     {gen_server,call,[inc_serv,{num,bad_arg}]}},
    [{gen_server,call,2,[{file,"gen_server.erl"},{line,180}]},
     {erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,576}]},
     {shell,exprs,7,[{file,"shell.erl"},{line,668}]},
     {shell,eval_exprs,7,[{file,"shell.erl"},{line,623}]},
     {shell,eval_loop,3,[{file,"shell.erl"},{line,608}]}]}
** exception exit: {{badarith,[{inc_serv,handle_call,3,
                                         [{file,"inc_serv.erl"},{line,22}]},
                               {gen_server,handle_msg,5,
                                           [{file,"gen_server.erl"},{line,588}]},
                               {proc_lib,init_p_do_apply,3,
                                         [{file,"proc_lib.erl"},{line,227}]}]},
                    {gen_server,call,[inc_serv,{num,bad_arg}]}}
     in function  gen_server:call/2 (gen_server.erl, line 180)

この後、私は期待していました-私のスーパーバイザーが再起動しinc_servます。しかし、それはしませんでした:

7> inc_serv:inc( 8 ).      
** exception exit: {noproc,{gen_server,call,[inc_serv,{num,8}]}}
     in function  gen_server:call/2 (gen_server.erl, line 180)

何が起こったのか理解するのを手伝ってくれませんか? そして、再起動できるようにするには、スーパーバイザーをどのように書き直すべきですかinc_serv

ありがとう

4

1 に答える 1

23

これは、実際には一種の競合状態です。

ご存じのとおり、Erlang シェル自体は通常の Erlang プロセスです。スーパーバイザをシェルから起動すると、スーパーバイザはシェルにリンクされます ( を使用しているためsupervisor:start_link/3)。

gen_server プロセスを呼び出すと、そのプロセスはクラッシュします (その後の出力からわかるように、スーパーバイザーによって正しく再起動され"Increment server started :)"ます)。

ただし、同時に、 を呼び出すとgen_server:call/2同じクラッシュが発生します (呼び出し中に gen_server がクラッシュすると、gen_server:call/2関数を介して同じクラッシュが発生します)。これにより、スーパーバイザにリンクされているシェル プロセスがクラッシュし、同じ理由でクラッシュします ( badarith)。

基本的に、スーパーバイザーは、gen_server を忠実に再起動した後、シェル プロセスによってバックスタブされます。そのようです:

       +---------(6)exit----------+    +---------(5)restart---------+
       |                          |    |                            |
       |                          v    |                            v
     Shell ---(1)start_link---> supervisor ---(2)start_link---> gen_server
     |  ^                         ^    |                         ^  |   ^
     |  |                         |    |                         |  |   |
     |  |                         |    +---------(7)exit---------+  |   |
     |  |                         |                                 |   |
     |  +-------------------------+--------------(4)exit------------+   |
     |                                                                  |
     +---------------------------(3)call--------------------------------+

これを回避するには、シェルで次のように呼び出しcatch inc_serv:inc(bad_arg).ます。

90> inc_serv:inc(7).        
8
91> catch inc_serv:inc(bad_arg).
"Increment server stopped"

=ERROR REPORT==== 23-Aug-2012::22:10:02 ===
** Generic server inc_serv terminating 
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination == 
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,20}]},
              {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
              {proc_lib,init_p_do_apply,3,
                        [{file,"proc_lib.erl"},{line,227}]}]}
"Increment server started :)"
{'EXIT',{{badarith,[{inc_serv,handle_call,3,
                              [{file,"inc_serv.erl"},{line,20}]},
                    {gen_server,handle_msg,5,
                              [{file,"gen_server.erl"},{line,588}]},
                    {proc_lib,init_p_do_apply,3,
                              [{file,"proc_lib.erl"},{line,227}]}]},
                    {gen_server,call,[inc_serv,{num,bad_arg}]}}}
92> inc_serv:inc(7).            
8
于 2012-08-23T19:55:10.417 に答える