I am trying to run a simple_one_for_one supervisor
where the supervisor
and worker
are placed in separate modules and i keep getting the below error when using 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}]}]}}}
Supervisor
-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]}}.
Worker
-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.
Command
>c("[somepath]/sup.erl"),A=sup:start_link(),B=supervisor:start_child(A,[]).
I can see clearly that the problem is when trying to add a child.Somehow the init
function is not called properly but i do not see why.(Badmatch)
I have tried adding a parameter in the A
of the MFA
of the ChildSpec
to no avail
2条答案
按热度按时间yh2wf1be1#
There are a number of problems with your code.
sup:start_link/0
is wrong; you're returning Pid instead of{ok, Pid}
.supervisor:start_link/2
, which doesn't register the supervisor name. Having the name is handy, so it's better to usesupervisor:start_link/3
:This associates the module name with its process id, allowing you to use the process name in your shell commands rather than using pid variables.
io:format/2
call insup:start_link/0
, presumably for debugging. A much better way of debugging is to callsys:trace(sup, true)
from your shell once you've started thesup
supervisor. You can turn it off from the shell as well, by specifyingfalse
instead oftrue
as the second argument.Fixing the above issues leaves use with the following definition of
sup:start_link/0
:Let's recompile, start the supervisor, then compile
worker
(after fixing its syntax errors), and then trace the supervisor as we try to start a child:This abbreviated trace output shows that
sup
died when it tried to start aworker
by callingworker:start_link/0
(the[]
indicates there are zero arguments). The child specification tellssup
to start it this way, since it containsand we started the child via
supervisor:start_child(sup, [])
. For asimple_one_for_one
child, the arguments sent to its start function are composed of the argument list from the child specification combined with the arguments specified in the call tosupervisor:start_child/2
; in this case, that's the equivalent of[] ++ []
which is the same as[]
, indicating no arguments. Let's change theworker:start_link/1
function toworker:start_link/0
instead, recompile it, and try again:This time, the abbreviated output shows a
badmatch
. This is becausespawn_link/3
returns a pid, butworker:start_link/0
is expecting it to return{ok, Pid}
. Let's fix that, and also fix the return value to be{ok, Pid}
instead of justPid
:Then let's recompile and try again:
OK, this time the supervisor actually started the child, but it died immediately because it's trying to call
worker:init/0
but onlyworker:init/1
is defined. Because the child dies immediately, the supervisor tries repeatedly to start it, based on its restart strategy:Because this is a hard error, it fails instantly every time, and the supervisor dies after 10 failed restarts in 60 seconds or less, just like it's supposed to:
It appears from your code that you're attempting to pass some sort of
Time
argument toworker:init/1
, so let's changestart_link/0
to pass a timestamp:Let's also fix
init/1
to take the argument directly, rather than in a list:Let's restart the supervisor, recompile
worker
, and try again:It looks like it worked. Let's see if the supervisor agrees, by asking it how many children it has:
It has one worker, just as we expected. Finally, let's send a message to the worker and see if it responds as expected:
Now it all seems to work.
One final fix is to change the modules in your child specification; rather than
[sup]
it should be the module itself,[worker]
. With that change, your revised working modules are below. You might also want to reconsider whether you want to usepermanent
for the children, since this is asimple_one_for_one
supervisor;transient
is likely a better choice, but I've left it as originally written. Consider reviewing the Supervisor Behavior documentation for more information.Supervisor
Worker
3gtaxfhh2#
In addition to the excellent answer already posted, I would like add a few points to explain the behavior observed in the question.
The start value in childspec is a tuple
{Mod, Fun, ArgsList}
and child process can be spawned by callingsupervisor:start_child(Supervisor, List)
. The supervisor start's the child process by callingerlang:apply(Mod, Fun, List++ArgsList)
.In this case, the start value is
{worker, start_link, []}
and child was spawned by callingsupervisor:start_child(A, [])
. The supervisor attempted to callerlang:apply(worker, start_link, [])
. This means the supervisor was expectingworker:start_link/0
to be defined in theworker
module. But theworker
module definedworker:start_link/1
. Hence theundef
error.Given the function definition
The best was to spawn the child process is
{worker, start_link, []}
supervisor:start_child(A, [[Value]])
It maybe simpler to define the function as
and call
supervisor:start_child(A, [Value])