最详细的Tomcat架构解析!深入理解Tomcat的架构原理以及Tomcat组件分析

2,576 阅读15分钟

这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战

HTTP工作原理

  • HTTP协议是浏览器与服务器之间的数据传输协议
  • HTTP协议作为应用层协议,是基于底层TCP/IP协议来进行数据传输的. 传输的数据包括HTML文件,图片,查询结果等
  • HTTP协议不涉及数据包Packet的传输,主要规定了浏览器与服务器之间的通信格式
步骤用户浏览器服务器
1用户发起用户请求
2浏览器发起TCP连接请求
3服务器接受请求并建立连接
4浏览器生成HTTP格式的请求数据包
5浏览器发送请求数据包
6服务器解析HTTP格式的请求数据包
7服务器执行请求
8服务器生成HTTP格式的响应数据包
9服务器发送响应数据包
10浏览器解析HTTP格式的响应数据包
11浏览器呈现HTML响应给用户
  • 用户通过浏览器进行操,比如输入网站并回车或者点击链接,浏览器获取到这个事件
  • 浏览器向服务端发出TCP连接请求
  • 服务器接受浏览器的连接请求,并经过TCP三次握手建立连接
  • 浏览器将请求数据打包成一个HTTP格式的数据包
  • 浏览器将请求数据包推入网络,数据包经过网络传输,最终到达服务器
  • 服务器获取到请求数据包后,以HTTP格式解析请求数据包,获取到浏览器的请求
  • 服务器执行浏览器的请求处理,比如提供静态文件或者调用服务器相关程序获取动态结果
  • 服务器将响应的结果,包括HTML和图片,按照HTTP格式进行打包
  • 服务器将响应数据包推入网络,数据包经过网络传输,最终到达浏览器
  • 浏览器获取到响应数据包后,以HTTP格式解析响应数据包,获取响应到的数据
  • 浏览器将响应数据展示在页面上呈现给用户
  • HTTP服务器Tomcat和Jetty的主要作用:
    • 接受连接
    • 解析请求数据
    • 处理请求
    • 发送响应

Tomcat架构

HTTP服务器请求处理

  • HTTP服务器不是直接调用业务类,而是将请求交给容器来处理,容器通过Servlet接口调用业务类
  • Servlet接口和Servlet容器的出现,达到了HTTP服务器与业务类解耦的目的
  • Servlet接口和Servlet容器的规范叫作Servlet规范
  • Tomcat按照Servlet规范实现了Servlet容器,同时具有HTTP服务器的功能
  • 如果要实现新业务,只需要实现一个Servlet接口,并注册到Tomcat服务器即Servlet容器中,由Tomcat完成其余的服务器操作

Servlet容器工作流程

  • 当用户请求某个资源时 ,HTTP会使用一个ServletRequest对象将用户的请求信息封装起来
  • 然后Servlet容器获取到请求,会根据URLServlet的映射关系,找到相应的Servlet
  • 如果Servlet还没有被加载,就使用反射机制创建这个Servlet, 并且调用Servletinit() 方法来完成初始化
  • 接着调用Servletservice方法来处理请求
  • 最后使用ServletResponse对象将请求响应信息封装起来 ,HTTP服务器会将响应封装信息发送给浏览器

Tomcat整体架构

  • Tomcat服务器需要实现两个核心功能:
    • 处理Socket连接,负责网络字节流与RequestResponse对象的转化
    • 加载和管理servlet, 以及处理Request请求
  • Tomcat设计两个核心组件连接器Connector和容器Container来分别完成这两件事:
    • 连接器Connector负责对外交流
    • 容器Container负责内部处理

连接器Coyote

  • CoyoteTomcat的连接器框架,是Tomcat提供的供客户端访问的外部接口:
    • 客户端通过Coyote与服务器建立连接,发送请求并接受响应
  • Coyote封装了网络底层通信,包括Socket请求和响应处理.为Catalina容器提供了统一的接口,使Catalina容器与具体的请求协议以及IO操作方式完全解耦:
    • CoyoteSocket输入转换封装为Request对象,交由Catalina容器进行处理
    • 处理完成后 ,Catalina通过Coyote提供的Response对象将结果写入输出流
  • Coyote作为独立的模块,只负责具体协议和IO相关操作,与Servlet的规范实现没有直接关系:
    • Request对象和Response对象并没有实现Servlet规范对应的接口
    • 而是在Catalina中将RequestResponse对象进一步封装为ServletRequestServletResponse 在这里插入图片描述

IO模型与网络协议

  • Coyote,Tomcat支持多种I/O模型和应用层协议:
    • Tomcat支持的IO模型: 默认是NIO
      • NIO: 非阻塞I/O, 采用JavaNIO类库实现
      • NIO2: 异步I/O, 采用JDK 7NIO2类库实现
      • APR: 采用Apache可移植运行库实现,是C/C++ 编写的本地库. 如果使用APR作为I/O模型,需要单独安装APR
    • Tomcat支持的应用层协议:
      • HTTP1.1: 大部分Web应用采用的访问协议
      • AJP: 用于和ApacheWeb服务器集成,以实现对静态资源的优化及集群部署,目前支持AJP1.3
      • HTTP2: HTTP2大幅度提升了Web性能
  • Tomcat为了实现支持多种I/O模型和应用层协议,一个容器可能对接多个连接器:
    • 单独的连接器或者容器都是无法对外提供服务的,需要组装起来才能工作,组装后的这个整体叫做Service组件
    • Service只是将连接器和容器组装在一起
    • 出于灵活性考虑,通常在Tomcat中配置多个Service, 可以实现通过不同的端口号访问同一台机器上部署的不同应用

连接器组件

在这里插入图片描述

  • Endpoint:
    • Coyote通信端点,即通信监听接口
    • 是具体的Socket请求的接收和发送处理器,是对传输层抽象,用来实现TCP/IP协议
    • Tomcat中并没有Endpoint接口,而是定义了一个抽象类AbstractEndpoint, 该抽象类定义了两个内部类:
      • Acceptor
        • 用于监听Socket连接请求
      • SocketProcessor
        • 用于处理接收到的Socket请求
        • 实现Runnable接口,在Run方法中调用了协议处理组件Processor进行处理
        • 为了提高处理能力 ,socketProcessor会被提交到线程池来执行,该线程池是Tomcat扩展原生的 Java线程池,被称作执行器Executor
  • Processor:
    • Coyote协议处理接口,是对应用层协议的抽象
    • Processor用来实现HTTP协议
    • Processor接收来自EndpointSocket数据,读取字节流解析成HttpRequest对象
    • 然后将HttpRequest通过Adapter转化成ServletRequest提交给容器处理
  • ProtocolHandler:
    • Coyote协议接口,通过EndpointProcessor实现针对具体协议的处理能力
    • Tomcat中提供了6个实现类:
      • AjpAprProtocol
      • AjpNioProtocol
      • AjpNio2Protocol
      • Http11AprProtocol
      • Http11NioProtocol
      • Http11Nio2Protocol
    • 在配置tomcat/conf/server.xml时,要指定具体的ProtocolHandler, 也可以指定协议名称.比如Http1.1, 如果安装了APR, 将会使用Http11AprProtocol, 否则使用Http11NioProtocol
  • Adapter:
    • 由于协议的不同,客户端传输的请求信息也会不同 ,Tomcat自定义一个Request类来存放这些请求信息
    • ProcotolHandler接口负责解析请求并生成Request类,但是这个Request不是标准的ServletRequest, 所有不能使用Tomcat中自定义的Request作为参数来调用容器进行数据处理
    • 通过运用适配器模式,引入CoyoteAdapter来解决这样的问题:连接器调用CoyoteAdapterService方法,传入Tomcat Request对象,然后CoyoteAdapter负责将Tomcat Request转化成ServletRequest, 最后再调用容器的Service方法

容器Catalina

  • Tomcat就是一个由一系列可配置的组件组成的web容器
  • CatalinaTomcatservlet容器
  • CatalinaServlet容器实现,通过松耦合的方式集成Coyote, 以完成按照请求协议进行数据读取
  • Tomcat模块分层示意图: 在这里插入图片描述
  • Tomcat本质上是一个Servlet容器,所以CatalinaTomcat的核心,其余模块都是为Catalina提供服务支撑的:
    • 连接器Coyote提供连接通信
    • JSP引擎Jasper提供JSP引擎
    • 表达式语言JavaEL提供EL表达式支持
    • 命名服务Naming提供JNDI服务
    • 服务器日志Juli提供日志服务

Catalina结构

在这里插入图片描述

  • Catalina负责管理Server,Server表示整个服务器
  • Server中有多个服务Service
  • 每个服务Service包含着多个Coyote实现的连接器组件Connector和一个容器组件Container
  • Tomcat启动时,会初始化一个Catalina实例
组件作用
Catalina解析Tomcat配置文件
根据配置文件创建服务器Server,并根据命令对服器Server进行管理
Server服务器Server表示整个Catalina Servlet容器及其余相关组件
服务器Server负责组装并启动Servlet引擎和Tomcat连接器
服务器Server通过实现Lifecycle接口,提供了一种优雅的启动和关闭整个系统的方式
Service服务Service是服务器Server的内部组件,一个服务器Server可以包含多个服务Service
服务Service将多个连接器Connector绑定到一个容器Container上
Connector连接器
用于处理与客户端通信
接收客户端请求,然后转发给相应的容器处理,最后向客户端返回响应结果
Container容器
处理用户的Servlet请求,并返回对象给web用户模块

Container结构

  • Tomcat中的容器Container包含4种组件:
    • Engine
    • Host
    • Context
    • Wrapper
  • 4种组件是有层级的父子关系
  • Tomcat通过一种分层架构,使得Servlet容器有更好的灵活性 在这里插入图片描述
容器组件含义
Engine表示整个Catalina的Servlet引擎
用来管理多个虚拟站点,一个Service最多只能有一个Engine,但是一个Engine可以包含多个Host
Host表示一个主机或者是一个虚拟站点
可以给Tomcat配置多个虚拟主机地址,一个虚拟主机地址下可以包含多个Context
Context表示一个web应用程序
一个尾部应用程序可以包含多个Wrapper
Wrapper表示一个Servlet容器
Wrapper作为容器中的最底层,不可以包含子容器
  • Tomcat采用组件化的设计,构成组件都是可配置的 ,Tomcat中对使用server.xml来对容器Container进行配置
  • Tomcat使用组合模式来管理这些容器Container, 这些容器具有父子关系,形成一个树形结构,具体实现方法:
    • 所有容器组件都实现了Container接口
    • 组合模式可以使得用户对单容器对象和组合容器对象的使用具有一致性
      • 单容器对象: 最底层的Wrapper
      • 组合容器对象: 指父容器Context,HostEngine

在这里插入图片描述

  • 容器Container接口继承了LifeCycle接口,用来统一管理各个组件的生命周期

Tomcat启动流程

  • 启动Tomcat, 需要启动bin目录下的startup脚本,在startup脚本中,调用了catalina脚本
  • catalina脚本中,调用了Bootstrap中的main() 方法
  • Bootstrapmain() 方法中调用了init() 方法,来创建Catalina并初始化类加载器
  • Bootstrapmain() 方法中调用了load() 方法,又调用了Catalinaload() 方法
  • Catalinaload() 方法中,需要进行初始化工作,并且需要构造Digister对象,用于解析XML
  • Catalina中创建Server, 并依次调用后续组件Server, Service, Engine, Host, Context的初始化init() 方法
  • 然后在Service中调用初始化init() 方法初始化Executor组件,接着在Service中调用Connectorinit() 方法初始化Connector, 最后调用ProtocolHandler组件的初始化init() 方法
  • 初始化完成后,在Bootstrapmain() 方法中调用start() 方法,依次启动后续组件
  • 依次调用后续组件Catalina, Server, Service, Engine, Host, Context的启动start() 方法
  • 然后在Service中调用启动start() 方法启动Executor组件,接着在Service中调用Connectorinit() 方法初始化Connector, 最后调用ProcotolHandler组件的启动start() 方法
  • Tomcat流程启动完成后,就完成加载Tomcat配置文件,初始化容器,监听对应的端口号,准备接受客户端请求

Lifecycle

  • 由于Tomcat中所有组件均存在初始化,启动,停止等生命周期方法,所以在设计Tomcat时,基于生命周期管理抽象出一个生命周期管理接口Lifecycle
  • Server组件 ,Service组件 ,Container组件 ,Executor组件以及Connctor组件都实现了生命周期Lifecycle接口,从而具有生命周期中的核心方法:
    • init() : 初始化组件
    • start() : 启动组件
    • stop() : 停止组件
    • destory() : 销毁组件 在这里插入图片描述

Tomcat组件接口的默认实现

在这里插入图片描述

  • Tomcat中提供了对Server, Service, Engine, Host, Context这些接口的默认实现
  • 对于Endpoint组件而言 ,Tomcat中没有对应的Endpoint接口,但是有一个抽象类AbstractEndpoint, 包括三个实现类:
    • NioEndpoint - NIO
    • Nio2Endpoint - NIO2
    • AprEndpoint - APR
  • Tomcat 8.5中,采用NioEndpoint默认实现 在这里插入图片描述
  • ProtocolHandler:
    • Coyote协议接口
    • 通过封装EndpointProcessor, 实现针对具体协议的处理功能
    • Tomcat中按照协议和IO提供了6ProtocolHandler实现. 默认使用的协议是Http11NioProtocol :
      • HTTP协议:
        • Http11NioProtocol - NIO. Tomcat默认使用的协议
        • Http11Nio2Procotol - NIO2
        • Http11AprProcotol - APR. 需要依赖于APR库
      • AJP协议:
        • AjpNioProcotol - NIO
        • AjpNio2Procotol - NIO2
        • AjpAprProcotol - APR. 需要依赖于APR库

总结

  • Tomcat启动流程是一个标准化的启动流程:
    • 统一按照生命周期管理接口Lifecycle的定义进行启动
    • 首先调用init() 方法进行组件的逐级初始化操作
    • 然后调用start() 方法启动
  • Tomcat中每一级组件除了完成自身的处理流程之外,还要负责调用子组件以响应生命周期管理方法
  • 组件与组件之间是松耦合的,这样可以很容易通过配置文件对Tomcat的组件进行配置修改和替换

Tomcat请求处理流程

Tomcat请求流程

  • Tomcat中是通过Mapper组件类来完成请求处理流程的
  • Mapper组件的功能就是将用户请求的URL定位到一个Servlet,Mapper组件的工作原理:
    • Mapper组件中保存了Web应用的配置信息,即容器组件与访问路径的映射关系. 这些配置信息可以看作是一个多层次的Map, 包括:
      • Host容器中配置的域名
      • Context容器里的Web应用路径
      • Wrapper容器里Servlet映射的路径
    • 当有一个请求时 ,Mapper组件通过解析URL中的域名和路径,再到对应的Web的配置信息中查找对应的路径,就能定位到一个Servlet, 然后请求Servlet中的执行方法. 一个请求URL只能定位到一个Wrapper, 即一个Servlet 在这里插入图片描述
  • Connector组件Endpoint中的Acceptor监听客户端套接字连接并接收Socket. 然后将连接传递给Executor处理,开始执行请求响应任务
  • Processor组件读取消息报文,解析请求行,请求体,请求头.封装成Request对象
  • Mapper组件根据请求行的URL值和请求头的Host值匹配Host容器 ,Context容器 ,Wrapper容器处理请求
  • CoyoteAdapter组件负责将Connector组件和Engine容器关联起来,将生成的Reuqest对象和响应对象Response传递到Engine容器中,调用Pipeline
  • Engine容器的管道开始处理流程,管道中包含若干个Valve, 每个Valve负责部分处理逻辑,执行完Valve后,会执行基础的StandardEngineValve, 负责调用Host容器的Pipeline
  • Host容器的管道开始处理流程,管道中包含若干个Valve, 每个Valve负责部分处理逻辑,执行完Valve后,会执行基础的StandardHostValve, 负责调用Context容器的Pipeline
  • Context容器的管道开始处理流程,管道中包含若干个Valve, 每个Valve负责部分处理逻辑,执行完Valve后,会执行基础的StandardContextValve, 负责调用Wrapper容器的Pipeline
  • Wrapper容器的管道开始处理流程,管道中包含若干个Valve, 每个Valve负责部分处理逻辑,执行完Valve后,会执行基础的StandardWrapperValve, 执行Wrapper容器对象的Servlet对象的处理方法 在这里插入图片描述
  • Tomcat的整体架构中,各个组件之间是松耦合的,确保了整体架构的伸缩性和扩展性
  • Tomcat内部的每个容器Container采用责任链模式完成具体的请求处理,来保证灵活性和扩展性
  • Tomcat中定义了PipelineVavle两个接口:
    • Pipeline: 构建责任链
    • Valve: 代表责任链上的每一个处理器
    • Pipeline中维护了一个基础的Valve,StandardXxxValve. 始终位于Pipeline的末端,在最后执行,封装了具体的请求处理和输出响应过程
    • 可以调用addVavle()方法为Pipeline添加自定义的Valve, 自定义添加的Valve位于末尾的基础Valve之前,并且按照添加顺序执行
    • Pipeline通过获取首个Valve来启动整个责任链的执行