Python Tornado - 实现POST立即返回,异步函数继续执行

8 投票
2 回答
2229 浏览
提问于 2025-04-16 05:03

我有一个处理程序,如下所示:

class PublishHandler(BaseHandler):

    def post(self):
        message = self.get_argument("message")
        some_function(message)
        self.write("success")

我遇到的问题是,some_function()这个函数执行起来需要一些时间。我希望在调用这个POST请求时,能够立即返回,而不是等这个函数执行完再返回。如果可以的话,我想让some_function()在另一个线程或进程中执行。

我使用的是berkeley db作为数据库,我想做的事情相对简单。

我有一个用户数据库,每个用户都有一个过滤器。如果过滤器匹配了消息,服务器就会把消息发送给这个用户。目前我在测试成千上万的用户,因此每次通过POST请求发布消息时,服务器都要遍历成千上万的用户来寻找匹配。这是我比较简单的实现方式,所以我想问,怎样才能做得更好呢?

2 个回答

1

我试过这个,我觉得在回调函数被调用之前,请求并没有完成。

我觉得一个不太好的解决办法是调用两层的add_callback,比如:

  def get(self):
    ...
    def _defered():
      ioloop.add_callback(<whatever you want>)
    ioloop.add_callback(_defered)
    ...

不过这些方法充其量也只是权宜之计。我现在在寻找更好的解决方案,可能最后会用到消息队列或者简单的线程解决方案。

7

你可以试着用你的 IOLoopadd_callback 方法来实现这个功能,像这样:

loop.add_callback(lambda: some_function(message))

Tornado 会在下一个 IOLoop 的循环中执行这个回调,这样可能会让请求在执行这段代码之前完成(我得深入研究 Tornado 的内部机制才能确定,或者直接测试一下)。

不过,这样的缺点是你写的那段耗时的代码还是需要时间来执行,这可能会阻塞其他请求。如果同时有很多请求进来,这就不太好了。

更稳妥的解决办法是把它放在一个单独的线程或进程中运行。对于 Python 来说,使用进程是最好的选择,因为有个叫 GIL 的东西(如果你不太了解这个,建议你查一下)。不过,如果你是在单核处理器的机器上,使用线程也能正常工作,而且可能更简单。

如果你选择使用线程,可以构建一个不错的“异步执行器”模块,里面包含一个互斥锁、一个线程和一个队列。如果你想用单独的进程,可以看看 multiprocessing 模块。

撰写回答