2.2、User’s guide (Asynchronous a
2018-03-28 本文已影响26人
宝宝家的隔壁老王
Asynchronous and non-Blocking I/O
实时网络功能需要保持每个用户的长链接,传统的同步 web 服务器,这意味着给每个用户分配一个线程,代价是昂贵的。
为了将并发连接的成本降到最低,tornado 使用单线程事件循环。这意味着所有应用程序代码应该是异步非阻塞的,因为一次只能激活一个操作。
异步和非阻塞是密切相关的,通常可以互换使用,但是他们是不一样的。
Blocking
当一个函数在 return 前等待其他事情的发生时,就会阻塞。一个函数阻塞原因有很多:网络 I/O, 磁盘 I/O, 互斥锁等。事实上,每个函数在运行并且使用 CPU 的时候都会有一点阻塞(相比网络或磁盘访问可忽略不计)。
一个函数可以在某方面阻塞然后再另外方面不阻塞。在 tornado 背景下,我们通常谈论的是网络 I/O 阻塞,尽管各种阻塞都会被最小化。
Asynchronous
异步函数在它完成前就 return 了,在触发应用的未来事件时通常要在后台进行一些工作(不像正常的同步函数,在 return 前完成所有的事)。
异步接口有很多风格:
- Callback argument 回调参数
- Return a placeholder (Future, Promise, Deferred)
- Deliver to a queue 实现一个队列
- Callback registry (e.g. POSIX signals)
无论使用哪种类型的接口被使用,调用方式也不相同。调用者没有免费的方式将同步函数变成异步函数 (系统如 gevent 使用轻量级的线程提供性能和异步系统,但是他们实际上没有让事情异步)
Examples
- 同步函数
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
def asynchronous_fetch(url, callback):
http_client = AsyncHTTPClient()
def handle_response(response):
callback(response.body)
http_client.fetch(url, callback=handle_response)
- Future 替代回调
from tornado.concurrent import Future
def async_fetch_future(url):
http_client = AsyncHTTPClient()
my_future = Future()
fetch_future = http_client.fetch(url)
fetch_future.add_done_callback(
lambda f: my_future.set_result(f.result()))
return my_future
原来的 Future 版本更复杂,但 Future 在 tornado 中仍然被推荐,因为他有2个主要的优势。
错误处理在未来更加一致,因为 Future.result 方法可以轻易的引发一个异常,并且 Futures 可以很好的结合协程使用。协程将在下一个章节中深入讨论。这个是我们示例函数的协程版,和同步版非常相似。
from tornado import gen
@gen.coroutine
def fetch_coroutine(url):
http_client = AsyncHTTPClient()
response = yield http_client.fetch(url)
raise gen.Return(response.body)
raise gen.Return(response.body) 是 python 2 的表述,因为此时生成器不允许使用 return。
为了克服这一点,tornado 使用特殊的异常类型叫 Return。协程捕捉到这个异常,然后当做 return 处理。
在 python 3.3 及其以后,可以直接使用 return response.body 起到相同的作用。
下一篇: 2.3、User’s guide (Queue)
上一篇: 2.1、User’s guide (Introduction)