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.