tornado源码分析-1

image

一个简单的tornado应用实例


1
2
3
4
5
6
7
8
9
10
11
12
13
14
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

tornado框架以Application为入口,以RequestHandler为接口,以IOLoop为核心进行构建。上面的例子创建了一个简单的Hello,World程序。tornado以异步高性能为目的,采用异步非堵塞网络IO模型,低层利用epoll(linux平台),特别适合高性能长连接的应用场景。

异步非堵塞编程模型


异步函数],会在结束所有操作前就返回,其中的一些操作(特别是耗时间的IO操作)会交给后台去运行,这些操作完成后再触发应用中的其它函数逻辑。一般有4种类型的异步接口:

  • 回调参数。
    通过参数传入callback等待完成后再进行最后的callback操作。
    asynchronous_fetch->http_client.fetch->handle_respones->callback
1
2
3
4
5
6
7
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实例完成一步步的回调,事件通知或者订阅者-发布者模型。
1
2
3
4
5
6
7
8
9
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
  • 提交给队列。
    nose.js是这种模型,每个操作是一个事件提交给后台的其他线程库,完成后再交给主线程中的队列进行回调操作。

  • 回调注册。
    POSIX信号是这种模型,通过注册回调函数。(这里我还不太清楚与回调参数有什么不同)

无论是哪种形式,异步模型都有一些通用逻辑,可以分为堵塞前操作堵塞操作通知堵塞后操作

  • 堵塞前操作,立即完成的,发生在堵塞操作前的逻辑。
  • 堵塞操作,堵塞操作,主要需要解决的部分。
  • 通知,堵塞操作完成后,如何通知已经完成了堵塞操作
  • 堵塞后操作,堵塞操作完成后的逻辑,一般处理堵塞操作的返回值。

同步堵塞模型比较简单,所有逻辑操作都捆绑在一起,没有也不必进行通知。异步非堵塞模型需要重点处理堵塞操作通知。一般的异步非堵塞使用callback模型,将堵塞前操作堵塞后逻辑显式分开,通过向堵塞操作传递堵塞后操作进行通知机制(callback)。

tornado中的异步非堵塞模型


tornado的异步接口利用Python的协程语法yield可以暂停操作,将堵塞逻辑通知堵塞后操作捆绑到一起,不用显式的拆分最大限度的保持和同步接口的一致性。通过yield让出堵塞操作交给IOLoop统一处理,再通过Future机制进行通知机制。

1
2
3
4
5
6
7
from tornado import gen
@gen.coroutine
def fetch_coroutine(url):
http_client = AsyncHTTPClient() # 堵塞前操作
response = yield http_client.fetch(url) # 堵塞操作
return response.body # 堵塞后操作

tornado的异步堵塞模型低层采用epoll模型,通过(隐式)Future机制进行回调通知,通过IOLoop处理堵塞操作。简单的IOLoop逻辑示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Simplified inner loop of tornado.gen.Runner
def run(self):
# send(x) makes the current yield return x.
# It returns when the next yield is reached
future = self.gen.send(self.next)
# 通过send 通知 协程进行 堵塞后操作
# future衔接 堵塞前和堵塞后的逻辑
def callback(f):
self.next = f.result()
self.run()
future.add_done_callback(callback)
# 向future中注册辅助函数,进行回调后的通知

tornado中核心类


tornado主要有两个核心类,IOLoop封装低层epoll集中处理堵塞操作。iostream封装read、write函数进行缓存和更方便的处理HTTP协议。具体分析稍后进行。