Tornado循环引擎
可用版本自
起uWSGI 1.9.19-dev
支持挂起引擎: greenlet
支持CPython版本: 所有支持tornado的版本
tornado循环引擎允许你将你的uWSGI栈与Tornado IOLoop类集成。
基本上,服务器的每一个I/O操作会被映射到一个tornado IOLoop回调。进行RPC、远程缓存或者简单的写入响应则由Tornado引擎管理。
由于uWSGI不是用一个基于回调的编程方法写的,因此与那种类型的库集成需要某些类型“挂起”引擎 (绿色线程/协程)
目前唯一支持的挂起引擎是”greenlet”。Stackless python也能用 (需要测试)。
当前并不支持PyPy (尽管因为continulet,技术上是可行的)。如果你感兴趣的话,给Unbit员工发邮件吧。
为什么?
Tornado项目自己包含了一个简单的WSGI服务器。在于Gevent插件相同的思想下,Loop引擎的目的是允许外部项目使用(尽情使用)uWSGI api,从而获得更好的性能、多功能性和 (也许是最重要的) 资源使用。
在你的tornado应用中,可以使用所有的uWSGI子系统 (从缓存,到websockets,到度量),而WSGI引擎则是其中一个久经考验的uWSGI。
安装
当前默认不内置tornado插件。要在单个二进制文件中同时拥有tornado和greenlet,你可以这样
- UWSGI_EMBED_PLUGINS=tornado,greenlet pip install tornado greenlet uwsgi
或者 (来自uWSGI源代码,如果你已经安装了tornado和greenlet的话)
- UWSGI_EMBED_PLUGINS=tornado,greenlet make
运行之
—tornado
选项是由tornado插件公开的,允许你设置最佳参数:
- uwsgi --http-socket :9090 --wsgi-file myapp.py --tornado 100 --greenlet
这将会在http端口9090上运行一个uWSGI实例,使用tornado作为I/O(和时间)管理,greenlet作为挂起引擎
会分配100个异步核心,允许你管理多达100个并发请求
集成WSGI和tornado api
出于WSGI的工作方式,处理基于回调的编程是相当难的 (如果有可能的话)。
有了greenlet,我们可以挂起我们的WSGI可调用的执行,直到一个tornado IOLoop事件可用:
- from tornado.httpclient import AsyncHTTPClient
- import greenlet
- import functools
- # this gives us access to the main IOLoop (the same used by uWSGI)
- from tornado.ioloop import IOLoop
- io_loop = IOLoop.instance()
- # this is called at the end of the external HTTP request
- def handle_request(me, response):
- if response.error:
- print("Error:", response.error)
- else:
- me.result = response.body
- # back to the WSGI callable
- me.switch()
- def application(e, sr):
- me = greenlet.getcurrent()
- http_client = AsyncHTTPClient()
- http_client.fetch("http://localhost:9191/services", functools.partial(handle_request, me))
- # suspend the execution until an IOLoop event is available
- me.parent.switch()
- sr('200 OK', [('Content-Type','text/plain')])
- return me.result
欢迎来到回调地狱
一如既往,判断编程方法并非uWSGI的工作。它是为系统管理员提供的工具,而系统管理员应该宽容开发者的选择。
使用这个方法,你将很快体验到的事情之一是回调地狱。
让我们扩展前面的例子,在发送响应回客户端之前等待10秒
- from tornado.httpclient import AsyncHTTPClient
- import greenlet
- import functools
- # this gives us access to the main IOLoop (the same used by uWSGI)
- from tornado.ioloop import IOLoop
- io_loop = IOLoop.instance()
- def sleeper(me):
- #TIMED OUT
- # finally come back to WSGI callable
- me.switch()
- # this is called at the end of the external HTTP request
- def handle_request(me, response):
- if response.error:
- print("Error:", response.error)
- else:
- me.result = response.body
- # add another callback in the chain
- me.timeout = io_loop.add_timeout(time.time() + 10, functools.partial(sleeper, me))
- def application(e, sr):
- me = greenlet.getcurrent()
- http_client = AsyncHTTPClient()
- http_client.fetch("http://localhost:9191/services", functools.partial(handle_request, me))
- # suspend the execution until an IOLoop event is available
- me.parent.switch()
- # unregister the timer
- io_loop.remove_timeout(me.timeout)
- sr('200 OK', [('Content-Type','text/plain')])
- return me.result
这里,我们链接了两个回调,最后一个负责将控制权交还WSGI可调用
代码可能看起来丑或者过于复杂 (与其他诸如gevent的方法相比),但是,这基本上是提高并发性最有效的方法 (同时在内存使用和性能方面)。诸如node.js这样的技术由于它们允许完成的结果,它们正变得流行起来。
WSGI生成器 (aka yield all over the place)
以下面的WSGI应用为例:
- def application(e, sr):
- sr('200 OK', [('Content-Type','text/html')])
- yield "one"
- yield "two"
- yield "three"
如果你已经使用uWSGI异步模式,那么你就会知道每次yield内部调用使用的挂起引擎 (在我们的例子中,是greenlet.switch())。
那意味着,我们在调用”application()”后会立即进入tornado IOLoop引擎。如果我们不在等待事件,那么能如何将控制权交还给我们的可回调对象?
已扩展uWSGI异步API来支持”schedule_fix”钩子。它允许你在调用挂起引擎后立即调用一个钩子。
在tornado这种情况下,这个钩子会被映射到某些像这样的东东:
- io_loop.add_callback(me.switch)
通过这种方式,在每次yield之后,一个me.switch()函数就会被调用,从而让可回调对象恢复。
有了这个钩子,你可以透明地托管标准的WSGI应用,而无需更改它们。
绑定和监听Tornado
在每一个worker中,在fork()之后会执行Tornado IOLoop。如果你想把Tornado绑定到网络地址上,那么记得为每个worker使用不同的端口:
- from uwsgidecorators import *
- import tornado.web
- # this is our Tornado-managed app
- class MainHandler(tornado.web.RequestHandler):
- def get(self):
- self.write("Hello, world")
- t_application = tornado.web.Application([
- (r"/", MainHandler),
- ])
- # here happens the magic, we bind after every fork()
- @postfork
- def start_the_tornado_servers():
- application.listen(8000 + uwsgi.worker_id())
- # this is our WSGI callable managed by uWSGI
- def application(e, sr):
- ...
记住:不要启动IOLoop类。一旦安装完成,uWSGI将会自己启动它。