Tornado6.0.3-源码分析之Application

一、回顾联结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方法的实现逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值