Erlang 存储子PIDs
我是Erlang初学者,正在尝试实现我的第一个Erlang应用程序。它是一个网络监控工具,应该对指定的主机执行ping请求。实际上发送ICMP并不是目的,我更感兴趣的是应用程序结构。目前我有monitor_app、monitor_sup(root sup)、pinger_sup和pinger(worker)。我是pinger_sup: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
-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
之前,因此其他进程