tornado ioloop source code read
原文链接 https://liutaihua.github.io/2014/04/17/tornado-ioloop-source-code-read.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
重新翻看看ioloop的源码, 以前只读到ioloop是tornado所有网络服务的基础,比如tcpserver, iostream都是将自己对应的callback通过ioloop挂载在对应的epoll事件上,
以达到非阻塞的效果。
这里总结下ioloop类的构建过程。
ioloop它有一个基类: Configurable定义在util.py文件内,这个Configurable类重定义了new工厂函数, 据源码里的描述是为了形成一个使用new函数来作为构造函数基类,
看这个new方法代码:
def __new__(cls, **kwargs):
base = cls.configurable_base()
args = {}
if cls is base:
impl = cls.configured_class()
if base.__impl_kwargs:
args.update(base.__impl_kwargs)
else:
impl = cls
args.update(kwargs)
instance = super(Configurable, cls).__new__(impl)
# initialize vs __init__ chosen for compatiblity with AsyncHTTPClient
# singleton magic. If we get rid of that we can switch to __init__
# here too.
instance.initialize(**args)
return instance
这段代码会在Configurable或它的子类也就是IOLoop在实例化的时候, 被执行, 它获取当前实例:impl,
将这个impl连同在实例化时传给init的参数, 一起作为参数args传给实例的initialize方法来做IOLoop的初始化.
这个过程里重要的2个方法是, configurable_base和 configured_class, 还有configurable_default,
ioloop重写了父类的 configurable_base方法, 它直接返回IOLoop类自己本身:
@classmethod
def configurable_base(cls):
return IOLoop
再看上面的new里, 对当前类cls与这个返回比较, 如果是同一个对象, 也就是说如果当前对象是IOLoop, 则执行configured_class:
@classmethod
def configured_class(cls):
"""Returns the currently configured class."""
base = cls.configurable_base()
if cls.__impl_class is None:
base.__impl_class = cls.configurable_default()
return base.__impl_class
这里会判断静态变量__impl_class是否已被赋值, 如果None, 则执行configurable_default方法,
获得最终需要的对象实例, 绕了这么久这里才是关键, 因为这个configurable_default方法里,
根据平台不同,会选择epoll, kqueue, select 其中的1个作为事件触发.
@classmethod
def configurable_default(cls):
if hasattr(select, "epoll"):
from tornado.platform.epoll import EPollIOLoop
return EPollIOLoop
if hasattr(select, "kqueue"):
# Python 2.6+ on BSD or Mac
from tornado.platform.kqueue import KQueueIOLoop
return KQueueIOLoop
from tornado.platform.select import SelectIOLoop
return SelectIOLoop
这里的对epoll, kqueue, select的选择过程, 返回的EPollIOLoop其实也是IOLoop的子类, 代码在platform/epoll.py里:
from tornado.ioloop import PollIOLoop
class EPollIOLoop(PollIOLoop):
def initialize(self, **kwargs):
super(EPollIOLoop, self).initialize(impl=select.epoll(), **kwargs)
这里可以看出, EPollIOLoop继承于PollIOLoop, PollIOLoop是IOLoop的子类, 源码就是ioloop.py里,
可以看到IOLoop类本身基本只是根据不同的平台, 最终在访问IOLoop.instance变量的时候,
返回不同平台下的不同的IOLoop子类, 比如linux下IOLoop.instance其实就是PollIOLoop的epool实例,
PollIOLoop类的初始化函数initialize需要一个impl参数, 而这个参数就是子类EPollIOLoop传进来的select.epoll()实例:
super(EPollIOLoop, self).initialize(impl=select.epoll(), **kwargs)
到此就完成了IOLoop的平台选择....