当我尝试从 eshell 启动时,我的主管崩溃了?

Posted

技术标签:

【中文标题】当我尝试从 eshell 启动时,我的主管崩溃了?【英文标题】:My supervisor crashes when I try to start it from eshell? 【发布时间】:2012-08-19 05:52:59 【问题描述】:

我对 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 shell 中执行了后续步骤:

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

谢谢

【问题讨论】:

【参考方案1】:

这实际上是一种竞争条件。

您可能知道,Erlang shell 本身就是一个普通的 Erlang 进程。当您从 shell 启动主管时,主管会链接到 shell(因为您使用 supervisor:start_link/3)。

当您调用您的 gen_server 进程时,该进程崩溃(并由主管正确重新启动,正如您在随后的 "Increment server started :)" 输出中看到的那样)。

然而,与此同时,您对gen_server:call/2 的调用将导致同样的崩溃(调用期间崩溃的gen_server 将通过gen_server:call/2 函数发出同样的崩溃)。然后,这会使链接到您的主管的 shell 进程崩溃,而后者又会因相同的原因而崩溃 (badarith)。

基本上,在你的 shell 进程忠实地重新启动你的 gen_server 之后,你的主管被你的 shell 进程背叛了。像这样:

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

您可以通过在 shell 中调用 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

【讨论】:

以上是关于当我尝试从 eshell 启动时,我的主管崩溃了?的主要内容,如果未能解决你的问题,请参考以下文章

当我尝试从 MySQL 检索信息时,我的页面崩溃了

从 AppStore 或 TestFlight 启动时应用程序崩溃,但在其他方面工作正常

当我尝试从 savedInstanceState 获取额外内容时,应用程序崩溃了

当我尝试从函数发送和接收列表时,PyQt5 接口崩溃

退出的主管

在启动时触发BroadcastReceiver的应用程序崩溃