1. Tomcat 基础

1.1 下载安装

官方地址 https://tomcat.apache.org/download-80.cgi

image.png

1.2 目录结构

bin :存放 Tomcat 的启动、停止等批处理脚本文件

  • startup.bat, startup.sh : 启动脚本
  • shutdown.bat, shutdown.sh :停止脚本

conf :用于存放 Tomcat 的相关配置文件

  • context.xml :用于定义所有 web 应用均需加载的 Context 配置,如果 web 应用指定了自己的 context.xml ,该文件将被覆盖
  • catalina.properties :Tomcat 的环境变量配置
  • catalina.policy :Tomcat 运行的安全策略配置
  • logging.properties :Tomcat 的日志配置文件, 可以通过该文件修改 Tomcat 的日志级别及日志路径等
  • server.xml :Tomcat 服务器的核心配置文件
  • tomcat-users.xml :定义 Tomcat 默认的用户及角色映射信息配置 web.xml
  • web.xml :Tomcat 中所有应用默认的部署描述文件, 主要定义了基础 Servlet 和 MIME 映射

lib :Tomcat 服务器的依赖包
logs :Tomcat 默认的日志存放目录
webapps :Tomcat 默认的 web 应用部署目录
work :web 应用 JSP 代码生成和编译的临时目录

1.3 源码编译

官方地址 https://github.com/apache/tomcat 也可以使用我编译后带解析的地址 https://github.com/masteryourself-opensource/tomcat(已改造成 maven 工程)

直接运行 org.apache.catalina.startup.Bootstrap#main 方法

2. Tomcat 架构

2.1 整体架构

Tomcat 要实现的两个核心功能是:

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

因此 Tomcat 设计了两个核心组件连接器 Connector 和容器 Container 来分别做这两件事情。连接器负责对外交流,容器负责内部处理
image.png

2.2 连接器 Coyote

2.2.1 Coyote 架构

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

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

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

2.2.2 IO 模型与协议

在 Coyote 中 , Tomcat 支持的多种 I/O 模型和应用层协议,具体包含哪些 IO 模型和应用层协议

Tomcat 支持的 IO 模型(自 8.5/9.0 版本起,Tomcat 移除了 对 BIO 的支持)

IO 模型 描述
NIO 非阻塞 IO,采用 Java NIO 类库实现
NIO2 异步 IO,采用 JDK1.7 最新的 NIO2 类库实现
APR 采用 Apache 可移植运行库实现,是 C/C++ 编写的本地库,需要单独安装 APR 库

Tomcat 支持的应用层协议

应用层协议 描述
HTTP/1.1 通用的 web 应用访问协议
AJP 用于和 web 服务器做集成(如 Apache),用来实现对静态资源的优化以及集群部署
HTTP/2 HTTP2 大幅度的提升了 HTTP 性能,是下一代 HTTP 协议
Tomcat 8.5/9.0 之后版本支持

在 8.0 之前,Tomcat 默认采用的 I/O 方式为 BIO,之后改为 NIO。无论 NIO、NIO2 还是 APR, 在性能方面均优于以往的 BIO。 如果采用APR, 甚至可以达到 Apache HTTP Server 的性能

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

2.2.3 连接器组件

2.2.3.1 EndPoint

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

2.2.3.2 Processor

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

2.2.3.3 ProtocolHandler

Coyote 协议接口, 通过 Endpoint 和 Processor , 实现针对具体协议的处理能力。Tomcat 按照协议和 I/O 提供了 6 个实现类 :AjpNioProtocolAjpAprProtocolAjpNio2ProtocolHttp11NioProtocolHttp11Nio2ProtocolHttp11AprProtocol。我们在配置 tomcat/conf/server.xml 时 ,至少要指定具体的 ProtocolHandler , 当然也可以指定协议名称 , 如 HTTP/1.1 ,如果安装了 APR,那么将使用 Http11AprProtocol , 否则使用 Http11NioProtocol

2.2.3.4 Adapter

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

2.3 容器 Catalina

Tomcat 是一个由一系列可配置的组件构成的 Web 容器,而 Catalina 是 Tomcat 的 servlet 容器。
Catalina 是 Servlet 容器实现,包含了之前讲到的所有的容器组件,以及后续章节涉及到的安全、会话、集群、管理等 Servlet 容器架构的各个方面。它通过松耦合的方式集成 Coyote,以完成按照请求协议进行数据读写。同时,它还包括我们的启动入口、Shell 程序等
image.png
Tomcat 本质上就是一款 Servlet 容器, 因此 Catalina 才是 Tomcat 的核心 , 其他模块都是为 Catalina 提供支撑的。 比如 :通过 Coyote 模块提供链接通信,Jasper 模块提供 JSP 引擎,Naming 提供 JNDI 服务,Juli 提供日志服务

2.3.1 Catalina 架构

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

Catalina 各个组件的职责

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

2.3.2 Container 架构

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

Container 各个组件的职责

容器 描述
Engine 表示整个 Catalina 的 Servlet 引擎,用来管理多个虚拟站点
一个 Service 最多有一个 Engine,但是一个引擎可包含多个 Host
Host(S) 代表一个虚拟主机,或者说一个站点,可以给 Tomcat 配置多个虚拟主机地址
一个虚拟主机下可以包含多个 Context
Context(S) 表示一个 web 应用程序
一个 web 应用程序可以包含多个 Wrapper
Wrapper(S) 表示一个 Servlet,Wrapper 作为容器的最底层,不能包含子容器

我们也可以再通过 Tomcat 的 server.xml 配置文件来加深对 Tomcat 容器的理解。Tomcat 采用了组件化的设计,它的构成组件都是可配置的,其中最外层的是 Server,其他组件按照一定的格式要求配置在这个顶层容器中

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Server port="8005" shutdown="SHUTDOWN">
  3. <Service name="Catalina">
  4. <!-- 定义线程池 -->
  5. <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
  6. maxThreads="150" minSpareThreads="4"/>
  7. <!-- 可以配置多个 Connector 连接器, 可以使用 executor="tomcatThreadPool" 配置线程池 -->
  8. <Connector port="8080" protocol="HTTP/1.1"
  9. connectionTimeout="20000"
  10. redirectPort="8443" />
  11. <!-- 用来管理虚拟站点 -->
  12. <Engine name="Catalina" defaultHost="localhost">
  13. <!-- 表示一个 web 应用程序 -->
  14. <Host name="localhost" appBase="webapps"
  15. unpackWARs="true" autoDeploy="true">
  16. </Host>
  17. </Engine>
  18. </Service>
  19. </Server>

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

3. Tomcat 源码解析

3.1 启动流程

image.png

  1. 启动 tomcat,需要调用 bin/startup.bat ,在 linux 目录下需要调用 bin/startup.sh ,这个脚本调用了 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 的配置文件,初始化容器组件,监听对应的端口号,准备接收客户端请求

    3.2 启动流程解析

    3.2.1 LifeCycle

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

    3.2.2 组件实现

    StandardXxx

    上面我们提到的 Server、Service、Engine、Host、Context 都是接口,默认实现类是 StandardServer、StandardService、StandardEngine、StandardHost、StandardContext

Endpoint

对于 Endpoint 组件来说,在 Tomcat 中没有对应的 Endpoint 接口, 但是有一个抽象类 AbstractEndpoint ,其下有三个实现类: NioEndpoint、Nio2Endpoint、AprEndpoint , 这三个实现类,分别对应 Coyote 支持的三种 IO 模型:NIO,NIO2,APR
在 Tomcat8.5 版本中,默认采用的是 NioEndpoint

ProtocolHandler

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

  • AjpNioProtocol :采用 NIO 的 IO 模型
  • AjpNio2Protocol:采用 NIO2 的 IO 模型
  • AjpAprProtocol :采用 APR 的 IO 模型,需要依赖于 APR 库

HTTP 协议

  • Http11NioProtocol :采用 NIO 的 IO 模型,默认使用的协议
  • Http11Nio2Protocol:采用 NIO2 的 IO 模型
  • Http11AprProtocol :采用 APR 的 IO 模型,需要依赖于 APR库

    3.3 请求流程

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

Mapper 组件的功能就是将用户请求的 URL 定位到一个 Servlet,它的工作原理是: Mapper 组件里保存了 Web 应用的配置信息,其实就是容器组件与访问路径的映射关系,比如 Host 容器里配置的域名、Context 容器里的 Web 应用路径,以及 Wrapper 容器里 Servlet 映射的路径,你可以想象这些配置信息就是一个多层次的 Map

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

  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 对象的处理方法

    3.4 请求流程解析

    image.png
    在前面所讲解的 Tomcat 的整体架构中,我们发现 Tomcat中 的各个组件各司其职,组件之间松耦合,确保了整体架构的可伸缩性和可拓展性,那么在组件内部,如何增强组件的灵活性和拓展性呢? 在 Tomcat中,每个 Container 组件采用责任链模式来完成具体的请求处理

在 Tomcat 中定义了 Pipeline 和 Valve 两个接口,Pipeline 用于构建责任链, 后者代表责任链上的每个处理器。Pipeline 中维护了一个基础的 Valve,它始终位于 Pipeline 的末端(最后执行),封装了具体的请求处理和输出响应的过程。当然,我们也可以调用 addValve()方法, 为 Pipeline 添加其他的 Valve, 后添加的 Valve 位于基础的 Valve 之前,并按照添加顺序执行。Pipiline 通过获得首个Valve 来启动整合链条的执行

4. 服务器配置

Tomcat 服务器的配置主要集中于 tomcat/conf 下的 catalina.policy、 catalina.properties、context.xml、server.xml、tomcat-users.xml、web.xml 文件

4.1 server.xml

server.xml 是 Tomcat 服务器的核心配置文件,包含了 Tomcat 的 Servlet 容器(Catalina)的所有配置

4.1.1 Server

Server 是 server.xml 的根元素,用于创建一个 Server 实例,默认使用的实现类是org.apache.catalina.core.StandardServer

  1. <Server port="8005" shutdown="SHUTDOWN">
  2. </Server>
属性 含义
port Tomcat 监听的关闭服务器的端口号
-1 表示禁用
shutdown 关闭服务器的指令字符串
telnet 127.0.0.1 8005 ,输入 SHUTDOWN 指令即可关闭 Tomcat 服务器

Server 内嵌的子元素有 Listener、GlobalNamingResources、Service

默认配置的 5 个Listener 含义

  1. <!-- 用于以日志形式输出服务器、操作系统、JVM 的版本信息 -->
  2. <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  3. <!-- 用于加载(服务器启动)和销毁(服务器停止)ARP。如果找不到 ARP,则会输出日志,并不影响 Tomcat 启动 -->
  4. <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  5. <!-- 用于避免 JRE 内存泄露问题 -->
  6. <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  7. <!-- 用户加载(服务器启动)和销毁(服务器停止)全局命名服务 -->
  8. <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  9. <!-- 用于在 Context 停止时重建 Executor 池中的线程,以避免 ThreadLocal 相关的内存泄露 -->
  10. <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

GlobalNamingResources 中定义了全局命名服务

  1. <GlobalNamingResources>
  2. <!-- Editable user database that can also be used by
  3. UserDatabaseRealm to authenticate users
  4. -->
  5. <Resource name="UserDatabase" auth="Container"
  6. type="org.apache.catalina.UserDatabase"
  7. description="User database that can be updated and saved"
  8. factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
  9. pathname="conf/tomcat-users.xml" />
  10. </GlobalNamingResources>

4.1.2 service

该元素用于创建 Service 实例,默认使用 org.apache.catalina.core.StandardService 。默认情况下,Tomcat 仅指定了 Service 的名称, 值为 “Catalina”

Service 可以内嵌的元素为 :Listener、Executor、Connector、Engine,其中 Listener 用于为 Service 添加生命周期监听器, Executor 用于配置 Service 共享线程池,Connector 用于配置 Service 包含的链接器, Engine 用于配置 Service中 链接器对应的 Servlet 容器引擎

  1. <Service name="Catalina">
  2. </Service>

一个 Server 服务器,可以包含多个 Service 服务

4.1.3 Executor

默认情况下,Service 并未添加共享线程池配置。 如果我们想添加一个线程池, 可以在下添加如下配置

  1. <Executor name="tomcatThreadPool"
  2. namePrefix="catalina‐exec‐"
  3. maxThreads="200"
  4. minSpareThreads="100"
  5. maxIdleTime="60000"
  6. maxQueueSize="Integer.MAX_VALUE"
  7. prestartminSpareThreads="false"
  8. threadPriority="5"
  9. className="org.apache.catalina.core.StandardThreadExecutor"/>
属性 含义
name 线程池名称,用于 Connector 指定
namePrefix 创建的每个线程的名称前缀
一个单独的线程名称为 namePrefix + threadNumber
maxThreads 池中最大线程数
minSpareThreads 活跃线程数,也就是核心线程数
这些线程不会被销毁,会一直存在
maxIdleTime 线程空闲时间,超过该时间后,空闲线程会被销毁
默认值为 600(1分钟),单位是毫秒
maxQueueSize 在被执行前最大线程排队数目,默认为 Int 的最大值,也就是广义的无限。除非特殊情况,这个值不需要更改,否则会有请求不会被处理的情况发生
prestartminSpareThreads 启动线程池时是否启动 minSpareThreads 部分线程
默认值为 false,即不启动
threadPriority 线程池中线程优先级,默认值为 5,值从 1 到 10
className 线程池实现类,未指定情况下,默认实现类为org.apache.catalina.core.StandardThreadExecutor
如果想使用自定义线程池首先需要实现 org.apache.catalina.Executor 接口


如果不配置共享线程池,那么 Catalina 各组件在用到线程池时会独立创建

4.1.4 Connector(性能优化)

Connector 用于创建链接器实例。默认情况下,server.xml 配置了两个链接器,一个支持 HTTP 协议,一个支持 AJP 协议。因此大多数情况下,我们并不需要新增链接器配置,只是根据需要对已有链接器进行优化

  1. <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
  2. <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
属性 含义
port 端口号,Connector 用于创建服务端 Socket 并进行监听, 以等待客户端请求 链接。
如果该属性设置为0,Tomcat 将会随机选择一个可用的端口号给当前Connector 使用
protocol 当前 Connector 支持的访问协议。 默认为 HTTP/1.1 , 并采用自动切换机制选择一个基于 JAVA NIO 的链接器或者基于本地 APR 的链接器(根据本地是否含有 Tomcat 的本地库判定)
如果不希望采用上述自动切换的机制, 而是明确指定协议,可以使用:org.apache.coyote.http11.Http11NioProtocol ,非阻塞式 Java NIO 链接器
org.apache.coyote.http11.Http11Nio2Protocol ,非阻塞式 JAVA NIO2 链接器
org.apache.coyote.http11.Http11AprProtocol , APR 链接器
org.apache.coyote.ajp.AjpNioProtocol , 非阻塞式 Java NIO 链接器
org.apache.coyote.ajp.AjpNio2Protocol ,非阻塞式 JAVA NIO2 链接器
org.apache.coyote.ajp.AjpAprProtocol, APR 链接器
maxThreads 由此连接器创建的最大请求处理线程数,这决定可同时处理的最大并发请求数
默认值是 200
minSpareThreads 保持运行状态的最小线程数
默认值是 10
acceptCount 接收传入的连接请求的最大队列长度
默认队列长度是 100
maxConnections 在任何给定的时间,服务器能接收和处理的最大连接数
NIO的默认值是 10000
connectionTimeout Connector 接收链接后的等待超时时间, 单位为毫秒
-1 表示不超时
keepAliveTimeout 在关闭这条连接之前,连接器将等待另一个HTTP请求的毫秒数
默认使用 connectionTimeout 属性值
maxKeepAliveRequests 和 Nginx 中的 keepalive_request 属性的功能是一样的
默认值是 100
redirectPort 当前 Connector 不支持 SSL 请求, 接收到了一个请求, 并且也符合 security-constraint 约束, 需要 SSL 传输,Catalina 自动将请求重定向到指定的端口
executor 指定共享线程池的名称,也可以通过 maxThreads、minSpareThreads 等属性配置内部线程池
URIEncoding 用于指定编码 URI 的字符编码
Tomcat8.x 版本默认的编码为 UTF-8
Tomcat7.x 版本默认为 ISO-8859-1

完整的配置参考如下

  1. <Connector port="8080"
  2. protocol="HTTP/1.1"
  3. maxThreads="1000"
  4. minSpareThreads="100"
  5. acceptCount="1000"
  6. maxConnections="1000"
  7. connectionTimeout="20000"
  8. keepAliveTimeout="300000"
  9. compression="on"
  10. compressionMinSize="2048"
  11. disableUploadTimeout="true"
  12. redirectPort="8443"
  13. URIEncoding="UTF8" />

4.1.5 Engine

Engine 作为 Servlet 引擎的顶级元素,内部可以嵌入: Cluster、Listener、Realm、 Valve 和 Host

  1. <Engine name="Catalina" defaultHost="localhost">
  2. </Engine>

属性说明

属性 含义
name 用于指定 Engine 的名称, 默认为 Catalina 。该名称会影响一部分 Tomcat 的 存储路径(如临时文件)
defaultHost 默认使用的虚拟主机名称, 当客户端请求指向的主机无效时, 将交由默认的虚拟主机处理, 默认为 localhost

4.1.6 Host

Host 元素用于配置一个虚拟主机, 它支持以下嵌入元素:Alias、Cluster、Listener、 Valve、Realm、Context。如果在Engine下配置Realm, 那么此配置将在当前 Engine 下的所有 Host 中共享。 同样,如果在 Host 中配置 Realm , 则在当前 Host 下的所有 Context 中共享

Context中的 Realm 优先级 > Host 的 Realm 优先级 > Engine 中的 Realm 优先级

  1. <Host name="localhost" appBase="webapps"
  2. unpackWARs="true" autoDeploy="true">
  3. </Host>
属性 含义
name 当前 Host 通用的网络名称, 必须与 DNS 服务器上的注册信息一致。 Engine 中包含的 Host 必须存在一个名称与 Engine 的defaultHost 设置一致
appBase 当前 Host 的应用基础目录, 当前 Host 上部署的 Web 应用均在该目录下 (可以是绝对目录,相对路径)
默认为 webapps
unpackWARs 设置为 true, Host 在启动时会将 appBase 目录下 war 包解压为目录
设置为 false,Host 将直接从 war 文件启动
autoDeploy 控制 tomcat 是否在运行时定期检测并自动部署新增或变更的 web 应用

4.1.7 Context

Context 用于配置一个 Web 应用,默认的配置如下

  1. <Context docBase="myApp" path="/myApp">
  2. </Context>
属性 含义
docBase Web 应用目录或者 War 包的部署路径。可以是绝对路径,也可以是相对于 Host appBase 的相对路径
path Web 应用的 Context 路径。如果我们 Host 名为 localhost, 则该 web 应用访问的根路径为 http://localhost:8080/myApp

它支持的内嵌元素为:CookieProcessor, Loader, Manager,Realm,Resources, WatchedResource,JarScanner,Valve

4.2 tomcat-users.xml

该配置文件中,主要配置的是 Tomcat 的用户,角色等信息,用来控制 Tomcat 中 manager, host-manager 的访问权限

4.2.1 host-manager

Tomcat启动之后,可以通过 http://localhost:8080/host-manager/html 访问该 Web 应用。 host-manager 默认添加了访问权限控制,当打开网址时,需要输入用户名和密码(在 conf/tomcat-users.xml 中配置)

对应的角色有两种

  1. admin-gui:用于控制页面访问权限
  2. admin-script:用于控制以简单文本的形式进行访问
    1. <role rolename="admin-gui"/>
    2. <role rolename="admin-script"/>
    3. <user username="masteryourself" password="masteryourself" roles="admin-gui, admin-script"/>

    4.2.2 manager

    manager 的访问地址为 http://localhost:8080/manager, 同样 manager 也添加了页面访问控制,因此我们需要为登录用户分配角色为
    1. <role rolename="manager-gui"/>
    2. <role rolename="manager-script"/>
    3. <user username="masteryourself" password="masteryourself" roles="admin-gui, admin-script, manager-gui, manager-script"/>

    4.3 JVM 参数配置

    4.3.1 catalina.bat

    1. set JAVA_OPTS=‐server Xms2048m Xmx2048m XX:MetaspaceSize=256m XX:MaxMetaspaceSize=256m XX:SurvivorRatio=8

    4.3.2 catalina.sh

    1. JAVA_OPTS="‐server ‐Xms1024m ‐Xmx2048m ‐XX:MetaspaceSize=256m ‐ XX:MaxMetaspaceSize=512m ‐XX:SurvivorRatio=8"
    | 配置 | 含义 | | —- | —- | | -Xms | 堆内存的初始大小 | | -Xmx | 堆内存的最大大小 | | -Xmn | 新生代的内存大小,官方建议是整个堆得 3/8 | | -XX:MetaspaceSize | 元空间内存初始大小, 在 JDK1.8 版本之前配置为 -XX:PermSize(永久代) | | -XX:InitialCodeCacheSize
    -XX:ReservedCodeCacheSize | 代码缓存区大小 | | -XX:NewRatio | 设置新生代和老年代的相对大小比例。这种方式的优点是新生代大小会随着整个堆大小动态扩展
    如 -XX:NewRatio=3 指定老年代 / 新生代为 3/1。 老年代占堆大小的 3/4,新生代占 1/4 | | -XX:SurvivorRatio | 指定伊甸园区 (Eden) 与幸存区大小比例
    如 -XX:SurvivorRatio=10 表示伊甸园区 (Eden) 是 幸存区 To 大小的 10 倍 (也是幸存区 From 的 10 倍)。 所以伊甸园区 (Eden) 占新生代 大小的 10/12, 幸存区 From 和幸存区 To 每 个占新生代的 1/12
    注意, 两个幸存区永远是一样大的 |

5. Web 应用配置

web.xml 是web应用的描述文件, 它支持的元素及属性来自于 Servlet 规范定义 。 在 Tomcat 中,Web 应用的描述信息包括 tomcat/conf/web.xml 中默认配置 以及 Web 应用 WEB-INF/web.xml 下的定制配置

5.1 ServletContext 初始化参数

我们可以通过添加 ServletContext 初始化参数,它配置了一个键值对,这样我们可以在应用程序中使用 javax.servlet.ServletContext.getInitParameter() 方法获取参数

  1. <contextparam>
  2. <paramname>contextConfigLocation</paramname>
  3. <paramvalue>classpath:applicationContext‐*.xml</paramvalue>
  4. <description>Spring Config File Location</description>
  5. </contextparam>

5.2 会话配置

用于配置 Web 应用会话,包括超时时间、Cookie 配置以及会话追踪模式。它将覆盖 server.xml 和 context.xml 中的配置

  1. <sessionconfig>
  2. <!-- 会话超时时间,默认是 30 分钟 -->
  3. <sessiontimeout>30</sessiontimeout>
  4. </sessionconfig>

5.3 Servlet 配置

  1. <servlet>
  2. <servletname>myServlet</servletname>
  3. <servletclass>org.masteryourself.tutotial.tomcat.MyServlet</servletclass>
  4. <initparam>
  5. <paramname>fileName</paramname>
  6. <paramvalue>init.conf</paramvalue>
  7. </initparam>
  8. <loadonstartup>1</loadonstartup>
  9. <enabled>true</enabled>
  10. </servlet>
  11. <servletmapping>
  12. <servletname>myServlet</servletname>
  13. <urlpattern>*.do</urlpattern>
  14. <urlpattern>/myservet/*</urlpattern>
  15. </servletmapping>
配置 含义
servlet-name 指定 servlet 的名称,该属性在 web.xml 中唯一
servlet-class 用于指定 servlet 类名
init-param 用于指定 servlet 的初始化参数, 在应用中可以通过 HttpServlet.getInitParameter() 获取
load-on-startup 用于控制在 Web 应用启动时,Servlet 的加载顺序
值小于0, web 应用启动时,不加载该 servlet, 第一次访问时加载
enabled 若为 false ,表示 Servlet 不处理任何请求
url-pattern 用于指定 URL 表达式,一个 servlet-mapping 可以同时配置多个 url-pattern

5.4 Listener 配置

Listener 用于监听 servlet 中的事件,例如 context、request、session 对象的创建、修改、删除,并触发响应事件。Listener 是观察者模式的实现,在servlet 中主要用于对 context、request、session对象的生命周期进行监控。在 servlet2.5 规范中共定义了 8 种 Listener。在启动时,ServletContextListener 的执行顺序与 web.xml 中的配置顺序一 致, 停止时执行顺序相反。

  1. <listener>
  2. <listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass>
  3. </listener>

5.5 Filter 配置

filter 用于配置 web 应用过滤器, 用来过滤资源请求及响应。经常用于认证、日志、加密、数据转换等操作, 配置如下

  1. <filter>
  2. <filtername>myFilter</filtername>
  3. <filterclass>org.masteryourself.tutotial.tomcat.MyFilter</filterclass>
  4. <asyncsupported>true</asyncsupported>
  5. <initparam>
  6. <paramname>language</paramname>
  7. <paramvalue>CN</paramvalue>
  8. </initparam>
  9. </filter>
  10. <filtermapping>
  11. <filtername>myFilter</filtername>
  12. <urlpattern>/*</urlpattern>
  13. </filtermapping>
配置 含义
filter-name 用于指定过滤器名称,在 web.xml 中,过滤器名称必须唯一
filter-class 过滤器的全限定类名, 该类必须实现 Filter 接口
async-supported 该过滤器是否支持异步
init-param 用于配置Filter的初始化参数, 可以配置多个, 可以通过 FilterConfig.getInitParameter() 获取
url-pattern 指定该过滤器需要拦截的 URL

5.6 欢迎页配置

welcome-file-list 用于指定 web 应用的欢迎文件列表,请求顺序是从上至下

  1. <welcomefilelist>
  2. <welcomefile>index.html</welcomefile>
  3. <welcomefile>index.htm</welcomefile>
  4. <welcomefile>index.jsp</welcomefile>
  5. </welcomefilelist>

5.7 错误页面配置

error-page 用于配置 Web 应用访问异常时定向到的页面,支持 HTTP 响应码和异常类两种形式

  1. <errorpage>
  2. <errorcode>404</errorcode>
  3. <location>/404.html</location>
  4. </errorpage> <errorpage>
  5. <errorcode>500</errorcode>
  6. <location>/500.html</location>
  7. </errorpage>
  8. <errorpage>
  9. <exceptiontype>java.lang.Exception</exceptiontype>
  10. <location>/error.jsp</location>
  11. </errorpage>

6. 安全配置

6.1 配置安全

  1. 删除 webapps 目录下的所有文件,禁用 tomcat 管理界面
  2. 2.注释或删除 tomcat-users.xml 文件内的所有用户权限
  3. 更改关闭 tomcat 指令或禁用
  • tomcat 的 server.xml 中定义了可以直接关闭 Tomcat 实例的管理端口(默认 8005)。 可以通过 telnet 连接上该端口之后,输入 SHUTDOWN (此为默认关闭指令)即可关闭 Tomcat 实例(注意,此时虽然实例关闭了,但是进程还是存在的) ```xml

  1. 4. 定义错误页面
  2. - 避免将堆栈信息响应给用户,也提供了用户体验
  3. <a name="k86qp"></a>
  4. ## 6.2 HTTPS 安全
  5. 1. 生成秘钥文件
  6. ```bash
  7. ruanrenzhao@MacBook-Pro conf % keytool -genkey -alias tomcat -keyalg RSA -keystore tomcat.keystore
  8. 输入密钥库口令:
  9. 再次输入新口令:
  10. 您的名字与姓氏是什么?
  11. [Unknown]: masteryourself
  12. 您的组织单位名称是什么?
  13. [Unknown]: masteryourself
  14. 您的组织名称是什么?
  15. [Unknown]: masteryourself
  16. 您所在的城市或区域名称是什么?
  17. [Unknown]: shanghai
  18. 您所在的省/市/自治区名称是什么?
  19. [Unknown]: shanghai
  20. 该单位的双字母国家/地区代码是什么?
  21. [Unknown]: CN
  22. CN=masteryourself, OU=masteryourself, O=masteryourself, L=shanghai, ST=shanghai, C=CN是否正确?
  23. [否]: Y
  24. 输入 <tomcat> 的密钥口令
  25. (如果和密钥库口令相同, 按回车):
  26. 再次输入新口令:
  1. 将秘钥库文件 tomcat.keystore 复制到 tomcat/conf 目录下
  2. 配置 tomcat/conf/server.xml

    1. <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
    2. maxThreads="150" schema="https" secure="true" SSLEnabled="true">
    3. <SSLHostConfig certificateVerification="false">
    4. <Certificate certificateKeystoreFile="/Users/ruanrenzhao/Downloads/apache-tomcat-8.5.73/conf/tomcat.keystore"
    5. certificateKeystorePassword="masteryourself" type="RSA" />
    6. </SSLHostConfig>
    7. </Connector>
  3. 使用 https 协议访问 Tomcat [https://localhost:8443/](https://localhost:8443/)