fundamental concepts
同步
调用者等待调用结果的返回
异步
调用者不等待调用结果的返回,而是通过间隔轮询、通知等方式得知结果
阻塞
调用方式影响后续指令的执行
非阻塞
调用方式不影响后续指令的执行
一般来说,阻塞是绝对的,非阻塞则是相对的,因为任何指令或调用的执行都要占用 CPU 周期,或网络,或 IO。非阻塞只是说调用或者资源消耗不影响后续逻辑执行,他们经得起等待。非阻塞往往和异步一起出现。
tornado 是一个异步非阻塞 httpserver,同时也是一个 web framework。
Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed. By using non-blocking network I/O, Tornado can scale to tens of thousands of open connections, making it ideal for long polling, WebSockets, and other applications that require a long-lived connection to each user。
在 tornado 中异步和非阻塞是通过 ioloop 和 Future 实现的,Future 是借助 python 的协程来实现非阻塞调用。tornado 提供了不同的异步调用形式来适配不同的调用场景,本文的目的就是为了说明不同形式是如何使用的以及它们的差别。
为了演示方便,这里使用了一个公共模块 util.py 来提供 ioloop 的启动和销毁,下文不做特殊说明皆是如此。
1 | # util.py |
最常用的调用方式
在 tornado 中最普遍的使用方式是把函数的调用结果封装成 Future,利用 ioloop 执行并异步获得结果。
1 | # -*- coding:utf-8 -*- |
在 #1
处通过tornado 提供的异步 httpclient 获得一个 Future,为 Future 添加执行结果回调函数获得执行结果,Future 的执行结果也是一个 Future。
输出结果如下
1 | ====> ioloop start |
这种调用方式必须手动启动 ioloop 并在调用结束之后手动销毁 ioloop,想要获得调用结果必须为 Future 添加回调。
只关心调用,不在乎结果
有时候我们并不在乎函数的调用结果,只要函数正确执行即可,ioloop 提供了 spawn_callback 来执行这样的操作:
1 | # -*- coding:utf-8 -*- |
借助 spawn_callback 可以直接执行函数调用,但是依然需要手动处理 ioloop 的开闭。
一次性执行
run_sync 可以自动开启 ioloop 并在函数执行结束之后关闭 ioloop,此种调用在执行一次性操作时非常有用,比如要对数据库进行一次性修改:
1 |
|
#1
是一个 motor Mongodb collection,借助 run_sync 可以非常方便对数据库执行操作,由于run_sync 只接受一个参数,如果函数调用需要传参,可以把函数封装成 lambda。
使用 callback*
如果调用需要与 callback 函数进行交互,可以利用 gen.task
1 | # -*- coding:utf-8 -*- |
使用 gen.task 可以直接返回一个 Future ,被调用的函数会自动增加一个 callback 函数,用来在函数执行结束之后把结果进行回调通知。
通过 threadPool 来调用阻塞操作
1 | #-*- coding:utf-8 -*- |
如果代码中有阻塞操作,可以借助 ThreadPoolExecutor 来完成异步的调用,这样把耗时的操作交给别的线程,可以使当前的线程继续执行后续的操作。这里的阻塞操作一般是 IO 或者其他系统调用。
并行执行
tornado 的 Future 是支持并行执行的,可以对多个 future 进行 yield 操作并返回多个结果。
1 | # -*- coding:utf-8 -*- |
文中所有代码见 tornado-asyn,欢迎指正。