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.WsSci
JSP
相关的是: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
:表示压缩响应的最小值,只有当响应报文大小大于这个值的时候才会对报文进行压缩,如果开启了压缩功能,默认值就是2048
KB。
compressableMimeType
:压缩类型,指定对哪些类型的文件进行数据压缩。
通过在 SpringBoot
配置文件,配置可以开启压缩。压缩数据后可以有效的减少数据的大小,一般可以减小1/3左右,提升网络传输速度
# 开启Gzip压缩,默认只压缩超过2048字节的数据
server:
compression:
enabled: true
mime-types: application/json
总结
最后的话,本篇文章介绍的仅仅是性能优化很少的一部分。本质上性能优化的范围涵盖非常广,而且也是一个非常复杂的过程,除了应用服务器优化,还包缓存优化、操作系统优化、前端页面优化、网络优化等。针对具体的应用场景,其优化方式也会多种多样,这并不是一些通用的规则可以概括的。这里仅仅起到一个启发和引导的作用。
参考
转载自:https://juejin.cn/post/7246264754141921341