如何在 gen_tcp:accept 上调用(和睡眠)并同时处理系统消息?

Posted

技术标签:

【中文标题】如何在 gen_tcp:accept 上调用(和睡眠)并同时处理系统消息?【英文标题】:How to call (and sleep) on gen_tcp:accept and still process system messages at the same time? 【发布时间】:2012-05-25 05:02:45 【问题描述】:

我对 erlang/otp 世界还是有点陌生​​,所以我想这是一个非常基本的问题。不过,我想知道执行以下操作的正确方法是什么。

目前,我有一个高级主管的申请。后者将监督调用 gen_tcp:accept 的工作人员(睡在上面),然后为每个接受的连接生成一个进程。注意:对于这个问题,listen() 在哪里完成是无关紧要的。

我的问题是关于使这些工作人员(在 gen_tcp:accept 上休眠的工作人员)尊重 otp 设计原则的正确方法,以便他们可以处理系统消息(处理关闭、跟踪等),根据我在这里读到的内容:http://www.erlang.org/doc/design_principles/spec_proc.html

所以,

是否可以为此使用 gen_fsm 或 gen_server 等可用行为之一?我的猜测是否定的,因为阻塞调用 gen_tcp:accept/1。是否仍然可以通过指定接受超时来做到这一点?如果是这样,我应该把 accept() 调用放在哪里? 或者我应该像上面链接中的示例一样从头开始编码(即:不使用现有行为)?在这种情况下,我想到了一个调用 gen_tcp:accept/2 而不是 gen_tcp:accept/1 的主循环(即:指定超时),然后立即编写一个接收块,以便我可以处理系统消息。这是正确的/可以接受的吗?

提前致谢:)

【问题讨论】:

【参考方案1】:

由于 Erlang 是事件驱动的,处理像 accept/1,2 那样阻塞的代码很尴尬。

就我个人而言,我会有一个主管,其中有一个 gen_server 用于监听器,另一个主管用于接受工作人员。 Handroll an accept worker 超时(gen_tcp:accept/2),有效地轮询(尴尬的部分),而不是接收状态消息。

这样,如果一个工人死亡,它会被它上面的主管重新启动。 如果侦听器死了,它会重新启动,但不会在重新启动依赖于该侦听器的工作树和主管之前。 当然,如果最高主管死了,它会重新启动。 但是,如果您在树上supervisor:terminate_child/2,那么您可以有效地禁用该套接字的侦听器和所有接受器。之后supervisor:restart_child/2可以重启整个listener+acceptor worker pool。

如果您希望应用为您管理此问题,cowboy 实现了上述功能。尽管是面向 http 的,但它很容易支持自定义处理程序来代替要使用的任何协议。

【讨论】:

感谢您的资源。 ranch_acceptor(来自牛仔源)不应该在其主循环中处理系统消息,以尊重 OTP 原则并能够受到监督吗? @marcelog ranch_acceptor 受到监督,请参阅 ranch_acceptors_sup。通过模块和主管,处理接受者进程的系统消息。【参考方案2】:

我实际上在另一个问题中找到了答案:Non-blocking TCP server using OTP principles,这里是http://20bits.com/article/erlang-a-generalized-tcp-server

编辑:对我有帮助的具体答案是:https://***.com/a/6513913/727142

【讨论】:

这两个答案都不能解决所描述的问题。两者都使用接收系统消息的gen_server,但都将accept“工作”放在工作进程之外。 trapexit 文章使用 prim_inet:accept,这是一个内部的、未记录的 API(如有更改,恕不另行通知)。 20 位文章通过proc_lib:spawn 生成了一个手动接受(添加了一个包装器,但没有解决 问题)。两者都避免了gen_tcp:accept 的阻塞性质,但释放了对accept 的控制并需要清理(分别管理生成的进程和必须处理消息)。 它确实回答了我的问题,它解释了如何编写一个特殊的过程,该过程使用带有超时的接受,然后处理系统消息,并且不使用未记录的功能。我不是在问如何编写非阻塞接受,也没有产生另一个进程来接受接受。答案中发布的描述特殊过程的代码是我在问题中询问的可能性之一 我没有看到 accept 在 20bits 或 trapexit 文章中与超时一起使用。您能否更新您的答案以显示您找到的使用超时的链接?我很想看到另一种方法,尤其是符合 spec_proc 的版本! @mlb:完成,我已将链接添加到该问题中的特定答案【参考方案3】:

您可以将其设置为类似于此的 gen_server:https://github.com/alinpopa/qerl/blob/master/src/qerl_conn_listener.erl。

如你所见,这个进程正在做 tcp accept 并处理其他消息(例如 stop(Pid) -> gen_server:cast(Pid,close)。)

HTH, 阿林

【讨论】:

以上是关于如何在 gen_tcp:accept 上调用(和睡眠)并同时处理系统消息?的主要内容,如果未能解决你的问题,请参考以下文章

在 Erlang 中通过 RPC 在远程节点上创建套接字时,无法接受套接字上的连接

Erlang TCP 服务器总是在接受时关闭

将erlang套接字存储到数据库

Erlang简单并行server

TCP 套接字关闭但未被进程检测到

Erlang 建立TCP连接后,怎么样从返回的Socket获取客户端ip和端口