我要解决的问题:拥有一个 Erlang TCP 服务器,监听特定端口(代码应位于某种外部接口/API中),每个传入连接都应由 gen_server 处理(即 gen_tcp:accept 应编写在 gen_server 内部),但我实际上不想最初生成预定义数量的进程来接受传入连接。这是否可能?
您应该有一个静态进程(实现为一个 gen_server
或自定义进程),执行以下过程:
gen_tcp:accept/1
监听传入连接gen_server
进程)gen_tcp:controlling_process/2
注意: 必须按照以上顺序执行,否则新进程可能在所有权移交之前使用该 socket。如果不这样做,则当新进程已经接管时,旧进程可能会收到与 socket 相关的消息,导致数据包丢失或处理不当。
监听进程应该只有一个职责,即为新连接生成工作进程。此进程在调用 gen_tcp:accept/1
时将被阻塞,这是可以接受的,因为已启动的工作进程将同时处理进行中的连接。阻塞在 accept 上可以确保在初始化新连接时获得最快的响应时间。如果进程需要在其中执行其他操作,可以使用 gen_tcp:accept/2
并在超时之间交替执行其他操作。
您可以在单个侦听 socket 上使用多个使用 gen_tcp:accept/1
等待的进程,进一步提高并发性并最小化 accept 的延迟。
另一个优化是预先启动一些 socket 工作进程以进一步减少接受新套接字之后的延迟。
第三点,可以通过使用 proc_lib
实现 OTP 设计原则来使您的进程更加轻量级 (了解更多)。但是,只有在基准测试并得出结论是 gen_server
行为影响性能时,才应该采用这种方式。
gen_tcp:accept
的问题在于它会阻塞,因此如果你在 gen_server
中调用它,你就会阻塞服务器无法接收其他消息。你可以尝试通过传递超时时间来避免这种情况,但这最终会变成一种轮询形式,最好避免使用。相反,你可以尝试使用 Kevin Smith's gen_nb_server;它使用了一个内部未记录的函数 prim_inet:async_accept
和其他 prim_inet
函数来避免阻塞。
gen_tcp:accept
转发到自己的 gen_server 的方法,那么我就不会在意它是否阻塞。但我猜想无法事先知道(比如从 gen_tcp:listen
)是否有一个需要接受的传入连接。感谢指向 gen_nb_server
!你在生产中使用过它吗? - hyperboreeangen_nb_server
来完成你的任务。你的情况非常简单。 - Adam Lindberggen_nb_server
,但已经在生产中使用了 prim_inet:async_accept
。 - Steve Vinoskigen_nb_server
,因为原问题中要求使用 gen_server
。我同意使用 proc_lib
进程可能更好、更容易;这正是我们在 Yaws 中生成接收器的方式。 - Steve Vinoski