一、回顾联结HTTPServer
从前面上一节对HTTPServer的源码分析,我们知道Application类需要满足继承至HTTPServerConnectionDelegate类,并实现其start_request方法,该方法返回一个HTTPMessageDelegate类的实例,从而后续HTTPServer可以把相应的http的请求提交给HTTPMessageDelegate类相关的实例去做逻辑处理;
二、Application的类间关系
通过查看源码,可以知道,Application并不是直接继承至HTTPServerConnectionDelegate类,而是在这其中穿插实现了一个Router类的相关功能,这个Router类就好比于是网络中的路由器的作用是一样的,其目的就是针对不同的url请求,找到相应的路由规则,提交给对应的处理方去进行逻辑处理。因为一个Web服务,在真实实现上,肯定是需要定义不同的url资源和相应的处理逻辑的。
先看下Application的继承图。Application类继承至ReversibleRouter类,而ReversibleRouter类继承至Router类,Router类继承至HTTPServerConnectionDelegate类,从而完成Application类继承至HTTPServerConnectionDelegate类的实现。接下来我们从Router类开始讲起。
2.1 Router类
从类的注释也已经明白这个类的作用了,它本身是定义一个路由功能的接口类,规定其子类要实现一个find_handler接口,来针对每个具体的http请求(HTTPServerRequest类实例),匹配找到最合适的处理对象(HTTPMessageDelete类实例),同时Router又实现了HTTPServerConnectionDelegate类的start_request接口,返回一个_RoutingDelegate类对象。这个_RoutingDelegate类直接继承至HTTPMessageDelegate类,用于后续的实际的HTTP消息的处理,即对于更低一层的HTTPServer类来说,HTTPServer类会把后续的http请求提交给_RoutingDelegate对象来处理,_RoutingDelegate类内部具体的功能,我们等会再分析。
进一步遍历查看Router类的子类,可以看到其目前只有两个子类:ReversibleRouter类和RuleRouter类。这两个子类,就相当于把Router类功能区分为两大块:
- ReversibleRouter类
该类本身也还是抽象的接口定义类,主要作用是支持命名的路由,其对子类又重新要求实现一个reverse_url的接口。 - RuleRouter类
该类是对路由规则查找的接口定义类。这里先不管这个类的子类实现,等会以实例讲解时,再具体说明。
2.2 RequestHandler类和Application类
RequestHandler类是对http请求的处理类,这是一个基础类,需要应用程序继承该类,并至少实现其中的一种HTTP方法(“GET”, “HEAD”, “POST”, “DELETE”, “PATCH”, “PUT”, “OPTIONS”)。一系列的RequestHandler类的实例构成的Web应用,就是一个Application类对象。就是说,实际上,对于一个特定的url资源,每一个http请求,要怎么具体处理,由继承自RequestHandler子类来实现,而Application类对象的作用就是当用户该这个url资源时,正确匹配找到url所对应的RequestHandler对象。所以Application就相当于处理容器一样,里面放置着很多具体的处理执行对象;
三、以例子带动分析
3.1 启动过程
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
# 初始化并返回一个web的应用对象app
app = make_app()
# 设置该web服务的监听端口是8888
app.listen(8888)
# 启动ioloop循环,即启动web服务功能
tornado.ioloop.IOLoop.current().start()
前面的两点只是从整体上先简单说明下tornado的web应用的情况。接下来在具体的代码分析上,我们以这个例子为主。这个程序的作用是:启动一个Web服务,这个web服务的功能就是当有人访问8888端口的根目录资源时,响应Hello, world。通俗点讲,就是当你在本地浏览器输入http://127.0.0.1:8888,或者http://127.0.0.1:8888/ 这样的访问链接时,给你响应返回一个Hello, world。
从上面可以看到,MainHandler继承自RequestHandler类,并实现了其中的get方法,前面也有说过,对于每个具体的http协议的方法处理,是通过继承自RequestHandler类,并重载实现即可。
我们再来看下,make_app内部,是如何初始化一个Application应用对象的。在前面中,只是定义了一个MainHandler的处理类,那么这个处理类是需要在客户访问什么网络资源时,才被回调执行呢?这个就是需要在初始化Application时要指定。也就是[(r"/", MainHandler)]这句,形式上是一个列表,里面的每一个对象本身又是一个元组。这句的作用简单来说就是,指明,当访问的资源路径是根路径 / 时,相应的处理对象是MainHanlder类实例。接下来,仔细看下是如何完成初始化工作的。
从Application的__init__方法,传递进来的handlers列表主要用于初始化了_ApplicationRouter类。这个_ApplicationRouter类是专门提供给Application类内部实现,用于完成路由查询功能的,也就是通过这个类来绑定Application和RequestHandler类之间的关系。
可以看到_ApplicationRouter类也还是往其父类继续传递rules对象,那我们就继承跟进。前面我们讲过,Router类主要有两个子类:ReversibleRouter和RuleRouter类,这里的ReversibleRuleRouter同时继承这两个类,就相当于同时具备这两个类的逻辑功能,既能路由定义查询(RuleRouter类功能)又支持反向获取路由(ReversibleRouter功能)。但是这里还没有使用rules这个初始对象,继续跟。
继续跟进后,我们可以看到终于有RuleRouter类使用这个rules列表对象了。这个RuleRouter类是实现路由规则容器的概念,其内有一个self.rules列表对象,专门用于保存各个路由规则。通过这里可以看到,在初始化Application时,传递的rules对象( 即[(r"/", MainHandler)] ),通过RuleRouter.add_rules方法,解析了这个rules列表,并把每个列表元素转化为Rule类对象后,添加保存在RuleRouter.rules列表容器中(即self.rules.append(self.process_rule(rule)))
走到这里,上层定义的路径规则([(r"/", MainHandler)] )已经被解析转化到了内部处理逻辑中了。接下来,我们来看下Rule类的定义和功能,理解这其中的规则定义和实现。下图表示整体的类间关系。
其中的红色线内的就是刚才讲的Application内部使用的_ApplicationRouter对象的继承方式,可以看到最终通过RuleRouter和Rule类相关联上。而这里的Rule类就是代表着一条路由规则,其成员变量由下面这些组成:
- self.matcher 一个匹配器,用于判断决定本条路由规则是否匹配上特定的请求
- self.target 本条规则的应用处理对象,如RequestHandler类实例、HTTPServerConnectionDelegate子类实例、或者其它路由规则对象等;
- self.target_kwargs 传递给self.target的参数
- self.name 名称,用在ReversibleRouter.reverse_url方法中,通过名称查找路由。
而对于其中的匹配器matcher,Tornado在实现时,是通过定一个Matcher基类,并派生出AnyMatches、HostMatches、DefaultHostMatches、PathMatches这四类匹配器。其中最主要的是通过实现match方法,达到不同的匹配查找结果,若匹配上,返回一个字典参数后续用于传递给Rule.target对象,若没有
匹配上,则返回None。
举个例子,比如AnyMatches表示任何都匹配上,因此其match方法就是不做任何处理,直接返回一个空字典对象。
现在在了解了Rule类和Matcher相关子类的实现后。我们再返回来查看RuleRouter.add_rules方法的实现。
def add_rules(self, rules: _RuleList) -> None:
"""Appends new rules to the router.
:arg rules: a list of Rule instances (or tuples of arguments, which are
passed to Rule constructor).
"""
# 遍历外部传递进来的rules列表对象,在这个用例里面是[(r"/", MainHandler)]
for rule in rules:
if isinstance(rule, (tuple, list)):
# 这里是对每个路由的参数限制,从Rule的初始化方法,我们可以看到其最多就支持四个参数。
assert len(rule) in (2, 3, 4)
if isinstance(rule[0], basestring_type):
# 若第一个参数是字符串,则默认表示为一个路径匹配规则,在初始化Rule时
# 匹配器采用PathMatches类。
rule = Rule(PathMatches(rule[0]), *rule[1:])
else:
# 若第一个参数不是字符串,则就直接折解元组或者列表对象rule作为Rule的参数就好。
# 这个方式更多的是意味着上层在传递rules对象时,就已经把相应的路由规则的匹配器
# 都指定好了,所以这里不再做其它判断,直接折解并初始化即可。
rule = Rule(*rule)
# 可以看到在rules.append之前,每个rule还先传递给self.process_rule方法去处理。
# 这里的process_rule就是RuleRouter提供给上层,也就是其子类一个可重载预处理每条rule的
# 机会。具体的功能,可以沿着_ApplicationRouter的继承关系一路追查即可。默认下就是原样返回
# rule对象。
self.rules.append(self.process_rule(rule))
查看到这里,我们知道在最开始的make_app代码中,实例化Application时,定义的路由规则[(r"/", MainHandler)] ,最终是被转化并保存一个Rule类对象中,这个Rule.matcher是一个PathMatches类对象,Rule.target是MainHandler,Rule.target_kwargs 和 Rule.name都是空的,同时要知道的是在Application内部用于路由匹配查找逻辑的是借助了_ApplicationRouter类。而对于Application.listen方法就简单了初始化一个HTTPServer,设置监听端口而已。
def listen(self, port: int, address: str = "", **kwargs: Any) -> HTTPServer:
"""Starts an HTTP server for this application on the given port.
"""
server = HTTPServer(self, **kwargs)
server.listen(port, address)
return server
最后就是启用io loop循环,让HTTPServer的端口监听真正运行起来就好。
tornado.ioloop.IOLoop.current().start()
讲到这里,整个Web应用程序的启动过程就完成了。接下来,就是等有客户端想访问监听的8888端口的根目录资源时(如本地浏览器访问http://127.0.0.1:8888),触发回调处理的流程而已。
3.2 触发回调处理
当有客户端访问这个Web服务时,是如何回调到MainHandler函数的呢?这个就要从之前就讲到的,HTTPServer在监听到一个新的连接后,会通过其HTTPServer.start_request方法,从Application层这边获取得到一个HTTPMessageDelegate类的子类实现实例。在Tornado中,Application是间接继承至Router类,而Router类继承至HTTPServerConnectionDelegate,并且Router类实现了start_request方法,所以当HTTPServer回调到Application的start_request方法时,实际上调用的就是这个Router.start_request方法,因此当触发回调后,从Application类这层来看相应的实现,首先就是这个Router.start_request方法。
class Router(httputil.HTTPServerConnectionDelegate):
"""Abstract router interface."""
def find_handler(
self, request: httputil.HTTPServerRequest, **kwargs: Any
) -> Optional[httputil.HTTPMessageDelegate]:
raise NotImplementedError()
def start_request(
self, server_conn: object, request_conn: httputil.HTTPConnection
) -> httputil.HTTPMessageDelegate:
# 可以看到这里并没有其它逻辑,只是直接返回一个_RoutingDelegate类实例,
# 而这个_RoutingDelegate就是直接继承至HTTPMessageDelegate类。
return _RoutingDelegate(self, server_conn, request_conn)
下面再来看下_RoutingDelegate类的初始化,可以看到这里面也没有其它初始化逻辑,只是简单保存一下成员变量而已,self.server_conn表示的是本次的HTTP网络概念上的连接,self.request_conn表示的是本次HTTP交互中请求与响应的逻辑上的连接对象,self.delegate表示真正用于处理本次HTTP请求的处理执行对象,实例化_RoutingDelegate时,并不知道待处理的HTTP请求的具体信息,所以此时还不知道self.delegate要指向谁;self.router就是指路由规则的容器对象,在这个实例实现中,就是指Application类对象。
class _RoutingDelegate(httputil.HTTPMessageDelegate):
def __init__(
self, router: Router, server_conn: object, request_conn: httputil.HTTPConnection
) -> None:
self.server_conn = server_conn
self.request_conn = request_conn
self.delegate = None # type: Optional[httputil.HTTPMessageDelegate]
self.router = router # type: Router
我们从之前分析的HTTP的流程中可以知道,当HTTPServer处理到成功读取了HTTP请求的首行、请求消息头域后,会通过调用HTTPMessageDelegate对象的headers_received方法,把相应读取的信息提供给上层处理,那么在这里实际是调用到了_RoutingDelegate.headers_received方法。下面来看下这个方法实现。
def headers_received(
self,
start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine],
headers: httputil.HTTPHeaders,
) -> Optional[Awaitable[None]]:
assert isinstance(start_line, httputil.RequestStartLine)
# 把属于本次连接,本次请求的内容保存,生成一个HTTPServerRequest对象,目的是保存信息和路由查找
request = httputil.HTTPServerRequest(
connection=self.request_conn,
server_connection=self.server_conn,
start_line=start_line,
headers=headers,
)
# 把本次的请求通过路由容器的find_handler方法来找到处理的实际对象。
# 那么在这里的router是Application类的对象。具体的查找流程,我们等会往下分析。
self.delegate = self.router.find_handler(request)
if self.delegate is None:
app_log.debug(
"Delegate for %s %s request not found",
start_line.method,
start_line.path,
)
# 如何没有找到合适的处理,就生成一个默认的处理对象。
self.delegate = _DefaultMessageDelegate(self.request_conn)
# 把相应的信息提交给这个self.delegate对象处理。
return self.delegate.headers_received(start_line, headers)
从上面的实现上,可以看到整体流程的关键是查找到对应路由规则的处理实现,是又再次转移到了self.delegate = self.router.find_handler(request)这个操作上了,也就是把控制查找的逻辑转移到了Application类上,这也说明_RoutingDelegate这个类实际上也只是起到一个中间桥梁的作用。
接下来,进一步查看Application.find_handler方法是如何进一步在应用层的层面上查找的。
def find_handler(
self, request: httputil.HTTPServerRequest, **kwargs: Any
) -> "_HandlerDelegate":
# 对于Application类来说,这里的default_route是指_ApplicationRouter对象,我们前面也有说到
# Application内部是使用这个对象来完成路由查找逻辑的,所以这里就转移到对应的_ApplicationRouter对象
route = self.default_router.find_handler(request)
if route is not None:
# 功能找到后,就返回相应的_HandlerDelegate对象
return cast("_HandlerDelegate", route)
# 往下表示没有查询匹配到时,再进一步判断下是否有设置默认的处理类,若有就使用该类,
# 若没有就使用ErrorHandler类,进行错误处理。
if self.settings.get("default_handler_class"):
return self.get_handler_delegate(
request,
self.settings["default_handler_class"],
self.settings.get("default_handler_args", {}),
)
return self.get_handler_delegate(request, ErrorHandler, {"status_code": 404})
对于Application类来说,其又将匹配查找逻辑转移到内部的成员self.default_router对象上,因此进一步追查_ApplicationRouter.find_handler的实现。_ApplicationRouter本身没有直接实现这个方法,但是从_ApplicationRouter类继承关系上,可以找到最终_ApplicationRouter.find_handler方法的实现在其父类RuleRouter中存在,因此这里实际上调用的是RuleRouter.find_handler方法。
def find_handler(
self, request: httputil.HTTPServerRequest, **kwargs: Any
) -> Optional[httputil.HTTPMessageDelegate]:
# 这里的self.rules,在这个例子中,就是我们最开始初始化时,用于保存路由规则的列表。
# 从前面的初始化中,可以知道,这时这个列表只有一个对象rule,就是转化[(r"/", MainHandler)]
# 后的rule规则
for rule in self.rules:
# 这里就用到了每条规则的匹配器的匹配方法。在这个例子中,这个matcher类型是PathMatches
target_params = rule.matcher.match(request)
# 若匹配上,则返回目标处理类的参数,注意返回{},也表示为匹配上。
if target_params is not None:
if rule.target_kwargs:
target_params["target_kwargs"] = rule.target_kwargs
# 这里再借助self.get_target_delegate方法,把匹配后相应的目标处理类rule.target,
# 这里的rule.target就是MainHandler类
# 请求信息request,目标处理类的参数target_params,转化为一个HTTPMessageDelegate
# 这里还是从继承关系上可以找到具体执行的是哪个类。具体的,下面再分析。
delegate = self.get_target_delegate(
rule.target, request, **target_params
)
if delegate is not None:
return delegate
return None
从前面可以看到,真正去匹配查找的,就是通过每个路由规则的匹配器。而对于我们这个例子来说,这里的匹配器matcher是指PathMatches类型,这个从最前面初始化过程就可以知道了。每个匹配逻辑,就是使用正则表达式去匹配特定的字段,比如对于PathMatches就是匹配request.path,这里的request.path就是指http请求的路径path。其它的匹配器都是相似的。
target_params = rule.matcher.match(request)
在前面的RuleRouter.find_handler方法中,还看到在匹配上获取到目标处理对象的参数后,需要通过
delegate = self.get_target_delegate的形式转化为特定的一个处理类对象。这个还是一样的,从_ApplicationRouter这个类开始看起,看下这个方法有没有直接实现。可以找到_ApplicationRouter.get_target_delegate是实现了这个方法,因此这里调用的就是这个_ApplicationRouter类的方法,而不是其父类的方法。
def get_target_delegate(
self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any
) -> Optional[httputil.HTTPMessageDelegate]:
# 判断target的类型,若目标处理类是RequestHandler的子类,就走这个分支,在我们这个例子中是满足
# 这个条件的,因为MainHandler就是继承自RequestHandler。
if isclass(target) and issubclass(target, RequestHandler):
# 可以看到这里又转移到了self.application内部去生成,在这个例子是Applicationo类对象的内部
return self.application.get_handler_delegate(
request, target, **target_params
)
# 若目标处理类不是RequestHandler的子类,进一步从_ApplicationRouter的父类中去生成相应的target
# delegate对象。这部分自行去进一步分析就可以了。
return super(_ApplicationRouter, self).get_target_delegate(
target, request, **target_params
)
_ApplicationRouter.get_target_delegate内转移到了 self.application.get_handler_delegate去生成,也就是调用了Application.get_target_delegate方法,从实现上可以出Application.get_target_delegate就是生成了一个_HandlerDelegate对象。
def get_handler_delegate(
self,
request: httputil.HTTPServerRequest,
target_class: Type[RequestHandler],
target_kwargs: Optional[Dict[str, Any]] = None,
path_args: Optional[List[bytes]] = None,
path_kwargs: Optional[Dict[str, bytes]] = None,
) -> "_HandlerDelegate":
return _HandlerDelegate(
self, request, target_class, target_kwargs, path_args, path_kwargs
)
走到这里,我们终于执行完了_RoutingDelegate.headers_received中,self.delegate = self.router.find_handler(request) 这一句,在内部查找匹配并生成对应的处理代理对象的过程,也就是说这里的self.delegate是_HandlerDelegate对象,然后就通过这个对象去执行http中头部消息的接收,即return self.delegate.headers_received(start_line, headers)。这部分就读者自己往下看下吧,其实也就是分析下_HandlerDelegate.execute方法的实现逻辑。