Erlang监视UDP侦听器



我正在学习Erlang。我想制作一个UDP侦听器,它将由一个主管进行监督。所以,如果监听器进程停止,主管将重新启动进程。最初,我只是制作了一个简单的UDP侦听器,它可以按预期工作。

startudplistener() ->
    {ok, Socket} = gen_udp:open(9000,[binary,{active,false}]),
    Pid = spawn(pdmanager,udplistener,[Socket]),
    {ok, Pid}.
udplistener(Socket) ->
    {ok,Packet} = gen_udp:recv(Socket,0),
    spawn(pdmanager,handleudp,[Packet]),
    udplistener(Socket).
handleudp(Packet) ->
    {_,_, Msg} = Packet,
    io:format("I have got message : ~s ~n",[Msg]),
    {handeling, Packet}.

所以,我想做的是监控udplistener进程。第一次,我将模块修改为gen_server模块。之后编写一个主管模块。我的主管是这样的:

-module(pdmanager_sup).
-behaviour(supervisor).
-export([start_link/1]).
-export([init/1]).
start_link(Port) ->
supervisor:start_link({local,?MODULE}, ?MODULE, Port).
init(Port) ->
    {ok, Socket} = gen_udp:open(Port, [binary, {active, false}]),
    {ok, {{one_for_one, 5, 60},
        [{listener,
            {pdmanager, start_link, [Socket]},
            permanent, 1000, worker, [pdmanager]}
        ]}}.

因此,我想打开一个新的udp套接字并将其传递给我的服务器,服务器将继续监听套接字,而主管将对其进行监控。因此,我想出了以下代码。

start_link(Socket) ->
    gen_server:start_link({local, pdmanager}, pdmanager, Socket, []).
init(Socket) ->
    io:format("UDP Server starting ~n",[]),
    spawn_link(pdmanager,udplistener,[Socket]),
    {ok, #state{socket=Socket}}.

我对在init函数中添加的spawn_link有点困惑。spawnlink正在打开另一个进程,但它正在与调用进程建立链接。据我所知,我的主管将在这里监控呼叫过程。那么,如果我的窃听器坏了,我的主管会怎么做呢?如果它没有按我预期的方式工作(我希望它会重新启动我的服务器),最好的方法是什么?

您的主管init回调实现创建了一个套接字并将其传递给工作者,这是一个问题。

除了版本升级的特殊情况外,您的supervisorinit回调实际上只会被调用一次,因此在supervisorinit中创建套接字并将其传递给worker意味着永远没有任何机制可以重新打开套接字。但是,如果在gen_server工作程序的init回调中打开套接字,那么当工作程序重新启动时,套接字的任何问题都将得到解决。

下一个问题是您正在非活动模式下使用套接字。处于非活动模式的套接字实际上只对相对裸露的进程(例如,不是gen_server)有用和有益,因为当您调用gen_udp:recv时,它可能会在等待数据到达时阻塞。。。这意味着gen_server被阻塞,无法为它应该提供的任何服务。因此,现在您正沿着OTP路径,使用supervisors和gen_server,您应该切换到在活动模式下使用套接字,这意味着UDP数据包将作为消息发送到gen_server。然后,您可以通过handle_info回调实现接收:

handle_info({udp, Socket, IP, InPortNo, Packet}, #state{socket=Socket}) ->
    io:format("whooopie, got a packet ~p~n", [Packet]),

如果你的gen_server工作线程死了,那没关系,端口也会去,主管会启动一个新的工作线程,它只会重新打开套接字并继续接收。。。

同样在worker init中,打开套接字后,注意套接字实际上是一个端口,您可能应该通过调用erlang:link/1来链接到它,如下所示:

{ok, Socket} = gen_udp:open(9000,[binary,{active,true}]),
erlang:link(Socket),

我不确定gen_udp是否会为你做这件事,但我看不出文档中有这样的说法,安全总比抱歉好。这现在意味着,如果端口死亡,但你的工人没有死亡,链接将导致你的工人也死亡,主管将重新启动你的工人,这将重新启动您的端口。如果你想避免工人死亡,你可以设置出口陷阱,但简单地让工人死亡更符合Erlangs失败早期原则IMO,这意味着如果你的套接字打开不断失败,主管会达到重启强度,做一些不同的事情,而不是让你的员工无意识地永远试图重新打开插座。所以,现在就这么做,如果你有理由的话,以后再改变你的策略。

最新更新