一切皆有可能
anywill ,anything will ....go better or worse ,go success or failure,go......一切皆有可能

Tornado 源码分析 – 异步篇

anywill~2018-06-01 /Tornado

出处:https://www.jianshu.com/p/8769093242f3

Tornado 所谓的异步:就是你调用我之后,我发现数据没准备好,那我就不处理,而是跳到程序的其他地方继续执行,等数据准备好之后再切回来继续执行。Tornado 的 IOLoop 就是一个总调度器,汇总了所有的 events 和 callbacks,然后同步执行。这会整体生提升性能,但不会降低单个请求的响应时间。

本文主要包括:

  • tornado.httpclient.AsyncHTTPClient 的实现
  • tornado.gen.engine 的实现
  • tornado.gen.YieldFuturetornado.concurrent.Future 的作用

Tornado 在 2.0 时候,已经完整实现非阻塞 HTTP 客户端,发起 HTTP 请求可以实现异步效果。

从一个简单的异步实例开始:

# 代码一
class AsyncHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        http_client = AsyncHTTPClient()
        http_client.fetch("http://example.com",
                          callback=self.on_fetch)

    def on_fetch(self, response):
        self.write("Downloaded!")
        self.finish()

tornado.web.asynchronous

其中 tornado.web.asynchronous 装饰器很简单,就是设置 self._auto_finish = False,这样当 AsyncHandler.get() 执行完之后,connection socket 不会被 close,需要主动调用 self.finish()在保持连接不关闭的情况下,把控制权让出去,等数据就绪之后再切回来,使异步实现成为可能。 下面是 asynchronous 简化后的代码:

# 代码二:
def asynchronous(method):
   @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if self.application._wsgi:
            raise Exception("@asynchronous is not supported for WSGI apps")
        self._auto_finish = False
        return method(self, *args, **kwargs)
    return wrapper

SimpleAsyncHTTPClient

AsyncHTTPClient 的处理流程,简单概括就是:与 HTTP Server 建立连接,等拿到 response 后再来调用回调函数 on_fetch。首先通过创建非阻塞的 socket 连接,然后放入到 ioloop 中,当数据可写/可读之后再接着处理。
「代码一」中调用 AsyncHTTPClient() 时,其实生成的是 SimpleAsyncHTTPClient 对象,有兴趣可以了解下实现。
下面是「代码一」执行流程图:

AsyncHTTPClient 执行流程

SimpleAsyncHTTPClient 的实现和 HTTPServer 很相似。
Tornado 3.0 中的 AsyncHTTPClient 的主要不同是:在 _on_body 处理完 Response Body 之后会调用 handle_response 把 reponse 设置给 future,并将 future 对象返回。AsyncHTTPClient.fetch 简化后的代码:

def fetch(request)
    def handle_response(response):
        if response.error:
            future.set_exception(response.error)
        else:
            future.set_result(response)
    self.fetch_impl(request, handle_response)
    return future

gen.engine 的实现:

以下的代码分析都是基于 Tornado 3.0。
使用 gen.engine 优化后的代码:

class GenEngineAsyncHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch("http://example.com")
        self.write("Downloaded!")
        self.finish()

其中 gen.engine 的作用就是把异步中 callback 的写法通过 yield 替代。

以上代码的详细处理流程:

  1. gen.engine 中,调用 result = func(*args, **kwargs),生成一个 generator。因为 get 中包含 yield,所以当函数被调用时,会生成 generator 对象。但是函数并不会执行,需要通过调用 nextsend 来执行。具体参考 generator 的用法。engine 简化后的代码就是:
def engine(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        runner = Runner(result, final_callback)
        runner.run()

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注