我要解决的问题:拥有一个侦听特定端口的 Erlang TCP 服务器(代码应驻留在某种面向外部的接口/API 中),并且每个传入连接gen_server
都应gen_tcp:accept
由) gen_server
,但我实际上并不想最初生成预定义数量的接受传入连接的进程)。这有可能吗?
4 回答
基本程序
您应该有一个执行以下过程的静态进程(实现为gen_server
自定义进程或自定义进程):
- 使用监听传入连接
gen_tcp:accept/1
- 每次它返回一个连接时,告诉主管产生一个工作进程(例如另一个
gen_server
进程) - 获取此进程的 pid
gen_tcp:controlling_process/2
使用新返回的套接字和该 pid调用- 将套接字发送到该进程
注意:您必须按此顺序执行此操作,否则新进程可能会在所有权移交之前使用套接字。如果不这样做,当新进程已经接管时,旧进程可能会收到与套接字相关的消息,从而导致丢弃或错误处理的数据包。
监听过程应该只有一个责任,那就是为新的连接产生工人。这个过程在调用时会阻塞gen_tcp:accept/1
,这很好,因为启动的工作人员将同时处理正在进行的连接。接受阻塞可确保启动新连接时的最快响应时间。如果该过程需要在其间做其他事情,gen_tcp:accept/2
则可以与在超时之间交错的其他操作一起使用。
缩放
您可以让多个进程
gen_tcp:accept/1
在单个侦听套接字上等待,从而进一步提高并发性并最大限度地减少接受延迟。另一个优化是预先启动一些套接字工作程序,以在接受新套接字后进一步减少延迟。
proc_lib
第三也是最后一点,通过使用(更多信息)在您自己的自定义流程中实施 OTP 设计原则,使您的流程更加轻量级。但是,只有在您进行基准测试并得出结论是这种gen_server
行为会减慢您的速度时,您才应该这样做。
问题gen_tcp:accept
在于它会阻塞,因此如果您在 agen_server
中调用它,则会阻止服务器接收其他消息。您可以尝试通过超时来避免这种情况,但这最终相当于一种最好避免的轮询形式。相反,您可以尝试使用 Kevin Smith 的 gen_nb_server;它使用内部未记录的函数prim_inet:async_accept
和其他prim_inet
函数来避免阻塞。
您可能想查看http://github.com/oscarh/gen_tcpd并使用 handle_connection 函数将您获得的进程转换为 gen_server。
正如史蒂夫所说,您应该使用“prim_inet:async_accept(Listen_socket,-1)”。现在传入连接将被您的 handle_info 回调接受(假设您的接口也是 gen_server),因为您使用了异步接受调用。
接受连接后,您可以生成另一个 ger_server(我会推荐 gen_fsm),并通过调用“gen_tcp:controlling_process(CliSocket,spwned 进程的 Pid)”将其作为“控制进程”。
在此之后,来自套接字的所有数据都将由该进程而不是您的接口代码接收。就像那样,将为另一个连接产生一个新的控制过程。