Tomcat基础及架构解析

目录

Tomcat 基础

1.1 web 概念

1.2 常见的web服务器

1.5 Tomcat 目录结构

2.Tomcat 架构

2.1 Http工作原理

2.2 Tomcat整体架构

2.2.3 Tomcat整体架构

2.3 连接器 - Coyote

2.3.3 连接器组件

EndPoint

Processor

ProtocolHandler

Adapter

2.4 容器 - Catalina

2.4.1 Catalina 地位

2.4.2 Catalina 结构


Tomcat 基础

1.1 web 概念

1 . 软件架构
1. C/S : 客户端 / 服务器端 ‐‐‐‐‐‐‐‐‐‐‐‐> QQ , 360 ....
2. B/S : 浏览器 / 服务器端 ‐‐‐‐‐‐‐‐‐‐‐‐> 京东, 网易 , 淘宝 , 传智播客
官网
2 . 资源分类
1. 静态资源: 所有用户访问后,得到的结果都是一样的,称为静态资源。静态资
源可以直接被浏览器解析。
* 如: html,css,JavaScript jpg
2. 动态资源 : 每个用户访问相同资源后,得到的结果可能不一样 , 称为动态资
源。动态资源被访问后,需要先转换为静态资源,再返回给浏览器,通过浏览器进行解析。
* 如: servlet/jsp,php,asp....
3 . 网络通信三要素
1. IP :电子设备 ( 计算机 ) 在网络中的唯一标识。
2. 端口:应用程序在计算机中的唯一标识。 0~65536
3. 传输协议:规定了数据传输的规则
1. 基础协议:
1. tcp : 安全协议,三次握手。 速度稍慢
2. udp :不安全协议。 速度快

1.2 常见的web服务器

1.2.1 概念
1 . 服务器:安装了服务器软件的计算机
2 . 服务器软件:接收用户的请求,处理请求,做出响应
3 . web 服务器软件:接收用户的请求,处理请求,做出响应。
web 服务器软件中,可以部署 web 项目,让用户通过浏览器来访问这些项目
1.2.2 常见 web 服务器软件
1). webLogic oracle 公司,大型的 JavaEE 服务器,支持所有的 JavaEE 规范,收费的。
2). webSphere IBM 公司,大型的 JavaEE 服务器,支持所有的 JavaEE 规范,收费的。
3). JBOSS JBOSS 公司的,大型的 JavaEE 服务器,支持所有的 JavaEE 规范,收费的。
4). Tomcat Apache 基金组织,中小型的 JavaEE 服务器,仅仅支持少量的 JavaEE 规范
servlet/jsp 。开源的,免费的

1.5 Tomcat 目录结构

Tomcat 的主要目录文件如下 :

2.Tomcat 架构

2.1 Http工作原理

HTTP 协议是浏览器与服务器之间的数据传送协议。作为应用层协议, HTTP 是基于 TCP/IP
协议来传递数据的( HTML 文件、图片、查询结果等), HTTP 协议不涉及数据包
Packet )传输,主要规定了客户端和服务器之间的通信格式。

1 ) 用户通过浏览器进行了一个操作,比如输入网址并回车,或者是点击链接,接着浏览
器获取了这个事件。
2 ) 浏览器向服务端发出 TCP 连接请求。
3 ) 服务程序接受浏览器的连接请求,并经过 TCP 三次握手建立连接。
4 ) 浏览器将请求数据打包成一个 HTTP 协议格式的数据包。
5 ) 浏览器将该数据包推入网络,数据包经过网络传输,最终达到端服务程序。
6 ) 服务端程序拿到这个数据包后,同样以 HTTP 协议格式解包,获取到客户端的意图。
7 ) 得知客户端意图后进行处理,比如提供静态文件或者调用服务端程序获得动态结果。
8 ) 服务器将响应结果(可能是 HTML 或者图片等)按照 HTTP 协议格式打包。
9 ) 服务器将响应数据包推入网络,数据包经过网络传输最终达到到浏览器。
10 ) 浏览器拿到数据包后,以 HTTP 协议的格式解包,然后解析数据,假设这里的数据是
HTML
11 ) 浏览器将 HTML 文件展示在页面上。
那我们想要探究的 Tomcat 作为一个 HTTP 服务器,在这个过程中都做了些什么事情呢?主
要是接受连接、解析请求数据、处理请求和发送响应这几个步骤

2.2 Tomcat整体架构

2.2.1 Http 服务器请求处理
浏览器发给服务端的是一个 HTTP 格式的请求, HTTP 服务器收到这个请求后,需要调用服
务端程序来处理,所谓的服务端程序就是你写的 Java 类,一般来说不同的请求需要由不同
Java 类来处理。
1 ) 图 1 , 表示 HTTP 服务器直接调用具体业务类,它们是紧耦合的。
2 ) 图 2 HTTP 服务器不直接调用业务类,而是把请求交给容器来处理,容器通过
Servlet 接口调用业务类。因此 Servlet 接口和 Servlet 容器的出现,达到了 HTTP 服务器与
业务类解耦的目的。而 Servlet 接口和 Servlet 容器这一整套规范叫作 Servlet 规范。
Tomcat 按照 Servlet 规范的要求实现了 Servlet 容器,同时它们也具有 HTTP 服务器的功
能。作为 Java 程序员,如果我们要实现新的业务功能,只需要实现一个 Servlet ,并把它
注册到 Tomcat (Servlet容器)中,剩下的事情就由 Tomcat 帮我们处理了。
2.2.2 Servlet 容器工作流程

为了解耦, HTTP 服务器不直接调用 Servlet ,而是把请求交给 Servlet 容器来处理,那
Servlet 容器又是怎么工作的呢?
当客户请求某个资源时, HTTP 服务器会用一个 ServletRequest 对象把客户的请求信息封
装起来,然后调用 Servlet 容器的 service 方法, Servlet 容器拿到请求后,根据请求的 URL
Servlet 的映射关系,找到相应的 Servlet ,如果 Servlet 还没有被加载,就用反射机制创
建这个 Servlet ,并调用 Servlet init 方法来完成初始化,接着调用 Servlet service 方法
来处理请求,把 ServletResponse 对象返回给 HTTP 服务器, HTTP 服务器会把响应发送给
客户端.

2.2.3 Tomcat整体架构

我们知道如果要设计一个系统,首先是要了解需求,我们已经了解了 Tomcat 要实现两个
核心功能:
1 ) 处理 Socket 连接,负责网络字节流与 Request Response 对象的转化。
2 ) 加载和管理 Servlet ,以及具体处理 Request 请求。
因此 Tomcat 设计了两个核心组件连接器( Connector )和容器( Container )来分别做这
两件事情。连接器负责对外交流,容器负责内部处理

2.3 连接器 - Coyote

2.3.1 架构介绍

Coyote Tomcat 的连接器框架的名称 , Tomcat 服务器提供的供客户端访问的外部接
口。客户端通过 Coyote 与服务器建立连接、发送请求并接受响应 。

Coyote 封装了底层的网络通信(Socket 请求及响应处理),为 Catalina 容器提供了统一
的接口,使 Catalina 容器与具体的请求协议及 IO 操作方式完全解耦。 Coyote Socket
入转换封装为 Request 对象,交由 Catalina 容器进行处理,处理请求完成后 , Catalina
Coyote 提供的 Response 对象将结果写入输出流 。

Coyote 作为独立的模块,只负责具体协议和 IO 的相关操作, 与 Servlet 规范实现没有直
接关系,因此即便是 Request Response 对象也并未实现 Servlet 规范对应的接口, 而
是在 Catalina 中将他们进一步封装为 ServletRequest ServletResponse

2.3.2 IO 模型与协议
Coyote 中 , Tomcat 支持的多种 I/O 模型和应用层协议,具体包含哪些 IO 模型和应用层
协议,请看下表:
Tomcat 支持的 IO 模型(自 8.5/9.0 版本起, Tomcat 移除了 对 BIO 的支持)

Tomcat 支持的应用层协议 :

Tomcat 为了实现支持多种 I/O 模型和应用层协议,一个容器可能对接多个连接器,就好比
一个房间有多个门。但是单独的连接器或者容器都不能对外提供服务,需要把它们组装
起来才能工作,组装后这个整体叫作 Service 组件。这里请你注意, Service 本身没有做什
么重要的事情,只是在连接器和容器外面多包了一层,把它们组装在一起。 Tomcat 内可
能有多个 Service ,这样的设计也是出于灵活性的考虑。通过在 Tomcat 中配置多个
Service ,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。

2.3.3 连接器组件

连接器中的各个组件的作用如下:

EndPoint

1 EndPoint Coyote 通信端点,即通信监听的接口,是具体 Socket 接收和发送处理
器,是对传输层的抽象,因此 EndPoint 用来实现 TCP/IP 协议的。
2 Tomcat 并没有 EndPoint 接口,而是提供了一个抽象类 AbstractEndpoint , 里面定
义了两个内部类: Acceptor SocketProcessor Acceptor 用于监听 Socket 连接请求。
SocketProcessor 用于处理接收到的 Socket 请求,它实现 Runnable 接口,在 Run 方法里
调用协议处理组件 Processor 进行处理。为了提高处理能力, SocketProcessor 被提交到
线程池来执行。而这个线程池叫作执行器( Executor) ,我在后面的专栏会详细介绍
Tomcat 如何扩展原生的 Java 线程池。

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 个实现类 : AjpNioProtocol
AjpAprProtocol AjpNio2Protocol Http11NioProtocol Http11Nio2Protocol
Http11AprProtocol 。我们在配置 tomcat/conf/server.xml 时 , 至少要指定具体的
ProtocolHandler , 当然也可以指定协议名称 , 如 : HTTP/1.1 ,如果安装了 APR ,那么
将使用 Http11AprProtocol , 否则使用 Http11NioProtocol

Adapter

由于协议不同,客户端发过来的请求信息也不尽相同, Tomcat 定义了自己的 Request
存放 这些请求信息。 ProtocolHandler 接口负责解析请求并生成 Tomcat Request 类。
但是这个 Request 对象不是标准的 ServletRequest ,也就意味着,不能用 Tomcat
Request 作为参数来调用容器。 Tomcat 设计者的解决方案是引入 CoyoteAdapter ,这是
适配器模式的经典运用,连接器调用 CoyoteAdapter Sevice 方法,传入的是 Tomcat
Request 对象, CoyoteAdapter 负责将 Tomcat Request 转成 ServletRequest ,再调用容
器的 Service 方法

2.4 容器 - Catalina

Tomcat 是一个由一系列可配置的组件构成的 Web 容器,而 Catalina Tomcat servlet
器。
Catalina Servlet 容器实现,包含了之前讲到的所有的容器组件,以及后续章节涉及到
的安全、会话、集群、管理等 Servlet 容器架构的各个方面。它通过松耦合的方式集成
Coyote ,以完成按照请求协议进行数据读写。同时,它还包括我们的启动入口、 Shell
序等。

2.4.1 Catalina 地位

Tomcat 的模块分层结构图, 如下:

Tomcat 本质上就是一款 Servlet 容器, 因此 Catalina 才是 Tomcat 的核心 , 其他模块
都是为 Catalina 提供支撑的。 比如 : 通过 Coyote 模块提供链接通信, Jasper 模块提供
JSP 引擎, Naming 提供 JNDI 服务, Juli 提供日志服务。

2.4.2 Catalina 结构

Catalina 的主要组件结构如下:
如上图所示, Catalina 负责管理 Server ,而 Server 表示着整个服务器。 Server 下面有多个
服务 Service ,每个服务都包含着多个连接器组件 Connector Coyote 实现)和一个容器
组件 Container 。在 Tomcat 启动的时候, 会初始化一个 Catalina 的实例。
Catalina 各个组件的职责:

2.4.3 Container 结构

Tomcat 设计了 4 种容器,分别是 Engine Host Context Wrapper 。这 4 种容器不是平
行关系,而是父子关系。, Tomcat 通过一种分层的架构,使得 Servlet 容器具有很好的灵
活性。

各个组件的含义 :

我们也可以再通过 Tomcat server.xml 配置文件来加深对 Tomcat 容器的理解。 Tomcat
采用了组件化的设计,它的构成组件都是可配置的,其中最外层的是 Server ,其他组件
按照一定的格式要求配置在这个顶层容器中。
那么, Tomcat 是怎么管理这些容器的呢?你会发现这些容器具有父子关系,形成一个树
形结构,你可能马上就想到了设计模式中的组合模式。没错, Tomcat 就是用组合模式来
管理这些容器的。具体实现方法是,所有容器组件都实现了 Container 接口,因此组合模
式可以使得用户对单容器对象和组合容器对象的使用具有一致性。这里单容器对象指的
是最底层的 Wrapper ,组合容器对象指的是上面的 Context Host 或者 Engine
2.5 Tomcat 启动流程
2.5.1 流程

步骤 :
1 ) 启动 tomcat , 需要调用 bin/startup.bat ( linux 目录下 , 需要调用 bin/startup.sh) , 在startup.bat 脚本中 , 调用了 catalina.bat
2 ) 在 catalina.bat 脚本文件中,调用了 BootStrap 中的 main 方法。
3 )在 BootStrap main 方法中调用了 init 方法 , 来创建 Catalina 及 初始化类加载器。
4 )在 BootStrap main 方法中调用了 load 方法 , 在其中又调用了 Catalina load
法。
5 )在 Catalina load 方法中 , 需要进行一些初始化的工作 , 并需要构造 Digester 对象 ,
于解析 XML
6 ) 然后在调用后续组件的初始化操作 。。。
加载 Tomcat 的配置文件,初始化容器组件 ,监听对应的端口号, 准备接受客户端请求
2.5.2 源码解析
2.5.2.1 Lifecycle
由于所有的组件均存在初始化、启动、停止等生命周期方法,拥有生命周期管理的特
性, 所以 Tomcat 在设计的时候, 基于生命周期管理抽象成了一个接口 Lifecycle ,而组
Server Service Container Executor Connector 组件 , 都实现了一个生命周期
的接口,从而具有了以下生命周期中的核心方法:
1 init ():初始化组件
2 start ():启动组件
3 stop ():停止组件
4 destroy ():销毁组件

从启动流程图中以及源码中,我们可以看出 Tomcat 的启动过程非常标准化, 统一按照生
命周期管理接口 Lifecycle 的定义进行启动。首先调用 init() 方法进行组件的逐级初始化操
作,然后再调用 start() 方法进行启动。
每一级的组件除了完成自身的处理外,还要负责调用子组件响应的生命周期管理方法,
组件与组件之间是松耦合的,因为我们可以很容易的通过配置文件进行修改和替换。
2.6 Tomcat 请求处理流程
2.6.1 请求流程
设计了这么多层次的容器, 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
, 是如何找到最终处理业务逻辑的 servlet

步骤如下 :
1) Connector 组件 Endpoint 中的 Acceptor 监听客户端套接字连接并接收 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 后会执行基础的 Valve--StandardEngineValve ,负责调用 Host 容器的
Pipeline
7) Host 容器的管道开始处理,流程类似,最后执行 Context 容器的 Pipeline
8) Context 容器的管道开始处理,流程类似,最后执行 Wrapper 容器的 Pipeline
9) Wrapper 容器的管道开始处理,流程类似,最后执行 Wrapper 容器对应的 Servlet 对象
的 处理方法。
请求流程源码解析

在前面所讲解的 Tomcat 的整体架构中,我们发现 Tomcat 中的各个组件各司其职,组件
之间松耦合,确保了整体架构的可伸缩性和可拓展性,那么在组件内部,如何增强组件
的灵活性和拓展性呢? 在 Tomcat 中,每个 Container 组件采用责任链模式来完成具体的
请求处理。
Tomcat 中定义了 Pipeline Valve 两个接口, Pipeline 用于构建责任链, 后者代表责
任链上的每个处理器。 Pipeline 中维护了一个基础的 Valve ,它始终位于 Pipeline 的末端
(最后执行),封装了具体的请求处理和输出响应的过程。当然,我们也可以调用
addValve() 方法, 为 Pipeline 添加其他的 Valve , 后添加的 Valve 位于基础的 Valve
前,并按照添加顺序执行。 Pipiline 通过获得首个 Valve 来启动整合链条的执行 。

猜你喜欢

转载自blog.csdn.net/weixin_47277897/article/details/120978972