简体   繁体   English

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

[英]Cowboy/Ranch kills handler process when client closes connection

I have Phoenix application with complex business logic behind HTTP endpoint.我在 HTTP 端点后面有具有复杂业务逻辑的 Phoenix 应用程序。 This logic includes interaction with database and few external services and once request processing has been started it must not be interrupted till all operations will be done.该逻辑包括与数据库和少量外部服务的交互,一旦请求处理开始,在所有操作完成之前不得中断。

But it seems like Cowboy or Ranch kills request handler process (Phoenix controller) if client suddenly closes connection, which leads to partially executed business process.但是,如果客户端突然关闭连接,这似乎会导致 Cowboy 或 Ranch 杀死请求处理程序进程(Phoenix 控制器),从而导致部分执行的业务流程。 To debug this I have following code in controller action:为了调试这个,我在 controller 操作中有以下代码:

Process.flag(:trap_exit, true)

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

And to simulate connection closing I set timeout: curl --request POST 'http://localhost:4003' --max-time 3 .为了模拟连接关闭,我设置了超时: curl --request POST 'http://localhost:4003' --max-time 3 After 3 seconds in IEx console I see that process is about to exit: Message: {:EXIT, #PID<0.4196.0>, :shutdown} .在 IEx 控制台 3 秒后,我看到该进程即将退出: Message: {:EXIT, #PID<0.4196.0>, :shutdown}

So I need to make controller complete its job and reply to client if it is still there or do nothing if connection is lost.所以我需要让 controller 完成它的工作并在它仍然存在时回复客户端,或者如果连接丢失则什么也不做。 Which will be the best way to achieve this:这将是实现这一目标的最佳方法:

  • trap exits in controller action and ignore exit messages;在 controller 操作中捕获退出并忽略退出消息;
  • spawn not linked Task in controller action and wait for its results;在 controller 动作中生成未链接的Task并等待其结果;
  • somehow configure Cowboy/Ranch so it will not kill handler process, if it is possible (tried exit_on_close with no luck)?以某种方式配置 Cowboy/Ranch 以便它不会杀死处理程序进程,如果可能的话(尝试exit_on_close没有运气)?

Handling processes will be killed after the request end, that is their purpose.处理进程将在请求结束后被杀死,这就是它们的目的。 If you want to process some data in the background, then start additional process.如果要在后台处理一些数据,则启动附加进程。 The simplest way to do so would be 2nd method you have proposed, but with slight modification of using Task.Supervisor .最简单的方法是您提出的第二种方法,但对使用Task.Supervisor稍作修改。

So in your application supervisor you start Task.Supervisor with name of your choice:因此,在您的应用程序主管中,您可以使用您选择的名称启动Task.Supervisor

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

Supervisor.start_link(children, strategy: :one_for_one)

And then in your request handler:然后在您的请求处理程序中:

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

That way you do not need to worry about handling situation when user is no longer there to receive message.这样您就不必担心当用户不再在那里接收消息时的处理情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM