tomcat源码分析——架构原理

参考文章Tomcat 系统架构与设计模式
参考文章Tomcat 工作原理
参考文章四张图了解tomcat

Tomcat的顶层结构图

Tomcat中最顶层的容器是Server,代表着整个服务器,从上图中可以看出,一个Server可以包含至少一个Service,用于具体提供服务。

Service主要包含两个部分:Connector和Container。从上图中可以看出 Tomcat 的心脏就是这两个组件,它们的作用如下:

Connector用于处理连接相关的事情,并提供Socket与Request和Response相关的转化;

Container用于封装和管理Servlet,以及具体处理Request请求。

一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但是可以有多个Connectors,这是因为一个服务可以有多个连接,如同时提供Http和Https链接,也可以提供向相同协议不同端口的连接,示意图如下(Engine、Host、Context下边会说到):


协议处理逻辑图

多个 Connector 和一个 Container 就形成了一个 Service,有了 Service 就可以对外提供服务了。但 Service 还要一个生存的环境,必须要有人能够给它生命、掌握其生死大权,而这就非 Server 莫属。所以,整个 Tomcat 的生命周期由 Server 控制。

另外,上述的包含关系或者说是父子关系,都可以在Tomcat的conf目录下的server.xml配置文件中看出

image.png

二、Tomcat顶层架构小结

Tomcat中只有一个Server,一个Server可以有多个Service,一个Service可以有多个Connector和一个Container;

Server掌管着整个Tomcat的生死大权;

Service 是对外提供服务的;

Connector用于接受请求并将请求封装成Request和Response来具体处理;

Container用于封装和管理Servlet,以及具体处理request请求。

以上是整个Tomcat顶层的分层架构和各个组件之间的关系以及作用,对于绝大多数的开发人员来说,Server和Service对我们来说确实很远,而开发中绝大部分进行配置的内容是属于Connector和Container的,所以接下来介绍一下Connector和Container。
三、Connector和Container的微妙关系

由上述内容我们大致可以知道:

一个请求发送到Tomcat之后,首先经过Service然后会交给我们的Connector,Connector用于接收请求,并将接收的请求封装为Request和Response来具体处理。Request和Response封装完之后再交由Container进行处理,Container处理完请求之后再返回给Connector,最后在由Connector通过Socket将处理的结果返回给客户端,这样整个请求的就处理完了!

Connector最底层使用的是Socket来进行连接的,Request和Response是按照HTTP协议来封装的,所以Connector同时需要实现TCP/IP协议和HTTP协议。

Tomcat既然处理请求,那么肯定需要先接收到这个请求,而想要接收请求这个东西,我们首先就需要看一下Connector。

四、Connector架构分析

Connector用于接受请求并将请求封装成Request和Response,然后交给Container进行处理,Container处理完之后在交给Connector返回给客户端。因此,我们可以把Connector分为四个方面进行理解:

(1)Connector如何接受请求的?

(2)如何将请求封装成Request和Response的?

(3)封装完之后的Request和Response如何交给Container进行处理的?

(4)Container处理完之后如何交给Connector并返回给客户端的?

首先看一下Connector的结构图(图B):

image

Connector就是使用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型,比如:Http11Protocol使用的是普通Socket来连接的,Http11NioProtocol使用的是NioSocket来连接的。

其中ProtocolHandler由包含了三个部件:Endpoint、Processor、Adapter。

Endpoint用来处理底层Socket的网络连接,Processor用于将Endpoint接收到的Socket封装成Request,Adapter用于将Request交给Container进行具体的处理。

Endpoint由于是处理底层的Socket网络连接,因此Endpoint是用来实现TCP/IP协议的,而Processor用来实现HTTP协议的,Adapter将请求适配到Servlet容器进行具体的处理。

Endpoint的抽象实现AbstractEndpoint里面定义的Acceptor和AsyncTimeout两个内部类和一个Handler接口。Acceptor用于监听请求,AsyncTimeout用于检查异步Request的超时,Handler用于处理接收到的Socket,在内部调用Processor进行处理。

至此,我们应该很轻松回答了(1)(2)(3)的问题,但(4)还是不知道。

接下来我们就来看一下Container是如何进行处理的,以及处理完之后是如何将处理完的结果返回给Connector的。
五、Container架构分析

Container用于封装和管理Servlet,以及具体处理Request请求,在Connector内部包含了4个子容器,结构图如下(图C):

image

(图C)

4个子容器的作用分别是:

(1)Engine:引擎,用来管理多个站点,一个Service最多只能有一个Engine;

(2)Host:代表一个站点,也可以叫虚拟主机,通过配置Host就可以添加站点;

(3)Context:代表一个应用程序,对应着平时开发的一套程序,或者一个WEB-INF目录以及下面的web.xml文件;

(4)Wrapper:每一Wrapper封装着一个Servlet。

image.png

六、Container如何处理请求的

Container处理请求是使用Pipeline-Valve管道来处理的!(Valve是阀门之意)

Pipeline-Valve是责任链模式,责任链模式是指在一个请求处理的过程中有很多处理者依次对请求进行处理,每个处理者负责做自己相应的处理,处理完之后将处理后的请求返回,再让下一个处理着继续处理。

image

但是,Pipeline-Valve使用的责任链模式和普通的责任链模式有些不同,区别主要有以下两点:

(1)每个Pipeline都有特定的Valve,而且是在管道的最后一个执行,这个Valve叫做BaseValve,BaseValve是不可删除的;

(2)在上层容器的管道的BaseValve中会调用下层容器的管道。

我们知道Container包含四个子容器,而这四个子容器对应的BaseValve分别在:

StandardEngineValve

StandardHostValve

StandardContextValve

StandardWrapperValve

Pipeline的处理流程图如下(图D):

image

(图D)

(1)Connector在接收到请求后会首先调用最顶层容器的Pipeline来处理,这里的最顶层容器的Pipeline就是EnginePipeline(Engine的管道)。

(2)在Engine的管道中依次会执行EngineValve1、EngineValve2等等,最后会执行StandardEngineValve,在StandardEngineValve中会调用Host管道,然后再依次执行Host的HostValve1、HostValve2等,最后在执行StandardHostValve,然后再依次调用Context的管道和Wrapper的管道,最后执行到StandardWrapperValve。

(3)当执行到StandardWrapperValve的时候,会在StandardWrapperValve中创建FilterChain,并调用其doFilter方法来处理请求,这个FilterChain包含着我们配置的与请求相匹配的Filter和Servlet,其doFilter方法会依次调用所有的Filter的doFilter方法和Servlet的service方法,这样请求就得到了处理!

(4)当所有的Pipeline-Valve都执行完之后,并且处理完了具体的请求,这个时候就可以将返回的结果交给Connector了,Connector在通过Socket的方式将结果返回给客户端。

Bootstrap执行过程

main函数

image.png

Tomcat启动时序图

Bootstrap的执行流程

  1. 执行static块
    为什么没有执行main方法,而先执行static块呢?
    原来一个类的运行,JVM做会以下几件事情:类装载、链接、初始化、实例化,而初始化阶段做的事情是初始化静态变量和执行静态方法等
    static块的作用是设置catalinaBaseFile、catalinaHomeFile
    调用Bootstrap.setAwait(true)
    Bootstrap.setAwait(true)内部通过反射,设置Catalina的await属性(默认为false)为true
    启动Catalina过程中,当Catalina将Tomcat的所有组件启动之后,会检查await属性,如果为true,会调用Catalina.await(),而Catalina.await()又会调用其内部的Server的await()
 if (await) {  
         await();  
          stop();  
        }  
public void await() {
 
        getServer().await();
 
    }

Server.await()包含一个while循环,此循环用于监听指定socket端口(默认为8005)的连接,当某个连接传入的参数为”SHUTDOWN”(默认为”SHUTDOWN”)时,终止此while循环(端口号和终止while循环的参数,在server.xml的Server标签设置)

Server.await()用来维持Bootstrap的main方法(main thread)处于运行状态,而线程池中监听http请求的线程是守护线程(daemon thread)

当Tomcat的指定端口接收到关闭命令时,Server.await()内的while循环终止,然后Catalina会调用stop()方法,关闭Tomcat的所有组件,最终Bootstrap的main thread终止,Tomcat关闭

2.5 调用Bootstrap.load(args)

Bootstrap.load(args)内部通过反射调用Catalina.load(args),Catalina将利用Digest(Digest详解)解析server.xml,创建相应组件的实例,之后调用Server.init(),Server初始化时,又会调用其内部的Service的init方法,即调用某一组件的init方法时,将触发其子组件的init方法

执行Bootstrap.load(args)将触发的动作


image.png

2.6 调用Bootstrap.start()

执行Bootstrap.start()将触发的动作


image.png

你可能感兴趣的:(tomcat源码分析——架构原理)