Erlang 存储子PIDs

Erlang 存储子PIDs,erlang,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

我是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) init中的*erlang:send_after*是一个好的解决方案吗?如果我需要进程立即开始执行任务,那么最好的做法是什么?它是在没有从外部触发其功能的情况下生成的

2) 目前,我使用pinger:ping(“127.0.0.1”)启动ping。指挥部。pinger模块要求pinger_sup启动子进程。进程启动后,我想停止它。最好的方法是什么?我应该通过sup终止它还是应该向他发送停止命令?我应该如何存储进程PID?目前我使用process dictionary,但在实现它之后,我意识到该字典实际上不属于sup(它是一个shell字典或任何其他stop_子调用程序)。如果我使用ETS并且sup被终止,这些死进程PID会永远留在ETS中导致内存泄漏吗

我感谢您的回答和代码注释

谢谢

1) erlang:send_after in init是一个好的解决方案吗

没有

如果我需要进程立即开始执行任务,那么最好的做法是什么?它是在没有从外部触发其功能的情况下生成的

看。将超时设置为0,然后执行以下操作:

handle_info(timeout, State) ->
    whatever_you_wish_to_do_as soon_as_server_starts,
    {noreply, State}.
零超时意味着服务器在处理任何其他调用/强制转换之前,将在完成初始化后立即向自己发送“超时”信息

另外,请参见。不要在handle_info(do_ping,State)中重复调用send_after,只需启动计时器并告诉他每隔?ping_间隔向您发送“do_ping”

2) 目前,我使用pinger:ping(“127.0.0.1”)启动ping。指挥部。pinger模块要求pinger_sup启动子进程。进程启动后,我想停止它。最好的方法是什么?我应该通过sup终止它还是应该向他发送停止命令

你应该给他发一个停止命令。如果gen_server可以自己完成,为什么要使用一个监控器来杀死gen_server呢?:-)

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

一旦所有者进程终止,ETS表就会被销毁。因此,没有内存泄漏

但是,存储PID的方式并不是“erlang方式”。相反,我建议创建一个主管树。我建议pinger_-sup启动一个主管来处理给定的IP,而不是将所有pinger工作人员置于pinger_-sup之下,然后记住哪个工作人员Ping哪个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产生的超时还是由error产生的超时。例如:

-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如果您不打算疯狂地创建和取消定时器,那么使用定时器模块要优雅得多。这是一个你必须做出的设计选择:你想要一个干净优雅的代码,还是一个在其他一切都崩溃时仍能承受的代码。我怀疑你需要后者。但是,您最清楚。

mybe“存储PID”需要什么
erlang:register(atom,pid)

谢谢你的回答,dijxtra,很抱歉耽搁了。我对你的一些答案表示怀疑,并试图找到更多信息。建议在pinger init函数中使用timeout=0,使其在handle_info中启动超时消息。handle_info的超时不是为某些错误情况保留的吗?在这种情况下,我确实需要将其作为超时处理?这种方法不会覆盖这种行为吗?那么您会说计时器模块比erlang:send\u after好。我发现了一个“常见注意事项”文档,其中说使用erlang:send\u after比使用计时器模块更有效。也许有什么我不明白的。你能澄清一下吗?你回答的其余部分是有意义的。虽然我有点意识到有人因为名字杀了什么东西。也许这是Erlang的方式,只是在我看来,基于对象名称而不是其状态来做出决策是不寻常的。不管怎样,我还是要试试:)我把你的评论中的答案添加到了答案本身,因为它太长了,不适合评论。一年后回到Erlang,我发现这个话题更有用,更具启发性。我希望我能投两次票。再次感谢:)注意:将超时设置为
0
并不意味着
timeout
将是收到的第一条消息。如果使用
gen\u server:start\u link/4
注册gen\u服务器,则注册发生在调用
模块:init/1
之前,因此其他进程