【问题标题】:Why does my supervisor fail on start_child with undef?为什么我的主管使用 undef 在 start_child 上失败?
【发布时间】:2020-08-02 05:32:36
【问题描述】:

我正在尝试运行simple_one_for_one supervisor,其中supervisorworker 被放置在单独的模块中,并且在使用supervisor:start_child 时我不断收到以下错误:

>A=sup:start_link().
>B=supervisor:start_child(A,[]).
{error,{'EXIT',{undef,[{worker,start_link,[],[]},
                       {supervisor,do_start_child_i,3,
                                   [{file,"supervisor.erl"},{line,379}]},
                       {supervisor,handle_call,3,
                                   [{file,"supervisor.erl"},{line,404}]},
                       {gen_server,try_handle_call,4,
                                   [{file,"gen_server.erl"},{line,661}]},
                       {gen_server,handle_msg,6,
                                   [{file,"gen_server.erl"},{line,690}]},
                       {proc_lib,init_p_do_apply,3,
                                 [{file,"proc_lib.erl"},{line,249}]}]}}}

主管

-module(sup).
-behaviour(supervisor).
-compile([export_all]).

start_link()->
    {ok,Pid}=supervisor:start_link(?MODULE,[]),
    io:format("sugi pl"),
    Pid.



init(_Args) ->
     RestartStrategy = {simple_one_for_one, 10, 60},
     ChildSpec = {
                  worker, 
                  {worker, start_link, []},  //tried adding here a parameter in the A
                  permanent,
                  brutal_kill, 
                  worker,
                  [sup]
                },
    {ok, {RestartStrategy,[ChildSpec]}}.

工人

-module(worker).
-compile(export_all).


start_link([Arg])->  //tried both [Arg] and Arg
    {ok,Pid}=spawn_link(?MODULE,init,[]),
    Pid.


init([Time])->
    receive->
        {From,Msg}->From !{Time,Msg},
                     init(Time)
    end.

命令

>c("[somepath]/sup.erl"),A=sup:start_link(),B=supervisor:start_child(A,[]).

我可以清楚地看到问题出在尝试添加孩子时。不知何故,init 函数没有被正确调用,但我不明白为什么。(Badmatch)
我曾尝试在MFAAChildSpecA 中添加一个参数,但无济于事

【问题讨论】:

  • 你确定worker已经编译加载了吗?
  • 我在erlang shell中运行我上面写的命令。如果worker模块被sup模块使用,是否需要编译它?它们都不会被编译吗?
  • undef,[{worker,start_link,[] 表示 worker:start_link/0 不存在,但 worker 只定义了 start_link/1。可能是您的模块不同步。确保 sup 和 worker 对 worker:start_link 使用相同的参数,并在重试之前重新编译这两个模块。

标签: module erlang erlang-supervisor


【解决方案1】:

您的代码存在许多问题。

  • sup:start_link/0的返回值错误;您将返回 Pid 而不是 {ok, Pid}
  • 虽然不是真的不正确,但您使用的是supervisor:start_link/2,它没有注册主管名称。有名字很方便,所以最好用supervisor:start_link/3

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

    这将模块名称与其进程 ID 相关联,允许您在 shell 命令中使用进程名称而不是使用 pid 变量。

  • 您在sup:start_link/0 中有一个io:format/2 调用,大概是为了调试。一个更好的调试方法是在你启动sup 主管后从你的shell 调用sys:trace(sup, true)。您也可以从 shell 将其关闭,方法是指定 false 而不是 true 作为第二个参数。

解决上述问题后,使用 sup:start_link/0 的以下定义:

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

让我们重新编译,启动supervisor,然后编译worker(在修复它的语法错误之后),然后在我们尝试启动一个child时跟踪supervisor:

1> c(sup).
sup.erl:3: Warning: export_all flag enabled - all functions will be exported
{ok,sup}
2> sup:start_link().
{ok,<0.94.0>}
3> sys:trace(sup, true).
ok
4> c(worker).
worker.erl:2: Warning: export_all flag enabled - all functions will be exported
worker.erl:5: Warning: variable 'Arg' is unused
{ok,worker}
5> supervisor:start_child(sup, []).
*DBG* sup got call {start_child,[]} from <0.81.0>
*DBG* sup sent {error,
                   {'EXIT',
                       {undef,
                           [{worker,start_link,[],[]},
                            {supervisor,do_start_child_i,3,
                                [{file,"supervisor.erl"},{line,379}]},
...

这个简短的跟踪输出显示sup 在尝试通过调用worker:start_link/0 启动worker 时死亡([] 表示参数为零)。子规范告诉sup 以这种方式启动它,因为它包含

{worker, start_link, []}

我们通过supervisor:start_child(sup, []) 启动了孩子。对于simple_one_for_one 子,发送到其启动函数的参数由来自子规范的参数列表与调用supervisor:start_child/2 中指定的参数组合而成;在这种情况下,这相当于[] ++ [],它与[] 相同,表示没有参数。让我们将worker:start_link/1 函数改为worker:start_link/0,重新编译,然后再试一次:

6> c(worker).
worker.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,worker}
7> supervisor:start_child(sup, []).
*DBG* sup got call {start_child,[]} from <0.81.0>
*DBG* sup sent {error,
                   {'EXIT',
                       {{badmatch,<0.94.0>},
                        [{worker,start_link,0,[{file,"worker.erl"},{line,6}]},
...

这一次,缩写输出显示badmatch。这是因为 spawn_link/3 返回一个 pid,但 worker:start_link/0 期望它返回 {ok, Pid}。让我们修复它,并将返回值修复为 {ok, Pid} 而不仅仅是 Pid

start_link()->
    Pid = spawn_link(?MODULE,init,[]),
    {ok, Pid}.

那我们重新编译再试一次:

8> c(worker).
worker.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,worker}
9> supervisor:start_child(sup, []).
*DBG* sup got call {start_child,[]} from <0.81.0>
*DBG* sup sent {ok,<0.106.0>} to <0.81.0>, new state {state,
                                                      {local,sup},
                                                      simple_one_for_one,
                                                      {[worker],
                                                       #{worker =>
                                                          {child,undefined,
                                                           worker,
                                                           {worker,
                                                            start_link,[]},
                                                           permanent,
                                                           brutal_kill,worker,
                                                           [sup]}}},
                                                      {maps,
                                                       #{<0.106.0> => []}},
                                                      10,60,[],0,sup,[]}
*DBG* sup got {'EXIT',<0.106.0>,{undef,[{worker,init,[],[]}]}}

好的,这次主管实际上启动了孩子,但它立即死亡,因为它试图调用worker:init/0,但只定义了worker:init/1。由于孩子立即死亡,主管根据其重启策略反复尝试启动它:

RestartStrategy = {simple_one_for_one, 10, 60},

因为这是一个硬错误,它每次都会立即失败,并且在 60 秒或更短的时间内重启 10 次失败后,主管会死掉,就像它应该做的那样:

=SUPERVISOR REPORT==== 20-Apr-2020::10:43:43.557307 ===
    supervisor: {local,sup}
    errorContext: shutdown
    reason: reached_max_restart_intensity
    offender: [{pid,<0.117.0>},
               {id,worker},
               {mfargs,{worker,start_link,[]}},
               {restart_type,permanent},
               {shutdown,brutal_kill},
               {child_type,worker}]
** exception error: shutdown

从您的代码看来,您正试图将某种Time 参数传递给worker:init/1,所以让我们更改start_link/0 以传递时间戳:

start_link()->
    Pid = spawn_link(?MODULE,init,[os:timestamp()]),
    {ok, Pid}.

让我们也修复init/1 直接接受参数,而不是在列表中:

init(Time) ->
    receive
        {From,Msg} ->
            From ! {Time,Msg},
            init(Time)
end.

让我们重启supervisor,重新编译worker,然后再试一次:

10> sup:start_link().
{ok,<0.119.0>}
11> sys:trace(sup, true).
ok
12> c(worker).
worker.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,worker}
13> {ok, Child} = supervisor:start_child(sup, []).
*DBG* sup got call {start_child,[]} from <0.118.0>
*DBG* sup sent {ok,<0.127.0>} to <0.118.0>, new state {state,
                                                       {local,sup},
                                                       simple_one_for_one,
                                                       {[worker],
                                                        #{worker =>
                                                           {child,undefined,
                                                            worker,
                                                            {worker,
                                                             start_link,[]},
                                                            permanent,
                                                            brutal_kill,
                                                            worker,
                                                            [sup]}}},
                                                       {maps,
                                                        #{<0.127.0> => []}},
                                                       10,60,[],0,sup,[]}
{ok,<0.127.0>}

看起来它成功了。让我们看看主管是否同意,问它有多少个孩子:

14> supervisor:count_children(sup).
...
[{specs,1},{active,1},{supervisors,0},{workers,1}]

正如我们预期的那样,它只有一名工人。最后,让我们向worker发送一条消息,看看它是否按预期响应:

15> Child ! {self(), "are you there?"}.
{<0.118.0>,"are you there?"}
16> flush().
Shell got {{1587,394860,258120},"are you there?"}
ok

现在看来一切正常。

最后一个解决方法是更改​​子规范中的模块;而不是[sup],它应该是模块本身,[worker]。通过该更改,您修改后的工作模块如下。您可能还想重新考虑是否要为孩子使用permanent,因为这是simple_one_for_one 主管; transient 可能是一个更好的选择,但我将其保留原样。考虑查看Supervisor Behavior documentation 了解更多信息。

主管

-module(sup).
-behaviour(supervisor).
-compile([export_all]).

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

init(_Args) ->
     RestartStrategy = {simple_one_for_one, 10, 60},
     ChildSpec = {
                  worker,
                  {worker, start_link, []},
                  permanent,
                  brutal_kill,
                  worker,
                  [worker]
                },
    {ok, {RestartStrategy,[ChildSpec]}}.

工人

-module(worker).
-compile(export_all).

start_link()->
    Pid = spawn_link(?MODULE,init,[os:timestamp()]),
    {ok, Pid}.

init(Time) ->
    receive
        {From,Msg} ->
            From ! {Time,Msg},
            init(Time)
    end.

【讨论】:

    【解决方案2】:

    除了已经发布的出色答案之外,我想补充几点来解释问题中观察到的行为。

    childspec 中的起始值是一个元组{Mod, Fun, ArgsList},子进程可以通过调用supervisor:start_child(Supervisor, List) 产生。主管通过调用erlang:apply(Mod, Fun, List++ArgsList) 启动子进程。

    在这种情况下,起始值为{worker, start_link, []},子代是通过调用supervisor:start_child(A, []) 生成的。主管试图拨打erlang:apply(worker, start_link, [])。这意味着主管期望在worker 模块中定义worker:start_link/0。但是worker 模块定义了worker:start_link/1。因此出现undef 错误。

    给定函数定义

    start_link([Arg]) ->
      %% do stuff
    

    最好是产生子进程是

    • 让子规范中的起始值为{worker, start_link, []}
    • 致电supervisor:start_child(A, [[Value]])

    将函数定义为可能更简单

    start_link(Arg) ->
      %% so stuff
    

    并致电supervisor:start_child(A, [Value])

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-13
      • 2017-09-17
      • 1970-01-01
      • 2017-02-21
      • 2012-01-10
      • 2018-12-22
      • 1970-01-01
      • 2010-09-16
      相关资源
      最近更新 更多