Tomcat 汤姆猫,哈哈, 基本属于那种大家天天用, 反而很容易忽视的角色. Tomcat 架构解析这本书详细地介绍了 Tomcat 的总体架构, 主要组件以及配置文件的使用, 书中掺杂了大量的源码来介绍业务流程, 这边都做了删减, 有兴趣的可以阅读原版.另外 Tomcat 官网也有相关文档, 只是比较混乱.
如有侵权,请联系删除.
第 1 章 Tomcat 介绍
Tomcat 是全世界最著名的基于 Java 语言的轻量级应用服务器, 是一款完全开源免费的 Servlet 容器实现. 同时, 它支持 HTML, JS 等静态资源的处理, 因此又可以作为轻量级 Web 服务器使用.
1.1 简介
1.1.1 Tomcat 历史
Tomcat 最初由 Sun 公司的软件架构师 James Duncan Davidson 开发, 该项目于 1999 年与 Apache 软件基金会旗下的 JServ 项目合并, 即为现在的 Tomcat.
1.3 Tomcat 目录结构
第 2 章 Tomcat 总体架构
本章主要包含如下几个部分.
- Tomcat 总体架构设计及 Tomcat 各组件的概念
- Tomcat 启动及请求处理过程
- Tomcat 的类加载器
2.1 总体设计
为了使读者能更深刻地理解 Tomcat 的相关组件概念, 我们将采用一种启发式的讲解方式来介绍 Tomcat 的总体设计.
2.1.1 Server
服务器最基本的描述:它接收客户端发来的请求数据并进行解析,完成相关业务处理,然后把处理结果作为响应返回给客户端.
通常情况下, 我们通过使用 Socket 监听服务器指定端口来实现该功能.
一个最简单的服务器设计如下图所示
- 我们通过 start() 方法启动服务器, 打开 Socket 链接, 监听服务器端口, 并负责在接收到客户端请求时进行处理并返回响应.
- 同时提供一个 stop() 方法来停止服务器并释放网络资源.
2.1.2 Connector 和 Container
将请求监听与请求处理放在一起扩展性很差, 比如我们想适配多种网络协议(AJP 协议, HTTP 协议等), 但是请求处理过程却相同的时候.
于是, 我们将网络协议与请求处理从概念上分离.
- 一个 Server 可以包含多个 Connector 和 Container。
- 其中 Connector 负责开启 Socket 并监听客户端请求、返回响应数据;
- Container 负责具体的请求处理。
- Connector 和 Container 分别拥有自己的 start() 和 stop() 方法来加载和释放自己维护的资源。
但是, 这个设计有个明显的缺陷, 即如何知晓来自某个 Connector 的请求由哪个 Container 处理呢?
更合理的方式如下图所示.
- 一个 Server 包含多个 Service (它们互相独立, 只是共享一个 JVM 以及系统类库).
- 一个 Service 负责维护多个 Connector 和一个 Container.
- 这样来自 Connector 的请求只能有它所属 Service 维护的 Container 处理.
在 Tomcat 中,Container 是一个更加通用的概念。
为了与 Tomcat 中的组件命名一致,我们将 Container 重新命名为 Engine,用以表示整个 Servlet 引擎。
修改后的设计如图2-4所示。
注意:
- 需要注意此处的描述,Engine 表示整个 Servlet 引擎,而非 Servlet 容器。
- 表示整个 Servlet 容器的是 Server。
- 引擎只负责请求的处理,并不需要考虑请求链接、协议等的处理。
2.1.3 Container 设计
我们需要在 Engine 容器中支持管理应用,当接收到 Connector 的处理请求时,Engine 容器能够找到一个合适的 Web 应用来处理。
我们使用 Context 来表示一个 Web 应用,并且一个 Engine 可以包含多个 Context。
如果我们希望只运行一个服务器实例, 但提供多个域名的服务, 可以将每个域名视为一个虚拟的主机, 在每个虚拟主机下包含多个 Web 应用.
我们用 Host 表示虚拟主机的概念, 一个 Host 可以包含多个 Context.
另外, 在一个 Web 应用中, 可包含多个 Servlet 实例以处理来自不同链接的请求.
在 Tomcat 中, Servlet 定义被称为 Wrapper, 基于此修改后的设计如图 2-7 所示.
补一张更形象的总体架构图
2.1.4 Lifecycle
- 我们很容易发现,所有组件均存在启动、停止等生命周期方法,拥有生命周期管理的特性.
- 因此,我们可以基于生命周期管理进行一次接口抽象.
- 我们针对所有拥有生命周期管理特性的组件抽象了一个生命周期通用接口.
- 该接口定义了生命周期管理的核心方法.
- Init(): 初始化组件
- start(): 启动组件
- stop(): 停止组件
- destory(): 销毁组件
2.1.5 Pipeline 和 Valve
- 在 Tomcat 中每个 Container(容器组件包括 Engine, Host, Context, Wrapper) 组件通过执行一个职责链来完成具体的请求处理.
- Tomcat 定义了 Pipeline(管道) 和 Valve(阀) 两个接口.
- 前者用于构造职责链, 后者代表职责链上的每个处理器.
- 我们可以从字面意思来理解–来自客户端的请求就像是流经管道的水一般, 经过每个阀进行处理.
2.1.6 Connector 设计
Connector 需要包含如下几项功能:
- 监听服务器端口,读取来自客户端的请求。
- 将请求数据按照指定协议进行解析。
- 根据请求地址匹配正确的容器进行处理。
- 将响应返回客户端。
只有这样才能保证将接收到的客户端请求交由与请求地址匹配的容器处理.
我们知道,Tomcat 支持多协议,默认支持 HTTP 和 AJP。
同时,Tomcat 还支持多种 IO 方式,包括 BIO(8.5版本之后移除)、NIO、APR.
而且在 Tomcat 8 之后新增了对 NIO2 和 HTTP/2 协议的支持。
因此,对协议和 IO 进行抽象和建模是需要重点关注的.
- 在 Tomcat 中,ProtocolHandler 表示一个协议处理器,针对不同协议和 IO 方式,提供了不同的实现,如 Http11NioProtocol 表示基于 NIO 的 HTTP 协议处理器。
- ProtocolHandler 包含一个 Endpoint 用于启动 Socket 监听,该接口按照 IO 方式进行分类实现,如 Nio2Endpoint 表示非阻塞式 Socket I/O。
- 还包含一个 Processor 用于按照指定协议读取数据,并将请求交由容器处理,如 Http11NioProcessor 表示在 NIO 的方式下 HTTP 请求的处理类。
- 在 Connector 启动时,Endpoint 会启动线程来监听服务器端口,并在接收到请求后调用 Processor 进行数据读取。
- 当 Processor 读取客户端请求后,需要按照请求地址映射到具体的容器进行处理,这个过程即为请求映射.
- Tomcat 通过 Mapper 和 MapperListener 两个类实现该功能。
- 前者用于维护容器映射信息,同时按照映射规则(Servlet规范定义)查找容器。
- 后者实现了 ContainerListener 和 LifecycleListener, 用于在容器组件状态发生变更时, 注册或取消对应的容器映射信息.
2.1.7 Executor
Tomcat 提供了 Executor 接口来表示一个可以在组件间共享的线程池(默认使用了JDK5提供的线程池技术),该接口同样继承自 Lifecycle,可按照通用的组件进行管理。
线程池的共享范围如何确定?
在 Tomcat 中 Executor 由 Service 维护,因此同一个 Service 中的组件可以共享一个线程池。
2.1.8 Bootstrap 和 Catalina
- Tomcat 通过类 Catalina 提供了一个 Shell 程序,用于解析 server.xml 创建各个组件,同时负责启动、停止应用服务器(只需要启动 Tomcat 顶层组件 Server 即可).
- Tomcat 使用 Digester 解析 XML 文件,包括 server.xml 以及 web.xml 等。
- 最后,Tomcat 提供了 Bootstrap 作为应用服务器启动人口。
- Bootstrap 负责创建 Catalina 实例,根据执行参数调用 Catalina 相关方法完成针对应用服务器的操作(启动、停止)。
- 也许你会有疑问,为什么 Tomcat 不直接通过 Catalina 启动,而是又提供了 Bootstrap 呢?
- 你可以查看一下 Tomcat 的发布包目录,Bootstrap 并不位于 Tomcat 的依赖库目录下($CATALINA_HOME/lib ),而是直接在 SCATALINA_HOME/bin 目录下。
- Bootstrap 与 Tomcat 应用服务器完全松耦合(通过反射调用 Catalina 实例),它可以直接依赖 JRE 运行并为 Tomcat 应用服务器创建共享类加载器,用于构造 Catalina 实例以及整个 Tomcat 服务器。
2.2 Tomcat 启动
- Tomcat 的启动过程非常标准化,统一按照生命周期管理接口 Lifecycle 的定义进行启动。
- 首先,调用 init() 方法进行组件的逐级初始化,然后再调用 start() 方法进行启动。
- 当然,每次调用均伴随着生命周期状态变更事件的触发。
- 每一级组件除完成自身的处理外,还要负责调用子组件相应的生命周期管理方法,
- 组件与组件之间是松耦合的设计,因此我们很容易通过配置进行修改和替换。
2.3 请求处理
- 从本质上讲,应用服务器的请求处理开始于监听的 Socket 端口接收到数据,结束于将服务器处理结果写入 Socket 输出流。
- 在这个处理过程中,应用服务器需要将请求按照既定协议进行读取,并封装为与具体通信方案无关的请求对象。
- 然后根据请求映射规则定位到具体的处理单元(在Java应用服务器中,多数是某个Web应用下的一个Servlet )进行处理。
- 当然,如果我们的应用不是基于简单的 Servlet API, 而是基于当前成熟的 MVC 框架((如Apache Struts、Spring MVC),那么在多数情况下请求将进一步匹配到 Servlet 下的一个控制器——这部分已经不属于应用服务器的处理范畴,而是由具体的 MVC 框架进行匹配。
- 当 Servlet 或者控制器的业务处理结束后,处理结果将被写入一个与通信方案无关的响应对象。
- 最后,该响应对象将按照既定协议写入输出流。
2.4 类加载器
本节将主要介绍 Tomcat 的类加载机制,包括 Tomcat 的类加载器层级设计以及 Web 应用的类加载过程。
2.4.1 J2SE 标准类加载器
我们都知道 JVM 默认提供了 3 个类加载器,它们以一种父子树的方式创建,同时使用委派模式确保应用程序可通过自身的类加载器(System)加载所有可见的Java类。结构如图2-19所示。
2.4.2 Tomcat 加载器
Tomcat 提供了 3 个基础的类加载器和 Web 应用类加载器, 而且这 3 个类加载器指向的路径和包列表均可以由 catalina.properties 配置.
- Common: 以 System 为父类加载器,是位于 Tomcat 应用服务器顶层的公用类加载器。其路径为 common.loader,默认指向 $CATALINA_HOME/lib下的包。
- Catalina: 以 Common 为父加载器,是用于加载 Tomcat 应用服务器的类加载器,其路径为 server.loader,默认为空。此时 Tomcat 使用 Common 类加载器加载应用服务器。
- Shared: 以 Common 为父加载器,是所有Web应用的父加载器,其路径为 shared.loader,默认为空。此时 Tomcat 使用 Common 类加载器作为 Web 应用的父加载器。
- Web应用: 以 Shared 为父加载器,加载 /WEB-INF/classes 目录下的未压缩的 Class 和资源文件以及 /WEB-INF/lib 目录下的 Jar 包。如前所述,该类加载器只对当前Web应用可见,对其他Web应用均不可见。
2.5 小结
本章从多个方面讲述了 Tomcat 的总体架构。
- 首先,以一种逐渐演进的方式讲解了 Tomcat 核心组件的设计以及如此设计的原因,便于读者更好地理解相关概念。
- 其次,讲解了主要组件概念及 Tomcat 的启动和请求处理过程,动态地描述了组件如何相互协作以实现服务器的基本功能。
- 最后讲述了 Tomcat 的类加载机制,包括 JVM 和 Tomcat 为我们提供的类加载特性,以及这些特性在架构层面的意义。
第 3 章 Catalina
本章主要介绍 Tomcat 的 Servlet 容器实现 Catalina.
3.1 什么是 Catalina
- 2001年,Tomcat 发布了重要的具有里程碑意义的版本 4.0。
- 在该版本中, Tomcat 完全重新设计了其 Servlet 容器的架构。
- 该部分工作由 Craig McClanahan 完成,他将这个新版本的 Servlet 容器实现命名为 Catalina。
- Tomcat 本质上是一款 Servlet 容器,因此 Catalina 是 Tomcat 的核心,其他模块均为 Catalina 提供支撑。
- 通过 Coyote 模块提供链接通信,Jasper 模块提供 JSP 引擎,Naming 提供 JNDI 服务,Juli 提供日志服务。
3.2 Digester
- Digester 是一款用于将 XML 转换为 Java 对象的事件驱动型工具,是对 SAX (同样为事件驱动型XML处理工具,已包含到 J2SE 基础类库当中)的高层次封装。
- Digester 针对 SAX 事件提供了更加友好的接口,隐藏了 XML 节点具体的层次细节,使开发者可以更加专注于处理过程。
- Digester 及 SAX 的事件驱动,简而言之,就是通过流读取 XML 文件,当识别出特定 XML 节点后便会执行特定的动作,或者创建 Java 对象,或者执行对象的某个方法。
- 因此 Digester 的核心是匹配模式和处理规则。
3.2.1 对象栈
- Digester 提供了一套对象栈( Digester 同名类)机制用于构造 Java 对象.
- Digester 的设计模式是指,在文件读取过程中,如果遇到一个 XML 节点的开始部分,则会触发处理规则事件创建 Java 对象,并将其放入栈。
- 当处理该节点的子节点时,该对象都将维护在栈中。
- 当遇到该节点的结束部分时,该对象将会从栈中取出并清除。
3.3 创建 Server
Catalina 通过解析 server.xml 创建 Server.
3.4 Web 应用加载
Servlet 容器的核心功能主要有两个: 部署 Web 应用 和 将请求映射到具体的 Servlet 进行处理.
3.5 Web 请求处理
3.5.1 总体过程
Tomcat 通过 org.apache.tomcat.util.http.mapper.Mapper 维护请求链接与 Host 、Context、Wrappe r 等Container 的映射。
同时,通过 org.apache.catalina.connector.MapperListener 监听器监听所有的 Host 、Context、Wrapper 组件,在相关组件启动、停止时注册或者移除相关映射。
此外,通过 org.apache.catalina.connector.CoyoteAdapter将Connector 与 Mapper、Container 联系起来。当 Connector 接收到请求后,首先读取请求数据,然后调用 CoyoteAdapter.service() 方法完成请求处理。
CoyoteAdapter.service 的具体处理过程如下(只列出主要步骤)。
- 根据 Connector 的请求( org.apache.coyote.Request)和响应( org.apache.coyote.Response)对象创建 Servlet 请求( org.apache.catalina.connector.Request )和响应( org.apache.catalina.connector.Response )。
- 转换请求参数并完成请求映射。
- 请求 URI 解码,初始化请求的路径参数。
- 检测 URI 是否合法,如果非法,则返回响应码 400。
- 请求映射,映射结果保存到 org.apache.catalina.connector.Request.mappingData,类型为org.apache.tomcat.util.http.mapper.MappingData,请求映射处理最终会根据 URI 定位到一个有效的 Wrapper。
- 如果映射结果 MappingData 的 redirectPath 属性不为空(即为重定向请求),则调用 org.apache.catalina.connector.Response.sendRedirect 发送重定向并结束。
- 如果当前 Connector 不允许追踪( allowTrace为false )且当前请求的 Method 为TRACE,则返回响应码 405。
- 执行连接器的认证及授权。
- 得到当前 Engine 的第一个 Valve 并执行( invoke ),以完成客户端请求处理。
- 如果为异步请求:
- 获得请求读取事件监听器(ReadListener );
- 如果请求读取已经结束,触发 ReadListener.onAllDataRead。
- 如果为同步请求:
- Flush并关闭请求输入流;
- Flush并关闭响应输出流。
第 4 章 Coyote
链接器接收来自客户端的请求,并按照既定协议(如 HTTP)进行解析,然后交由 Servlet 容器处理。
4.1 什么是 Coyote
- Coyote 是 Tomcat链接器框架的名称,是 Tomcat 服务器提供的供客户端访问的外部接口。
- 客户端通过 Coyote 与服务器建立链接、发送请求并接收响应。
- Coyote 封装了底层的网络通信(Socket 请求及响应处理),为 Catalina 容器提供了统一的接口,使 Catalina 容器与具体的请求协议及 IO 方式解耦。
- Coyote 将 Socket 输人转换为 Request 对象,交由 Catalina 容器进行处理.
- 处理请求完成后,Catalina 通过 Coyote 提供的 Response 对象将结果写入输出流。
4.3 HTTP
- HTTP 协议可以说是互联网应用最广泛的网络协议,也是所有服务器均支持的基本的协议。
- 它是一种基于请求与响应模式的、无状态的应用层协议。
4.4 AJP
- AJP ( Apache JServ Protocol )是 Alexei Kosut 创建的定向包( packet-oriented)通信协议,采用二进制格式传输可读文本。
- 从该协议的命名不难看出,其最初的目标便是用于Apache HTTP Server ( Web服务器)与 Apache JServ 之间的通信,以提升 We b服务器与应用服务器集成时的通信性能。
4.5 HTTP/2.0
与 HTTP/1.1 相比,HTTP/2.0 在传输方面进行了如下重要改进。
- 采用二进制格式传输数据而非 HTTP/1.1 的文本格式。
- HTTP/2.0 对消息头采用了 HPACK 压缩,提升了传输效率。
- 基于帧和流的多路复用,真正实现了基于一个链接的多请求并发处理。
- 支持服务器推送。
4.6 I/O
在 8.5 版本之前(8.0版本之后), Tomcat 同时支持 BIO、NIO、NIO2、APR 这 4 种 I/O 方式,其中 NIO2 为 8.0 版本新增。
自 8.5 版本开始,Tomcat 移除了对 BIO 的支持。
4.6.1 BIO
BIO 即阻塞式 IO,是 Java 提供的最基本的 I/O 方式。
在网络通信(此处主要讨论 TCP/IP 协议)中,需要通过 Socket 在客户端与服务端建立双向链接以实现通信,其主要步骤如下。
- 服务端监听某个端口是否有链接请求。
- 客户端向服务端发出链接请求。
- 服务端向客户端返回Accept(接受)消息,此时链接成功。
- 客户端和服务端通过Send()、write()等方法与对方通信。
- 关闭链接。
4.6.2 NIO
- 传统的 BIO 方式是基于流进行读写的,而且是阻塞的,整体性能比较差。
- 为了提高 I/O 性能,JDK 于 1.4 版本引入 NIO,它弥补了原来 BIO 方式的不足,在标准 Java 代码中提供了高速、面向块的 I/O。
- 通过定义包含数据的类以及以块的形式处理数据, NIO 可以在不编写本地代码的情况下利用底层优化,这是 BIO 所无法做到的。
4.6.3 NIO2
NIO2 是 JDK7 新增的文件及网络 IO 特性,它继承自 NIO,同时添加了众多特性及功能改进,其中最重要的即是对异步 IO (AIO)的支持。
4.6.4 APR
APR ( Apache Portable Runtime ),即 Apache 可移植运行库。正如官网所言,APR 的使命是创建和维护一套软件库,以便在不同操作系统(Windows、Linux等)底层实现的基础上提供统一的 API。
4.7 小结
- 本章我们介绍了 Tomcat 链接器框架 Coyote 以及目前支持的协议、I/O 方式。
- 通过本章可以了解到客户端请求从接收到交由 Servlet 容器处理的整个过程。
第 6 章 Tomcat 配置管理
6.1 JVM 配置
set JAVA_OPTS=-server -Xms1024m -Xmx2048m -XX: PermSize=256m -XX:MaxPermSize=512m
需要添加到启动文件( catalina.bat/catalina.sh)的首行。
参数说明
- Xms:堆内存的初始大小。
- Xmx:堆内存上限。
- XX:PermSize:非堆内存初始大小,在 JDK 8 中已经由 -XX:MetaspaceSize 替代。
- XX:MaxPermSize:非堆内存上限,在 JDK 8 中已经由 -XX:MaxMetaspaceSize 替代。
6.2 服务器配置
Tomcat 服务器的配置主要集中于 $CATALINA_HOME/conf 下的 catalina.policy , catalina.properties、context.xml、 server.xml、tomcat-users.xml、web.xml 文件。
6.2.1 catalina.properties
该文件主要用于 Catalina 容器启动阶段的配置,如服务器类加载器路径等.
6.2.2 server.xml
server.xml 是 Tomcat 应用服务器的核心配置文件,它包括 Tomcat Servlet 容器(Catalina)的所有组件的配置,用于创建 Tomcat Servlet 容器。
-
Server
- server.xml的根元素为 Server,用于创建一个 Server 实例,默认使用的实现类为org.apache.catalina.core.StandardServer。
- Server 可以内嵌的子元素为: Service、GlobalNamingResources 和 Listener.
- 其中,GlobalNamingResources 定义了全局的命名服务。
Listener 用于为 Server 添加生命周期监听器.
-
Service
- 该元素用于创建 Service 实例,默认使用 org.apache.catalina.core.StandardService.
- 默认情况下,Tomcat 仅指定了 Service 的名称,值为“Catalina”。
- Service 可以内嵌的元素为:Listener、Executor 、Connector、Engine。
-
Executor
- 默认情况下, Service 并未添加共享线程池配置。
- 如果我们想添加一个线程池,可以在 < Service > 下添加如下配置:
- < Executor name= “tomcatThreadPool” namePrefix=“catalina-exec -” maxThreads=“150” minSpareThreads= “4” />
- maxThreads: 线程池中活动线程的最大数目,默认为200。
- minSpareThreads: 备用线程的最小数量,默认为25。
-
Connector
-
Connector 用于创建链接器实例。默认情况下,server.xml配置了两个链接器,一个支持HTTP协议,一个支持AJP协议。因此大多数情况下,我们并不需要新增链接器配置,但是需要针对已有链接器进行优化。
-
一个简单的链接器配置如下:
< Connector port=""808o"protocol=“HTTP/1.1” connectionTimeout=“200oo” redirectPort="8443”/> -
redirectPort: 如果当前Connector支持non-SSL请求,并且接收到一个请求,符合< security-constraint >约束,需要SSL传输,Catalina自动将请求重定向到此处指定的端口。
-
-
Engine
- Engine 作为 Servlet 引擎的顶级元素,它支持以下嵌入元素:Cluster、Listener、Realm、Valve 和 Host。
- 其中,Cluster 用于集群配置。
- Listener 用于添加 Engine 一级的生命周期监听器, 默认情况下未配置任何监听器。
- Valve 用于添加一个 org.apache.catalina.Valve 实现,拦截 Engine 一级的请求处理(除非需要跨 Host、跨 Web 应用进行拦截处理,否则不要轻易在 Engine 元素下配置 Valve )。
- Host用于配置Tomcat虚拟主机组件。
-
Host配置
- Host元素用于配置一个虚拟主机,它支持以下嵌入元素: Alias、Cluster、Listener、Valve ,Realm和Context。
- 其中 Cluster、Listener、Valve、Realm 与Engine 相同,不同的是作用域为 Host。
- Alias 用于为 Host 配置别名,这样我们除了通过 Host 名称访问当前主机外,还可以通过别名访问。
- Context 用于配置 Host 下运行的 Web 应用。
-
Context配置
-
Context 用于配置一个 Web 应用,它支持的内嵌元素为:CookieProcessor、Loader、Manager、Realm、Resources、WatchedResource、JarScanner、Valve.
-
CookieProcessor 用于将 HTTP 请求中的 Cookie 头信息转换为 javax.servlet.http.Cookie 对象,或者将 Cookie 对象转换为 HTTP 响应中的 Cookie 头信息。如不指定,将默认使用 org.apache.tomcat.util.http.Rfc6265CookieProcessor。
-
Loader 用于配置当前 Context 对应的 Web 应用的类加载器。如不指定,将默认为 org.apache.catalina.loader. WebappLoader。
-
Manager 用于配置当前 Web 应用的会话管理器。如不指定,将默认为 org.apache.catalina.session.StandardManager。会话管理器存在独立启动和集群两种方式。
-
Resources 用于配置Web应用的有效资源(类文件、JAR 文件、HTML 等静态文件、JSP 等)。除非 Web 应用部分文件位于 Web 根目录之外,或存储于其他文件系统、数据库或者版本控制库,一般不需要额外添加资源,此时 Tomcat 会自动创建一个默认的文件系统,可以满足绝大多数的需要。
-
WatchedResource 用于配置自动加载文件检测。当指定文件发生更新时,Web 应用将会自动重新加载。对于每个 Web 应用, Tomcat 默认检测的文件是 WEB-INF/web.xml、SCATALINA_BASE/conf/web.xml。
-
JarScanner 用于配置 Web 应用 JAR 包以及目录扫描器, 扫描 Web 应用的 TLD 及 web-fragment.xml 文件. 如不配置,默认使用 org.apache.tomcat.util.scan.StandardJarScanner.
-
-
CookieProcessor
- Tomcat提供了两个CookieProcessor实现: org.apache.tomcat.util.http.LegacyCookieProcessor 和 org.apache.tomcat.util.http.Rfc6265CookieProcessor,默认值为 Rfc6265CookieProcessor,
-
Loader
-
Loader 并不是一个 Java 类加载器,而是一个 Tomcat 组件,用于创建、加载以及销毁 Web 应用类加载器。它的生命周期管理与其他 Tomcat 组件一致。正因为如此,Web应用类加载器才会随着Web应用的状态而改变。
-
Tomcat 提供的默认实现为 org.apache.catalina.loader. webappLoader。大多数情况下,我们并不需要配置 Loader。但是在特殊情况下,Loader 配置可以使我们能够灵活控制 Java 类加载方式和顺序。
-
-
Manager配置
- Tomcat提供了两种独立会话管理器方案: org.apache.catalina.session.StandardManager 和 org.apache.catalina.session.PersistentNanager。
StandardManager 是 Tomcat 的标准实现,它提供了一种简单的会话存储方案,在 Tomcat 停止时(正常停止,而非强行关闭)会将所有会话串行化到一个文件中(默认为 SESSIONS.ser ),而在 Tomcat 启动时,将从该文件加载有效会话。
- Tomcat提供了两种独立会话管理器方案: org.apache.catalina.session.StandardManager 和 org.apache.catalina.session.PersistentNanager。
-
Resources
在 8.0 之前的版本中,Tomcat 通过< Resources >配置 Web 应用的静态资源,以实现类加载以及提供 HTML、JSP 或其他静态文件的访问服务。
6.2.3 context.xml
- 此处的 context.xml 文件既指 SCATALINA_BASE/conf/context.xml,又指 Web 应用中的 META-INF/context.xml 文件。
- 前者配置所有 Web 应用的公共信息,后者配置每个 Web 应用的定制化信息。
6.3 Web应用配置
- web.xml 是 Web 应用的部署描述文件,它支持的元素及属性来自于 Servlet 规范定义。
- 在 Tomcat 中,Web 应用的部署描述信息包括 SCATALINA_BASE/conf/web.xml 中的默认配置以及 Web 应用 WEB-INF/web.xml 下的定制配置.
Web部署描述文件的配置主要分为如下几类:
- ServletContext初始化参数
- 会话配置
- Servlet声明及映射
- 应用生命周期监听器
- Filter定义及映射
- MIME类型映射
- 欢迎文件列表
- 错误页面
- 本地化及编码映射
- 安全配置
- JNDI配置
6.3.1 ServletContext 初始化参数
我们可以通过< context-param>添加 ServletContext 初始化参数,它配置了一个键值对.
我们可以使用它配置加载文件路径、应用运行状态(开发、测试、生产)以及日志等。其配置方式如下,可以同时配置多个初始化参数。
< context-param>
< description>The servletContext parameter</ description>
< param-name>name</ param-name>
< param-value>The parameter value</ param-value>
</ context-param>
6.3.2 会话配置
< session-config>用于配置 Web 应用会话,包括超时时间、Cookie 配置以及会话追踪模式。
它将覆盖 server.xml 和 context.xml 中的配置。
< session-config>
< session-timeout>30</ session-timeout>
< cookie-config>
< name>JSESSIONID</ name>
< domain>sample.myApp.com< / domain>
< path>/</ path>
< comment>The session cookie< / comment>
< http-only>true</ http-only>
< secure>true</ secure>
< max-age>3600</ max-age>
</ cookie-config>
< tracking-mode>COOKIE</ tracking-mode>
</ session-config>
< session-timeout>用于配置会话超时时间,单位为分钟。
- < cookie-config>用于配置会话追踪 Cookie, 需要关注的是 http-only 和 secure,这两个属性主要用来控制 Cookie 的安全性。
- 只有当会话追踪模式是 Cookie 时,该配置才会生效(Tomcat默认支持通过 Cookie 追踪会话)。
- 此外,< domain>用于配置当前 Cookie 所处的域, path 用于配置 Cookie 所处的相对路径。
6.3.3 Servlet 声明及映射
- load-on-startup: 用于控制在 Web 应用启动时,Servlet 的加载顺序。
- 如果值小于0,在 Web 应用启动时,将不加载该 Servlet。
6.3.4 应用生命周期监听器
- < listener>用于添加Web应用生命周期监听器,可以同时配置多个。
- 监听器必须实现 javax.servlet.ServletContextListener 接口。
- Web 应用启动时会调用监听器的 contextInitialized() 方法,停止时调用其contextDestroyed() 方法。
- 启动时,ServletContextListener 的执行顺序与 web.xml 中的配置顺序一致,停止时执行顺序恰好相反。
6.3.5 Filter 定义及映射
- < filter>用于配置 Web 应用过滤器,用来过滤资源请求及响应。
- 经常用于认证、日志、加密、数据转换等。
6.3.6 MIME 类型映射
- MIME (Multipurpose Internet Mail Extensions )即多用途互联网邮件扩展类型,用于设定某类型的扩展名文件将采用何种应用程序打开。
- 当我们通过请求访问该扩展名的资源文件时,浏览器将自动使用指定的应用程序打开返回的资源文件。
- 多用于客户端自定义的文件名,如 Word 、Excel 等。
6.3.9 本地化及编码映射
< locale-encoding-mapping-list>用于指定本地化与响应编码的映射关系, 如果未显式地指明响应编码, 服务器将按照当前本地化信息确定响应编码。
< locale-encoding-mapping-list>
< locale-encoding-mapping>
< locale>zh</ locale>
< encoding>GBK</ encoding>
</ locale-encoding-mapping>
</ locale-encoding-mapping-list>
6.3.10 安全配置
通过 web.xml 中的安全配置, 可以为 Web 应用增加页面访问权限。
6.6 小结
- 本章从多个方面介绍了Tomcat的配置。
- 首先是 JVM 和服务器配置文件,如果希望深度定制及优化应用服务器,这是必须要详细了解的。
- 其次是 Web 应用配置,这部分虽然属于 Servlet 规范的内容,但是与 Web 应用的运行部署密切相关,因此也纳入了本章进行讲解。
- 同时,还介绍了 Tomcat 提供的几个过滤器实现,它们可以用于 Web 应用安全或者负载环境。
- 最后,简单介绍了 Tomcat 应用管理,包括 Web 界面、URL 命令行、Ant 任务及JMX。
第 7 章 Web服务器集成
7.1 Web 服务器与应用服务器的区别
-
Web 服务器
- Apache HTTP Server、Nginx、Lighttpd、IIS 等
-
应用服务器
- Tomcat、JBoss、Weblogic、WebSphere 等
-
Web服务器拥有性能优势,可以支持负载均衡,
-
应用服务器可以提供各种技术组件用于系统开发构建,可以支持应用集群。
-
应用服务器对于静态资源的处理普遍性能相对较差,
而 Web 服务器则可以充分利用操作系统本地 IO 的优势。 -
同时,对于静态资源,Web 服务器可以通过缓存等各种方式来提高其访问性能。
-
Web 服务器普遍支持作为前置的请求调度器以支持负载均衡.
7.4 与 Ngnix 集成
7.4.4 Tomcat 集成
Nginx 集成多个应用
Nginx 负载均衡配置
7.6 小结
本章首先简单介绍了一下 Web 服务器与应用服务器的区别,并列举了常见的几个需要 Web 服务器与应用服务器集成的场景。
第 8 章 Tomcat 集群
可以使用 spring-session 来将会话缓存在 redis 中.
参考 https://blog.csdn.net/jsbylibo/article/details/106544932
第 9 章 Tomcat 安全
9.1 配置安全
9.1.1 安装部署问题
-
移除 Tomcat 自带的几个 Web 应用, docs, examples, ROOT.
-
如果我们已经使用第三方工具来管理 Tomcat, 那么 host-manager 和 manager 可以直接移除.
-
如果我们希望使用这两个包,那么可以为它们增加IP访问限制(Remote-AddrValve,可以查看这两个应用下 META-INF/context.xml 文件中的内容。8.5 版本之前,需要手动开启,而 8.5 版本之后,则默认开启并只有本机可以访问)。
-
我们应当避免使用 root 用户权限启动 Tomcat (在 Linux 环境下),而应该单独为 Tomcat 服务器建立一个用户,并且授予运行应用服务器所需的最小系统权限,例如 Tomcat 用户不能远程访问 Linux 服务器.
9.1.2 server.xml 配置
-
移除不必要的组件
删除 AJP 链接器(8009). -
如果我们不需要使用 host-manager 和 manager 管理 Tomcat, 可以将< Engine>下的< Realm>以及名为 “UserDatabase” 的 Resource 删除。
-
修改关键配置
修改 server.xml 中的 shutdown 监听端口和 SHUTDOWN 指令字符串.
如果不使用该功能,可以直接将其禁用(将 port 属性设置为-1 ); -
如果部署的 Web 应用仅允许有限的客户端访问(如一些管理型应用),此时可以通过 Remote-AddrValve 控制访问客户端 IP。
第 10 章 Tomcat 性能调优
10.1 Tomcat 性能测试及诊断
10.1.2 性能测试工具
- ApacheBench
- ApacheBench ( ab)是一款 Apache Server 基准测试工具
- Apache JMeter
- JMeter 是一款采用 Java 开发的负载测试工具,它既可以用于测试静态资源访问,又可以用于测试动态请求,如 Web ( HTTPHTTPS)、FTP、JDBC、SOAP、LDAP、JMS、Java、JUnit,等等。
- JMeter 可以用于模拟服务器、网络、对象使用等不同类型的高负载,以综合分析应用系统性能。
10.1.3 数据采集及分析
-
网络
- Linux平台有许多可以查看网络使用情况的工具,查看总体带宽的如 nload、bmon、slurm 等,查看每个套接字的如 iftop、iptraf、tcptrack、pktstat 、netwatch 等。
-
内存/CPU
-
首先,可以通过 Windows 的任务管理器或者 Linux 的 top 命令查看服务器整体的资源使用情况。
-
如果我们仅仅希望查看服务器总体的 CPU、内存以及 I/O 读写情况,还可以使用 vmstat 命令.
-
幸运的是,对于基于 Java 的应用,JDK 在发布包中提供了内存及垃圾回收监控工具(位于$JAVA_HOME/bin)用于分析JVM的运行状况。
-
这些工具中,最常用的是 jstat 命令,它的命令格式如下:
jstat [ general0ption | outputoptions vmid [ interval[s|ms] [ count ] ] -
除了 jstat,Java 还提供了 jmap 命令用于打印进程的堆内存详情、产生对象数量以及内存使用情况.
-
VisualVM 是一款 Java 应用监控以及故障排除的工具,它利用诸如 jvmstat、JMX、SA、AttachAPI 等技术获取运行数据,采用最快、最轻量级的技术以使被监控应用的开销降至最低。
-
-
数据库访问
-
首先,我们可以通过数据库自带的功能来查看 SQL 语句的执行情况,对于 Oracle 来说,可以通过 VSSQLAREA、VSSQLTEXT 两个视图来查看 SQL 语句的执行,如执行时间、读写情况等.
-
对于MySQL来说,它可以支持以日志的形式记录执行缓慢的SQL
-
其次,我们还可以通过一些工具,以非侵入的形式在应用级记录 SQL 日志,如 p6spy,下载地址为: https:/lgithub.com/p6spy/p6spy。
-
第 11 章 Tomcat 附加功能
11.1 Tomcat 的嵌入式启动
- 除了 Tomcat 之外,在嵌入式方面使用较多的便是 Jetty ( http://www.eclipse.org/jetty/ )和 Undertow ( http://undertow.io/ )。
- 这两个项目分别由 Eclipse 和 Redhat 维护。
- 因此,从一定程度上讲, Tomcat 更适合企业级应用嵌入式,而 Jetty 和 Undertow 更适合轻量级、分布式的云计算环境。
SpringBoot 项目中使用 Jetty servlet 容器替换 Tomcat 容器
<!-- SpringBoot 项目中使用 Jetty servlet 容器替换 Tomcat 容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
11.2 Tomcat 中的 JNDI
- JNDI ( Java Naming and Directory Interface,Java 命名目录接口)是一套用于Java 目录服务的 API。
- Java 应用可以通过 JNDI API 按照命名查找数据和对象。
- 与大多数主机系统使用的 Java API 一样,JNDI 独立于其底层的实现,指定了一个服务提供者接口(SPIR),便于以松耦合的形式插人入框架。
- 根据供应商的不同,JNDI 可以用于服务器、文件、数据库等。
- JNDI 在 J2EE 中使用非常广泛,很多技术都需要 JNDI 作为支持,诸如 LDAP、CORBA、RMI、EJB、JDBC 和 JMS 等。
- JNDI 典型的应用场景包括以下两种。
- 将应用连接到一个外部服务,如数据库、LDAP;
- Servlet 通过 JNDI 查找 Web 容器提供的配置信息。