【Tomcat】Tomcat整体架构及其设计精髓分析(下)
Tomcat工作原理 + Tomcat生命周期实现剖析(下期)
三、Tomcat工作原理
思考:Tomcat 是怎么确定请求是由哪个 Wrapper 容器里的 Servlet 来处理的呢?
(1)请求定位 Servlet 的过程
Tomcat 是用 Mapper 组件来完成这个任务的。
Mapper 组件的功能就是将用户请求的 URL 定位到一个 Servlet。
它的工作原理是:Mapper 组件里保存了 Web 应用的配置信息,其实就是容器组件与访问路径的映射关系。
- Host 容器里配置的域名
- Context 容器里的 Web 应用路径
- Wrapper 容器里 Servlet 映射的路径
你可以想象这些配置信息就是一个多层次的 Map。当一个请求到来时,Mapper 组件通过解析请求 URL 里的域名和路径,再到自己保存的 Map 里去查找,就能定位到一个 Servlet。
一个请求 URL 最后只会定位到一个 Wrapper 容器,也就是一个 Servlet。
以上面的图为例,请求是 http://localhost:8080/mvc/hello.do
- 首先,连接器监听到该请求(监听8080端口)
- 然后,通过父组件Service找到Engine,对该请求进行处理(Service与Engine是一对一的关系,找到了Service,就可以确定是哪一个Engine)
- 通过 “ localhost ” 找到对应的 host 。
- 再通过访问路径path,找到对应的Context,对应的Wrapper。
(2)请求在容器中的调用过程
连接器中的 Adapter 会调用容器的 Service 方法来执行 Servlet
最先拿到请求的是Engine 容器,Engine 容器对请求做一些处理后
会把请求传给自己子容器 Host
继续处理,依次类推,最后这个请求会传给 Wrapper 容器,Wrapper 会调用最终的 Servlet 来处理。
那么这个调用过程具体是怎么实现的呢?
答:使用 Pipeline-Valve 管道
Pipeline-Valve 是责任链模式,责任链模式是指在一个请求处理的过程中有很多处理者依次对请求进行处理,每个处理者负责做自己相应的处理,处理完之后将再调用下一个处理者继续处理。 Valve 表示一个处理点,比如权限认证和记录日志。(每个人做自己的事情,做完了下一个人接着做)
Pipeline 中维护了 Valve 链表,Valve 可以插入到 Pipeline 中,对请求做某些处理。整个调用链的触发是 Valve 来完成的,Valve 完成自己的处理后,调用 getNext.invoke() 来触发下一个 Valve 调用。
每一个容器都有一个 Pipeline 对象,只要触发这个 Pipeline 的第一个 Valve,这个容器里 Pipeline 中的 Valve 就都会被调用到。Basic Valve 处于 Valve 链表的末端,它是 Pipeline 中必不可少的一个 Valve,负责调用下层容器的 Pipeline 里的第一个 Valve。
整个调用过程由连接器中的 Adapter 触发的,它会调用 Engine 的第一个 Valve
Wrapper 容器的最后一个 Valve 会创建一个 Filter 链,并调用 doFilter() 方法,最终会调到 Servlet 的 service 方法。
Valve 和 Filter 的区别
- Valve 是 Tomcat 的私有机制,与 Tomcat 的基础架构 、API 是紧耦合的。Servlet API 是公有的标准,所有的 Web 容器包括 Jetty 都支持 Filter 机制。
- Valve 工作在 Web 容器级别,拦截所有应用的请求;而 Servlet Filter 工作在 应用级别,只能拦截某个 Web 应用的所有请求。
四、Tomcat生命周期实现剖析
Tomcat的组件
如果想让Tomcat能够对外提供服务, 我们需要创建、组装并启动Tomcat组件;在服务停止的时候,我们还需要释放资源,销毁 Tomcat组件,这是一个动态的过程。
Tomcat 需要动态地管理这些组件的生命周期。
在我们实际的工作中,设计一个比较大的系统或者框架时需要考虑如下的这些问题
- 如何统一管理组件的创建、初始化、启动、停止和销毁?
- 如何做到代码逻辑清晰?
- 如何方便地添加或者删除组件?
- 如何做到组件启动和停止不遗漏、不重复?
Tomcat组件具有两层关系
- 第一层关系是组件有大有小,大组件管理小组件,比如 Server 管理 Service,Service 又管理连接器和容器。
- 第二层关系是组件有外有内,外层组件控制内层组件,比如连接器是外层组件,负责对外交流,外层组件调用内层组件完成业务功能。也就是说,请求的处理过程是由外层组件来驱动的。
Tomcat组件创建规则(顺序)
这两层关系决定了系统在创建组件时应该遵循一定的顺序。
- 第一个原则是先创建子组件,再创建父组件,子组件需要被“注入”到父组件中。
- 第二个原则是先创建内层组件,再创建外层组件,内层组建需要被“注入”到外层组件。
最直观的做法就是将所有的组件按照先小后大、先内后外的顺序创建出来,然后组装在一起。
但是,这种思路不仅会造成代码逻辑混乱和组件遗漏,而且也不利于后期的功能扩展。
为了解决这个问题,我们需要找到一种通用的、统一的方法来管理组件的生命周期,就像汽车“ 一键启动 ”那样的效果。