我正在使用从http://learnyousomeerlang.com/building-applications-with-otp获取的主管树的代码,但我得到了一个noproc异常,当我尝试让主管启动子进程时,我无法弄明白。这是我的shell交互:
1> application:start(test).
root supervisor init
ok
2> test_sup:start_service(service_sup,{service_worker, start_link,[]}).
{ok,<0.39.0>}
worker supervisor initialise (M: service_worker,F: start_link,A: [])
3> test_app:run(service_worker,[]).
server run: (name: service_worker args: [])
** exception exit: {noproc,{gen_server,call,[service_worker,{run,[]}]}}
in function gen_server:call/2 (gen_server.erl, line 182)
代码是:
-module(test_app).
-behaviour(application).
-export([start/2, stop/1, run/2]).
start(_StartType, _StartArgs) ->
test_sup:start_link().
run(Name, Args) ->
service_serv:run(Name, Args).
=====
-module(test_sup).
-behaviour(supervisor).
-export([start_link/0, init/1, stop/0, start_service/2, stop_service/1]).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
start_link() ->
supervisor:start_link({local, service}, ?MODULE, []).
init([]) ->
io:format("root supervisor init~n"),
{ok, {{one_for_one, 5, 10},
[]}}.
start_service(Name, MFA) ->
ChildSpec = {Name,
{service_sup, start_link, [Name, MFA]},
permanent, 10500, supervisor, [service_sup]},
io:format("start service supervisor (Name: ~p, MFA: ~p): ", [Name, MFA]),
supervisor:start_child(service, ChildSpec).
[snip]
====
-module(service_sup).
-export([start_link/2, init/1]).
-behaviour(supervisor).
start_link(Name, MFA) ->
supervisor:start_link(?MODULE, {Name, MFA}).
init({Name, MFA}) ->
MaxRestart = 1,
MaxTime = 3600,
{ok, {{one_for_all, MaxRestart, MaxTime},
[{serv,
{service_serv, start_link, [Name, self(), MFA]},
permanent,
5000,
worker, [service_serv]}]}}.
======
-module(worker_sup).
-export([start_link/1, init/1]).
-behaviour(supervisor).
start_link(MFA) ->
supervisor:start_link(?MODULE, MFA).
init({M,F,A}) ->
{ok, {{simple_one_for_one, 5, 3600},
[{service_worker,
{M,F,A},
temporary, 5000, worker, [M]}]}}.
===
-module(service_serv).
-behaviour(gen_server).
-export([start/3, start_link/3, run/2,
status/1, ping/1, stop/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
code_change/3, terminate/2]).
-define(WORKER_SUP_SPEC(MFA),
{worker_sup,
{worker_sup, start_link, [MFA]},
permanent,
10000,
supervisor,
[worker_sup]}).
-record(state, {sup,
refs,
queue=queue:new()
}).
start(Name, Sup, MFA) when is_atom(Name) ->
gen_server:start({local, Name}, ?MODULE, {MFA, Sup}, []).
start_link(Name, Sup, MFA) when is_atom(Name) ->
gen_server:start_link({local, Name}, ?MODULE, {MFA, Sup}, []).
init({MFA, Sup}) ->
self() ! {start_worker_supervisor, Sup, MFA},
{ok, #state{}}.
run(Name, Args) ->
io:format("server run: (name: ~p args: ~p) ~n",[Name, Args]),
gen_server:call(Name, {run, Args}).
handle_info({start_worker_supervisor, Sup, MFA}, S = #state{}) ->
{ok, Pid} = supervisor:start_child(Sup, ?WORKER_SUP_SPEC(MFA)),
{noreply, S#state{sup=Pid}};
handle_info({'DOWN', Ref, process, _Pid, _}, S = #state{refs=Refs}) ->
case gb_sets:is_element(Ref, Refs) of
true ->
handle_down_worker(Ref, S);
false -> %% Not our responsibility
{noreply, S}
end;
handle_info(Msg, State) ->
{noreply, State}.
handle_call({run, Args}, _From, S = #state{sup=Sup, refs=R}) ->
io:format("handle run call ~n"),
{ok, Pid} = supervisor:start_child(Sup, Args),
Ref = erlang:monitor(process, Pid),
{reply, {ok, run, Pid}, S#state{refs=gb_sets:add(Ref,R)}};
[snip]
====
-module(service_worker).
-behaviour(gen_server).
-export([start_link/4, stop/1]).
-export([init/0, init/1, handle_call/3, handle_cast/2,
handle_info/2, code_change/3, terminate/2]).
start_link(Task, Delay, Max, SendTo) ->
gen_server:start_link(?MODULE, {Task, Delay, Max, SendTo} , []).
stop(Pid) ->
gen_server:call(Pid, stop).
init({Task, Delay, Max, SendTo}) ->
io:format("initialise worker ~n"),
%% {ok, {Task, Delay, Max, SendTo}}.
{ok, {Task, Delay, Max, SendTo}, Delay}.
[剪断]
答案 0 :(得分:1)
你生成的代码量有点难以解析,但最让我印象深刻的是你显然使用原子“service_worker”来引用你开始的过程。
如果您使用该原子注册流程(通过调用erlang:register(service_worker, Pid)
或使用gen_server:start_link({local, service_worker}, ?MODULE, Args, Opts)
启动流程),这一切都很好。您似乎没有这样做,并且您收到的错误消息支持该评估。
** exception exit: {noproc,{gen_server,call,[service_worker,{run,[]}]}}
in function gen_server:call/2 (gen_server.erl, line 182)
此错误告诉我们的是gen_server:call
无法找到进程(noproc
)。 gen_server:call
的参数包含在错误消息中,在人们希望找到Pid
的位置,我们会找到service_worker
。
此外,您的service_worker
模块似乎由simple_one_for_one
主管启动。当您需要多个相同“类型”的进程(例如,相同的回调模块)时,使用这样的管理程序。这样的主管也不会自己创办工人(你必须调用supervisor:start_child(SupPid, ExtraArgs)
)。
这是我目前尝试的两个主要问题。要快速进行修复,请尝试添加{local, service_worker}
或{local, ?MODULE}
作为service_worker模块中gen_server:start_link
调用的第一个参数。请记住,如果您打算启动多个service_worker进程(因为一次只能将一个进程注册到一个原子),这将不起作用。