2014-09-30 48 views
6

我有一个烧瓶应用程序,它将给定的URL连接到外部服务(具有不同但通常较长的响应时间)并在那里搜索某些内容。之后,对检索到的数据有一些CPU繁重的操作。这也需要一些时间。烧瓶和/或龙卷风 - 处理耗时的外部网络服务调用

我的问题:来自外部的响应可能需要一些时间。你不能做太多的事情,但是当你一次有多个请求时它就会成为一个大问题 - 对外部服务的请求会阻塞线程,其余的都在等待。

明显浪费时间,它会杀死应用程序。

我听说过这个叫做Tornado的异步库。并有我的问题:

  1. 这是否意味着它可以设法处理多个请求,并在外部响应后立即触发回调?
  2. 我可以用我当前的烧瓶应用程序(可能不是因为我猜WSGI?)实现吗?或者我需要将整个应用程序重写为Tornado?
  3. 那些CPU繁重的操作 - 会阻挡我的线程吗?无论如何,做一些负载平衡是个好主意,但我很好奇Tornado是如何处理这个问题的。
  4. 可能的陷阱,陷阱?

回答

5

内置于烧瓶中的Web服务器并不打算在生产环境中使用,这正是您列出的原因 - 它是单线程的,并且如果有任何请求阻塞时间不长,容易陷入困境。烧瓶文件lists several options for deploying it in a production environment; mod_wsgi,gunicorn,uSWGI等。所有这些部署选项都提供了通过线程,进程或非阻塞I/O来处理并发的机制。但是,请注意,如果您正在执行CPU绑定操作,唯一会提供真正并发性的选项是使用多个进程。

如果要使用tornado,则需要用tornado样式重写应用程序。由于其架构基于显式异步I/O,因此如果将其部署为WSGI应用程序,则无法使用其异步功能。 “tornado风格”基本上意味着对所有I/O操作使用非阻塞API,并使用子进程处理任何长时间运行的CPU绑定操作。该tornado文档介绍了如何进行异步I/O调用,但这里是它如何工作的一个基本的例子:

from tornado import gen 

@gen.coroutine 
def fetch_coroutine(url): 
    http_client = AsyncHTTPClient() 
    response = yield http_client.fetch(url) 
    return response.body 

response = yield http_client.fetch(curl)调用实际上是异步的;它将在请求开始时将控制权返回给龙卷风事件循环,并且一旦收到响应就会再次恢复。这允许多个异步HTTP请求同时运行,全部在一个线程内运行。但请注意,fetch_coroutine中的任何内容不是异步I/O将阻止事件循环,并且在代码运行时不能处理其他请求。

要处理长时间运行的CPU绑定操作,您需要将工作发送到子进程以避免阻塞事件循环。对于Python,通常意味着使用multiprocessingconcurrent.futures。我想看看this question以获取更多关于如何最好地将这些库与tornado进行整合的信息。请注意,您不希望维护大于系统中CPU数量的进程池,因此请考虑在指定时间的情况下,您希望在任何给定时间运行多少个并发CPU限制操作将其扩展到单台机器之外。

龙卷风文档has a section dedicated to running behind a load balancer也是如此。他们建议为此使用NGINX。

1

龙卷风似乎比Flask更适合这项工作。在tornado.ioloop的实例中运行的Tornado.web.RequestHandler的子类应该为您提供非阻塞请求处理。我期望它看起来像这样。

import tornado 
import tornado.web 
import tornado.ioloop 
import json 

class handler(tornado.web.RequestHandler): 
    def post(self): 
     self.write(json.dumps({'aaa':'bbbbb'})) 


if __name__ == '__main__': 
    app = tornado.web.Application([('/', handler)]) 
    app.listen(80, address='0.0.0.0') 
    loop = tornado.ioloop.IOLoop.instance() 
    loop.start() 

,如果你想你的帖子的处理程序是异步的,你可以用tornado.gen.coroutine与“AsyncHTTPClient or grequests`装饰。这会给你无阻塞的请求。尽管我并不完全确定,但你也可能把你的计算放在协同程序中。

+0

只要将'@ tornado.gen.coroutine'装饰器添加到处理程序中,不会使其异步。你必须在处理程序中实际进行非阻塞调用。如果你在函数中做了任何阻塞操作,无论你是否使用'coroutine'装饰器,整个事件循环都会被阻塞。 – dano 2014-10-01 01:05:42

+0

你是对的,我的错。你可以使用grequests来获得异步请求吗? – ragingSloth 2014-10-01 01:50:04

+0

'grequests'使用'gevent'来提供异步I/O,是的。如果你使用'tornado',你可能会想使用它的内置'AsyncHTTPClient',因为它与龙卷风事件循环集成在一起。 – dano 2014-10-01 02:28:58