这里将从服务开启到请求进入之后的顺序进行源码解析。分析有误的地方希望大家指出哦~
开发环境:
- python3.7、tornado5.0
我们先创建一个最简单的tornado应用,从简单的栗子开始进行源码分析:
1 | import tornado.httpserver |
程序运行
首先,进入main中:
1 | tornado.options.parse_command_line() |
这一句通常是配合define("port", default=8000, help="run on the given port", type=int)
一起用的。他会解析命令行参数
实例化tornado.web.Application
接下来,实例化tornado.web.Application
对象,最简单的方式,就是这样的:
1 | app = tornado.web.Application( |
在初始化的过程中,发生了什么呢?
在源码中,我们发现,官方对他的解释是A collection of request handlers that make up a web application
,也就是说,其实他就是一个handler的集合,并且他可以自己本身成为一个web应用。我们发现他继承ReversibleRouter
类,它本身是一个抽象类,在ReversibleRouter类中,只有一个方法,这个方法可以理解为路由的反向解析。继续深挖,ReversibleRouter类又继承了Router
类,Router类又继承了httputil.HTTPServerConnectionDelegate
,因此,这样我们可以梳理出来一个tornado.web.Application
的继承顺序。
我们继续说初始化发生的事情。在Application
的__init__()
方法中有四个参数:
1 | def __init__(self, handlers=None, default_host=None, transforms=None, |
- handlers:是一个url路由列表,每一个路由都由一个元组组成;
- default_host:当tornado接受到request但是没有指定handler或者没有能够匹配的handler的时候,使用default_host做自动跳转
- transforms:HTTP传输压缩等,默认GZipContentEncoding 和 ChunkedTransferEncoding
- settings:对于一些静态文件、debug配置、ui的设置
当实例化Application的时候,主要做了以下操作:
加载ui模版
1 | def __init__(self, handlers=None, default_host=None, transforms=None, |
获取静态文件路径配置,并将favicon.ico和robots.txt添加到handlers中。这也是为什么当我们第一次请求的时候会发现找不到favicon.ico,tornado会自己到/static/favicon.ico
找。
1 | def __init__(self, handlers=None, default_host=None, transforms=None, |
获取debug
参数,如果为True
他会在代码修改后,自动重启。但是生产环境下不要打开debug。
1 | def __init__(self, handlers=None, default_host=None, transforms=None, |
将Application与handlers进行绑定,内部会将handlers的对应关系告诉Application
1 | def __init__(self, handlers=None, default_host=None, transforms=None, |
到此为止,Application已经初始化结束。关于tornado.web.RequestHandler
具体的实现后续会讲,小小的期待哦~
实例化tornado.httpserver.HTTPServer
实例化HTTPServer
时,会将Application传入,就像这样:
1 | http_server = tornado.httpserver.HTTPServer(app) |
tornado.httpserver.HTTPServer
是一个单线程非阻塞的HTTP服务,它多继承自TCPServer
,Configurable
, httputil.HTTPServerConnectionDelegate
。通常情况下,在实例化一个类的时候,会依次调用__new__()
和__init__()
,但是在tornado.httpserver.HTTPServer的__init__()
注释中,它是这样解释的:Ignore args to __init__; real initialization belongs in initialize since we're Configurable. (there's something weird in initialization order between this class, Configurable, and TCPServer so we can't leave __init__ out completely)
,也就是说,HTTPServer初始化会通过Configurable类找到HTTPServer的initialize()
(HTTPServer,Configurable和TCPServer之间的初始化顺序有些奇怪,所以我们不能完全抛弃__init__()
)。
通过debug的方式,我们发现,他会首先进入Configurable
的__new__()
中,其实当前真正的类对象是HTTPServer
,因为HTTPServer继承自Configurable。简单一点来讲,在这里主要做的就是调用HTTPServer
的initialize()
,并且HTTPServer是单例的:
1 | class Configurable(object): |
进入到HTTPServer.initialize()之后,我们发现request_callback
是一个必传的参数,这个参数对应的就是我们传入的Application对象了。
http_server.listen(options.port)
实例化tornado.httpserver.HTTPServer对象后,我们需要调用listen(),开始监听端口并接受给定端口上的连接。
1 | def listen(self, port, address=""): |
在这个方法里,做了两件事:
- 通过调用bind_sockets(port, address=address),在给定端口创建监听socket
- 告诉socket开始接收请求,并将每一个socket都加入到IOLoop中,通过,为这个socket绑定事件