2016-02-29 28 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