Erlang: порождает более одного процесса под наблюдателем - PullRequest
1 голос
/ 01 декабря 2011

Я пытаюсь реализовать простой супервизор и просто перезапустить его дочерние процессы, если они потерпят неудачу. Но я даже не знаю, как создать более одного процесса под наблюдателем! Я посмотрел на простой код супервизора на этом сайте и нашел что-то

-module(echo_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).

start_link() ->
    {ok, Pid} = supervisor:start_link(echo_sup, []),
    unlink(Pid).
init(_Args) ->
    {ok,  {{one_for_one, 5, 60},
       [{echo_server, {echo_server, start_link, []},
         permanent, brutal_kill, worker, [echo_server]},

        {echo_server2, {echo_server2, start_link, []},
         permanent, brutal_kill, worker, [echo_server2]}]}}.

Я предполагал, что помещение части "echo_server2" в функцию init () приведет к созданию другого процесса под этим супервизором, но в итоге я получаю исключение: сообщение о завершении работы.

Оба файла "echo_server" и "echo_server2" имеют одинаковый код, но разные имена. Так что я сейчас растерялся.

-module(echo_server2).
-behaviour(gen_server).

-export([start_link/0]).
-export([echo/1, crash/0]).
-export([init/1, handle_call/3, handle_cast/2]).

start_link() ->
    {ok,Pid} = gen_server:start_link({local, echo_server2}, echo_server2, [], []),
    unlink(Pid).

%% public api
echo(Text) ->
    gen_server:call(echo_server2, {echo, Text}).
crash() ->
    gen_server:call(echo_server2, crash).

%% behaviours
init(_Args) ->
    {ok, none}.
handle_call(crash, _From, State) ->
    X=1,
    {reply, X=2, State};
handle_call({echo, Text}, _From, State) ->
    {reply, Text, State}.
handle_cast(_, State) ->
    {noreply, State}.

Ответы [ 2 ]

4 голосов
/ 01 декабря 2011

Сначала вам нужно прочитать несколько документов об OTP / gen_server и OTP / supervisors. В вашем коде есть несколько ошибок.

1) В модуле echo_sup измените функцию start_link следующим образом:

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

Не знаю, почему вы unlink/1 после запуска процесса.

2) В обоих echo_servers измените функцию start_link на:

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

Вы не должны изменять возвращаемое значение этой функции, потому что супервизор ожидает одно из следующих значений:

{ok,Pid} | ignore | {error,Error}
2 голосов
/ 06 декабря 2011

Вам не нужны два разных модуля только для запуска двух экземпляров одного и того же сервера.Проблема конфликта связана с тегом в дочерней спецификации, который должен быть уникальным.Это первый элемент в кортеже.Таким образом, вы можете получить что-то вроде:

    [{echo_server, {echo_server, start_link, []},
      permanent, brutal_kill, worker, [echo_server]},
     {echo_server2, {echo_server, start_link, []},
      permanent, brutal_kill, worker, [echo_server]}]}}.

Почему вы отсоединяете дочерние процессы?Руководитель использует эти ссылки, чтобы контролировать своих детей.Ошибка, которую вы получаете, заключается в том, что супервизор ожидает, что функции, которые запускают дочерние элементы, возвращают {ok,ChildPid}, именно так он получает pid дочерних элементов, поэтому, когда он получает другое возвращаемое значение, он не запускается при запуске дочерних элементов и затемдо себя.Все в соответствии с тем, как это должно работать.

Если вы хотите зарегистрировать оба сервера, вы можете изменить функцию start_link так, чтобы имя использовалось в качестве аргумента и передавалось, чтобы вы могли явно передать его вчерез детскую спец.Итак:

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

и

    [{echo_server, {echo_server, start_link, [echo_server]},
      permanent, brutal_kill, worker, [echo_server]},
     {echo_server2, {echo_server, start_link, [echo_server2]},
      permanent, brutal_kill, worker, [echo_server]}]}}.

Использование имени модуля в качестве зарегистрированного имени для сервера - это всего лишь соглашение, которое работает только при запуске одного экземпляра сервера.

...