为什么Tornado的WSGI支持阻止多个请求?

时间:2016-02-29 15:01:39

标签: python-3.x 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 个答案:

答案 0 :(得分:1)

Tornado' WSGIContainer并非专为高流量使用而设计。请参阅its docs中的警告:

  

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

通常,最好在单独的进程中运行WSGI和Tornado应用程序,以便WSGI部分可以拥有专为WSGI设计的服务器。 WSGIContainer只应在有特定原因将它们组合到同一进程中时使用,然后应谨慎使用以避免阻塞事件循环太长时间。在Tornado原生的RequestHandler中尽可能做到最好,这样就可以使用协程而不是阻塞。