项目经验_之_服务器Tomcat参数配置
前言
Github:https://github.com/HealerJean
一、线上实例
1、JD
export maxParameterCount="1000"
export acceptCount="1000"
export maxSpareThreads="750"
export maxThreads="1000"
export minSpareThreads="50"
export URIEncoding="UTF-8"
export JAVA_OPTS=" -Xms5324m -Xmx5324m -XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=512m -XX:MaxDirectMemorySize=512m -XX:ConcGCThreads=2 -XX:ParallelGCThreads=4 -XX:CICompilerCount=2 -Djava.library.path=/usr/local/lib -server -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/export/Logs -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/export/Logs/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M -Djava.awt.headless=true -Dsun.net.client.defaultConnectTimeout=60000 -Dsun.net.client.defaultReadTimeout=60000 -Djmagick.systemclassloader=no -Dnetworkaddress.cache.ttl=30 -Dsun.net.inetaddr.ttl=30 "
二、参数介绍
1、线程参数配置
| 参数 | 设计哲学 | 参考 | |
|---|---|---|---|
maxThreads |
并发能力上限,受 CPU 和内存制约 |
200 | |
minSpareThreads |
快速响应,避免冷启动延迟 | 50 | |
maxSpareThreads |
资源节约,避免空闲线程浪费 | 150 | |
acceptCount |
缓冲突发,平滑流量波动 | 500 | |
maxParameterCount |
安全防护,防恶意攻击 | 1000 |
1)maxIdleTime
最大空闲时间,超过这个空闲时间,且线程数大于
minSpareThreads的,都会被回收,默认值1分钟(60000ms);
2)minSpareThreads
-
含义:最小空闲线程数,任何情况都会存活的线程数,即便超过了最大空闲时间,也不会被回收,默认值4;
-
原理:
-
避免请求到来时临时创建线程的开销(线程创建有成本)。
-
Tomcat 启动后会预先创建这些线程,确保“热启动”。
-
-
推荐值:
-
一般设为
maxThreads的20%~30%。 -
50是合理的,保证有一定预热线程应对突发流量。
-
3)maxSpareThreads
-
含义:最大空闲线程数,在最大空闲时间(
maxIdleTime)内活跃过,此时空闲,当空闲时间大于maxIdleTime则被回收,小则继续存活,等待被调度,默认值50; -
原理:
-
防止空闲线程过多浪费内存和系统资源。
-
当负载下降时,
Tomcat会逐步回收多余线程。
-
-
推荐值:通常设为
maxThreads的60%~80%。
4)maxThreads
-
含义:最大线程数,大并发请求时,
tomcat能创建来处理请求的最大线程数,超过则放入请求队列中进行排队,默认值为200;-
WindowsTomcat允许每个进程maxThreads(最大线程数)2000 -
LinuxTomcat允许每个进程maxThreads(最大线程数)1000
-
- 原理:
- 每个线程处理一个请求(同步阻塞模式下)。
- 如果所有线程都在忙,新请求只能排队等待空闲线程。
- 超过
maxThreads的请求将被拒绝或排队(取决于acceptCount)。 - 为什么不是越大越好?
- 每个线程默认占用约 1MB 栈内存(可通过
-Xss调整),200线程 ≈200MB内存。 - 更重要的是:
CPU上下文切换开销。8 核 CPU 同时运行 200 个线程,会导致频繁切换,降低整体吞吐。
5)acceptCount
-
含义:当所有线程都在忙碌时,等待分配线程的请求最大排队数(即 server socket 的 backlog 队列长度)
-
原理:
-
当
maxThreads全部占用时,新请求不会立即拒绝,而是进入等待队列。 -
队列满后,新请求将被拒绝(返回
Connection refused或TCPreset)。
-
-
重要细节:
-
这个队列是 操作系统层的 TCP 连接队列,不是 Java 层的。
-
实际队列长度受
min(acceptCount, 操作系统 backlog)限制。 -
Linux 默认
net.core.somaxconn=128,需调大才能生效。
-
-
推荐值:
- 队列大小应小于
maxThreads -
acceptCount=500是常见设置,适合有一定突发流量的场景。 -
但注意:队列太长会导致请求排队过久,用户体验差(高延迟)。
- 建议结合超时机制(如前端设置
5s超时),避免无限等待。
- 队列大小应小于
6)connectionTimeout
网络连接超时,假设设置为0表示永不超时,这样设置隐患巨大,通常可设置为30000ms,默认60000ms。
7)maxConnections
tomcat最多能并发处理的请求(连接);
8)maxParameterCount
-
含义:单个请求中允许的最大参数个数(如 URL 查询参数或 POST 表单字段数)。
-
原理:
-
防止恶意请求(如 Hash Collision Attack、参数爆炸)导致内存溢出或 CPU 飙升。
-
默认值通常是
1000,Tomcat 会拒绝超过此数的请求。
-
-
推荐值:
-
1000是安全且合理的默认值。 -
如果业务确实需要大量参数(如批量上传表单),可适当调高。
-
但不建议设为
-1(无限制),有安全风险。
-
2、其他
1)-Dsun.net.client.defaultConnectTimeout
连接建立超时设置
2)-Dsun.net.client.defaultReadTimeout
内容获取超时设置
3)、-Djmagick.systemclassloader
生成缩略图的一个框架的配置=60000
4)、-Dnetworkaddress.cache.ttl
jvmdns缓存超时的设置,建议小于 30
5)、-Dsun.net.inetaddr.ttl
jvmdns缓存超时的设置
6)、java.awt.headless
这个参数一般我们都是放在最后使用的,这全参数的作用是这样的,有时我们会在我们的J2EE工程中使用一些图表工具如:
jfreechart,用于在web网页输出GIF/JPG等流,在winodws环境下,一般我们的app server在输出图形时不会碰到什么问题,但是在linux/unix环境下经常会碰到一个exception导致你在winodws开发环境下图片显示的好好可是在linux/unix下却显示不出来,因此加上这个参数以免避这样的情况出现。
三、FAQ
问题1:连接、线程、队列如何设置
| 参数 | 当前值 | 是否合理 | 说明 |
|---|---|---|---|
maxThreads |
500 | ✅ 合理 | 适合 8 核机器,I/O 密集型服务 |
acceptCount |
500 | ✅ 合理 | 与 maxThreads 匹配,避免雪崩 |
maxSpareThreads |
150 | ✅ 合理 | 防止空闲线程过多 |
minSpareThreads |
50 | ✅ 合理 | 预热线程,提升响应速度 |
Q1:maxThreads 和 acceptCount、maxConnections
⬤ 情况1:接受一个请求,此时 tomcat 起动的线程数没有到达 maxThreads ,tomcat 会起动一个线程来处理此请求。
⬤ 情况2:接受一个请求,此时 tomcat 起动的线程数已经到达 maxThreads,tomcat 会把此请求放入等待队列,等待空闲线程。
⬤ 情况3:接受一个请求,此时 tomcat 起动的线程数已经到达 maxThreads,等待队列中的请求个数也达到了 acceptCount,此时 tomcat 会直接拒绝此次请求,返回 connection refused
Q2:maxThreads 和 maxConnections
答案: 比较容易弄混的是maxThreads 和 maxConnections 这两个参数: maxThreads 是指Tomcat 线程池做多能起的线程数,而 maxConnections 则是 Tomcat 一瞬间做多能够处理的并发连接数。比如 maxThreads = 1000,maxConnections = 800
假设某一瞬间的并发时1000,那么最终 Tomcat的线程数将会是800,即同时处理800个请求,剩余200进入队列“排队”,如果 acceptCount = 100,那么有 100 个请求会被拒掉。
Q3:maxThreads 如何配置
答案:一般的服务器操作都包括量方面:1计算(主要消耗cpu),2等待(io、数据库等)
⬤ 第一种极端情况,如果我们的操作是纯粹的计算,那么系统响应时间的主要限制就是 cpu 的运算能力,此时 maxThreads应该尽量设的小,降低同一时间内争抢 cpu 的线程个数,可以提高计算效率,提高系统的整体处理能力。
⬤ 第二种极端情况,如果我们的操作纯粹是IO 或者数据库,那么响应时间的主要限制就变为等待外部资源,此时 maxThreads 应该尽量设的大,这样 才能提高同时处理请求的个数,从而提高系统整体的处理能力。此情况下因为 tomcat 同时处理的请求量会比较大,所以需要关注一下 tomcat 的虚拟机内存设置和 linux的 open file 限制。
Q4:acceptCount的配置
答案:一般是设置的跟 maxThreads一样大,这个值应该是主要根据应用的访问峰值与平均值来权衡配置的。
⬤ 如果设的较小,可以保证接受的请求较快相应,但是超出的请求可能就直接被拒绝
⬤ 如果设的较大,可能就会出现大量的请求超时的情况,因为我们系统的处理能力是一定的。
问题2: DNS 缓存
-Dnetworkaddress.cache.ttl-
含义:该参数用于设置成功解析的
DNS记录在JavaDNS缓存中的存活时间(Time-To-Live,TTL),单位为秒。 -
默认值:
Java 8及更高版本:Infinity(无限缓存,但可能受操作系统或网络库限制)。
-
-
-Dsun.net.inetaddr.ttl-
含义:该参数是
Java早期版本(如 Java 6 及更早)中用于控制DNS缓存TTL的属性,功能与networkaddress.cache.ttl相同,但属于内部实现(以sun.开头的包)。 -
默认值:
-1(永久缓存)。
-
Q1:作为范围是什么?
-
作用范围:
- 仅影响当前
Java应用的域名解析行为,不会影响同一机器上其他进程或操作系统的DNS缓存。 - 有通过域名访问的服务才会受到
JavaDNS缓存的影响,而直接使用 IP 地址访问的服务则不会涉及 DNS 解析和缓存问题
- 仅影响当前
-
缓存内容:存储的是
Java应用通过InetAddress类(或其他网络库)解析域名时获取的IP 地址结果。 -
影响:
-
域名访问(受
DNS缓存影响)-
// 使用域名访问(受 Java DNS 缓存控制) String url = "http://payment-service/api/pay"; // Kubernetes Service / 域名
-
-
首次访问时,
Java会进行DNS解析(如payment-service→10.1.1.100)。 -
后续请求默认永久缓存该
IP(除非设置-Dnetworkaddress.cache.ttl)。 -
如果
DNS记录变更(如Pod重启,IP 变为10.1.1.200),Java仍可能使用旧IP,导致连接失败。
-
Q1:为什么建议避免或限制这两个参数?
- 动态
DNS场景:若你的应用需要频繁响应DNS记录变更(如负载均衡、故障转移),过长的TTL会导致应用继续访问旧地址,引发连接失败或服务中断。 - 网络环境变化:在移动网络、
VPN切换等场景下,旧的DNS缓存可能指向不可达的IP地址,此时需要快速刷新缓存。 - 安全考虑:
TTL过久可能使应用继续连接已被劫持或恶意篡改的旧 IP 地址。
最佳实践
- 不设置参数:采用
Java默认行为(永久缓存),适合DNS记录稳定且极少变更的场景。 - 设置短
TTL(≤30 秒):平衡缓存效率与变更响应速度,推荐大多数生产环境使用
问题3:线程之间的关系
maxThreads 和 Java 线程池创建的线程,本质上都是 JVM 内的“用户线程”,共享 CPU 和内存资源,但它们属于不同的“用途层级”:
maxThreads是Web容器层 的线程,负责处理HTTP请求, 不处理RPC- 只控制
Tomcat处理HTTP请求的线程数,不影响Netty或其他RPC框架自身的线程模型。
- 只控制
Java线程池的线程是 应用层 的线程,负责执行异步任务、定时任务等
问题4:500 线程最多能支持多少 TPS?
| 公式 | 名称 | 适用场景 |
|---|---|---|
maxThreads = TPS × 平均响应时间(秒) |
利特尔法则 | 预估系统并发请求数,即“在飞”(in-flight)请求总量 |
利特尔法则计算并发需求:我们来反推:要达到 15,000 TPS,需要什么条件?
| 响应时间(ms) | 所需线程数(理论) | 是否可能 |
|---|---|---|
| 10ms | 150 | ✅ 完全可能 |
| 20ms | 300 | ✅ 可能 |
| 33.3ms | 500 | ✅ 刚好极限 |
| 50ms | 750 | ❌ 超出 maxThreads=500 |
- 如果你的接口平均响应时间 ≤ 33ms,那么
maxThreads=500是可以支撑 15K TPS 的 - 如果响应时间 >
33ms,则必须增加线程数或使用异步模型


