IOLoop源码笔记

IOLoop的初始化

初始化过程中选择 epoll 的实现方式,Linux 平台为 epoll,BSD 平台为 kqueue,其他平台如果安装有C模块扩展的 epoll 则使用 tornado对 epoll 的封装,否则退化为 select。

class Configurable(object):
def __new__(cls, *args, **kwargs):
    base = cls.configurable_base()
    init_kwargs = {}
    if cls is base:
        impl = cls.configured_class()
        if base.__impl_kwargs:
            init_kwargs.update(base.__impl_kwargs)
    else:
        impl = cls
    init_kwargs.update(kwargs)
    instance = super(Configurable, cls).__new__(impl)
    # initialize vs __init__ chosen for compatibility with AsyncHTTPClient
    # singleton magic.  If we get rid of that we can switch to __init__
    # here too.
    instance.initialize(*args, **init_kwargs)
    return instance

IOLoop继承Configurable类,会在创建是调用new创建类。作者用的是ubuntu,所以会返回一个继承自IOLoop的PollIOLoop类,并且调用类的初始化initialize函数。如下:

def initialize(self, impl, time_func=None, **kwargs):
      super(PollIOLoop, self).initialize(**kwargs)
      self._impl = impl
      if hasattr(self._impl, 'fileno'):
          set_close_exec(self._impl.fileno())
      self.time_func = time_func or time.time
      self._handlers = {}
      self._events = {}
      self._callbacks = []
      self._callback_lock = threading.Lock()
      self._timeouts = []
      self._cancellations = 0
      self._running = False
      self._stopped = False
      self._closing = False
      self._thread_ident = None
      self._blocking_signal_threshold = None
      self._timeout_counter = itertools.count()

      # Create a pipe that we send bogus data to when we want to wake
      # the I/O loop when it is idle
      self._waker = Waker()
      self.add_handler(self._waker.fileno(),
                       lambda fd, events: self._waker.consume(),
                       self.READ)

# 添加一个socket和它的回调函数
def add_handler(self, fd, handler, events):
    fd, obj = self.split_fd(fd)
    self._handlers[fd] = (obj, stack_context.wrap(handler))
    self._impl.register(fd, events | self.ERROR)

PollIOLoop初始化的过程中创建了一个 Waker 对象,将Waker的fd注册到事件回调函数中,设置相应的回调函数(
这么做是为了可以通过Waker类来唤醒阻塞的循环事件)。

PollIOLoop.start()

PollIOLoop 的核心调度集中在 start() 方法中,可以调用该实例的stop()来停止循环。
由于start()代码太长,作者只取了一些主要片段介绍。

with self._callback_lock:
    callbacks = self._callbacks
    self._callbacks = []

self._callback_lock是一个线程锁,self._callbacks支持多线程操作。self._callbacks主要用来放置异步事件。

for callback in callbacks:
    self._run_callback(callback)

运行回调异步事件和超时事件。

try:
    event_pairs = self._impl.poll(poll_timeout)
except Exception as e:
    # Depending on python version and IOLoop implementation,
    # different exception types may be thrown and there are
    # two ways EINTR might be signaled:
    # * e.errno == errno.EINTR
    # * e.args is like (errno.EINTR, 'Interrupted system call')
    if errno_from_exception(e) == errno.EINTR:
        continue
    else:
        raise

self._imple.poll是一个epoll的操作,循环会阻塞在这等待外部连接进入。tornado定义了一个wake()类,可以通过wake类来唤醒循环。

self._events.update(event_pairs)
while self._events:
    fd, events = self._events.popitem()
    try:
        fd_obj, handler_func = self._handlers[fd]
        handler_func(fd_obj, events)
    except (OSError, IOError) as e:
        if errno_from_exception(e) == errno.EPIPE:
            # Happens when the client closes the connection
            pass
        else:
            self.handle_callback_exception(self._handlers.get(fd))
    except Exception:
        self.handle_callback_exception(self._handlers.get(fd))
fd_obj = handler_func = None

当有事件进入时,通过fd从self._handlers中取出相应的回调函数运行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 事件驱动编程 事件驱动编程是一种网络编程范式,程序的执行流由外部事件来决定,特点是包含一个事件循环,当外部事件发生...
    JunChow520阅读 3,794评论 0 1
  • 本文将通过最简单的Hello world代码分析Tornado本身的结构,在此基础上再简单介绍下Tornado适用...
    靡不有初LB阅读 2,966评论 0 7
  • 简介 Tornado龙卷风是一个开源的网络服务器框架,它是基于社交聚合网站FriendFeed的实时信息服务开发而...
    JunChow520阅读 53,985评论 4 46
  • 参考教程 为什么用Tornado? 异步编程原理 服务器同时要对许多客户端提供服务,他的性能至关重要。而服务器端的...
    内心强大的Jim阅读 6,192评论 1 8
  • 什么是流Stream呢? 流Stream是一种抽象的数据结构,类似水流当在水管中流动时可以从某个地方源源不断流向另...
    JunChow520阅读 2,041评论 0 1