Существует ряд проблем с вашим кодом.
- Возвращаемое значение
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.