Tomcat整体架构解析

 

comment:本文基于Tomcat7.0.68

1.整体结构

架构图: 
这里写图片描述

1.1各组件解释:

从顶层开始:

  • Server是Tomcat的最顶层元素,是service的集合,即可包含多个service,Server控制整个Tomcat的生命周期。
  • Service由一个Container和多个Connector组成(或者说由Connector,Engine和线程池[可选]组成),形成一个独立完整的处理单元,对外提供服务。

一般情况下我们并不需要配置多个Service,conf/server.xml默认配置了一个“Catalina”的<Service>。 
Tomcat将Engine,Host,Context,Wrapper统一抽象成Container。 
Connector接受到请求后,会将请求交给Container,Container处理完了之后将结果返回给Connector 
下面看Container的结构: 
这里写图片描述

  • Engine:没有父容器,一个 Engine代表一个完整的 Servlet 引擎,它接收来自Connector的请求,并决定传给哪个Host来处理,Host处理完请求后,将结果返回给Engine,Engine再将结果返回给Connector。
  • Host:Engine可以包含多个Host,每个Host代表一个虚拟主机,这个虚拟主机的作用就是运行多个应用,它负责安装和展开这些应用,并且标识这个应用以便能够区分它们,每个虚拟主机对应的一个域名,不同Host容器接受处理对应不同域名的请求。
  • Context:Host可以包含多个Context,Context是Servlet规范的实现,它提供了Servlet的基本环境,一个Context代表一个运行在Host上的Web应用
  • Wrapper: Context可以包含多个Wrapper, Wrapper 代表一个 Servlet,它负责管理一个 Servlet,包括的 Servlet 的装载、初始化、执行以及资源回收。Wrapper 是最底层的容器,它没有子容器了,所以调用它的 addChild 将会报错。

组件包含关系

这里写图片描述 
Standard*XXXX*是组件接口的默认实现类。

其它组件

Tomcat 还有其它组件,如安全组件 security、logger、session、naming 等其它组件。这些组件共同为 Connector 和 Container 提供必要的服务。

1.2组件的生命线Lifecycle

Tomcat中很多组件具有生命周期,如初始化、启动、关闭,这些组件的生命周期具有共性,因此Tomcat中将其抽象为接口Lifecycle,来控制组件的生命周期,它通过 事件机制 实现各个容器间的内部通讯。 
Lifecycle接口的方法: 
这里写图片描述 
继承关系图: 
这里写图片描述
StandardServer,StandardService,Connector和上面4个容器等很多组件都实现了Lifecycle,组件实现这个接口就可以统一被拥有它的组件控制了,这样一层一层的直到一个 最高级的组件 就可以控制 Tomcat 中所有组件的生命周期,这个最高的组件就是 Server

2.启动流程

在bin目录下执行了./startup.sh 或者执行 ./catalina.bat start命令时,实际调用了Bootstrap启动类的main方法,并传递了start参数。 
Bootstrap#main方法的启动流程: 
参考http://blog.csdn.net/Zerohuan/article/details/50752635#t6 
附上别人总结的一张启动时序图: 
这里写图片描述
补充如下:

  • service的initInternal方法中调用 container.init() 是如何层级调用Engine,Host,Context,Wrapper的初始化方法? 
    • StandardService中container属性就是StandardEngine,这个init只将StandardEngine进行了初始化。Host,Context,Wrapper的初始化在哪里?在调用Host的start时,进行初始化if (state.equals(LifecycleState.NEW)) {init();}
  • 同上,service的startInternal调用container.start,调用了Engine,Host的startInternal,Context的start在哪里调用了? 
    • Host启动时触发事件,HostConfig监听到STARTING事件后会调用Context.start。
  • 在tomcat7中,mapperListener的初始化和启动是封装在Connector中的。
  • 关于Connector初始化和启动的更多细节,可参考本人另一篇blog http://blog.csdn.net/cx520forever/article/details/52198050

3.pipeline valve机制

3.1名词解释

  • pipeline 管道,可以比作车间生产线,在这里可认为是容器的逻辑处理总线
  • valve 阀门,可以比作生产线上的工人,负责完成各自的部分工作。 
    生产线(pipeline)可以配置其工人的位置。 
    与车间生产线不同的是,tomcat中pipeline的不同是valve拿到输入,处理完成后,需要将输出返回给调用方。

3.2总体分析

四个基本容器对象里面都有一个pipeline及valve模块,是容器类必须具有的模块,对象生成时set该属性。Pipeline就像是每个容器的逻辑总线。在pipeline上按照配置的顺序,加载各个valve。通过pipeline完成各个valve之间的调用,各个valve实现具体的应用逻辑。 
tomcat组件图: 
tomcat组件图 
从上图中看到,在Connector接收到一次连接并转化成HttpServletRequest请求对象后,请求传递如下: 
Connector–>Engine的Pipeline的ValveA中–>Engine Valve–>Host Pipeline的Error Report Valve和Host Value–>Context Valve–>Wrapper Valve中,在这里会经过一个过滤器链(Filter Chain)–>Servlet中。 
Servlet处理完成后一步步返回,最后Connector拿到response。

3.3接口及默认实现

pipeline

接口中定义的方法: 
接口中定义的方法
一个pipeline包含多个Valve,这些阀共分为两类,一类叫基础阀(通过getBasic、setBasic方法调用),一类是普通阀(通过addValve、removeValve调用)。管道都是包含在容器中,所以有getContainer和setContainer方法。一个管道一般有一个基础阀(通过setBasic添加),可以有0到多个普通阀(通过addValve添加)。 
isAsyncSupported:当管道中的所有阀门都支持异步时返回ture,否则返回false 
该接口的标准实现是:org.apache.catalina.core.StandardPipeline 
Engine、Host、Context及Wrapper的pipeline属性都继承自父类ContainerBase。

Valve

接口方法: 
这里写图片描述 
重点关注setNext、getNext、invoke这三个方法,通过setNext设置该阀的下一阀,通过getNext返回该阀的下一个阀的引用,invoke方法则执行该阀内部自定义的请求处理代码。 
ValveBase:是Valve接口的基本实现 
四大容器类r都有各自缺省的标准valve实现。它们分别是

  • StandardEngineValve:StandardEngine中的唯一阀门,主要用于从request中选择其host映射的Host容器StandardHost。
  • StandardHostValve:StandardHost中最后的阀门,主要用于从request中选择其context映射的Context容器StandardContext以及访问request中的Session以更新会话的最后访问时间。
  • StandardContextValve:StandardContext中的唯一阀门,主要作用是禁止任何对WEB-INF或META-INF目录下资源的重定向访问,对应用程序热部署功能的实现,从request中获得StandardWrapper。
  • StandardWrapperValve:StandardWrapper中的唯一阀门,主要作用包括调用StandardWrapper的loadServlet方法生成Servlet实例和调用ApplicationFilterFactory生成Filter链。

Value继承体系类图:

这里写图片描述

自定义Valve

Valve实现了具体业务逻辑单元。可以定制化valve(实现特定接口),然后配置在server.xml里。每层容器都可以配置相应的valve,当只在其作用域内有效。例如engine容器里的valve只对其包含的所有host里的应用有效。 
配置举例:

<Engine name="Catalina" defaultHost="localhost">  
  <Valve className="MyValve0"/>  
  <Valve className="MyValve1"/>  
  <Valve className="MyValve2"/>  
   ……  
  <Host name="localhost"  appBase="webapps">  
  </Host>  
</Engine
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

当在server.xml文件中配置了一个定制化valve时,会调用pipeline对象的addValve方法,将valve以链表方式组织起来,代码如下;

@Override
    public void addValve(Valve valve) {

        // Validate that we can add this Valve
        if (valve instanceof Contained)
            ((Contained) valve).setContainer(this.container);

        // Start the new component if necessary
        if (getState().isAvailable()) {
            if (valve instanceof Lifecycle) {
                try {
                    ((Lifecycle) valve).start();
                } catch (LifecycleException e) {
                    log.error("StandardPipeline.addValve: start: ", e);
                }
            }
        }

        // Add this Valve to the set associated with this Pipeline
        //将配置的valve添加到链表中,并且每个容器的标准valve在链表的尾端 
        if (first == null) {
            first = valve;
            valve.setNext(basic);
        } else {
            Valve current = first;
            while (current != null) {
                if (current.getNext() == basic) {
                    current.setNext(valve);
                    valve.setNext(basic);
                    break;
                }
                current = current.getNext();
            }
        }

        container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

valve按照容器作用域的配置顺序来组织valve,每个valve都设置了指向下一个valve的next引用。同时,每个容器缺省的标准valve都存在于valve链表尾端,最后被调用。 
Pipeline内部维护first和basic两个阀,其它相关阀通过getNext来获取。

标准valve的调用逻辑图: 
这里写图片描述 
从StandardEngineValve开始, 所有的基础阀的实现最后都会调用其下一级容器,所有的普通阀都会执行getNext().invoke(request, response);,一直到StandardWrapperValve,完成请求处理过程。因为Wrapper是对一个Servlet的包装,所以它的基础阀内部调用的过滤器链的doFilter方法和Servlet的service方法。 
上述机制保证了请求传递到servlet去处理。

当采用tomcat默认初始配置时,Valve链如下: 
这里写图片描述
这些阀门Valve通过invoke方法彼此串联起来,最终构成的执行顺序十分类似于一个管道。

4.Tomcat中的设计模式

4.1模板方法模式

把通用的骨架步骤抽象到父类中,子类去实现特定的某些步骤。 
举例: 
如LifecycleBase类中init和start方法,其中的nitInternal和startInternal方法是抽象方法,所有容易都直接或间接继承了LifecycleBase,在初始化和启动时被每个容器会调用其init和start方法,这些抽象方法都是在子类中实现的。

4.2责任链模式

  • Tomcat中的ApplicationFilterChain实现了Filter拦截和实际Servlet的请求,是典型的责任链模式。
  • Pipeline-Valve机制也是责任链模式,从Engine到Host再到Context一直到Wrapper都是通过一个链来传递请求。

4.3观察者模式

Tomcat通过LifecycleListener对组件生命周期组件Lifecycle进行监听,各个组件在其生命期中会有各种行为,而这些行为都会触发相应的事件,Tomcat就是通过侦听这些事件达到对这些行为进行扩展的目的。在看组件的init和start过程中会看到大量如:fireLifecycleEvent(CONFIGURE_START_EVENT, null);这样的代码,这就是对某一类型事件的触发,如果你想在其中加入自己的行为,就只用注册相应类型的事件即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值