当客户端关闭连接时,Cowboy/Ranch 杀死处理程序进程

Posted

技术标签:

【中文标题】当客户端关闭连接时,Cowboy/Ranch 杀死处理程序进程【英文标题】:Cowboy/Ranch kills handler process when client closes connection 【发布时间】:2021-07-05 08:17:28 【问题描述】:

我的 Phoenix 应用程序在 HTTP 端点后面具有复杂的业务逻辑。该逻辑包括与数据库和少量外部服务的交互,一旦请求处理开始,在所有操作完成之前不得中断。

但是,如果客户端突然关闭连接,这似乎会导致 Cowboy 或 Ranch 杀死请求处理程序进程(Phoenix 控制器),从而导致部分执行的业务流程。为了调试这个,我在控制器操作中有以下代码:

Process.flag(:trap_exit, true)

receive do
  msg -> Logger.info("Message: #inspect msg")
after
  10_000 -> Logger.info("Timeout")
end

为了模拟连接关闭,我设置了超时:curl --request POST 'http://localhost:4003' --max-time 3。 在 IEx 控制台 3 秒后,我看到该进程即将退出:Message: :EXIT, #PID<0.4196.0>, :shutdown

所以我需要让控制器完成它的工作并在它仍然存在时回复客户端,或者如果连接丢失则什么都不做。这将是实现这一目标的最佳方式:

在控制器操作中捕获退出并忽略退出消息; spawn 未在控制器操作中链接 Task 并等待其结果; 以某种方式配置 Cowboy/Ranch,使其不会杀死处理程序进程,如果可能的话(尝试 exit_on_close 没有运气)?

【问题讨论】:

【参考方案1】:

处理进程会在请求结束后被杀死,这就是它们的目的。如果要在后台处理一些数据,则启动附加进程。最简单的方法是您提出的第二种方法,但对使用 Task.Supervisor 稍作修改。

因此,在您的应用程序主管中,您可以使用您选择的名称开始 Task.Supervisor

children = [
  Task.Supervisor, name: MyApp.TaskSupervisor
]

Supervisor.start_link(children, strategy: :one_for_one)

然后在您的请求处理程序中:

parent = self()
ref = make_ref()

Task.Supervisor.start_child(MyApp.TaskSupervisor, fn() ->
  send(parent, ref, do_long_running_stuff())
end)

receive do
  ^ref, result -> notify_user(result)
end

这样您就不必担心当用户不再在那里接收消息时的处理情况。

【讨论】:

以上是关于当客户端关闭连接时,Cowboy/Ranch 杀死处理程序进程的主要内容,如果未能解决你的问题,请参考以下文章

当客户端被物理杀死时,总是无法从服务器端关闭通道(netty)

Gunicorn - 如果客户端关闭他们的连接,如何杀死工人?

当应用程序被杀死/关闭时,世博会通知不会触发方法

为啥我的客户端会杀死我的服务器?

如何在 Redis 上杀死 Node 打开的空闲客户端?

为啥当客户端关闭连接时我的简单 C 服务器退出?