异步和非阻塞I/O

实时web功能需要为每个用户提供一个长链接。在传统的同步web服务器中, 这意味着为每个用户提供一个线程,但这个成本是很昂贵的。

为了尽量减少并发链接带来的开销,Tornado使用单线程事件循环的方式。 这意味着所有的应用代码都需要是异步非阻塞的,因为在任意时间只有一 个操作在执行。

异步和非阻塞的关系是非常近且经常交换使用的,但他们并不是相同的意思

阻塞

一个函数在等待某些执行的返回值的时候会被 阻塞 。一个函数被阻塞有很多原因: 网络I/O,硬盘I/O,互斥锁等。事实上, 每个 函数在运行和使用CPU的时候或多或少 会被阻塞(举个极端的例子来说明为什么对待CPU阻塞要和对待一般阻塞一样的认真, 例如hash加密 bcrypt, 需要消耗几百毫秒的CPU 时间, 而这已经超过一般的网络或者磁盘I/O消耗的时间了)。

一个函数可以在某些方面阻塞另外一些方面阻塞。在Tornado中,我们一般讨论的阻塞指的是网络环境下的I/O阻塞,尽管所有的阻塞都被尽量减少了。

异步

异步 函数在会在完成之前返回,在应用中触发下一个动作之前通常会在后台执行一些工作(和正常的 同步 函数在返回前就执行完所有的事情不同)。这里列 举了几种风格的异步接口:

  • 回调参数
  • 返回一个占位符 (Future, Promise, Deferred)
  • 将等待执行的动作添加到异步队列(例如 celery)
  • 注册回调函数 (例如 POSIX signals)

不论使用哪种类型的接口, 按照定义 异步函数与它们的调用者都有着不同的交互方式;也没有什么对调用者透明的方式使得同步函数异步(类似 `gevent<http://www.gevent.org>`_ 使用轻量级线程的系统性能虽然堪比异步系统,但它们并没有真正的让事情异步).

Tornado中的异步操作通常返回占位符对象(Futures), 例外的是像 IOLoop 这样的底层组件使用回调。 Futures 通常用 await or yield 关键字转换成结果.

例子

一个简单的同步函数:

from tornado.httpclient import HTTPClient

def synchronous_fetch(url):
    http_client = HTTPClient()
    response = http_client.fetch(url)
    return response.body

把上面的例子的同步函数用原生协程重写:

from tornado.httpclient import AsyncHTTPClient

async def asynchronous_fetch(url):
    http_client = AsyncHTTPClient()
    response = await http_client.fetch(url)
    return response.body

或者与旧版本的Python兼容, 使用 tornado.gen 模块:

from tornado.httpclient import AsyncHTTPClient
from tornado import gen

@gen.coroutine
def async_fetch_gen(url):
    http_client = AsyncHTTPClient()
    response = yield http_client.fetch(url)
    raise gen.Return(response.body)

协程是有点神奇,但它们在内部是这样实现的

from tornado.concurrent import Future

def async_fetch_manual(url):
    http_client = AsyncHTTPClient()
    my_future = Future()
    fetch_future = http_client.fetch(url)
    def on_fetch(f):
        my_future.set_result(f.result().body)
    fetch_future.add_done_callback(on_fetch)
    return my_future

请注意,协程在执行结束之前返回其 Future。 这是协程 异步 的原因。

你可以用协同程序做任何事情,你也可以通过传递回调对象来达到目的,但协程提供了重要的手段,让你以类似同步代码的方式来组织代码。 这对于错误处理尤为重要 ,因为 try /except 块通过协程可以像你期望的那样正常工作,而这很难通过回调实现。 协程将在指南的下一节深入讨论。