2016-02-29 26 views
0

想象一下以下龙卷风应用:为什么Tornado的WSGI支持会阻止多个请求?

import logging 
import time 

from tornado import gen, httpserver, ioloop, web, wsgi 


def simple_app(environ, start_response): 
    time.sleep(1) 
    status = "200 OK" 
    response_headers = [("Content-type", "text/plain")] 
    start_response(status, response_headers) 
    return [b"Hello, WSGI world!\n"] 

class HelloHandler(web.RequestHandler): 
    @gen.coroutine 
    def get(self): 
     yield gen.moment 
     self.write('Hello from tornado\n') 
     self.finish() 

def main(): 
    wsgi_app = wsgi.WSGIContainer(simple_app) 
    tornado_app = web.Application(
     [ 
      ('/tornado', HelloHandler), 
      ('.*', web.FallbackHandler, dict(fallback=wsgi_app)), 
     ], 
     debug=True, 
    ) 
    http_server = httpserver.HTTPServer(tornado_app) 
    http_server.listen(8888) 
    current_loop = ioloop.IOLoop.current() 
    current_loop.start() 


if __name__ == '__main__': 
    main() 

现在,如果你运行这个并试图让http://localhost:8888/龙卷风块,直到WSGI请求完成(一秒睡眠后在这里)。这件事我知道。但是,如果您一个接一个地发送很多请求,那么IOLoop可能会永久封锁。

我想这样的标杆:

$ ab -n 20 -c 2 localhost:8888 

在第二端我试图让其他网址:

$ curl http://localhost:8888/tornado 

我得到了非WSGI请求的响应直到所有其他并发的WSGI请求已完成。这仅适用于已删除yield gen.moment的情况。

任何人都可以解释这里发生了什么,以及如何防止Tornado阻止我的所有请求,而不仅仅是其中之一?

回答

1

Tornado的WSGIContainer不适用于高流量使用。请参阅its docs中的警告:

WSGI是一个同步接口,而Tornado的并发模型基于单线程异步执行。这意味着使用Tornado的WSGIContainer运行WSGI应用程序的可扩展性要低于在多线程WSGI服务器(如gunicorn或uwsgi)中运行相同的应用程序。只有在将Tornado和WSGI组合在一个超过减少的可伸缩性的同一过程中有益时才使用WSGIContainer。

通常,最好在不同的进程中运行WSGI和Tornado应用程序,以便WSGI部件可以有一个专为WSGI设计的服务器。 WSGIContainer只应在有特定原因将它们组合在同一过程中时使用,然后应谨慎使用以避免阻塞事件循环太长时间。最好在Tornado原生的RequestHandlers中尽可能多地执行,这样可以使用协程来代替阻塞。

+0

非常感谢。我将WSGI和Tornado结合起来可以带来一些好处,所以我希望通过稍后使用优先级队列来减少阻塞问题,以便在一次阻塞WSGI调用后,Tornado运行IOLoop,并且每个人都有机会在下一次之前进入中间层阻止WSGI呼叫。但是,如果Tornado没有运行IOLoop,只要有任何*未完成的WSGI请求,这将不起作用。 – Norman8054