Erlang监督UDP监听器

时间:2015-11-10 23:22:39

标签: erlang

我正在学习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感到困惑。 spawn_link正在打开另一个进程,但它正在与调用进程建立链接。根据我的理解,我的主管将在此监控呼叫过程。那么,我的主管如何表现,因为我的udplistener失败了?如果它不能按照我期望的方式工作(我希望它会重启我的服务器),最好的方法是什么?

1 个答案:

答案 0 :(得分:3)

您的supervisor init回调实现创建了一个套接字并将其传递给worker是一个问题。

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

下一个问题是您在非活动模式下使用套接字。非活动模式下的套接字实际上只对相对简单的进程(即不是gen_server说)很有用,因为当你调用gen_udp:recv时它会在等待数据到达时阻塞...这意味着gen_server被阻塞了无法提供应有的服务。所以现在你要通过主管和gen_servers进入OTP路径,你应该切换到在主动模式下使用套接字,这意味着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失败,这意味着如果你的套接字打开失败,主管将重启强度和做一些不同的事情,而不是让你的工人盲目地继续尝试重新打开套接字。所以现在这样做,如果你有原因,可以在以后改变你的策略。