tornado的gen.engine浅析

来源:互联网 发布:高性能浏览器网络 编辑:程序博客网 时间:2024/06/13 22:07

1. 首先看看示例代码,也是官方给出的:

复制代码
 1 class Main(RequestHandler): 2     @tornado.web.asynchronous 3     @gen.engine 4     def get(self): 5         if _DEBUG: 6             pdb.set_trace() 7         http_client = AsyncHTTPClient() 8         response = yield gen.Task(http_client.fetch, "http://code.rootk.com") 9         self.write(str(response))10         self.finish()
复制代码

 

2. 初始化异步的HTTP客户端,tornado自带的。

http_client = AsyncHTTPClient()

 

3. @gen.engine

这是一个装饰器,它装饰生成器函数,看看实现的代码:

复制代码
 1 def engine(func): 2     @functools.wraps(func) 3     def wrapper(*args, **kwargs): 4         runner = None 5  6         def handle_exception(typ, value, tb): 7             # if the function throws an exception before its first "yield" 8             # (or is not a generator at all), the Runner won't exist yet. 9             # However, in that case we haven't reached anything asynchronous10             # yet, so we can just let the exception propagate.11             if runner is not None:12                 return runner.handle_exception(typ, value, tb)13             return False14         with ExceptionStackContext(handle_exception) as deactivate:15             # gen是一个RequestHandler的子类的方法16             gen = func(*args, **kwargs)17             # 如果方法是一个生成器18             if isinstance(gen, types.GeneratorType):19                 # 实例化Runner类,传入生成器作为参数20                 runner = Runner(gen, deactivate)21                 runner.run()22                 return23             assert gen is None, gen24             deactivate()25             # no yield, so we're done26     return wrapper
复制代码

(1). 在代码runner = Runner(gen, deactivate)之中,gen指的是示例代码中get这个生成器函数。

(2). 然后调用实例的run方法。

 

4. 分析一下类的Runner实例的run方法。

run函数循环会执行三遍:
第一遍,当gen.engine执行时,遇到runner.run()会执行一遍,最后会调用Task的start方法
这次执行的结果是让示例代码中的yield语句运行完毕。

第二遍,start方法执行完毕后,http_client.fetch方法会回调Runner的result_callback方法,会再次执行。
这次执行的结果是向生成器函数发送执行结果:
yielded = self.gen.send(next)
next的内容就是在上面的代码中:
next = self.yield_point.get_result()
而且self.finished = True,被设置为True。

第三遍,最后start方法执行完毕(它是由第一步调用的),run方法里的循环会最后一次运行。
当它发现self.finished被设置为True时,直接return。

实现代码:

复制代码
 1 if self.running or self.finished: 2             return 3         try: 4             self.running = True 5             while True: 6                 # 如果上一次执行没有抛出异常 7                 if self.exc_info is None: 8                     try: 9                         if not self.yield_point.is_ready():10                             return11                         # self.yield_point是一个_NullYieldPoint类的实例12                         # 实例的get_result方法总是返回None13                         next = self.yield_point.get_result()14                     except Exception:15                         self.exc_info = sys.exc_info()16                 try:17                     if self.exc_info is not None:18                         self.had_exception = True19                         exc_info = self.exc_info20                         self.exc_info = None21                         yielded = self.gen.throw(*exc_info)22                     else:23                         # 这里调用生成器函数的send方法,参数为None24                         # 对应到示例中的代码,就完整的执行了下面这一句25                         # yield gen.Task(http_client.fetch, "http://code.rootk.com")26                         # 返回了一个Task类的实例27                         # 因为可以同时执行多个任务,所以返回的也有可能是含有一个Task类的列表28                         # 下面会做判断29                         yielded = self.gen.send(next)30                 except StopIteration:31                     self.finished = True32                     if self.pending_callbacks and not self.had_exception:33                         # If we ran cleanly without waiting on all callbacks34                         # raise an error (really more of a warning).  If we35                         # had an exception then some callbacks may have been36                         # orphaned, so skip the check in that case.37                         raise LeakedCallbackError(38                             "finished without waiting for callbacks %r" %39                             self.pending_callbacks)40                     self.deactivate_stack_context()41                     return42                 except Exception:43                     self.finished = True44                     raise45                 # 判断yield返回的内容46                 # 如果是列表类型(说明返回了多个任务,即多个Task类的实例)47                 if isinstance(yielded, list):48                     yielded = Multi(yielded)49                 # 如果是YieldPoint类的实例,就是Task类本身(Task类是YieldPoint类的子类)50                 # 说明只有一个任务(一个Task类的实例)51                 if isinstance(yielded, YieldPoint):52                     self.yield_point = yielded53                     try:54                         # 调用Task类实例的start方法55                         # 参数为Runner类实例自身56                         self.yield_point.start(self)57                     except Exception:58                         self.exc_info = sys.exc_info()59                 else:60                     self.exc_info = (BadYieldError("yielded unknown object %r" % yielded),)61         finally:62             self.running = False
复制代码

到这里Runner类实例的run方法执行完毕,接下来执行到了Task类实例的start方法。

 

5. 看看Task类实例的start方法的实现代码:

复制代码
 1 def start(self, runner): 2     # runner是一个Runner类实例 3     self.runner = runner 4          5     # object每次返回了不同的python的对象实例 6     # 每次生成的对象的id都不一样,所以把它当作一个唯一的key 7     self.key = object() 8          9     # 调用runner的register_callback方法,以key作为参数10     # 该方法只有一句代码:self.pending_callbacks.add(key)11     # 在self.pending_callbacks这个set类型数据中,加入key12     runner.register_callback(self.key)13         14     # 加入callback,以key作为参数15     # result_callback是一个装饰器16     # 返回的是内层函数17     # !!!下面的self.func执行成功后,会调用这个返回的内层函数!!!18     self.kwargs["callback"] = runner.result_callback(self.key)19     20     # 执行真正的函数,示例代码中执行的就是http_client.fetch函数21     # self.kwargs中已经加入了一个关键字参数:'callback'22     self.func(*self.args, **self.kwargs)
复制代码

 

6. 负责返回内容的回调函数,就是Runner类实例的result_callback方法,它是一个装饰器。

复制代码
 1 # 外层函数接受key作为参数 2 def result_callback(self, key): 3     # 内层函数负责返回内容 4     # 由http_client.fetch函数调用 5     # result就是fetch函数返回的内容 6     def inner(*args, **kwargs): 7         if _DEBUG: 8             pdb.set_trace() 9         if kwargs or len(args) > 1:10             result = Arguments(args, kwargs)11         elif args:12             result = args[0]13         else:14             result = None15         #最后调用Runner类实例的set_result方法16         self.set_result(key, result)17     return inner
复制代码

 

7. 将返回的内容添加到Runner类实例的result字典中

def set_result(self, key, result):    """Sets the result for ``key`` and attempts to resume the generator."""    # 将内容加入到self.results字典中    self.results[key] = result    #最后调用一遍run    self.run()
0 0
原创粉丝点击