我是Erlang初学者,正在尝试实现我的第一个Erlang应用程序。它是一个网络监控工具,应该对指定的主机执行ping请求。实际上发送ICMP不是目的,我更感兴趣的是应用程序结构。目前我有monitor_app,monitor_sup (根),pinger_sup和pinger (工人)。这是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)目前我使用ping启动ping:ping(“127.0.0.1”)。命令。pinger模块要求pinger_sup启动子进程。在这个过程开始后,我想停止它。最好的方法是什么?我应该用sup终止它,还是我应该给他发送停止命令?如何存储进程PID?目前,我使用过程字典,但是在实现它之后,我意识到该字典实际上不属于sup (它是一个shell字典或任何其他stop_child调用者)。如果我使用ETS并终止sup,这些死进程PID会永远留在ETS中导致某种内存泄漏吗?
我很感谢你的回答和代码评论。
谢谢!
发布于 2011-11-16 14:55:58
( 1) erlang:send_after在init中是一个很好的解决方案吗?
不是的。
什么是最好的实践,如果我需要过程,开始做这项工作,它是在没有从外部触发他的功能的情况下产生的?
见这里。将超时设置为0,然后执行:
handle_info(timeout, State) ->
whatever_you_wish_to_do_as soon_as_server_starts,
{noreply, State}.零超时意味着服务器在处理任何其他调用/强制转换之前,会在它完成时立即发送“超时值”信息。
另外,请参见定时器模块。不要重述调用send_after in handle_info(do_ping,State),只需启动计时器并告诉他向您发送"do_ping“每个?PING_INTERVAL。
2)目前我使用ping启动ping:ping(“127.0.0.1”)。命令。pinger模块要求pinger_sup启动子进程。在这个过程开始后,我想停止它。最好的方法是什么?我应该用sup终止它,还是我应该给他发送停止命令?
你应该给他发个停止命令。如果gen_server可以自己完成,为什么使用主管来杀死gen_server?:-)
如何存储进程PID?目前,我使用过程字典,但是在实现它之后,我意识到该字典实际上不属于sup (它是一个shell字典或任何其他stop_child调用者)。如果我使用ETS并终止sup,这些死进程PID会永远留在ETS中导致某种内存泄漏吗?
一旦所有者进程终止,ETS表就会被销毁。所以那里没有内存泄漏。
但是,以这样的方式存储PID并不是“erlang方式”。相反,我建议制作一棵主管树。我建议pinger_sup启动一个管理器来处理给定的IP,而不是将所有pinger员工都放在这个IP下,然后记住哪个工作人员执行哪个IP。然后这个主管开始需要多少工人。
现在,当你想停止敲击某些IP的时候,你只要杀死那个IP的主管,他就会自动杀死他的孩子。
你怎么知道哪个主管处理哪个IP?那么,把IP放在主管的名字里:-)当产生处理IP的主管时,做如下的事情:
-module(pinger_ip_sup).
start_link(Ip) ->
supervisor:start_link({global, {?MODULE, Ip}}, ?MODULE, []).然后,当你想停止敲击一个叶问的时候,你只要用{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 -如果您不打算像疯狂地创建和取消计时器,那么使用计时器模块就更优雅了。这是你必须做出的一个设计选择:你想要一个干净而优雅的代码,还是当其他所有的代码都中断时,代码能够站立起来。我怀疑你需要后者。但是你最清楚。
发布于 2013-10-12 11:32:44
我想你需要什么“存储监督子pid”是二郎:注册(原子,pid)
https://stackoverflow.com/questions/8126391
复制相似问题