Tomcat 9 源码解析 -- tomcat框架设计

Tomcat整体架构与重要组件

1.Tomcat整体架构

如果你浏览过Tomcat源码相关文章,你一定对此图不陌生.

Tomcat即是一个HTTP服务器,也是一个servlet容器,主要目的就是包装servlet,并对请求响应相应的servlet,纯servlet的web应用似乎很好理解Tomcat是如何装载servlet的,但,当使用一些MVC框架时,如spring MVC、strusts2,可能就找不出servlet在哪里?其实spring MVC框架就是一整个servlet,在web.xml中配置如下:

<!-- Spring MVC servlet -->
	<servlet>
		<servlet-name>SpringMVC</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-mvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
		<async-supported>true</async-supported>
	</servlet>
	<servlet-mapping>
		<servlet-name>SpringMVC</servlet-name>
		<!-- 此处可以可以配置成*.do,对应struts的后缀习惯 -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>

他的总体结构用下图来表示。

如上图所示,tomcat由Server、Service、Engine、Connerctor、Host、Context组件组成,

其中带有s的代表在一个tomcat实例上可以存在多个组件,比如Context(s),

tomcat允许我们部署多个应用,每个应用对应一个Context。

这些组件在tomcat的conf/server.xml文件中可以找到,对tomcat的调优需要改动该文件
 

server.xml
<Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
      </Host>
    </Engine>
</Service>

 

更细致一点的图形表示:

 

1. Tomcat主要有两个组件,连接器【Connector】和容器【Container】,所谓连接器就是一个http请求过来了,连接器负责接收这个请求,然后转发给容器。容器即servlet容器,容器有很多层,分别是Engine,

    Host,Context,Wrapper。最大的容器Engine,代表一个servlet引擎,接下来是Host,代表一个虚拟机,然后是Context,代表一个应用,Wrapper对应一个servlet。从连接器

    传过来连接后,容器便会顺序经过上面的容器,最后到达特定的servlet。要说明的是Engine,Host两种容器在不是必须的。实际上一个简单的tomcat只要连接器和容器就可以了,

    但tomcat的实现为了统一管理连接器和容器等组件,额外添加了服务器组件(server)和服务组件(service),添加这两个东西的原因我个人觉得就是为了方便统一管理连接器和

    容器等各种组件。一个server可以有多个service,一个service包含多个连接器和一个容器,当然还有一些其他的东西,看下面的图就很容易理解Tomcat的架构了:

  1.  

图一: 

  1. Server掌管着整个Tomcat的生死大权;
  2. Service 是对外提供服务的;
  3. Connector用于接受请求并将请求封装成Request和Response来具体处理;
  4. Container用于封装和管理Servlet,以及具体处理request请求。

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

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

Connector

由上面的图我们可以看出来一个Service服务可以包含多个Connector,实际上Tomcat中包含了3个Connector,分别对应HTTP请求,HTTPS请求以及AJP请求。那么Connector在Tomcat中到底扮演了什么样的角色呢?Tomcat中的Connector负责的任务就是接受请求,创建request和response,解析请求中数据,将数据封装到request中,最后将request和response作为参数传递给Container对象!

由于不同协议规则不同,解析起来也是不一样的,所以需要多个Connector。

Container

(ContainerBase、StandardServer、StandardService、WebappLoader、Connector、StandardContext、StandardEngine、StandardHost、StandardWrapper等容器都继承了LifecycleMBeanBase,因此这些容器都具有了同样的生命周期并可以通过JMX进行管理)

Container实际上只是容器的概称。而Tomcat中包含有多个Container。那么Container的作用是什么呢?

Container的作用就是接收Connector传递的request和response,对request和response进行了一些过滤封装,同时调用对应的Servlet去处理该request,最后将处理完成后的结果封装到response中返回给客户端!

每个Container从大到小分别为Engine-->Host-->Context-->Wrapper

  1. Engine容器代表整个Catalina servlet引擎
  2. Host容器表示一个虚拟主机,Tomcat自带的是一个叫localhost的主机
  3. Context容器表示一个WEB应用程序
  4. Wrapper表示一个独立的Servlet

对于一个Engine可以包含多个Host,Tomcat自带了一个叫做localhost的Host容器,一个Host容器可以包含多个Context,一个Context可以包含多个Wrapper。

是不是感觉这几个单词很熟悉,是的!在Tomcat中我们可以使用Tomcat默认提供的这些容器,也可以在CATALINA_HOME/conf下的server.xml中配置,而这几个单词正是对应server.xml中的各个配置选项的名称!

 

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类继承关系: 

 

 Http11NioProtocol类继承关系:

 

 

Endpont的继承关系 以及 类中定义的属性    :

NioEndpoint
这个NioEndpoint作为Tomcat NIO的IO处理策略,主要提供工作线程和线程池:
   Socket接收者Acceptor
   Socket轮询者Poller
   工作线程池

主要包含 LimitLatch、Acceptor、Poller、SocketProcessor、Excutor  5个部分:
   LimitLatch 是一个连接控制器,负责连接限制,nio模式下默认10000,达到阈值则拒绝连接请求。 (AbstractEndpoint 类中的属性 connectionLimitLatch )
      当一个请求进入到Tomcat的时候,就会调用 AbstractEndpoint # countUpOrAwaitConnectio()
      
   Acceptor 负责接收请求,默认由1个线程负责,将请求的事件注册到事件列表中。
   Poller 负责轮询上述产生的事件,将就绪的事件生成  SokcetProcessor  ,交给Excutor去执行。
   SocketProcessor 里面的doRun方法,封装了Socket的读写,完成Container调用逻辑。
   
看到 Endpoint 的属性中出现了很多 SynchronizedStack ,这个数据结构为Tomcat量身定做,是ConcurrentLinkedQueue一个GC-free的轻量级替代方案,
提供扩容方案,最大128,但没有提供减少容量的方法。减少容量必然带来数组对象的回收,适用于数据量比较固定的场景,另外这个数据结构本身由数组维护,
减少了维护节点的开销。
而另一个 SynchronizedQueue 则是一个GC-free的容器,只不过这个是一个FIFO无界容器。

 

 

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

首先看一下Connector的结构图

图二: 

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个子容器,结构图如下

图三: 

 

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

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

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

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

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

Context和Host的区别是Context表示一个应用,我们的Tomcat中默认的配置下webapps下的每一个文件夹目录都是一个Context,其中ROOT目录中存放着主应用,其他目录存放着子应用,而整个webapps就是一个Host站点。

我们访问应用Context的时候,如果是ROOT下的则直接使用域名就可以访问,例如:www.gaohaicheng.com,如果是Host(webapps)下的其他应用,则可以使用www.gaohaicheng.com/docs进行访问。当然默认指定的根应用(ROOT)是可以进行设定的,只不过Host站点下默认的主营用是ROOT目录下的。

看到这里我们知道Container是什么,但还是不知道Container是如何进行处理的,以及处理完之后是如何将处理完的结果返回给Connector的。

 

Container如何处理请求的

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

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

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

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

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

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

StandardEngineValve

StandardHostValve

StandardContextValve

StandardWrapperValve

Pipeline的处理流程图如下

图四: 

 

(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的方式将结果返回给客户端。

总结

至此,我们已经对Tomcat的整体架构有了大致的了解,从   图一、二、三、四    可以看出来每一个组件的基本要素和作用,各位读者在脑海里应该有一个大概的轮廓了。如果在面试的时候,能够简单地聊一下Tomcat,将上面的内容脱口而出,想必面试官一定会对你刮目相看的!

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值