Почему мой руководитель терпит неудачу на start_child с undef? - PullRequest
1 голос
/ 19 апреля 2020

Я пытаюсь запустить simple_one_for_one supervisor, где supervisor и worker размещены в отдельных модулях, и я получаю сообщение об ошибке ниже при использовании 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)
Я попытался добавить параметр в A MFA ChildSpec безрезультатно

1 Ответ

5 голосов
/ 20 апреля 2020

Существует ряд проблем с вашим кодом.

  • Возвращаемое значение sup:start_link/0 неверно; вы возвращаете Pid вместо {ok, Pid}.
  • Хотя это не совсем верно, вы используете supervisor:start_link/2, который не регистрирует имя супервизора. Наличие имени удобно, поэтому лучше использовать supervisor:start_link/3:

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

    Это связывает имя модуля с его идентификатором процесса, позволяя использовать имя процесса в командах оболочки вместо использования переменных pid. .

  • У вас есть вызов io:format/2 в sup:start_link/0, предположительно для отладки. Гораздо лучший способ отладки - вызвать sys:trace(sup, true) из вашей оболочки, как только вы запустите супервизор sup. Вы также можете отключить его из оболочки, указав false вместо true в качестве второго аргумента.

Исправление вышеуказанных проблем оставляет использование со следующим определением sup:start_link/0:

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

Давайте перекомпилируем, запустим супервизор, затем скомпилируем worker (после исправления его синтаксических ошибок) и затем проследим супервизор, когда мы пытаемся запустить дочерний элемент:

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, вызвав worker:start_link/0 ([] указывает на отсутствие аргументов). Дочерняя спецификация указывает 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},

Поскольку это серьезная ошибка, он мгновенно завершается ошибкой, и супервизор умирает после 10 неудачных перезапусков в течение 60 секунд или меньше, как и предполагалось:

=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.

Давайте перезапустим супервизор, перекомпилируем 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}]

У него есть один работник, как мы и ожидали. Наконец, давайте отправим сообщение работнику и посмотрим, ответит ли он так, как ожидалось:

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, вероятно, лучший выбор, но я оставил его как изначально написано. Для получения дополнительной информации рассмотрите документацию по поведению супервизора .

Супервизор

-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.
...