Удаленные узлы поддерживают процессы - PullRequest
2 голосов
/ 08 августа 2011

Quis Custodiet Ipsos Custodes? - (Децим Юний Ювеналис)

У меня есть следующие настройки:

На одном узле ('one@erlang.enzo') запущен серверный процесс, в котором есть сторожевой таймер, запускающий один другой узел ('two@erlang.enzo'). Когда сервер запускается, он запускает свой сторожевой таймер на удаленном узле. Когда сервер завершает работу некорректно, сторожевой таймер снова запускает сервер. При выходе из сторожевого таймера сервер запускает его снова.

Сервер запускается как часть уровня запуска после запуска сети.

Сервер также контролирует удаленный узел и запускает сторожевой таймер, как только он (то есть узел) подключается. Теперь потеря соединения между сервером и сторожевым таймером может иметь две причины: во-первых, сеть может выйти из строя; во-вторых, узел может потерпеть крах или быть убитым.

Мой код , кажется, работает, но у меня есть небольшое подозрение, что происходит следующее:

  • Когда сторожевой узел выключен (или уничтожен, или разбился) и перезапущен, сервер правильно перезапускает свой сторожевой таймер.
  • Но , когда происходит сбой в сети, и сторожевой узел продолжает работать, сервер запускает новый сторожевой таймер, когда соединение восстанавливается, и оставляет один сторожевой таймер зомби.

Мои вопросы :

  • (A) я создаю зомби?
  • (B) В случае потери сети, как сервер может проверить, жив ли сторожевой таймер (и наоборот)?
  • (C) Если B возможно, как я могу переподключить старый сервер и старый сторожевой таймер?
  • (D) Какие еще основные (и второстепенные) недостатки вы, уважаемый читатель, обнаружите в моей настройке?

РЕДАКТИРОВАТЬ: Сообщения die и kill_dog предназначены для подделки изящных выходов и не сделают его после отладки.

Вот код:


-module (watchdog).
-compile (export_all).

init () ->
    io:format ("Watchdog: Starting @ ~p.~n", [node () ] ),
    process_flag (trap_exit, true),
    loop ().

loop () ->
    receive
        die -> 1 / 0;
        {'EXIT', _, normal} ->
            io:format ("Watchdog: Server shut down.~n");
        {'EXIT', _, _} ->
            io:format ("Watchdog: Restarting server.~n"),
            spawn ('one@erlang.enzo', server, start, [] );
        _ -> loop ()
    end.

-module (server).
-compile (export_all).

start () ->
    io:format ("Server: Starting up.~n"),
    register (server, spawn (fun init/0) ).

stop () ->
    whereis (server) ! stop.

init () ->
    process_flag (trap_exit, true),
    monitor_node ('two@erlang.enzo', true),
    loop (down, none).

loop (Status, Watchdog) ->
    {NewStatus, NewWatchdog} = receive
        die -> 1 / 0;
        stop -> {stop, none};
        kill_dog ->
            Watchdog ! die,
            {Status, Watchdog};
        {nodedown, 'two@erlang.enzo'} ->
            io:format ("Server: Watchdog node has gone down.~n"),
            {down, Watchdog};
        {'EXIT', Watchdog, noconnection} ->
            {Status, Watchdog};
        {'EXIT', Watchdog, Reason} ->
            io:format ("Server: Watchdog has died of ~p.~n", [Reason] ),
            {Status, spawn_link ('two@erlang.enzo', watchdog, init, [] ) };
        _ -> {Status, Watchdog}
    after 2000 ->
        case Status of
            down -> checkNode ();
            up -> {up, Watchdog}
        end
    end,
    case NewStatus of
        stop -> ok;
        _ -> loop (NewStatus, NewWatchdog)
    end.

checkNode () ->
    net_adm:world (),
    case lists:any (fun (Node) -> Node =:= 'two@erlang.enzo' end, nodes () ) of
        false ->
            io:format ("Server: Watchdog node is still down.~n"),
            {down, none};
        true ->
            io:format ("Server: Watchdog node has come online.~n"),
            monitor_node ('two@erlang.enzo', true),
            Watchdog = spawn_link ('two@erlang.enzo', watchdog, init, [] ),
            {up, Watchdog}
    end.

1 Ответ

1 голос
/ 08 августа 2011

Использование global модуля для регистрации сторожевого таймера должно предотвратить вашу озабоченность:

watchdog.erl:

-module (watchdog).
-compile (export_all).

init () ->
    io:format ("Watchdog: Starting @ ~p.~n", [node () ] ),
    process_flag (trap_exit, true),
    global:register_name (watchdog, self ()),
    loop ().

loop () ->
    receive
        die -> 1 / 0;
        {'EXIT', _, normal} ->
            io:format ("Watchdog: Server shut down.~n");
        {'EXIT', _, _} ->
            io:format ("Watchdog: Restarting server.~n"),
            spawn ('one@erlang.enzo', server, start, [] );
        _ -> loop ()
    end.

server.erl:

checkNode () ->
    net_adm:world (),
    case lists:any (fun (Node) -> Node =:= 'two@erlang.enzo' end, nodes () ) of
        false ->
            io:format ("Server: Watchdog node is still down.~n"),
            {down, none};
        true ->
            io:format ("Server: Watchdog node has come online.~n"),
            global:sync (), %% not sure if this is necessary
            case global:whereis_name (watchdog) of
                undefined -> 
                    io:format ("Watchdog process is dead"),
                    Watchdog = spawn_link ('two@erlang.enzo', watchdog, init, [] );
                Watchdog ->
                    io:format ("Watchdog process is still alive")
            end,
            {up, Watchdog}
    end.
...