【发布时间】:2011-07-23 16:37:49
【问题描述】:
我要解决的问题:拥有一个侦听特定端口的 Erlang TCP 服务器(代码应驻留在某种面向外部的接口/API 中),并且每个传入连接应由 gen_server 处理(即甚至gen_tcp:accept 也应该在gen_server 中编码),但我实际上并不想最初生成预定义数量的接受传入连接的进程)。这有可能吗?
【问题讨论】:
我要解决的问题:拥有一个侦听特定端口的 Erlang TCP 服务器(代码应驻留在某种面向外部的接口/API 中),并且每个传入连接应由 gen_server 处理(即甚至gen_tcp:accept 也应该在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 的指针!你在生产中使用过吗?
gen_nb_server 来完成您的工作。你的案子很简单。
gen_nb_server,但在生产中使用过prim_inet:async_accept。
gen_nb_server,因为原始问题将gen_server 列为要求。我同意使用proc_lib 过程可能会更好、更容易;这正是我们在Yaws 中生成接受者的方式。
您应该使用 Steve 所说的“prim_inet:async_accept(Listen_socket, -1)”。 现在传入的连接将被您的 handle_info 回调接受 (假设您的接口也是 gen_server)因为您使用了异步 接听电话。
在接受连接后,您可以生成另一个 ger_server(我建议 gen_fsm) 并通过调用将其作为“控制过程” "gen_tcp:controlling_process(CliSocket, 生成进程的Pid)"。
在此之后,该进程将接收来自套接字的所有数据 而不是通过您的界面代码。像那样一个新的控制过程 将产生另一个连接。
【讨论】:
您应该有一个执行以下过程的静态进程(实现为gen_server 或自定义进程):
gen_tcp:accept/1 监听传入连接
gen_server 进程)gen_tcp:controlling_process/2
注意:您必须按此顺序进行操作,否则新进程可能会在所有权移交之前使用套接字。如果不这样做,当新进程已经接管时,旧进程可能会收到与套接字相关的消息,从而导致丢弃或错误处理的数据包。
监听进程应该只负责一项职责,那就是为新连接生成工作线程。这个进程在调用gen_tcp:accept/1 时会阻塞,这很好,因为启动的worker 将同时处理正在进行的连接。接受阻塞可确保启动新连接时的最快响应时间。如果进程需要在中间做其他事情,gen_tcp:accept/2 可以与其他在超时之间交错的操作一起使用。
您可以让多个进程在单个侦听套接字上使用 gen_tcp:accept/1 等待,从而进一步提高并发性并最大限度地减少接受延迟。
另一个优化是预先启动一些套接字工作程序,以进一步减少接受新套接字后的延迟。
第三个也是最后一个,通过使用 proc_lib (more info) 在您自己的自定义流程中实施 OTP 设计原则,使您的流程更加轻量级。但是,只有在您进行基准测试并得出结论是 gen_server 行为会减慢您的速度时,您才应该这样做。
【讨论】:
您可能想查看http://github.com/oscarh/gen_tcpd 并使用handle_connection 函数将您获得的进程转换为gen_server。
【讨论】: