Erlang: OTP - правильная передача дочерних аргументов; работники, выходящие с исключениями

У меня есть дерево супервизора/работника, организованное следующим образом:

game_super -> {keyserver: worker, loginserver: super}
loginserver -> loginlistener: worker (socket pool)

Сервер ключей работает нормально. Он просто возвращает строку случайных байтов, которые он использует для создания токенов входа. Его периодически заменяют.

Мой сервер входа в систему используется для работы под R16. Затем я обновился до R18, потому что хотел использовать карты. IDK, если это проблема ... Хотя я в этом сомневаюсь. Другое изменение, которое я сделал, заключается в том, что мне нужны карты для удобства настройки дочерних элементов.

У меня есть несколько серверов Redis, каждый со своей целью. Один ограничивает скорость. Это единственный действующий сейчас. Я звоню game_core:redis_init() в game_supervisor:init([]). Он возвращает {ok,{#MapPids, #MapScriptKeys}. Где значения #MapScriptKeys — это кортежи, содержащие все хэши Sha скриптов LUA.

Я получаю exception exit: {shutdown,{failed_to_start_child,login,<0.178.0>}} Pids явно меняются, но это был последний результат после вызова game_supervisor:start_inshell().

Я вставил io:format("~p\n",[State]), непосредственно перед {ok, State} в login_listener:init(State), и он распечатывает все... один раз. Учитывая, что это прямо перед возвращением... как это не получается?

Код основан на: http://learnyousomeerlang.com/buckets-of-sockets.

Это похоже на: исключение супервизора Erlang при запуске рабочего , где одно и то же имя был повторно использован для детей?

Супервайзер игры

-module(game_supervisor).
-behavior(supervisor).

-include("constants.hrl").

-export([start/0, start/1, start_inshell/0, init/1]).

start(Args) ->
    spawn(fun() ->
            supervisor:start_link({local, ?MODULE}, ?MODULE, Args)
        end).

start_inshell() ->
    {ok, Pid} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
    unlink(Pid).

start() -> start([]).

init([]) ->
    {ok, RedisState} = game_core:redis_init(),
    {ok, {
        {one_for_one, 2, 10}, [ %more than 2 restarts w/i 10 seconds all killed
        {key,
            {key_server, start_link, []},
            permanent,
            10000,
            worker,
            [key_server]},
        {login,
            {login_server, start_link, [RedisState]},
            permanent,
            10000,
            supervisor,
            [login_server]}%,
%       {game,
%           {game_server, start_link, [RedisState]},
%           permanent,
%           10000,
%           supervisor,
%           [game_server]}
    ]}}.

Сервер входа

-module(login_server).
-behavior(supervisor).

-include("constants.hrl").

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

start_link(Args) ->
    spawn(fun() ->
            supervisor:start_link({local, ?MODULE}, ?MODULE, Args)
    end).

start_link() -> start_link([]).

init({Pid,Scripts}) ->
    process_flag(trap_exit, true),
    {ok, Socket} = gen_tcp:listen(?PORT_LISTEN_LOGIN, [{active,once}, {reuseaddr, true}]),

    %Construct socket pool
    spawn_link(fun start_pool/0),

    %Put all redis information for login clients here
    RedisState = {maps:get(limiter,Pid),maps:get(limiter,Scripts)},

    %Child specification
    {ok, {
        {simple_one_for_one, 2, 10}, [  %more than 2 restarts w/i 10 seconds all killed
        {listener,
            {login_listener, start_link, [{Socket, RedisState}]},
            temporary,
            1000,
            worker,
            [login_listener]}
    ]}}.

start_pool() ->
    [start_socket() || _ <- lists:seq(1,32)],
    ok.

start_socket() -> supervisor:start_child(?MODULE, []).

Обработчик входа

-module(login_listener).
-behavior(gen_server).

-include("constants.hrl").

-export([start_link/1]).

%required callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start_link(State) ->
    gen_server:start_link(?MODULE, State,[]).

init(State) ->
    process_flag(trap_exit, true),
    gen_server:cast(self(), accept),
    {ok, State}.

handle_cast(accept, State) ->                   %Param(s): Msg, State
    {Socket, RedisState} = State,

    {ok, AcceptSocket} = gen_tcp:accept(Socket),
    login_server:start_socket(),
    {noreply, {AcceptSocket, RedisState}}.

handle_info({tcp, Socket, Str}, State) ->           %Param(s): Info
    %%Login
    {_, {PidLimiter,{ShaLimiter}}} = State,
    {ok, {ClientIP, _ }} = inet:peername(Socket),

    {ok, Result} = game_core:rate_limit(PidLimiter, ShaLimiter, ClientIP),

    ok = inet:setopts(Socket, [{active, once}]),
    {noreply, State}.

%%Surpress warnings
handle_call(_, _, State) -> {noreply, State}.       %Param(s): Request, From
terminate(_, _) -> ok.                              %Param(s): Reason, State
code_change(_, State,_) -> {ok, State}.         %Param(s): OldVsn, Extra

person Nolan Robidoux    schedule 06.09.2015    source источник


Ответы (1)


Почему ваш супервизор запускает функции, вызывающие spawn/1? Для game_supervisor измените это:

start(Args) ->
    spawn(fun() ->
            supervisor:start_link({local, ?MODULE}, ?MODULE, Args)
        end).

к этому:

start(Args) ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, Args).

И для login_server измените это:

start_link(Args) ->
    spawn(fun() ->
            supervisor:start_link({local, ?MODULE}, ?MODULE, Args)
    end).

к этому:

start_link(Args) ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, Args).
person Steve Vinoski    schedule 06.09.2015
comment
Спасибо. Хотелось бы больше понять внутренности. Рад, что попробовал, прежде чем что-то напечатать. Раньше это не вызывало проблем, когда я только передавал сокет... но опять же, это был R16. Нет веской причины, почему (невежество).... просто скопировал стиль со стр. 398 Программирование на Эрланге, 2-е изд. - person Nolan Robidoux; 07.09.2015