Erlang 套接字直到第二个 setopts active,once 才接收

Posted

技术标签:

【中文标题】Erlang 套接字直到第二个 setopts active,once 才接收【英文标题】:Erlang socket doesn't receive until the second setopts active,onceErlang 套接字直到第二个 setopts active,once 才接收 【发布时间】:2011-12-11 07:24:08 【问题描述】:

首先我要道歉,我提供了这么多信息以尽可能清楚地说明问题所在。如果还有什么需要澄清的,请告诉我。

(运行erlang R13B04,内核2.6.18-194,centos 5.5)

我有一个很奇怪的问题。我有以下代码来监听和处理套接字:

%Opts used to make listen socket
-define(TCP_OPTS, [binary, packet, raw, nodelay, true, reuseaddr, true, active, false,keepalive,true]).

%Acceptor loop which spawns off sock processors when connections
%come in
accept_loop(Listen) ->
    case gen_tcp:accept(Listen) of
    ok, Socket ->
        Pid = spawn(fun()->?MODULE:process_sock(Socket) end),
        gen_tcp:controlling_process(Socket,Pid);
    error,_ -> do_nothing
    end,
    ?MODULE:accept_loop(Listen).

%Probably not relevant
process_sock(Sock) ->
    case inet:peername(Sock) of
    ok,Ip,_Port -> 
        case Ip of
        172,16,_,_ -> Auth = true;
        _ -> Auth = lists:member(Ip,?PUB_IPS)
        end,
        ?MODULE:process_sock_loop(Sock,Auth);
    _ -> gen_tcp:close(Sock)
    end.

process_sock_loop(Sock,Auth) ->
    try inet:setopts(Sock,[active,once]) of
    ok ->
        receive
        tcp_closed,_ -> 
            ?MODULE:prepare_for_death(Sock,[]);
        tcp_error,_,etimedout -> 
            ?MODULE:prepare_for_death(Sock,[]);

        %Not getting here
        tcp,Sock,Data ->
            ?MODULE:do_stuff(Sock,Data);

        _ ->
            ?MODULE:process_sock_loop(Sock,Auth)
        after 60000 ->
            ?MODULE:process_sock_loop(Sock,Auth)
        end;
    error,_ ->
        ?MODULE:prepare_for_death(Sock,[]) 
    catch _:_ -> 
        ?MODULE:prepare_for_death(Sock,[])
    end.

整个设置工作正常,并且在过去几个月中一直在工作。该服务器作为具有长期保持 tcp 连接的消息传递服务器运行,它平均拥有大约 10 万个连接。然而,现在我们正试图更多地使用服务器。我们正在与 erlang 服务器建立两个长期保持的连接(将来可能会更多),并且每个连接每秒都会发出几百个命令。在常见情况下,这些命令中的每一个都会产生一个新线程,该线程可能会从 mnesia 进行某种读取,并基于此发送一些消息。

当我们尝试测试这两个命令连接时,奇怪的事情就来了。当我们打开命令流时,任何新连接都有大约 50% 的几率挂起。例如,如果我要连接并发送字符串“blahblahblah”,则使用 netcat,服务器应该立即返回一个错误。在这样做时,它不会在线程之外进行任何调用(因为它所做的只是试图解析命令,这将失败,因为 blahblahblah 不是命令)。但是大约 50% 的时间(当两个命令连接正在运行时)输入 blahblahblah 会导致服务器在返回该错误之前只停留 60 秒。

在尝试调试时,我使用了wireshark。 tcp 握手总是立即发生,当来自客户端(netcat)的第一个数据包被发送时,它会立即确认,告诉我内核的 tcp 堆栈不是瓶颈。我唯一的猜测是问题出在 process_sock_loop 函数上。它有一个接收,它将在 60 秒后返回到函数的顶部,并再次尝试从套接字中获取更多信息。我最好的猜测是以下情况正在发生:

建立连接,线程移至 process_sock_loop active,once 已设置 线程接收,但即使有数据也没有得到 60 秒后线程回到 process_sock_loop 的顶部 再次设置active, once 这次数据通过,一切照常进行

我不知道为什么会这样,当我们关闭这两个命令连接时,一切都恢复正常,问题就消失了。

有什么想法吗?

【问题讨论】:

【参考方案1】:

您对 set active,once 的第一次调用可能由于您对 spawn 的调用和对 control_process 的调用之间的竞争条件而失败

可能是间歇性的,可能取决于主机负载。

执行此操作时,我通常会生成一个阻止类似以下内容的函数: 采取,袜子

然后在袜子上调用你的循环,设置 active,once。

所以你将接收器更改为 spawn,设置 control_process 然后 Pid ! 采取,袜子

类似的东西。 注意:我不知道当您不是控制进程时 active,once 调用是否真的抛出,如果没有,那么我刚才所说的有道理。

【讨论】:

感谢您的回复!我最终尝试将我的 erlang 版本升级到 R14,并且在此过程中也升级到了 CentOS 6,之后问题没有再次出现。问题消失的唯一原因可能是由于新版本的 cpu 负载较少,一旦它获得更多负载,它会再次出现,但我无法让它再次发生。 原来升级并没有完全解决问题。但是,添加了这个小修复程序。谢谢! 如果有人检查此答案,ERL-90 包含有关此挂起的实际原因的更多信息。

以上是关于Erlang 套接字直到第二个 setopts active,once 才接收的主要内容,如果未能解决你的问题,请参考以下文章

Erlang:打印第一个参数和第二个参数之间的素数列表

为啥套接字关闭 Erlang

Flask_socketIO 不与第二个客户端通信

你能溢出 Erlang 进程的消息队列吗?

Libevent 仅在第二个 buffer_write 之后写入套接字

第二个 AsyncTask 未执行