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 才接收的主要内容,如果未能解决你的问题,请参考以下文章