Erlang gen_tcp被动模式与一次性模式的区别

Erlang gen_tcp被动模式与一次性模式的区别,erlang,gen-tcp,Erlang,Gen Tcp,我在看书。在第17章的主动和被动插座中,说明: 您可能认为对所有服务器使用被动模式是正确的 方法不幸的是,当我们处于被动模式时,我们可以等待 数据仅来自一个套接字。这对于编写服务器是无用的 必须等待来自多个套接字的数据 幸运的是,我们可以采用混合方法,既不阻塞也不阻塞 非阻塞。我们使用选项{active,once}打开套接字。在里面 在该模式下,套接字处于活动状态,但仅用于一条消息。之后 控制进程已发送消息,它必须显式调用 inet:setopts可重新接收下一条消息。系统 将阻止,直到发生这种

我在看书。在第17章的主动和被动插座中,说明:

您可能认为对所有服务器使用被动模式是正确的 方法不幸的是,当我们处于被动模式时,我们可以等待 数据仅来自一个套接字。这对于编写服务器是无用的 必须等待来自多个套接字的数据

幸运的是,我们可以采用混合方法,既不阻塞也不阻塞 非阻塞。我们使用选项{active,once}打开套接字。在里面 在该模式下,套接字处于活动状态,但仅用于一条消息。之后 控制进程已发送消息,它必须显式调用 inet:setopts可重新接收下一条消息。系统 将阻止,直到发生这种情况。这是两全其美

相关代码:

% passive mode
loop(Socket) ->     
    ​case​ gen_tcp:recv(Socket, N) ​of​​    
        {ok, B} ->​     
            ... do something with the data ...​     
            loop(Socket);​  
        {error, closed}​    
            ...​    
    ​end​.

% once mode
loop(Socket) ->     
    ​receive​​  
        {tcp, Socket, Data} ->​     
            ... do something with the data ...​     
            ​%% when you're ready enable the next message​​     
            inet:setopts(Sock, [{active, once}]),​  
            loop(Socket);​  
        {tcp_closed, Socket} ->​    
            ...​    
    ​end​.
我看不出两者之间有什么真正的区别
gen_tcp:recv
被动
模式下与
接收
一次
模式下的作用基本相同。
一次
模式如何解决
被动
模式的问题:

不幸的是,当我们处于被动模式时,我们可以等待 数据仅来自一个套接字。这对于编写服务器是无用的 必须等待来自多个套接字的数据


利用这一点的代码如下所示:

loop(Socket1, Socket2) ->     
    ​receive​​  
        {tcp, Socket1, Data} ->​     
            ... do something with the data ...​     
            ​%% when you're ready enable the next message​​     
            inet:setopts(Socket1, [{active, once}]),​  
            loop(Socket1, Socket2);​
        {tcp, Socket2, Data} ->
            ... do something entirely different
            inet:setopts(Socket2, [{active, once}]),​  
            loop(Socket1, Socket2);
        ...
    end.
然而,根据我的经验,你通常不会做那样的事情;通常,每个套接字都有一个进程。活动模式的优点是,您可以同时等待来自其他Erlang进程的网络数据和消息:

loop(Socket) ->     
    ​receive​​  
        {tcp, Socket, Data} ->​     
            ... do something with the data ...​     
            ​%% when you're ready enable the next message​​     
            inet:setopts(Socket, [{active, once}]),​  
            loop(Socket);​  
        reverse_flux_capacitor ->​    
            reverse_flux_capacitor(),
            %% keep waiting for network data
            loop(Socket)
    ​end​.

此外,在编写“真正的”Erlang/OTP应用程序时,您通常会编写gen_服务器模块而不是循环函数,TCP消息会在
handle_info
回调函数中与其他消息一起得到很好的处理。

主要区别在于您选择对该套接字上的事件做出反应时。使用主动套接字,您的进程将接收消息,使用被动套接字,您必须自行决定调用
gen\u tcp:recv
。这对你意味着什么

编写Erlang程序的典型方法是让它们对事件做出反应。遵循这个主题,大多数Erlang进程等待表示外部事件的消息,并根据它们的性质对它们作出反应。当您使用活动套接字时,您能够以与其他事件完全相同的方式处理套接字数据的方式进行编程:作为Erlang消息。当您使用被动套接字编写时,您必须选择何时检查套接字以查看它是否有数据,并对何时检查Erlang消息做出不同的选择——换句话说,您必须编写轮询例程,而这在很大程度上失去了Erlang的优势

因此,
active\u once
active
之间的区别是

使用活动套接字,任何能够建立连接的外部参与者都可以用数据包轰炸进程,无论系统是否能够跟上。如果您想象一个服务器有上千个并发连接,其中每个数据包的接收都需要一些重要的计算或访问其他有限的外部资源(这不是一个奇怪的场景),那么您最终不得不选择如何处理过载

只有
活动
套接字,您已经做出了选择:您将让服务降级,直到事情开始失败(超时或其他)

使用
active\u once
sockets,您有机会做出一些选择。
active\u once
套接字允许您在套接字上接收一条消息,并再次将其设置为
passive
,直到您将其重置为
active\u once
。这意味着您可以编写一个阻塞/同步调用,检查整个系统是否可以安全地继续处理消息,并将其插入到处理结束和侦听套接字的下一个
receive
开始之间,甚至可以选择输入
receive
,而无需重新激活如果系统过载,但您的进程需要同时处理其他Erlang消息,则使用套接字

设想一个名为
sysmon
的命名进程,它位于该节点上,并检查外部数据库是否过载。您的进程可以接收数据包,对其进行处理,并在允许套接字向其发送另一条消息之前,让系统监视器知道它已准备好进行更多的工作。系统监视器还可以向侦听进程发送一条消息,告诉它们在侦听数据包时暂时停止接收数据包,这在
gen_tcp:recv
方法中是不可能的(因为您正在接收套接字数据或检查Erlang消息,但不能同时执行这两种操作):

这是一种实现系统范围节流的方法的开始,使处理通常最困难的部分变得容易:跨网络的、系统外部的、完全不受您控制的元素。再加上一些早期决策(如“在完全拒绝新连接之前,我们要承受多大的负载?”),这种能力可以以Erlang消息的形式接收套接字数据,但不会让自己受到它们的轰炸(或者填满邮箱,使寻找非套接字消息的成本变得非常昂贵),与我们在石器时代(甚至今天在其他语言中)使用的手动处理套接字相比,感觉非常神奇


这是Fred Hebert写的一篇有趣的文章,他是LYSE的作者,关于过载:。这并不特定于Erlang,但他所写的思想在Erlang中比大多数其他语言更容易实现,这可能与队列作为容量管理技术(误导性)的普遍使用有关。

我明白了。因此,关键点不是套接字,而是消息传递。这本书的这一部分写得很差,令人产生误解。是的,为什么不使用并行服务器?@legos
loop(S = {Socket, OtherState}) ->
    sysmon ! {self(), ready},
    receive
        {tcp, Socket, Data} ->
            ok = process_data(Data, OtherState),
            loop(S);
        {tcp_closed, Socket} ->
            retire(OtherState),
            ok;
        {sysmon, activate} ->
            inet:setopts(Socket, [{active, once}]),
            loop(S);
        {sysmon, deactivate} ->
            inet:setopts(Socket, [{active, false}]),
            loop(S);
        {other, message} ->
            system_stuff(OtherState),
            loop(S)
    end.