tornado源码分析系列 [网络层 IOLoop类] 优秀系列文章可以看看

看看IOLoop的类组织结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| - - - IOLoop
         - - - __init__( self , impl = None )
         - - - instance( cls )
         - - - initialized( cls )
         - - - add_handler( self , fd, handler, events)
         - - - update_handler( self , fd, events)
         - - - remove_handler( self , fd)
         - - - set_blocking_signal_threshold( self , seconds, action)
         - - - set_blocking_log_threshold( self , seconds)
         - - - log_stack( self , signal, frame)
         - - - start( self )
         - - - stop( self )
         - - - running( self )
         - - - add_timeout( self , deadline, callback)
         - - - remove_timeout( self , timeout)
         - - - add_callback( self , callback)
         - - - _wake( self )
         - - - _run_callback( self , callback)
         - - - handle_callback_exception( self , callback)
         - - - _read_waker( self , fd, events)
         - - - _set_nonblocking( self , fd)
         - - - _set_close_exec( self , fd)
- - - |

从上一章的Demo里面可以看到最重要的对外提供的方法有

0.instance() @classmethod

1.add_handler(...)

2.start()

类似于传统的事件驱动方式,这里的使用方式也很简单

 

从IOLoop类中看起:

先是自己定义了几个EPOLL的宏,就是EPOLL的事件类型

#epoll 的事件类型,类似于这里的宏定义
_EPOLLIN = 0x001
_EPOLLPRI = 0x002
_EPOLLOUT = 0x004
_EPOLLERR = 0x008
_EPOLLHUP = 0x010
_EPOLLRDHUP = 0x2000
_EPOLLONESHOT = (1 << 30)
_EPOLLET = (1 << 31)

# Our events map exactly to the epoll events
#将这几个事件类型重定义一番
NONE = 0
READ = _EPOLLIN
WRITE = _EPOLLOUT
ERROR = _EPOLLERR | _EPOLLHUP | _EPOLLRDHUP

常用的就是三种,READ,WRITE,ERROR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ioloop的构造函数
     def  __init__( self , impl = None ):
         #选择异步事件循环监听方式,默认是epoll,后面的_impl都是指的是epoll
         self ._impl  =  impl  or  _poll()
         #自省,查看 self._impl 中是否有 fileno
         #如果有,就关闭起exec性质
         if  hasattr ( self ._impl,  'fileno' ):
             self ._set_close_exec( self ._impl.fileno())
         # _set_close_exec 是一个类方法,下面有定义
     # 当 FD_CLOEXEC 设置了以后,exec() 函数执行的时候会自动关闭描述符
"""     def _set_close_exec(self, fd):
             flags = fcntl.fcntl(fd, fcntl.F_GETFD)
             fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)   """
         #handlers 是一个函数集字典
         self ._handlers  =  {}
         self ._events  =  {}
         #回调函数使用的是列表
         self ._callbacks  =  []
         #用来记录链接超时
         self ._timeouts  =  []
         self ._running  =  False
         self ._stopped  =  False
         self ._blocking_signal_threshold  =  None
 
         # Create a pipe that we send bogus data to when we want to wake
         # the I/O loop when it is idle
         #判断是否是 NT 操作系统
         if  os.name ! =  'nt' :
             #创建一个管道 ,返回的为读写两端的文件描述符
             r, w  =  os.pipe()
             #设置为非阻塞
             self ._set_nonblocking(r)
             self ._set_nonblocking(w)
            
             self ._set_close_exec(r)
             self ._set_close_exec(w)
             #分别以读方式和写方式打开管道
             self ._waker_reader  =  os.fdopen(r,  "rb" 0 )
             self ._waker_writer  =  os.fdopen(w,  "wb" 0 )
         else :
             #如若不是 NT 系统,改用win32 支持的管道类型
             self ._waker_reader  =  self ._waker_writer  =  win32_support.Pipe()
             =  self ._waker_writer.reader_fd
         #将 管道的  read端与 函数 _read_waker  关联,事件类型为 READ
         #这里也是IO 多路复用的一种机制,将管道的描述符也添加进多路复用的IO 管理
         self .add_handler(r,  self ._read_waker,  self .READ)

注意最后的几点,将管道描述符的读端也加入事件循环检查,并设置相应的回调函数,这样做的好处是以便事件循环阻塞而没有相应描述符出现,需要在最大timeout时间之前返回,就可以向这个管道发送一个字符,用来终止阻塞在监听阶段的事件循环监听函数。

看看waker是这样定义的:

1
2
3
4
5
def  _wake( self ):
     try :
         self ._waker_writer.write( "x" )
     except  IOError:
         pass

需要唤醒阻塞中的事件循环监听函数的时候,只需要向管道写入一个字符,就可以提前结束循环

instance就是简单的返回一个实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def  instance( cls ):
     """Returns a global IOLoop instance.
 
     Most single-threaded applications have a single, global IOLoop.
     Use this method instead of passing around IOLoop instances
     throughout your code.
 
     A common pattern for classes that depend on IOLoops is to use
     a default argument to enable programs with multiple IOLoops
     but not require the argument for simpler applications:
 
         class MyClass(object):
             def __init__(self, io_loop=None):
                 self.io_loop = io_loop or IOLoop.instance()
     """
     if  not  hasattr ( cls "_instance" ):
         cls ._instance  =  cls ()
     return  cls ._instance

instance()是一个静态方法,代表此IOLoop是一个单实例方法,一个进程只有一个

在add_handler()里面

1
2
3
4
5
6
7
8
#将文件描述符发生相应的事件时的回调函数对应
def  add_handler( self , fd, handler, events):
     """Registers the given handler to receive the given events for fd."""
     self ._handlers[fd]  =  stack_context.wrap(handler)
     #在 epoll 中注册对应事件
     #epoll_ctl
     self ._impl.register(fd, events |  self .ERROR)
#更新相应的事件类型

可以看到,使用字典的方式,每一个fd就对应一个handler,下次事件循环返回的时候按照返回后的fd列表,依次调用相应的callback

|------

在tornado中,函数是通过stack_context.wrap()包装过,可以用来记录上下文

如果需要调用被包装过的函数,需要调用方法

_run_callback(self, callback)  

这个函数将包装过的callback作为参数出入,然后执行函数

1
2
3
4
5
6
7
def  _run_callback( self , callback):
     try :
         callback()
     except  (KeyboardInterrupt, SystemExit):
         raise
     except :
         self .handle_callback_exception(callback)

当函数执行发生异常时,可以记录下函数执行状态  

-------|

_impl.register就是被封装过的epoll的epoll_ctl,参数是EPOLL_CTL_ADD

见同一个文件下的_EPoll类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class  _EPoll( object ):
     """An epoll-based event loop using our C module for Python 2.5 systems"""
     _EPOLL_CTL_ADD  =  1
     _EPOLL_CTL_DEL  =  2
     _EPOLL_CTL_MOD  =  3
 
     def  __init__( self ):
         self ._epoll_fd  =  epoll.epoll_create()
 
     def  fileno( self ):
         return  self ._epoll_fd
 
     def  register( self , fd, events):
         epoll.epoll_ctl( self ._epoll_fd,  self ._EPOLL_CTL_ADD, fd, events)
 
     def  modify( self , fd, events):
         epoll.epoll_ctl( self ._epoll_fd,  self ._EPOLL_CTL_MOD, fd, events)
 
     def  unregister( self , fd):
         epoll.epoll_ctl( self ._epoll_fd,  self ._EPOLL_CTL_DEL, fd,  0 )
 
     def  poll( self , timeout):
         return  epoll.epoll_wait( self ._epoll_fd,  int (timeout  *  1000 ))

  

总结:这一章讲了IOLoop中的几个重要函数,后面依次会有分析其他方法,还有其中一些细节值得平常注意的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值