tornado是异步Web服务器吗?
我正在学习如何写一个可以处理成千上万连接的后台服务器。
我看了一些示例代码,但发现它们还是用的同步逻辑。
比如说: (摘自 http://www.tornadoweb.org/en/stable/gen.html)
@gen.coroutine
def get(self):
http_client = AsyncHTTPClient()
response1, response2 = yield [http_client.fetch(url1), http_client.fetch(url2)]
print(response1.body, response2.body)
很明显,print
语句在获取两个请求的响应之前是无法执行的,否则会因为访问不存在的数据而抛出异常。
因此,在最后两行之间必须有一个阻塞,但是,阻塞不是tornado的特点,它强调的是非阻塞、异步和事件驱动……那么,它是怎么处理成千上万的连接的呢?
2 个回答
没错!
在处理长时间运行的任务和慢速输入输出(IO)操作时,使用 yield
和 coroutine
这两种方式很容易让人混淆。我刚刚在这方面犯了一个大错误。
在长时间运行的任务中
- 每次都会重复调用生成器的
next()
方法,并执行一小部分工作。 - 如果有多个
coroutine
同时运行,调度器会一个一个地调用每个next()
方法,这样就能在这些任务之间共享 CPU 时间。因此,这些任务之间是合作的,所以叫做coroutine
。
- 每次都会重复调用生成器的
在慢速 IO 操作中
- 每个
yield
点只会调用一次next()
方法。 - 一旦在进行 IO 操作时遇到
yield
,这个 IO 操作就交给操作系统内核处理。调度器会在 IO 操作完成后添加一个回调,这个回调会调用next()
方法。 - 现在,调度器要决定什么时候调用
next()
方法。这是由操作系统级别的异步功能来实现的,比如epoll
或IOCP
,它们会通知调度器 IO 操作何时完成。 - 整个流程是,运行到进行 IO 的那一点,然后
yield
以交出执行权。IO 完成后,调度器会通过调用next()
方法继续执行。 - 这种控制流的效果和回调模式是完全一样的,都是:
- 运行到进行 IO 的那一点
- 交出执行权,让进程可以做其他工作
- IO 完成后,继续运行。
- 唯一的区别是,一个是在原来的函数中继续执行,另一个是继续到一个新的回调函数。
- 每个
所以,总结一下:
在长时间运行的任务中,调度器会在进程空闲时调用每个
next()
方法。在慢速 IO 操作中,
next()
方法只会调用一次,当 IO 操作完成时。
我认为如果你明白这一点,就会理解使用 yield
和 coroutine
实际上可以和 callback
有相同的效果。
没错,tornado
是异步的。你提到的例子是一个 coroutine
(协程);它实际上是非阻塞的,并且在调用 yield
时会把控制权交回给 tornado 的事件循环。只有当两个 http_client.fetch
的调用都完成后,控制权才会返回到 get
函数。
这两个例子在 tornado
中实际上是功能上等价的:
class AsyncHandler(RequestHandler):
@asynchronous
def get(self):
http_client = AsyncHTTPClient()
http_client.fetch("http://example.com",
callback=self.on_fetch)
def on_fetch(self, response):
do_something_with_response(response)
self.render("template.html")
还有一个协程版本:
class GenAsyncHandler(RequestHandler):
@gen.coroutine
def get(self):
http_client = AsyncHTTPClient()
response = yield http_client.fetch("http://example.com")
do_something_with_response(response)
self.render("template.html")
协程让你可以写出看起来像是同步的异步代码,这样更容易阅读。当上面的代码执行到 yield
时,get
会暂停,并把 http_client.fetch
返回的 Future
对象交给 gen.coroutine
装饰器。这个 gen.coroutine
装饰器有一些神奇的功能,它会安排在 fetch
调用返回的 Future
准备好后,再把结果传回给 get
。