当用户中止连接时执行某些操作 (Sinatra + Thin)

3
我正在编写一个应用程序,有时需要进行非常长时间的数据库请求。如果客户端重新加载或关闭页面,我想执行一些代码以处理数据库请求。
我希望Rack会有钩子来处理这种情况,但据我所见,这比Rack更深入一层。
到目前为止,我唯一能找到的钩子是通过猴子补丁thin Connection类中的unbind函数来实现的。
module Thin
  class Connection < EventMachine::Connection

    def unbind

      # DO something here

      @request.async_close.succeed if @request.async_close
      @response.body.fail if @response.body.respond_to?(:fail)
      @backend.connection_finished(self)
    end
  end
end

这将重写Thin的解除绑定函数,并让我进入EventMachine调用的断开连接。

是否有更好的方法?


2
你应该考虑将长时间运行的请求变成异步的。这样会更加清晰,也能解决你目前的问题。 - randomuser
我实际上正在使用EventMachine、em-synchrony和sinatra-synchrony以异步方式运行请求。然而,这仍然没有解决我的问题——无论请求是同步还是异步,我都想知道用户何时不再关心来自数据库的结果。 - Ben
据我所知,sinatra-synchrony仅并发运行请求。因此,我仍然认为@Gunjan的建议适用。 - Paulo Casaretto
正如我之前提到的,我的问题与请求是否同步无关。问题在于某些数据库查询非常庞大,可能会使数据库无法使用,如果用户离开页面,我需要能够终止它们。无论数据库请求是否异步执行,这个问题都存在。 - Ben
你可以使用上面的async_close方法。例如,可以查看Sinatra::ExtendedRack#setup_close - Andrea Amantini
如果您使用 Sinatra::Helpers::Streaming 响应,您可以设置回调函数,在上述 async_close 调用时被调用(延迟)。这很方便,可以在客户端断开连接时进行一些清理工作(例如在 JavaScript 端的 EventSource#close)。 - Andrea Amantini
1个回答

1

经过一番搜索,我发现Thin提供了一种替换“后端”的机制,即服务器如何连接客户端。我将其与rack env中的值相结合,处理特定请求实例并知道是否需要终止查询:

class Backend < Thin::Backends::TcpServer

  def initialize(host, port, options={})
    super(host, port)
  end

  def connection_finished(connection)
    super(connection)

    if connection.request.env["query_killer"]
      connection.request.env["query_killer"].kill
    end

  end

end

这可以通过命令行参数包含到thin中:

thin start  -r 'my_module/backend' --backend MyModule::Backend

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接