tomcat的原理-组件container

在 tomcat的原理-组件connector中,我们说CoyteAdapter类的sevice方法中 将request 和response 交给容器处理,那tomcat的container结构又是怎样的呢?

总述

先来张container的架构图:


image.png

1.container是一个接口, 它用于接收request 并返回response对象的一种容器
2.engine 是servlet引擎 ,是tomcat 容器中的顶级容器 ,管理多host。

  1. context 是servlet 的上下文,代表独立的应用程序。一个context可以有多个servlet。
  2. wrapper 是代表单独的servlet。
  3. tomcat中采用父子关系设计这些容器。为什么要这样设计?
    a.正好符合一个engine多host , 多 context ,多servlet 这种结构.
    b.分层架构,各司其职。
    c.组合模式 ,你中有我,我中有你。
  4. 最后我们用server.xml 来理解下tomcat 中的container关系:
image.png

分析

回顾上篇我们参数一个web请求访问tomcat会经过如下组件:

  1. accptor: 接收网络请求,发送事件给poller组件。
  2. poller: 接收I/O事件, 丢给业务线程池。
  3. processor: 处理socket。
  4. adapter :适配 httpServletReqeust 和 httpServletResponse
  5. 交给container 执行pipline。

来说说tomcat 的pipeline机制。
1.pipeline的结构?
本质就是个链表
初始状态: pipeline中用 两个属性 Valve first , Valve basic , first == null
初次添加 valve(假设叫a) : 给first 赋值 a 并将basic 连接到first后面
再次添加 valve(假设叫b): basic 连到b之后, b连接到basic 原来的前节点。

image.png

代码如下 : 类StandardPipeline 的addValve()

image.png

2.pipeline如何初始化的?
在container 实例化的时候,各个容器的pipeline完成初始化, 并设置基础的basic valve。
ContainerBase属性中:


image.png

StandardEngine类构造器中:


image.png

3.何时调用pipline?
在CoyoteAdapter类中的service方法中调用tomcat的pipline机制.


image.png

a. 由container的父子图我们知道,connector.getService().getContainer() 我们可以获取到 standardEngine对象
b. getFirst().invoke() 如果first == null , 直接调用basic valve 的invoke() ;否则 我们会在pipeline 的valve链上一直调用他的next valve 的invoke方法,直到最后一个basic valve 。
c. standardEngine 对象的pipleline属性的basic 为StandardEngineValve对象 , 会调用host 容器 的pipeline中的各个valve 直到 basicValve 。 过程和Engine容器的 pipeline一致。


image.png

image.png

d. 我们直接看StandardHostValve 的invoke() 方法 。


image.png

e.在看 StandardContextValve 的 invoke() 方法, 同 StandardHostValve 的invoke() 方法 ,同理调用Context容器中的每个valve。

image.png

在StandardContextValve 类中可以直接从request对象中获取到wrapper,我们思考一个问题 request.getWrapper()是如何将 wapper 封装到request对象中的??


image.png

结论: 在container的生命周期中,会使用发布订阅模式,发送事件,Mapper组件通过监听的方式去注册容器 , reqeust 对象在封装的时候从mapper 中匹配所需的容器对象装载到MappingData中,代码如下。

1.service中初始化mapper 和 mapperListner。


image.png
  1. mapperListner 实现了 ContainerListener, LifecycleListener接口


    image.png
  1. 在容器的生命周期方法中或添加子容器方法中会触发mapperListener 的containerEvent()或lifecycleEvent() ,会将容器注册到Mapper中
image.png
image.png
  1. 当tomcat 内部的request 在经过CoyteAdapter的service方法的时, 会调用Mapper的map方法将容器设置到request的MappingData中:


    image.png
image.png

f.context容器是如何添加子容器wrapper的?

我们知道wrapper 对应与我们web.xml 的servlet ,对于一个应用来说 我们可以配置很多servlet ,如果使用degister【后面分析tomcat lifecycle 再谈】 配置放入到context 中是不切实际的,
tomcat 通过发布订阅模式 监听器的方式来给context 配置子容器,上代码:

1.在StandardContext 的startInternal() 方法中, 会发送Lifecycle.CONFIGURE_START_EVENT 的事件


image.png
  1. ContextConfig 实现了LifecycleListener 接口,意味着当StandardContext 发送CONFIGURE_START_EVENT 事件后 ,ContextConfig会处理事件。


    image.png
  2. configureStart() 方法中调用了webconfig() 方法 , 在webConfig()方法中, 会扫描web.xml 文件 , WebXmlParser 解析文件, 加载ServletContainerInitializers , 配置context 的 过滤器FilterDef 等信息, 创建Wrapper 容器并添加到子容器中等操作
    ContextConfig 的webconfig()方法:


    image.png
image.png
image.png
image.png

这样context 就可以在start()生命周期方法中 , 去调用子容器wrapper 的start()方法了

总结:context 使用事件发送给contextConfig 配置context 属性/ 子容器等信息


image.png

g. 最后我们看下StandardWapperValve 的 invoke() 方法。


image.png

我们看下wrapper 是如何创建servlet的?

  1. 加载servlet


    image.png

实例工厂创建servlet 对象 , 其中servletClass 是contextConfig 解析web.xml 的时候set 到wrapper 中的。


image.png
  1. servlet 初始化 loadServlet方法中 调用了 initServlet(servlet)
image.png
image.png

为什么使用 门面模式 StandardWrapperFacade?
个人理解 :StandardWrapper 实现了servletConfig接口 ,StandardWrapperFacade 也实现了 servletConfig 接口,
但是StandardWrapper 中的很多wrapper 的细节 撸码者并不想暴露给servlet 所以使用 facade模式包装 StandardWrapper ,及只暴露servlet 所关心的。

到这里我们的servlet 已经创建好了, 在很久以前我开发中经常遇到乱码的问题 , 当时我记得写filter 来解决tomcat的乱码问题,在tomcat 中在调用我们的servlet 之前有一系列的filter , 下面我们看看tomcat 实现的Filter机制。

image.png

1.使用工厂模式创建拦截器链filterChain , 将web.xml中的配置的filter加入到filterChain的 filters 数组中 并 将n(filter数)自增1 , pos 用来记录当前执行的连接器下标。

  1. filterChain的doFilter()方法中: 当 n< pos 的时候 pos ++ , 并找到下一个filter【filter1】 继续执行 filter1 的dofilter()方法 , 再找 下下个filter【filter2】 继续执行 filter2 的dofilter()方法 ,依次类推 直到 n == pos

  2. 当 n== pos 的时候调用servlet的service方法处理业务

来回顾下责任链模式, 写个demo:

  1. 定义fiter 接口


    image.png
  2. 定义拦截链


    image.png
  1. 创建拦截器A B C 都实现了Filter 接口
image.png
image.png
  1. main 方法测试 :


    image.png
  2. 输出


    image.png

6.结论

image.png

总结

  1. tomcat 通过Adapter 的service 方法将 request 和response 交给 Engine Host Context wrapper 容器
  2. 每个容器中包含一个pipleLine ,而pipleLine 由多个valve 和一个标准BasicValue(StandardHostValve ...)
  3. Wrapper 容器的最后一个 Valve 会创建一个 Filter 链,并调用 doFilter() 方法,最终会调到 Servlet的 service方法。

来张图吧:

image.png

后面我们记录下tomcat 的生命周期和打破双亲委派机制 , 有错误的地方,请大佬指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容