存储主管儿童PID

Sot*_*ric 4 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}}.
Run Code Online (Sandbox Code Playgroud)

并且自己:

-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}.
Run Code Online (Sandbox Code Playgroud)

它缺乏评论,但我认为任何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中导致内存泄漏吗?

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

谢谢!

dij*_*tra 7

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

没有.

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

看到这里.将超时设为0,然后执行:

handle_info(timeout, State) ->
    whatever_you_wish_to_do_as soon_as_server_starts,
    {noreply, State}.
Run Code Online (Sandbox Code Playgroud)

零超时意味着在处理任何其他调用/强制转换之前,服务器将在完成初始化后立即向其发送"超时"信息.

另外,请参阅计时器模块.而不是在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, []).
Run Code Online (Sandbox Code Playgroud)

然后,当你想停止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}.
Run Code Online (Sandbox Code Playgroud)

这样您就可以使用此机制并处理超时错误.但是,真正的问题是:你是否期望超时错误?

至于计时器:是的,计时器有一些开销,以防你计划进行DDOS攻击或类似的事情:-D如果你不打算像疯狂一样创建和取消计时器,那么使用计时器模块会更优雅.这是一个你必须做出的设计选择:你想要一个干净优雅的代码,或者代码,当其他一切都破坏时它们可以代替.我怀疑你需要后者.但是,你最了解.