【问题标题】:Erlang Supervisor fail to start_child with no errors simple_one_for_oneErlang Supervisor 无法 start_child 且没有错误 simple_one_for_one
【发布时间】:2013-03-21 22:48:19
【问题描述】:

主管似乎无法默默地启动孩子...

这是主管

-behaviour(supervisor).
-export([start_socket/0, init/1, start_link/1]).

-define(SSL_OPTIONS, [{active, once},
                      {backlog, 128},
                      {reuseaddr, true},
                      {packet, 0},
                      {cacertfile, "./ssl_key/server/gd_bundle.crt"},
                      {certfile, "./ssl_key/server/cert.pem"},
                      {keyfile, "./ssl_key/server/cert.key"},
                      {password, "**********"}
                     ]).

start_link(Port) ->
    Role = list_to_atom(atom_to_list(?MODULE) ++ lists:flatten(io_lib:format("~B", [Port]))),
    supervisor:start_link({local, Role}, ?MODULE, [Port]).

init([Port]) ->
    R = ssl:listen(Port, ?SSL_OPTIONS),
    LSocket = case R of
                  {ok, LSock} ->
                      LSock;
                  Res ->
                      io:fwrite("gateway_sup Error: ~p~n", [Res])
              end,
    spawn_link(fun empty_listeners/0),
    ChildSpec = [{socket,
                  {gateway_serv, start_link, [LSocket]},
                  temporary, 1000, worker, [gateway_serv]}
                ],
    {ok, {{simple_one_for_one, 3600, 3600},
          ChildSpec
         }}.

empty_listeners() ->
    io:fwrite("---------------------- empty_listeners~n"),
    [start_socket() || _ <- lists:seq(1,128)],
    ok.

start_socket() ->
    io:fwrite("++++++++++++++++++++++ start_socket~n"),
    supervisor:start_child(?MODULE, []).

还有 gen_server

-module(gateway_serv).

-behaviour(gen_server).
-export([start_link/1, init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]).

start_link(LSocket) ->
io:fwrite("#################~n"),
    gen_server:start_link(?MODULE, [LSocket], []).

init([LSocket]) ->
io:fwrite("/////////////////~n"),
    gen_server:cast(self(), accept),
    {ok, #client{listenSocket=LSocket, pid=self()}}.

handle_cast(accept, G = #client{listenSocket=LSocket}) ->
    {ok, AcceptSocket} = ssl:transport_accept(LSocket),
    gateway_sup:start_socket(),
    case ssl:ssl_accept(AcceptSocket, 30000) of
    ok ->
        timer:send_after(10000, closingSocket),
        ssl:setopts(AcceptSocket, [{active, once}, {mode, list}, {packet, 0}]),
        {noreply, G#client{listenSocket=none, socket=AcceptSocket}};
    {error, _Reason} ->
        {stop, normal, G}
    end;
handle_cast(_, G) ->
    {noreply, G}.

gen_server 的 start_link/1 显然从未被调用(通过 io:fwrite 检查)。

似乎无法找出原因...

【问题讨论】:

    标签: erlang erlang-supervisor gen-server


    【解决方案1】:

    对我来说有些奇怪,您在主管的 init 函数中启动 empty_listener 调用 start_socket() 调用 supervisor:start_child ,此时主管没有完成其初始化阶段。因此,调用主管启动子进程的进程与主管本身之间存在竞争。

    我认为这段代码应该在init函数之外:

    • 首先使用start_link(Port)启动supervisor,
    • 当它返回时调用函数 start_socket()。

    我做了一个使用这种模式的应用程序,我有 2 级主管:

    main supervisor (one_for_all strategy)
    |                         |
    |                         |
    v                         v
    application   ------->    supervisor (simple_one_for_one strategy)
    server      start_child   worker factory
                              |
                              |
                              v*
                              many children
    

    编辑: 忘记这个竞争条件,

    我做了一个测试,在 init 函数结束前引入了延迟,我看到 start_child 函数在等待 init 结束时没有任何丢失。 OTP 家伙比我想象的还要谨慎......

    【讨论】:

    • 在我看来,他正在使用 LYSE 中提出的策略。我错了吗?这个技术我用了很多次,没有遇到任何问题
    • 没错,在learnyousomeerlang.com/buckets-of-sockets#sockserv-revisited 找到的,它与我编写的其他代码完美配合...但是在这里,我不知道,似乎找不到问题。
    • 正如 user601836 所回答的,缺少一个参数来访问您的主管,我已经尝试了您的代码进行这些修改,它工作正常。但我真的认为你不应该从主管的init函数中调用start_child。即使它工作一次,你也不确定它是否会一直工作。如果您想避免使用另一个模块,您可以在转换回调中向主管发送一条消息,该消息将在初始化阶段之后生成该函数。
    【解决方案2】:

    当你注册你使用的主管时:

    Role = list_to_atom(atom_to_list(?MODULE) ++ lists:flatten(io_lib:format("~B", [Port]))),
    

    因此当你打电话时:

    start_socket() ->
        io:fwrite("++++++++++++++++++++++ start_socket~n"),
        supervisor:start_child(?MODULE, []).
    

    您正在呼叫一个不存在的主管。

    你应该这样称呼它:

    supervisor:start_child(Role, []).
    

    您可以将Role 作为参数传递给函数。

    【讨论】:

    • 在启动任何服务器之前,您得到的行是在主管的 init 回调中打印的。
    • @user601836 是的,根据主管 LearnYouSomeErlang 的说法,应该可以开始了。
    • 看到了!接受了!谢谢你。我快疯了。 +1
    猜你喜欢
    • 2011-10-11
    • 1970-01-01
    • 1970-01-01
    • 2011-02-16
    • 2017-07-07
    • 1970-01-01
    • 1970-01-01
    • 2017-05-02
    • 1970-01-01
    相关资源
    最近更新 更多