Tomcat

基础

软件架构说明示例
C/S客户端/服务器端QQ,360...
B/S浏览器/服务器端淘宝网...
网络资源说明示例
静态资源用户访问后得到的结果都是一样的,可以直接被浏览器解析。HTML/CSS/JS/JPG...
动态资源用户访问相同资源后得到的结果各不一样。动态资源访问后需先转换成为静态资源再返回给浏览器,通过浏览器进行解析。Servlet/JSP/PHP/ASP...

网络通信三要素

网络通信说明
传输协议数据传输的规则
主机地址IP地址,计算机在网络中的唯一标识。
端口号应用程序在计算机中的唯一标识,取值范围0~65536。

Web服务器

服务器是指安装服务器软件的计算机,服务器软件是指用来接收用户请求、处理请求、作出响应的软件。而Web服务器软件是指可以部署Web项目,让用户通过浏览器可以访问的软件。

Web服务器软件说明
WebLogicOracle,收费,大型JavaEE服务器,支持JavaEE规范。
WebSphereIBM,收费,大型JavaEE服务器,支持JavaEE规范。
JBOSSJBOSS,收费,大型JavaEE服务器,支持JavaEE规范。
TomcatApache基金会组织,开源,中小型JavaEE服务器,仅支持少量JavaEE规范,比如Servlet、JSP。

Tomcat历史

Tomcat最初由SUN公司的软件架构师James Duncan Davidson开发,称为JavaWebServer。1999年在Davidson帮助下该项目于1991年与Apache软件基金会旗下的JServ项目合并,其后发不了第一个版本3.x,也就是现在的Tomcat,该版本实现了Servlet2.2和JSP1.1规范。

2001年Tomcat发布了4.0,作为里程碑式的版本,Tomcat完全重新设计了其架构,并实现了Servlet2.3和JSP1.2规范。

Tomcat安装配置

https://tomcat.apache.org

架构

HTTP工作原理

  • HTTP协议是浏览器与服务器之间的数据传输协议
  • HTTP是基于TCP/IP协议来传递数据的应用层协议
  • HTTP不涉及数据包packet的传输,主要规定了客户端和服务器之间的通信格式。
HTTP

Tomcat整体架构

浏览器发送给服务器一个HTTP格式的请求后,HTTP服务器会接受到这个请求,需要调用服务端程序来处理,所谓的服务端程序也就是程序员编写的Java类,一般而言不同的请求需要由不同的Java类来处理。

HTTP服务器请求处理

如果HTTP服务器直接调用业务类会直接产生紧耦合关系,为了解耦,HTTP服务器不直接调用业务类而是将请求交给Servlet容器来处理,容器通过Servlet接口来调用业务类。Servlet接口和Servlet容器的出现,达到HTTP服务器也业务类解耦的目的。Servlet接口和Servlet容器这一整套规范称为Servlet规范。

Tomcat按照Servlet规范的要求实现了Servlet容器,同时也具有HTTP服务器的功能。程序要实现业务功能时只需要实现一个Servlet,并将其注册到Tomcat的Servlet容器中,剩下的事情就会由Tomcat帮我们处理。

Servlet容器工作流程

为了解耦HTTP服务器不直接调用Servlet而是把请求交给Servlet容器来处理,Servlet容器是怎样工作的呢?

Servlet容器工作流程
  1. 当客户端请求某个资源时,HTTP服务器会调用ServletRequest对象将请求信息封装起来,然后调用Servlet容器的service方法。
  2. Servlet容器拿到请求后,根据请求的URL和Servlet的映射关系,找到对应地Servlet。
  3. 若此Servlet还没有被加载就会使用反射机制来创建,然后调用Servlet的init方法完成初始化,接收再调用Servlet的service方法来处理请求。
  4. 最后将ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端。

Tomcat整体架构

Tomcat需要实现两个核心功能

  • 处理Socket连接,负责网络字节流和Request和Response对象的转化。
  • 加载和管理Servlet,以及具体处理Request请求。

因此Tomcat设计了两个核心组件:连接器(Connector)、容器(Container)来分别做这两件事

  • 连接器:负责对外交流
  • 容器:负责内部处理
Tomat整体架构

连接器(Coyote)

  • Tomcat的连接器框架又称为Coyote,是Tomcat提供的供客户端访问的外部接口,客户端通过Coyote与服务器建立连接,发送请求并接受响应。
  • Coyote封装了底层的网络通信(Socket请求以及响应处理),为Catalina容器提供了统一的接口。使Catalina容器与具体的请求协议、IO操作方式完全解耦。
  • Coyote会将Socket输入转换封装为Request对象,交由Catalina容器处理,处理请求完成后Catalina通过Coyote提供的Response对象将结果写入输出流。
  • Coyote作为独立模块只负责具体协议和IO相关操作,与Servlet规范实现没有实际关系,因此即便是Request和Response对象也没有实现Servlet规范对应的接口,而是在Catalina中将它们进一步封装为了ServletRequest和ServletResponse。
Coyote

Coyote中Tomcat支持多种I/O模型

I/O模型说明
NIO非阻塞I/O,采用Java NIO类库实现。
NIO2异步I/O,采用JDK 7最新的NIO2类库实现。
APR采用Apache可移植运行库实现,C/C++编写的本地库,需单独安装。

Coyote中Tomcat支持的应用层协议

应用层协议说明
HTTP/1.1大部分Web应用采用的访问协议
HTTP/2HTTP2.0大幅提升了Web性能,是下一个的HTTP协议。
AJP用于和Web服务器集成(如Apache)以实现对静态资源的优化以及集群部署,当前支持AJP/1.3。
协议分层

Tomcat为了实现支持多种I/O模型与应用层协议,一个容器可能对接多个连接器。由于单独的连接器或容器不能对外提供服务,需要将其组装起来才能工作,组装后的整体叫做Service组件。Service只是在连接和容器外层多包了一层,把它们组装在一起而已。

Tomcat内部可能有多个Service,这样设计是处于对灵活性的考虑。通过在Tomcat中配置多个Service,可以实现通过不同的端口来访问同一台机器上部署的不同应用。

连接器组件

组件说明
EndPointCoyote通信端点,通信监听的接口。
ProcessorCoyote协议处理接口
ProtocolHandlerCoyote协议接口
Adapter将Request转换为ServletRequest
连接器组件

EndPoint

  • EndPoint是Coyote通信端点(通信监听的接口),是具体Socket连接和发送的处理器,是对传输层的抽象,用来实现TCP/IP协议。
  • Tomcat并没有提供EndPoint接口,而是提供了一个抽象类AbstractEndPoint。

AbstractEndpoint中定义了两个内部类Acceptor和SocketProcessor。

内部类说明
Acceptor用于监听Socket连接请求
SocketProcessor用于处理接受到的Socket请求,实现了Runnable接口,在Run方法里会调用协议处理组件Processor,来提高处理能力。

SocketProcessor会被提交到线程池来执行,这个线程池又成为执行器Executor。

Processor

  • Processor是Coyote协议处理接口,如果说Endpoint是用来实现TCP/IP协议的,那Processor就是用来实现HTTP协议的。
  • Processor接收来自EndPoint的Socket,读取字节流解析成Tomcat的Request和Response对象,再通过Adapter将其提交给容器处理。
  • Processor是对应用层协议的抽象

ProtocolHandler

  • ProtocolHandler是Coyote协议接口,通过Endpoint和Processor来实现针对具体协议的处理能力。

Tomcat按照协议和I/O提供了6个实现类

ProtocolHandler实现类协议I/O
AjpNioProtocolAJPNIO
AjpNio2ProtocolAJPNIO2
AjpAprProtocolAJPAPR
Http11NioProtocolHTTP1.1NIO
Http11Nio2ProtocolHTTP1.1NIO2
Http11AprProtocolHTTP1.1APR

配置Tomcat的tomcat/conf/server.xml时至少需要指定具体ProtocolHandler,也可以指定协议名称。

Adapter

由于协议不同,客户端发送过来的请求信息也不尽相同,Tomcat定义了自己的Request类来存放请求信息。

ProtocolHandler接口负责解析请求并生成Tomcat的Request类,但这个Request类不是标准的ServletRequest,这就意味着不能使用Tomcat Request作为参数来调用容器。

Tomcat设计者的解决方案是引用Coyote的Adapter(采用适配器设计模式),连接器通过调用Coyote的Adapter的service方法,传入Tomcat的Request对象。通过Adapter将Request转换为ServletRequest,然后再调用容器的service方法。

容器(Catalina)

  • Tomcat本身是由一系列可配置的组件构成的Web容器,而Cateline则是Tomcat的Servlet容器。
  • Catalina是Servlet容器实现,包含了所有的容器组件以及安全、会话、集群、管理等Servlet容器架构的各个方面。
  • Catalina通过松耦合的方式集成Coyote,以完成按照请求协议来读写数据。同时还包含了启动入口、Shell程序等。
Tomcat模块分层

Tomcat本质上是一款Servlet容器,Catalina才是Tomcat的核心,其他模块都是为Catalina提供支撑。比如:

  • 通过Coyote模块提供链路通信
  • 通过Jasper模块提供JSP引擎
  • 通过Naming模块提供JNDI服务
  • 通过Juli模块提供日志服务
Catalina组件结构

Catalina负责管理Server(表示整个服务器),Server下存在多个服务Service,每个Service都包含着多个Coyote实现的Connector连接器组件和一个Container容器组件。当Tomcat启动时会初始化一个Catalina实例。

组件说明
Catalina负责解析Tomcat配置文件以此创建服务器Server组件,并根据命令来对其管理。
Server服务器,表示整个CatalinaServlet容器以及其它组件,负责组装并安装Servlet引擎、Tomcat连接器。Server通过实现LifeCycle接口来提供优雅启动和关闭的方式。
Service服务,Server内部组件,一个Server包含多个Service,它将若干个Connector组件绑定到一个Container(Engine)上。
Connector连接器,处理与客户端的通信,负责接收客户端请求,转给相关容器处理,最后向客户端响应处理结果。
Container容器,负责处理用户的Servlet请求并返回对象给Web用户

Container

  • Tomcat设计了4种容器,分别是Engine、Host、Context、Wrapper,这4种容器不是平行关系而是父子关系。
  • Tomcat通过一种分层的架构,使Servlet容器具有很好地灵活性。
Container
组件说明
Engine表示整个Catalina的Servlet引擎,用来管理多个虚拟站点,一个Service最多只能有一个Engine,一个Engine可包含多个Host。
Host表示一个虚拟主机,可以给Tomcat配置多个虚拟主机地址,一个虚拟主机可包含多个Context。
Context表示一个Web应用程序,一个Web应用程序可包含多个Wrapper。
Wrapper表示一个Servlet,Wrapper作为容器最底层,不能包含子容器。

可通过Tomcat配置文件conf/server.xml来理解Tomcat容器。Tomcat采用组件化设计,构成组件都是可以配置的,其中最外层是Server,其他组件按照一定的格式要求配置在顶层Server容器中。

$ vim conf/server.xml
<Server port="8005" shutdown="SHUTDOWN">
  <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">
      <Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true">
      </Host>
    </Engine>
  </Service>
</Server>

Tomcat是如何管理这些容器的呢?由于容器具有父子关系形成了一个树形结构,与之相应的设计模式是组合模式。Tomcat就是利用组合模式来管理这些容器。具体实现是所有容器组件都实现了Container接口,因此组合模式可以使得用户对单容器对象和组合具有一致性。这里的单容器对象指的是最底层的Wrapper,组合容器对象指的是上面的Context/Host/Engine。

Tomcat

启动流程

加载Tomcat的配置文件,初始化容器组件,监听对应的端口号,准备接收客户端请求。

启动流程

Lifecycle

所有组件均存在初始化、启动、停止等生命周期方法,拥有生命周期管理的特性,Tomcat在设计时,基于生命周期管理抽象成了一个接口Lifecycle。而组件Server、Service、Container、Executor、Connector均实现了这个生命周期接口,从而具有生命周期中的核心方法:

生命周期方法说明
init()初始化组件
start()启动组件
stop()停止组件
destroy()销毁组件
生命周期接口

组件的默认实现

组件的默认实现

Endpoint组件

Tomcat中没有对应的Endpoint接口,但有一个抽象类AbstractEndpoint,其下有三个实现类分别是NioEndpoint、Nio2Endpoint、AprEndpoint,它们分别对应连接器Coyote支持的三种I/O模型:NIO、NIO2、APR。

ProtocolHandler

Coyote协议接口,通过封装Endpoint和Processor实现针对具体协议的处理功能。Tomcat按照协议和I/O提供了6个实现类。

协议类型实现类说明
AJPAjpNioProtocol采用NIO的I/O模型
AJPAjpNio2Protocol采用NIO2的I/O模型
AJPAjpAprProtocol采用APR的I/O模型,需依赖APR库。
HTTPHttp11NioProtocol采用NIO的I/O模型,默认使用的协议。
HTTPHttp11Nio2Protocol采用NIO2的I/O模型
HTTPHttp11AprProtocol采用APR的I/O模型,需依赖APR库。

请求处理

Tomcat设计这么多层次的容器,是怎样确定每个请求应该由那个Wrapper容器里的Servlet来处理呢?Tomcat使用Mapper组件来完成这个任务。

Mapper组件会将用户请求的URL定位到一个Servlet,工作原理是Mapper组件中保存了Web应用的配置信息(容器组件与访问路径之间的映射关系),比如Host容器中配置的域名、Context容器中的Web应用程序、Wrapper容器中Servlet映射的路径等,这些配置信息最终形成了一个多层次的Map。

当一个请求到来时Mapper组件通过解析请求URL中的域名和路径,再到自己保存的Map中去查找,就能定位到一个Servlet,注意一个请求URL最后只会定位到一个Wrapper容器,也就是一个Servlet。

例如:当用户访问 http://www.itcast.cn/bbs/findAll 时Tomcat是如何找到最终处理业务逻辑的Servlet的呢?

请求主机

从Tomcat设计架构层面来分析请求处理

  1. Connector组件Endpoint中的Acceptor监听客户端Socket连接并接收Socket。
  2. 将连接交给线程池Executor处理,开始执行请求响应任务。
  3. Processor组件读取消息报文,解析请求行、请求体、请求头、最后封装成Request对象。
  4. Mapper组件根据请求行的URL值和请求头的Host值,匹配由哪个Host容器、Context容器、Wrapper容器来处理请求。
  5. CoyoteAdaptor组件负责将Connector组件和Engine容器关联起来,把生成的Request对象和响应Response对象传递到Engine容器中,调用Pipeline。
  6. Engine容器的管道开始处理,管道中包含若干Valve(阀门),每个Valve负责部门处理逻辑,执行完Valve后执行基础的ValveStandardEngineValve,负责调用Host容器的Pipeline。
  7. Host容器的管道开始处理,流程类似,最后执行Context容器的Pipeline。
  8. Context容器的管道开始处理,流程类似,最后执行Wrapper容器的Pipeline。
  9. Wrapper容器的管道开始处理,流程类似,最后执行Wrapper容器对应的Servlet对象的处理方法。
请求处理
创建JavaWeb项目

源码分析

Tomcat架构中各个组件各司其职,组件之间松耦合,确保了整体架构的可伸缩性和可扩展性。组件内部是如何增强其灵活性和可扩展性的呢?

Tomcat中每个Catalina组件采用了责任链模式来完成具体的请求处理,Tomcat中定义了Pipeline和Valve两个接口:

接口说明
Pipeline用于构建责任链
Valve表示责任链上的每个处理器

Pipeline中维护了一个基础的Valve,它始终位于Pipeline的末端(最后执行),封装了具体的请求处理和输出响应的过程。 可以直接调用addValue()方法为Pipeline添加其他Valve,后添加的Valve位于基础Valve之前,并按照添加顺序执行。Pipeline通过获得首个Valve来启动整合链条的执行

请求流程源码解析
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值