Tornado源码剖析
pdb调试
简单的介绍一下pdb的调试, 更详细的命令查看python pdbf官方文档
|
|
| 命令 | 说明 |
|---|---|
| n | 运行下一行代码 |
| p | 计算p后面的表达式(当前上下文中), 并打印表达式的值 |
| s | 进入函数 |
| r | 从函数中返回 |
| b | 动态设置断点 |
| w | 打印当前栈信息 |
| q | 退出pdb |
Tornado剖析
Tornadois a Python web framework and asynchronous networking library, originally developed at FriendFeed. By usingnon-blocking network I/O, Tornado can scale to tens of thousands of open connections, making it ideal for long polling, WebSockets, and other applications that require a long-lived connection to each user.
源码剖析基于Tornado 2.0.0及以下测试源码
|
|
首先运行一上源码(服务器端), 另外开启一个Terminal来发出请求(也可以使用浏览器来访问). 服务端收到请求后, 会在我们pdb.set_trace()停下等待调试.
|
|
一. 首先Application调用__init__, 通过self.add_handlers(".*$", handlers)配置路由, 配置静态资源资源路径.
二. tornado.httpserver.HTTPServer接收一个Application参数并命名为request_callback, 注意此处Application实例被命名为request_callback 留意HTTPServer中有一个属性self._sockets保存fd到socket object的映射.
三. 调用HTTPServer的listen(options.port)方法开始做socket监听. listen中做了两件事. 一: bind创建socket对象设置并设置非阻塞, 并进行socket.bind()和socket.listen()监听. 并在self._sockets保存socket描述符和socket对象的映射. 二: start函数初始化一个IOLoop对象(单例对象), 并遍历self._socketssocket描述符, 将其添加到IOLoop并绑定读事件
为了方便查看, 本来去掉源码中的异常捕获和一些干扰阅读的细节
|
|
四. tornado.ioloop.IOLoop.instance().start()中通过步骤三中全局的IOLoop对象执行start. 此处核心代码为event_pairs = self._impl.poll(0.2). 其中self._impl根据不同平台来选择select/poll/epoll/kqueue, 每当有事件可读时, 则执行其回调函数self._handle_events. 整个回调函数为HTTPServer中的handler_events函数
|
|
五. 最后我们发现, 让了一圈又回到HTTPServer._handle_events函数上. 函数中创建了IOStream对象和HTTPConnection对象.
|
|
其中iostream.IOstream回通过client socket(connection)来初始化, 并且完成将fd注册到IOLoop的epoll中. 当fd可读时, 会调用IOStream._handle_events函数做回调, 描述符可读, 即客户端发送了HTTP, 读取客户端发送的数据写入IOStream中的self._read_buffer读缓冲区中.
|
|
然后通过iostream来初始化HTTPConnection, 注意HTTPConnection中的self.request_callback属性就是在HTTPServer初始化时的Application. 其中执行self.stream.read_until并读取缓冲区的数据, 然后回调HTTPConnection._on_headers解析HTTP请求的头部, 然后出现了最重要的一步!!!, self.request_callback(self._request), 终于出现了, 这是最吼的! self.request_callback就是我们用来初始化HTTPServer的Application对象呀!!! 这货有个黑魔法函数__call__, 可以直接对Application对象进行传参调用!!! 传入客户端的request请求到Application中.
|
|
六. 重新看栈信息self.request_callback(self._request) web.py(1282)__call__()完全符合我们的分析. __call__接收self._request, 其中通过_request中host信息作路径匹配, 如果路径相匹配则返回我们创建的Handler类
|
|
我们再次查看栈信息, 发现此时的handler即为MainHandler, 并执行对象的_execute方法
|
|
七. handler._execute找到request中的HTTP方法, 然后执行对应的函数. 我们在MainHandler中实现了get方法, 此处会调用对应的方法. 并将数据发送给客户端.
|
|
八. 完成数据写入client socket后, HTTPRequest.finish()被调用执行移除时间和关闭客户端socket操作. 是不是已经晕了, 稍等我们来画个图来理一理整个流程.
