存储主管儿童PID

时间:2011-11-14 18:36:14

标签: erlang

我是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}}.

并且自己:

-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)目前我使用 pinger:ping(“127.0.0.1”)。命令启动ping。 pinger模块请求pinger_sup启动子进程。该过程开始后我想停止它。这样做的最佳方式是什么?我应该通过sup终止它还是我应该发送停止命令?我应该如何存储过程PID?目前我使用进程字典但在实现之后我意识到字典实际上不属于sup(它是shell字典或任何其他stop_child调用者)。如果我使用ETS并且sup被终止,那么这些死进程PID会永远停留在ETS中导致内存泄漏吗?

我感谢任何答案和代码评论。

谢谢!

2 个答案:

答案 0 :(得分:7)

  

1) erlang:send_after 在init中是一个很好的解决方案吗?

没有

  

如果我需要流程开始立即完成工作,那么最佳实践是什么?它是在没有从外部触发其功能的情况下产生的?

here。将超时设为0,然后执行:

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

零超时意味着服务器在完成初始化之后会立即发送“超时”信息,然后再处理任何其他调用/强制转换。

另请参阅timer module。而不是在handle_info(do_ping,State)中重新调用send_after,只需启动计时器并告诉他每次发送“do_ping”?PING_INTERVAL。

  

2)目前我使用pinger:ping(“127.0.0.1”)启动ping。命令。 pinger模块请求pinger_sup启动子进程。该过程开始后我想停止它。这样做的最佳方式是什么?我应该通过sup终止它还是我应该发送停止命令?

你应该给他发一个停止命令。为什么使用supervisor来杀死gen_server,如果gen_server可以单独执行呢? : - )

  

我应该如何存储过程PID?目前我使用进程字典但在实现之后我意识到字典实际上不属于sup(它是shell字典或任何其他stop_child调用者)。如果我使用ETS并且sup被终止,那么这些死进程PID会永远停留在ETS中导致内存泄漏吗?

所有者进程终止后,ETS表将被销毁。所以,那里没有内存泄漏。

但是,以你的方式存储PID不是“erlang方式”。相反,我建议制作一个主管树。而不是将所有pinger工作者放在pinger_sup下然后记住哪个工作人员ping哪个IP,我建议pinger_sup启动一个处理给定IP的主管。然后这位主管启动了所需数量的工人。

现在,当你想停止ping一些IP时,你只需要杀死该IP的主管,他就会自动杀死他的孩子。

你怎么知道哪个主管处理哪个IP?好吧,把IP放在主管的名字中:-)当产生处理IP的主管时,做这样的事情:

-module(pinger_ip_sup).

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

然后,当你想停止ping 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 :(得分:1)

mybe你需要什么“存储主管儿童PID”是 二郎:寄存器(原子,PID)