启动工人的Erlang主管例外

时间:2014-10-02 13:23:02

标签: exception erlang supervisor

我正在使用从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}.

[剪断]

1 个答案:

答案 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进程(因为一次只能将一个进程注册到一个原子),这将不起作用。