Tomcat——调优指南
在上一篇文章 Tomcat——总体架构解析,我们学习了
Tomcat整体架构,还知道了Tomcat是如何启动起来的,以及Tomcat的请求处理流程。今天我们来看下,如果优化Tomcat。
启动优化
关于如何让Tomcat 启动变快,官方网站有专门的文章来介绍这个话题 ,HowTo FasterStartUp - Apache Tomcat - Apache Software Foundation,推荐大家阅读下,但是其中很多的配置,已经不适用现有的 Spring Boot 内嵌式启动的方式,下面主要是 Spring Boot 使用方式优化启动速度的几条建议。
清理 JAR 文件
删除所有不需要的 JAR 文件。JVM 的类加载器在加载类时,需要查找每一个JAR 文件,去找到所需要的类。如果删除了不需要的 JAR 文件,查找的速度就会快一些。需要注意的是我们的项目中不应该出现 Servlet API 或者 Tomcat 自身的 JAR,如果使用 Maven 来构建你的应用,对Servlet API 的依赖应该范围是 provided
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
禁用 WebSocket 、JSP
Tomcat 7.0.47 之后的版本, 会扫描 WebSocket 注解的API实现. 包括 @ServerEndpoint 注解的类, 以及实现了 ServerApplicationConfig 接口的类。如果项目中不需要使用 WebSockets, 则可以删除 WebSocket 相关的 jar 包 (websocket-api.jar、tomcat-websocket.jar)。或者设置containerSciFilter 属性
Context 元素有一个 containerSciFilter 属性。 可以禁止 Tomcat 容器提供的插件功能: 如 WebSocket支持(Tomcat 7 及以后的版本可配置), JSP支持等(Tomcat 8 之后可配置)。
WebSocket相关的是:org.apache.tomcat.websocket.server.WsSciJSP相关的是:org.apache.jasper.servlet.JasperInitializer
@Component
public class MyTomcatCustomizer implements
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addContextCustomizers(new TomcatContextCustomizer() {
@Override
public void customize(Context context) {
context.setContainerSciFilter("org.apache.tomcat.websocket.server.WsSci");
}
});
factory.addContextCustomizers(new TomcatContextCustomizer() {
@Override
public void customize(Context context) {
context.setContainerSciFilter("org.apache.jasper.servlet.JasperInitializer");
}
});
}
}
禁止 Tomcat TLD 扫描
Tomcat 为了支持JSP,在应用启动的时候会扫描JAR里的TLD文件,加载里面定义的tag libraries(标签库),在 Tomcat 的启动日志里,你可能会碰到这种提示:
org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
可以配置 JarScanFilter 在启动中禁止扫描
@Component
public class MyTomcatCustomizer implements
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addContextCustomizers(new TomcatContextCustomizer() {
@Override
public void customize(Context context) {
((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
}
});
}
}
性能优化
Tomcat 的关键指标
在了解性能优化之前,我们先来看下Tomcat 的关键指标
Tomcat 的关键指标有:吞吐量、响应时间、错误数、线程池、CPU 以及 JVM 内存 。前三个指标是我们最关心的业务指标 ,后面三个指标是跟系统资源有关的
线程池调优
线程池的调优就是设置合理的线程池参数。我们先来看看 Tomcat 线程池中有哪些关键参数 ,Apache Tomcat 8 配置参考 - 执行器(线程池)

maxThreads
每个传入的请求都由 Tomcat 中的一个线程处理。 maxThreads 可以设置最大线程数。如果 maxThreads 属性设置得太低,则请求需要等到线程可用于处理请求。这会增加请求的的响应时间。
最大线程数取决于硬件及其拥有的 CPU 数量。硬件越好,处理器数量越多,Tomcat 需要支持的并发性就越大。一般建议在 500 ~ 800 ,最好的是根据自己的业务与配置反复压测调整,从而选择适合自己的以达到最优。
maxQueueSize
建议使用默认值,除非在压测的过程发现了瓶颈。如果大量任务来不及处理都堆积在maxQueueSize 中,会导致内存耗尽,这个时候就需要设置一个限制。
minSpareThreads
默认是 25 个线程,这个可以根据自己系统空闲度去适当的增加或者减少,从而减少线程池反复地创建和销毁线程的时间。
小结
线程池的各种参数,其中最重要的参数是最大线程数 maxThreads。实际场景中,我们需要在理想值的基础上进行压测,来获得最佳线程数
IO 调优
Tomcat 支持的 I/O 模型有:
BIO: 阻塞IO,Java提供的最基本的 IO 方式NIO:非阻塞 I/O,采用Java NIO类库实现。NIO2:异步 I/O,采用JDK7最新的NIO2类库实现。APR:采用Apache可移植运行库实现,是C/C++编写的本地库
我们通过官网的表格对比看下:The HTTP Connector
| BIO | NIO | NIO2 | APR | |
|---|---|---|---|---|
| Tomcat 版本 | 自 3.0.x 起 | 自 6.0.x 起 | 自 8.0.x 起 | 自 5.5.x 起 |
| 轮询支持 | 否 | 是 | 是 | 是 |
| 轮询队列大小 | N/A | maxConnections | maxConnections | maxConnections |
| 读请求头 | 阻塞 | 非阻塞 | 非阻塞 | 非阻塞 |
| 读请求体 | 阻塞 | 阻塞 | 阻塞 | 阻塞 |
| 写响应 | 阻塞 | 阻塞 | 阻塞 | 阻塞 |
| 等待新的请求 | 阻塞 | 非阻塞 | 非阻塞 | 非阻塞 |
| SSL支持 | Java SSL | Java SSL | Java SSL | Open SSL |
| SSL握手 | 阻塞 | 非阻塞 | 非阻塞 | 阻塞 |
| 最大链接数 | maxConnections | maxConnections | maxConnections | maxConnections |
在 Tomcat 8.0之前, 默认采用的I/O方式为BIO,之后改为NIO。无论NIO、NIO2还是APR,在性能方面均优于以往的BIO。如果采用APR,甚至可以达到接近于Apache HTTP Server的响应性能。
@Component
public class MyTomcatCustomizer implements
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
//设置使用协议
factory.setProtocol("org.apache.coyote.http11.Http11NioProtocol");
}
}
内存调优

Tomcat运行在Java虚拟机(JVM)之上。因此,内存调优就是 JVM 配置调优了。最常用的 JVM调优配置 :
# 使用 server 编译器
-server
# 将堆的初始值和最大值直接设置成对应的数值避免频繁GC
-Xms2048m -Xmx2048m
其他的配置建议使用默认值,在实际工作中要根据具体的错误信息去分析背后的原因,尤其是内存不够时,需要生成 Heap Dump 来分析,看再去调整各种 JVM 参数。
网络优化
先看下 Tomcat 几个比较关键的参数:Apache Tomcat 9 配置参考
DNS查找设置
enableLookups: 是否反查域名,以返回远程主机的主机名,默认为 false。
DNS 查找的成本很高,如果此值设置为 true,可能会使 Tomcat 看起来很慢。为了获得最佳性能,请将正在使用的所有连接器的 enableLookups 设置为 false
连接数设置
maxConnections :服务器将接受的最大连接数。当Tomcat 接收的连接数达到 maxConnections 时不会再从 队列中取走连接。
acceptCount:指定当所有可以使用的处理请求的线程数都被使用时,可传入连接请求的最大队列长度,超过这个数的请求将不予处理,默认为100个。
Tomcat 的最大并发连接数等于maxConnections + acceptCount。为了获得最佳性能,请将 acceptCount 设置为足够大,以可以接收突发连接。如果值过低,客户端会触发 Connection reset 。如果该值过高,队列将占用额外的服务器内存。
缓冲区设置
rxBufSize:套接字接收缓冲区
txBufSize : 套接字接收缓冲区
使用 NIO 和 NIO2 连接器时,您可以配置 Tomcat 使用的套接字读取缓冲区和写入缓冲区的大小。socket.rxBufSize 和 socket.txBufSize 的值越大,支持的吞吐量就越高
响应压缩
ompression:是否对响应的数据进行 GZIP 压缩,off:禁止压缩;on:允许压缩;force:都进行压缩,默认值为off。
compressionMinSize:表示压缩响应的最小值,只有当响应报文大小大于这个值的时候才会对报文进行压缩,如果开启了压缩功能,默认值就是2048KB。
compressableMimeType:压缩类型,指定对哪些类型的文件进行数据压缩。
通过在 SpringBoot 配置文件,配置可以开启压缩。压缩数据后可以有效的减少数据的大小,一般可以减小1/3左右,提升网络传输速度
# 开启Gzip压缩,默认只压缩超过2048字节的数据
server:
compression:
enabled: true
mime-types: application/json
总结
最后的话,本篇文章介绍的仅仅是性能优化很少的一部分。本质上性能优化的范围涵盖非常广,而且也是一个非常复杂的过程,除了应用服务器优化,还包缓存优化、操作系统优化、前端页面优化、网络优化等。针对具体的应用场景,其优化方式也会多种多样,这并不是一些通用的规则可以概括的。这里仅仅起到一个启发和引导的作用。
参考
转载自:https://juejin.cn/post/7246264754141921341