Tornado源码分析之IOLoop

在Tornado框架上发生的故事,起源于IOLoop。IOLoop就像是Tornado的心脏,他接受来自Browser,爬虫,蜘蛛发出的链接,接收他们的数据,然后按Http协议进行解析,将解析的结果,经由url路由,分配到不同的RequestHandler进行处理,RequestHandler处理好结果,再由IOLoop发往客户端。

IOLoop

      IOLoop位于ioloop.py模块,扒开它的源码,发现文件有600多行,其实大部分是注释(作者的编码非常好,看代码非常舒服),真正代码可能不到200行。IOLoop类是该模块中最重要的类,而start()方法是该类重要的方法,用于启动ioloop接收来自客户端的链接,把这个方法的主要代码提出来简单说明一下,就知道他的基本工作原理:

  1. def start(self):  
  2.     while True:  
  3.         # ...  
  4.         try:  
  5.             # self._impl是一个poll对象,tornado在linux上,是epoll,在macos上的kqueue,其他平台上也有相关的封装,下文有说明  
  6.             event_pairs = self._impl.poll(poll_timeout)      
  7.         except Exception, e:  
  8.             # ...  
  9.   
  10.         self._events.update(event_pairs)        # 这里才是“心脏”。把从poll中获取到的IO事件添加到 _events列表中  
  11.         while self._events:  
  12.             fd, events = self._events.popitem()  
  13.             try:  
  14.                 # 从 _events列表中获取一个IO事件,并触发回调  
  15.                 # self._handlers是一个字典,key为socket的 fileno,value为一个函数类似handler(fileno, events)的回调函数  
  16.                 self._handlers[fd](fd, events)    
  17.             except (OSError, IOError), e:  
  18.                 #...  
  19.             except Exception:  
  20.                 #...  
  21.     
  22. # 注册/取消 IO事件的代码  
  23. def add_handler(self, fd, handler, events):  
  24.     """注册IO事件"""  
  25.     self._handlers[fd] = stack_context.wrap(handler)  
  26.     self._impl.register(fd, events | self.ERROR)  
  27.   
  28. def update_handler(self, fd, events):  
  29.     """更新IO事件"""  
  30.     self._impl.modify(fd, events | self.ERROR)  
  31.   
  32. def remove_handler(self, fd):  
  33.     """删除指定文件描述符的IO事件"""  
  34.     self._handlers.pop(fd, None)  
  35.     self._events.pop(fd, None)  
  36.     try:  
  37.         self._impl.unregister(fd)  
  38.     except (OSError, IOError):  
  39.         logging.debug("Error deleting fd from IOLoop", exc_info=True)  

        start()方法大概是这样工作的:从poll对象获取IO事件,然后将IO事件提交给相应的handler处理。上面代码略去了处理callback, timeout的逻辑,代码不难,感兴趣的同学自己扒代码来看。说白了,IOLoop其实就是一个reactor

关于poll

      ioloop模块使用poll对象,在不同的平台上实现是不同的,在linux下就是epoll,macos下为kqueue,而在windows下,为select,如下:

不同平台,poll不一样

TCPServer与HttpServer

        TCPServer在netutil模块中定义,内容通过ioloop的实现网络连接、通信处理。看下它的构造函数:

  1. def __init__(self, io_loop=None, ssl_options=None):  
  2.     self.io_loop = io_loop  # IOLoop实例引用  
  3.     self.ssl_options = ssl_options  
  4.     self._sockets = {}  # fd -> socket object  
  5.     self._pending_sockets = []  
  6.     self._started = False  

        TCPServer中,比较重要的方法,看一下add_sockets,如下:

  1. def add_sockets(self, sockets):  
  2.     if self.io_loop is None:  
  3.         self.io_loop = IOLoop.instance()  
  4.   
  5.     for sock in sockets:  
  6.         self._sockets[sock.fileno()] = sock  
  7.         # 向poll注册IO事件,并设置好回调函数。这里把实例方法_handle_connection作为回调。该方法在HttpServer中被重写  
  8.         add_accept_handler(sock, self._handle_connection,  
  9.                            io_loop=self.io_loop)    
  10.   
  11. def add_accept_handler(sock, callback, io_loop=None):  
  12.     if io_loop is None:  
  13.         io_loop = IOLoop.instance()  
  14.     def accept_handler(fd, events):  
  15.         while True:  
  16.             try:  
  17.                 connection, address = sock.accept()  # 接收新链接  
  18.             except socket.error, e:  
  19.                 if e.args[0in (errno.EWOULDBLOCK, errno.EAGAIN):  
  20.                     return  
  21.                 raise  
  22.             callback(connection, address)    
  23.     # 向ioloop注册IO事件  
  24.     io_loop.add_handler(sock.fileno(), accept_handler, IOLoop.READ)  

 

HTTPServer在httpserver模块中,继承了TCPServer,它的代码很简单,如下:

  1. class HTTPServer(TCPServer):  
  2.     def __init__(self, request_callback, no_keep_alive=False, io_loop=None,  
  3.                  xheaders=False, ssl_options=None, **kwargs):  
  4.         self.request_callback = request_callback    # tornado.web.Application 对象,下节分析  
  5.         self.no_keep_alive = no_keep_alive  
  6.         self.xheaders = xheaders  
  7.         TCPServer.__init__(self, io_loop=io_loop, ssl_options=ssl_options,  
  8.                            **kwargs)  
  9.   
  10.     def handle_stream(self, stream, address):   
  11.         """tcpserver接收到链接后的回调"""  
  12.         HTTPConnection(stream, address, self.request_callback,  
  13.                        self.no_keep_alive, self.xheaders)  

上面的代码可以看到,HTTPServer只重写了handle_stream方法,而handle_stream在TCPServer中是作为接收到链接时的回调,这里handle_stream只实例了一个HttpConnection对象,所以接下来事件,与HTTPConnection相关。后面很精彩,下文待续。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值