【问题标题】:Can't start process when starting erlag node启动erlag节点时无法启动进程
【发布时间】:2017-06-02 17:23:38
【问题描述】:

我试图在从命令行创建一个节点时启动一个主管和一个 gen_server 进程,我使用以下命令启动该节点:

erl -name vm01@192.168.146.129 -s ets_sup start vm02@192.168.146.129 calc

但是,当我使用whereis检查新创建的节点上的进程时,我发现进程是undefined。我直接在节点外壳上运行 ets_sup 和 ets_srv 没有问题,但是从命令行启动节点不起作用。我想知道为什么会这样?

ets_sup.erl:

-module(ets_sup).
-behaviour(supervisor).
-export([start/1, start_link/1, init/1]).

start([A, B]) ->
        start_link([A, B]).


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

init([A, B]) ->
        {ok, {{one_for_all, 0, 1},
                [{ets_srv, {ets_srv, start_link, [A, B]}, permanent, 5000, worker, [ets_srv]}]}}.

ets_srv.erl:

-module(ets_srv).
-behaviour(gen_server).
-compile(export_all).

-record(state, {a, b}).

start_link(A,B) ->
        gen_server:start_link({local, ?MODULE}, ?MODULE, [A, B], []).

init([A, B]) ->
        {ok, #state{a = A, b = B}}.

check_sys() ->
        gen_server:call(?MODULE, check_sys).

handle_call(check_sys, _From, #state{a = A, b = B} = State) ->
        {reply, {A, B}, State}.

handle_info(_Info, State) -> {noreply, State}.

handle_cast(_Req, State) -> {noreply, State}.

code_change(_Ol, State, _Ex) -> {ok, State}.

terminate(_R, _S) -> ok.

【问题讨论】:

    标签: erlang


    【解决方案1】:

    我认为您确实使用参数列表启动了函数ets_sup:start/1。您可以通过在ets_sup:start_link/1 之前添加io:format(...) 来验证它。

    但是执行函数ets_sup:start/1 的进程是一个早期的进程,他很快就死了,原因是shutdown,并且由于你的主管与它相关联,它也连同它的所有子进程一起死了。

    你必须从一个不会死的进程中调用这个函数(通常,它是应用程序管理器的角色)。例如,这样做:

    start([A, B]) ->
        % spawn a new process
        spawn(fun () -> 
                   start_link([A, B]),
                   % add a loop to keep it alive
                   loop()
                   end).
    
    
    loop() ->
        receive
            stop -> ok;
            _ -> loop()
        end.
    

    编辑,但不是答案

    我修改了你的代码:

    • 在初始化服务器中添加process_flag(trap_exit, true),,以便捕获退出消息,
    • 在服务器终止函数中添加io:format("server terminate with reason ~p, process ~p~n",[_R,self()]),,以便打印主管最终发送的退出原因(注意:如果退出消息由另一个进程发送,将触发handle_info)。
    • 在主管中添加ets_srv:check_sys(),就在服务器启动之后,以检查它是否正确加注。

    这是修改后的代码。

    -module(ets_sup).
    -behaviour(supervisor).
    -export([start/1, start_link/1, init/1]).
    
    start([A, B]) ->
        start_link([A, B]).
    
    
    start_link([A, B]) ->
        supervisor:start_link({local, ?MODULE}, ?MODULE, [A, B]),
        ets_srv:check_sys().
    
    init([A, B]) ->
        {ok, {{one_for_all, 0, 1},
                [{ets_srv, {ets_srv, start_link, [A, B]}, permanent, 5000, worker, [ets_srv]}]}}.
    
    -module(ets_srv).
    -behaviour(gen_server).
    -compile(export_all).
    
    -record(state, {a, b}).
    
    start_link(A,B) ->
        gen_server:start_link({local, ?MODULE}, ?MODULE, [A, B], []).
    
    init([A, B]) ->
        process_flag(trap_exit, true),
        {ok, #state{a = A, b = B}}.
    
    check_sys() ->
        gen_server:call(?MODULE, check_sys).
    
    handle_call(check_sys, _From, #state{a = A, b = B} = State) ->
        io:format("check_sys state ~p, process ~p~n",[State,self()]),
        {reply, {A, B}, State}.
    
    handle_info(_Info, State) ->
        {noreply, State}.
    
    handle_cast(_Req, State) ->
        {noreply, State}.
    
    code_change(_Ol, State, _Ex) ->
        {ok, State}.
    
    terminate(_R, _S) ->
        io:format("server terminate with reason ~p, process ~p~n",[_R,self()]),
        ok.
    

    运行此版本表明主管正确启动了服务器,然后向其发送关闭消息。如果在 shell 中启动主管,则不会发生这种情况。

    C:\src>erl -s ets_sup start vm02@192.168.146.129 calc
    check_sys state {state,'vm02@192.168.146.129',calc}, process <0.56.0>
    server terminate with reason shutdown, process <0.56.0>
    Eshell V8.2  (abort with ^G)
    1> whereis(ets_srv).
    undefined
    2> ets_sup:start(['vm02@192.168.146.129',calc]).
    check_sys state {state,'vm02@192.168.146.129',calc}, process <0.61.0>
    {'vm02@192.168.146.129',calc}
    3> whereis(ets_srv).
    <0.61.0>
    4> ets_srv:check_sys().
    check_sys state {state,'vm02@192.168.146.129',calc}, process <0.61.0>
    {'vm02@192.168.146.129',calc}
    5> exit(whereis(ets_srv),shutdown).
    true
    6> whereis(ets_srv).
    <0.61.0>
    7> exit(whereis(ets_srv),kill).
    ** exception exit: shutdown
    8> whereis(ets_srv).
    undefined
    9>
    

    我已经验证,如果你以同样的方式启动一个普通进程(不是主管),使用spawn_link,它不会收到任何退出消息。

    -module (st).
    
    -compile([export_all]).
    
    start(Arg) ->
        do_start(Arg).
    
    do_start(Arg) ->
        io:format("spawn from ~p~n",[self()]),
        register(?MODULE,spawn_link(fun () -> init(Arg) end)).
    
    init(Arg) ->
        io:format("init with ~p in ~p~n",[Arg,self()]),
        process_flag(trap_exit, true),
        Pid = self(),
        spawn(fun() -> monitor(process,Pid), receive M -> io:format("loop received ~p~n",[M]) end end),
        loop(Arg).
    
    loop(Arg) ->
        receive
            state ->
                io:format("state is ~p~n",[Arg]),
                loop(Arg);
            stop ->
                io:format("stopping~n");
            _ ->
                loop(Arg)
        end.
    

    执行给出:

    C:\src>erl -s st start vm02@192.168.146.129 calc
    spawn from <0.3.0>
    init with ['vm02@192.168.146.129',calc] in <0.55.0>
    Eshell V8.2  (abort with ^G)
    1> whereis(st).
    <0.55.0>
    2> exit(whereis(st),shutdown).
    true
    3> whereis(st).
    <0.55.0>
    4> st ! state.
    state is ['vm02@192.168.146.129',calc]
    state
    5> st ! stop.
    stopping
    loop received {'DOWN',#Ref<0.0.4.66>,process,<0.55.0>,normal}
    stop
    6> whereis(st).
    undefined
    7>
    

    编辑,另一种“分离”主管的方式

    我所做的所有测试都表明主管收到了关闭消息。不知道为什么,平时我都是用应用机制来启动一个监督树,从来没有遇到过这种情况。

    我建议您取消主管与其父级的链接,这样它就不会收到关闭消息:

    -module(ets_sup).
    -behaviour(supervisor).
    -export([start/1, start_link/1, init/1]).
    
    start([A, B]) ->
        start_link([A, B]).
    
    
    start_link([A, B]) ->
        supervisor:start_link({local, ?MODULE}, ?MODULE, [A, B]),
        ets_srv:check_sys().
    
    init([A, B]) ->
        {links,[Parent]} = process_info(self(),links),
        unlink(Parent),
        {ok, {{one_for_all, 0, 1},
                [{ets_srv, {ets_srv, start_link, [A, B]}, permanent, 5000, worker, [ets_srv]}]}}.
    

    现在可以了:

    C:\src>erl -s ets_sup start vm02@192.168.146.129 calc
    check_sys state {state,'vm02@192.168.146.129',calc}, process <0.56.0>
    Eshell V8.2  (abort with ^G)
    1> ets_srv:check_sys().
    check_sys state {state,'vm02@192.168.146.129',calc}, process <0.56.0>
    {'vm02@192.168.146.129',calc}
    2>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-24
      • 2019-05-13
      • 2022-06-16
      • 2019-07-18
      • 2018-11-03
      • 2016-10-04
      • 2016-05-05
      • 1970-01-01
      相关资源
      最近更新 更多