[转]tornado ioloop start 的过程
2015-08-13 20:23
531 查看
解决了我一个疑问的一篇文章
转载自kaka_ace’s blog
本文链接地址: Tornado底层学习 (1) — tornado ioloop start 的过程
每一个 tornado 应用都会把 tornado ioloop 导入到代码中, 通过 ioloop 事件触发
机制, 处理 http request, 或者其他的协议的连接消息. tornado 在 Linux 系统中优先
使用 epoll 的封装, 基于 epoll 做事件处理.
现在写一个简短的 httpserver 小程序:
调试环境:
* IDE: Pycharm 4.0
* 系统: Ubuntu 14.04
* Python 版本: 3.4.0
* tornado 库版本: 4.0.2
不到 50 行的应用代码, 抛去其他的业务逻辑, 调试和阅读 ioloop start 之前
所做的工作.
先展示 ioloop.py 里 class 关系图:
IOLoop 是 PollIOLoop 的基类, 而下面将会讲解 PollIOLoop 如何调用真正的平台自己的
io 库.
现在我们分析 main() 函数中 三个语句:
1: http_server = tornado.httpserver.HTTPServer(Application())
2: http_server.listen(8080)
3: tornado.ioloop.IOLoop.instance().start()
语句1 解析: HTTPServer init 调用时, ioloop 参数默认是 None,
HTTPServer 定义在 httpserver.py 继承 (TCPServer, httputil.HTTPServerConnectionDelegate)
tornado.web.Application 定义在 web.py 继承 httputil.HTTPServerConnectionDelegate
Application() 作为 request_callback 参数传入 HTTPServer init
语句2 解析: listen(8080)
listen源码:
调用 add_sockets 函数中, 因为 ioloop 初始为 None, 因此调用 self.io_loop = IOLoop.current()
当前线程中 获取 ioloop 引用, 语句3里 instance() 操作中就没有必要再生成新的 ioloop类了,
因为 一个线程中 仅保留有一个 ioloop 单例类.
那么开始重点分析 ioloop实例的生成过程:
观察 IOLoop.current()的调用:
我们调试单步挨个语句到 instance() 函数时, 第一次使用实例需要创建,
因此调用 with IOLoop._instance_lock: (instance 的代码是 Python 单例类
实现的很好的代码例子, 使用到了 threading.Lock() )
IOLoop 继承了 Configurable 类(声明在 util.py), 并没有显示定义 init(),
而是在实例类创建的过程中, 重写了 new() 函数, 显示地操作 IOLoop 实例创建的行文.
再阅读 Configurable 类 new 函数的定义:
IOLoop configurable_base 与 configurable_default 源码定义:
即 IOLoop 调用了基类 Configurable new 函数, 代码中又调用了
重写的 configurable_xxx 函数来实现找到 平台底层 io 库的目的.
实际上 EPollIOLoop 派生于 PollIOLoop, PollIOLoop 里封装了更
详细的 ioloop 行为, 例如 add_handler update_handler remove_handler
事件的注册与注销, 读写事件监听都封装在其中, 同时支持三个事件io库,
即跨平台支持(通过更具体的派生类 eg: EPollIOLoop )
此时 语句3 的start 函数其实是 EPollIOLoop 的 start() 调用.
总结:
1. tornado 单例为了保证全局(线程中唯一, 引入threading.Lock() 保证单例生成成功)
见 with IOLoop._instance_lock: 语句( _instance_lock = threading.Lock() )
2. tornado IOLoop 返回的单例 ioloop 实例引用并不是 IOLoop 类本身实例化,而是其派生出的类的实例化, 根据 Python select 基础库属性选择系统提供的 io 库.
eg: Linux epoll, BSD: kqueue, Windows: select
并且派生类围绕其底层接口做封装操作.
3. 为了实现 2描述的跨平台支持, IOLoop 实例在生成时, tornado 利用了 new方法干预了类的实例生成, 其代码实现很好地利用了 Python 语言本身提供的机制.
4. tornado ioloop 单例类为保证每一个线程中唯一, 且在任何 io 事件能正常执行, 因此框架代码随处可见 IOLoop.current() 调用, 源码框架其实还是有点瑕疵的, (^ ^) 嘻嘻……
转载自kaka_ace’s blog
本文链接地址: Tornado底层学习 (1) — tornado ioloop start 的过程
每一个 tornado 应用都会把 tornado ioloop 导入到代码中, 通过 ioloop 事件触发
机制, 处理 http request, 或者其他的协议的连接消息. tornado 在 Linux 系统中优先
使用 epoll 的封装, 基于 epoll 做事件处理.
现在写一个简短的 httpserver 小程序:
调试环境:
* IDE: Pycharm 4.0
* 系统: Ubuntu 14.04
* Python 版本: 3.4.0
* tornado 库版本: 4.0.2
#!/usr/bin/env python # -*- coding: utf-8 -*- # # @file t_tornado.py # @author kaka_ace <xiang.ace@gmail.com> # @date Jan 25 2015 # @breif # import tornado.auth import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web class BaseHandler(tornado.web.RequestHandler): pass class HelloHandler(BaseHandler): def post(self): return class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/hello", HelloHandler), ] settings = dict( #debug=True, # debug 设置 True 时, Application.__init__ 调用 tornado.autoreload.start() ) tornado.web.Application.__init__(self, handlers, **settings) def main(): http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(8080) tornado.ioloop.IOLoop.instance().start() if __name__ == "__main__": main()
不到 50 行的应用代码, 抛去其他的业务逻辑, 调试和阅读 ioloop start 之前
所做的工作.
先展示 ioloop.py 里 class 关系图:
IOLoop 是 PollIOLoop 的基类, 而下面将会讲解 PollIOLoop 如何调用真正的平台自己的
io 库.
现在我们分析 main() 函数中 三个语句:
1: http_server = tornado.httpserver.HTTPServer(Application())
2: http_server.listen(8080)
3: tornado.ioloop.IOLoop.instance().start()
语句1 解析: HTTPServer init 调用时, ioloop 参数默认是 None,
HTTPServer 定义在 httpserver.py 继承 (TCPServer, httputil.HTTPServerConnectionDelegate)
tornado.web.Application 定义在 web.py 继承 httputil.HTTPServerConnectionDelegate
Application() 作为 request_callback 参数传入 HTTPServer init
语句2 解析: listen(8080)
listen源码:
def listen(self, port, address=""): """Starts accepting connections on the given port. ... 忽略 """ sockets = bind_sockets(port, address=address) self.add_sockets(sockets) def add_sockets(self, sockets): """Makes this server start accepting connections on the given sockets. ... """ if self.io_loop is None: # listen 操作时即生成了 ioloop self.io_loop = IOLoop.current() for sock in sockets: self._sockets[sock.fileno()] = sock add_accept_handler(sock, self._handle_connection,io_loop=self.io_loop)
调用 add_sockets 函数中, 因为 ioloop 初始为 None, 因此调用 self.io_loop = IOLoop.current()
当前线程中 获取 ioloop 引用, 语句3里 instance() 操作中就没有必要再生成新的 ioloop类了,
因为 一个线程中 仅保留有一个 ioloop 单例类.
那么开始重点分析 ioloop实例的生成过程:
观察 IOLoop.current()的调用:
@staticmethod def current(): """Returns the current thread's `IOLoop`. ... 省略注释说明 """ # 先判断单例 "instance" 属性是否存在 current = getattr(IOLoop._current, "instance", None) if current is None: return IOLoop.instance() return current # instance 定义 @staticmethod def instance(): """Returns a global `IOLoop` instance. ... 省略注释说明 """ if not hasattr(IOLoop, "_instance"): # 利用 Python with 语句来封装线程锁的上下文操作 , 可以省去 UnLock 代码 with IOLoop._instance_lock: if not hasattr(IOLoop, "_instance"): # New instance after double check IOLoop._instance = IOLoop() return IOLoop._instance
我们调试单步挨个语句到 instance() 函数时, 第一次使用实例需要创建,
因此调用 with IOLoop._instance_lock: (instance 的代码是 Python 单例类
实现的很好的代码例子, 使用到了 threading.Lock() )
IOLoop 继承了 Configurable 类(声明在 util.py), 并没有显示定义 init(),
而是在实例类创建的过程中, 重写了 new() 函数, 显示地操作 IOLoop 实例创建的行文.
再阅读 Configurable 类 new 函数的定义:
def __new__(cls, **kwargs): # cls 是 Configurable 派生出的 IOLoop 引用 base = cls.configurable_base() # IOLoop 重写该函数, 返回 IOLoop class 自身类引用 args = {} if cls is base: # 调用了 cls.configurable_default(), 在 IOLoop 见下一段的代码例子 # impl 此时即 EPollIOLoop (Linux 系统中调试) impl = cls.configured_class() if base.__impl_kwargs: args.update(base.__impl_kwargs) else: impl = cls args.update(kwargs) # 此时 tornado 利用了 Python __new__ magic function 干预了 IOLoop 的实例生成, # 而真正的 IOLoop 类实际是派生类 EPollIOLoop # instance 是 EPollIOLoop 的实例类. tornado 根据系统平台,统一使用基类 # IOLoop 作为对外接口提供给开发者, 隐藏了平台底层实际的对象. 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
IOLoop configurable_base 与 configurable_default 源码定义:
class IOLoop(Configurable): @classmethod def configurable_base(cls): return IOLoop @classmethod def configurable_default(cls): # 从 select 库中选择合适 io 机制, Linux 优先选择 epoll. if hasattr(select, "epoll"): # platform.py 定义了 EPollIOLoop 实现, 派生于 IOLoop 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
即 IOLoop 调用了基类 Configurable new 函数, 代码中又调用了
重写的 configurable_xxx 函数来实现找到 平台底层 io 库的目的.
实际上 EPollIOLoop 派生于 PollIOLoop, PollIOLoop 里封装了更
详细的 ioloop 行为, 例如 add_handler update_handler remove_handler
事件的注册与注销, 读写事件监听都封装在其中, 同时支持三个事件io库,
即跨平台支持(通过更具体的派生类 eg: EPollIOLoop )
此时 语句3 的start 函数其实是 EPollIOLoop 的 start() 调用.
总结:
1. tornado 单例为了保证全局(线程中唯一, 引入threading.Lock() 保证单例生成成功)
见 with IOLoop._instance_lock: 语句( _instance_lock = threading.Lock() )
2. tornado IOLoop 返回的单例 ioloop 实例引用并不是 IOLoop 类本身实例化,而是其派生出的类的实例化, 根据 Python select 基础库属性选择系统提供的 io 库.
eg: Linux epoll, BSD: kqueue, Windows: select
并且派生类围绕其底层接口做封装操作.
3. 为了实现 2描述的跨平台支持, IOLoop 实例在生成时, tornado 利用了 new方法干预了类的实例生成, 其代码实现很好地利用了 Python 语言本身提供的机制.
4. tornado ioloop 单例类为保证每一个线程中唯一, 且在任何 io 事件能正常执行, 因此框架代码随处可见 IOLoop.current() 调用, 源码框架其实还是有点瑕疵的, (^ ^) 嘻嘻……
相关文章推荐
- Tornado Web服务器中处理空白字符的解决方案
- python为tornado添加recaptcha验证码功能
- Tornado Web服务器多进程启动的2个方法
- 高性能web服务器框架Tornado简单实现restful接口及开发实例
- Tornado服务器中绑定域名、虚拟主机的方法
- Python Web服务器Tornado使用小结
- tornado捕获和处理404错误的方法
- Web服务器框架 Tornado简介
- tornado把static_path指向到七牛
- tornado 源码初识
- tornado 源码分析 之 异步io的实现方式
- 实践,用tornado实现自定义协议server
- redis集群搭建
- 用Python(Tornado)模拟登录小米帐号
- nginx+tornado与nginx+go的性能测试
- tornado使用UIModule块
- Tornado Application对象实例化时"debug=Ture"参数的作用原理
- Python编写的DHT的搜索引擎(测试)
- tornado实现高性能无阻塞udp通信(2)——实现异步udp客户端
- tornado实现高性能无阻塞udp通信(1)——server端实现