Хранение контрольных детей PID - PullRequest
4 голосов
/ 14 ноября 2011

Я новичок в Erlang и пытаюсь реализовать свое первое приложение Erlang.Это инструмент сетевого мониторинга, который должен выполнить запрос ping к указанному хосту.На самом деле отправка ICMP не является целью, меня больше интересует структура приложения.В настоящее время у меня есть monitor_app, monitor_sup (root sup), pinger_sup и pinger (worker).Это pinger_sup:

-module(pinger_sup).
-behaviour(supervisor).
-export([start_link/0, start_child/1, stop_child/1]).
-export([init/1]).

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

start_child(Ip) ->
    {ok, Pid} = supervisor:start_child(?MODULE, Ip),
    put(Pid, Ip),
    {ok, Pid}.

stop_child(Ip) ->
    Children = get_keys(Ip),
    lists:foreach(fun(Pid) ->
            pinger:stop(Pid)
        end,
        Children).

init(_Args) ->
    Pinger = {pinger, {pinger, start_link, []},
        transient, 2000, worker, [pinger]},
    Children = [Pinger],
    RestartStrategy = {simple_one_for_one, 4, 3600},
    {ok, {RestartStrategy, Children}}.

И сам pinger:

-module(pinger).
-behaviour(gen_server).
-export([start_link/1, stop/1, stop_ping/1, ping/1, ping/2]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(PING_INTERVAL, 5000).

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

stop(Pid) ->
    gen_server:cast(Pid, stop).

stop_ping(Ip) ->
    pinger_sup:stop_child(Ip).

ping(Ip) ->
    pinger_sup:start_child(Ip).

ping(_Ip, 0) ->
    ok;
ping(Ip, NProc) ->
    ping(Ip),
    ping(Ip, NProc - 1).

init(Ip) ->
    erlang:send_after(1000, self(), do_ping),
    {ok, Ip}.

handle_call(_Request, _From, State) ->
    {noreply, State}.

handle_cast(stop, State) ->
    io:format("~p is stopping~n", [State]),
    {stop, normal, State}.

handle_info(do_ping, State) ->
    io:format("pinging ~p~n", [State]),
    erlang:send_after(?PING_INTERVAL, self(), do_ping),
    {noreply, State};
handle_info(Info, State) ->
    io:format("Unknown message: ~p~n", [Info]),
    {noreply, State}.

terminate(_Reason, State) ->
    io:format("~p was terminated~n", [State]),
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

В нем нет комментариев, но я думаю, что это очень просто и знакомо любому разработчику Erlang.У меня есть несколько вопросов об этом коде:

1) Является ли * erlang: send_after * в init хорошим решением?Какова лучшая практика, если мне нужно, чтобы процесс начал выполнять работу немедленно, он был запущен без запуска его функций извне?

2) В настоящее время я запускаю ping с помощью pinger: ping ("127.0.0.1"). команда.Модуль pinger попросит pinger_sup запустить дочерний процесс.После того, как процесс был запущен, я хочу остановить его.Каков предпочтительный способ сделать это?Должен ли я прекратить его с помощью sup или отправить ему команду остановки?Как хранить PID процесса?В настоящее время я использую словарь процессов, но после его реализации я понял, что словарь на самом деле не принадлежит к sup (это словарь оболочки или любой другой вызывающий объект stop_child).В случае, если я использую ETS, и поддержка sup была прервана, будут ли эти мертвые PID процесса навсегда оставаться в ETS, вызывая утечку памяти?

Я ценю любые ответы и комментарии к коду.

Спасибо!

Ответы [ 2 ]

7 голосов
/ 16 ноября 2011

1) Является ли erlang: send_after в init хорошим решением?

номер

Какова лучшая практика, если мне нужно, чтобы процесс начал выполнять работу немедленно, она была порождена, не вызывая его функции извне?

См. здесь . Установите таймаут 0, а затем выполните:

handle_info(timeout, State) ->
    whatever_you_wish_to_do_as soon_as_server_starts,
    {noreply, State}.

Нулевой тайм-аут означает, что сервер отправит себе информацию «тайм-аут», как только завершит инициализацию, перед обработкой любого другого вызова / приведения.

Также см. модуль таймера . Вместо рекурсивного вызова send_after в handle_info (do_ping, State), просто запустите таймер и скажите ему отправлять вам «do_ping» каждые? PING_INTERVAL.

2) В настоящее время я запускаю ping с помощью pinger: ping ("127.0.0.1"). команда. Модуль pinger попросит pinger_sup запустить дочерний процесс. После того, как процесс был запущен, я хочу остановить его. Каков предпочтительный способ сделать это? Должен ли я прекратить его с помощью sup или отправить ему команду остановки?

Вы должны отправить ему команду остановки. Зачем убивать gen_server с помощью супервизора, если gen_server может сделать это сам? : -)

Как хранить PID процесса? В настоящее время я использую словарь процессов, но после его реализации я понял, что словарь на самом деле не принадлежит к sup (это словарь оболочки или любой другой вызывающий объект stop_child). В случае, если я использую ETS и sup отключается, будут ли эти мертвые PID процесса оставаться в ETS навсегда, вызывая утечку памяти?

Таблицы ETS уничтожаются, как только завершается процесс владельца. Так что утечки памяти нет.

Но хранить PID так, как вы это делаете, - не «путь Эрланга». Вместо этого я предлагаю сделать дерево супервизора. Вместо того, чтобы ставить всех работников pinger под pinger_sup и затем вспоминать, какой работник пингует с какого IP, я предлагаю, чтобы pinger_sup запускал supervisor , который обрабатывает данный IP. И тогда этот начальник начинает необходимое количество рабочих.

Теперь, когда вы хотите прекратить пинговать какой-нибудь IP, вы просто убиваете супервизора за этот IP, и он автоматически убивает своих детей.

И как бы вы узнали, какой руководитель имеет дело с каким IP? Ну, поместите IP в имя супервизора :-) При создании супервизора, который имеет дело с IP, сделайте что-то вроде этого:

-module(pinger_ip_sup).

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

Затем, когда вы хотите прекратить пинговать Ip, вы просто убиваете супервизора по имени {global, {pinger_ip_sup, Ip}}, и он убивает своих детей: -)

редактировать относительно комментариев:

Если вы хотите обработать ошибку, которая приводит к тайм-аутам, вы можете зарезервировать переменную состояния, которая сообщит вам, истек ли тайм-аут, вызванный init, или тайм-аут, вызванный ошибкой. Например:

-record(pinger_state, {ip, initialized}).

init(Ip) ->
    State = #pinger_state{ip = Ip, initialized = false}.
    {ok, State, 0}.

handle_info(timeout, State#pinger_state{initialized = false}) ->
    whatever_you_wish_to_do_as soon_as_server_starts,
    New_state = State#pinger_state{initialized = true}.
    {noreply, New_state}.

handle_info(timeout, State#pinger_state{initialized = true}) ->
    error_handling,
    {noreply, State}.

Таким образом, вы можете использовать этот механизм и обрабатывать ошибки тайм-аута. Но реальный вопрос здесь такой: ожидаете ли вы ошибок тайм-аута вообще?

Что касается таймера: да, у таймера есть некоторые накладные расходы в случае, если вы планируете делать DDOS-атаки или что-то в этом роде :-D Если вы не планируете создавать и отменять таймеры, такие как сумасшедшие, гораздо более элегантно использовать модуль таймера , Это выбор дизайна, который вы должны сделать: хотите ли вы чистый и элегантный код или код, который может стоять, когда все остальное ломается. Я сомневаюсь, что вам нужно последнее. Но вы знаете лучше.

1 голос
/ 12 октября 2013

mybe, что вам нужно для "Хранения PID-файлов супервизора" - это erlang: register (atom, pid)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...