一篇讲了Spring Boot上线前必改的5项配置,第1项提了句"Tomcat线程数默认200不够",评论区炸了——光调maxThreads根本救不了。Tomcat线程池是一个由8个参数组成的联动系统,任何一个参数用默认出厂值,高并发下都会成为瓶颈。
上个月帮一个团队做压测复盘,他们的Tomcat只改了maxThreads,其他7个全是默认值。大促第1天还能扛,第3天连接数堆积、线程全部阻塞,整个集群的平均响应时间从200ms飙到8秒,上游全部超时熔断。
下面8个参数是Tomcat线程池的完整清单,每一项都标了出厂默认值(Tomcat 9.0.120)和为什么不能留默认。
1
maxThreads:默认200?QPS过千就排队
出厂默认值:200
事故场景:某电商峰值QPS 3000,200个线程每个请求平均处理100ms,理论吞吐上限仅2000 QPS。超出部分全部进入等待队列,排队超过2秒直接超时。监控显示Tomcat线程池使用率100%,新请求在队列里干等。
正确配置(按业务估算):
# 峰值QPS × 平均响应时间(秒) × 1.5倍buffer # 3000 QPS × 0.1s × 1.5 = 450,至少配到500 server: tomcat: threads: max: 500经验公式:maxThreads = 峰值QPS × 平均RT × 1.5。开发机200够用,生产环境拍脑袋留200就是埋雷。
2
minSpareThreads:默认10?冷启动直接卡死
出厂默认值:10
事故场景:流量突增时,Tomcat需要从10个空闲线程扩容到maxThreads。扩容过程要新建线程对象、分配栈内存,瞬时几百个请求同时进来,10个空闲线程瞬间耗尽,后面的请求全部阻塞在扩容等待上,冷启动期RT从200ms飙到3秒。
正确配置:
server: tomcat: threads: min-spare: 50 # 常驻50个空闲线程,避免扩容抖动minSpareThreads建议设为maxThreads的10%~20%。maxThreads=500时,min-spare配50~100,让线程池永远有"热线程"待命。
3
maxConnections:默认8192?够了但别瞎改
出厂默认值:8192(NIO模式)
为什么要注意:8192对绝大多数应用是够的。但有人照着老教程把maxConnections改成200,结果超过8192的连接被操作系统直接拒绝(connection refused),客户端大面积报连接失败。
正确配置:
server: tomcat: max-connections: 8192 # NIO模式默认就够,不要低于maxThreads太多maxConnections应≥maxThreads。如果maxThreads=500但maxConnections=200,多出300个线程永远拿不到连接,等于白配。
4
acceptCount:默认100?队列太长等于慢性自杀
出厂默认值:100
事故场景:达到maxConnections后,新连接进入操作系统accept队列。队列长度100,意味着最多缓冲100个连接。但有人听信"队列越大越能扛",把acceptCount改成10000——结果连接全部堆积在队列里,客户端不报错但请求永远不处理,用户看到的是"页面一直转圈"。
正确配置:
server: tomcat: accept-count: 200 # 略大于maxThreads的buffer,不要无限大acceptCount过大是隐蔽灾难:连接不拒绝、请求不处理、监控不报警。队列应该短到能快速失败(fail fast),而不是无限缓冲。
5
connectionTimeout:默认20000ms?慢连接拖垮线程
出厂默认值:20000ms(server.xml里的值,属性本身默认60000ms)
事故场景:客户端网络慢或恶意保持连接不发送请求,一个连接占着线程20秒不动。200个线程里如果有50个被慢连接占着,实际可用线程只剩150个,吞吐量直接腰斩。
正确配置:
server: tomcat: connection-timeout: 5000 # 5秒不发送请求直接断,释放线程对外服务connection-timeout配5000~10000ms足够。内部微服务可以更短(2000ms),快速暴露网络问题。
6
keepAliveTimeout:默认继承connectionTimeout?长连接浪费
出厂默认值:不单独设置时,继承connectionTimeout的值
事故场景:HTTP/1.1默认keep-alive复用连接。如果connectionTimeout=20000ms,keep-alive也会保持20秒。高并发下大量空闲keep-alive连接占着线程不释放,线程池被"假繁忙"的连接填满。
正确配置:
# Spring Boot未直接暴露该属性,需用TomcatServletWebServerFactory @Bean public TomcatServletWebServerFactory tomcatFactory() { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.addConnectorCustomizers(connector -> { connector.setAttribute("keepAliveTimeout", 5000); // 5秒后关闭空闲keep-alive connector.setAttribute("maxKeepAliveRequests", 100); // 单连接最多100请求 }); return factory; }keepAliveTimeout建议设为connectionTimeout的1/2~1/4(如connection-timeout=5000则keepAliveTimeout=2000),让空闲连接尽快释放。
7
maxKeepAliveRequests:默认100?频繁重建连接
出厂默认值:100
为什么要注意:单条keep-alive连接最多处理100个请求后关闭重建。对频繁短请求的场景(如API网关),每100个请求就要重建一次TCP连接+TLS握手,额外开销不可忽视。但设成-1(无限)又会导致连接永不释放。
正确配置:
# 通过TomcatServletWebServerFactory设置 connector.setAttribute("maxKeepAliveRequests", 1000); // 内部服务可放到1000对外公网服务保持100即可(客户端连接不可控);内网微服务可放到500~1000,减少连接重建开销。
8
maxQueueSize:默认Integer.MAX_VALUE?最隐蔽的雷
出厂默认值:Integer.MAX_VALUE(约21亿,等于无限排队)
事故场景:这是Tomcat内部线程池的任务队列上限。默认无限意味着当maxThreads耗尽,任务会无限堆积在内存队列里。某团队大促时任务队列堆积到200万个,Old GC频繁、Full GC每次8秒,最终OOM。
正确配置:
# 通过自定义Executor限制队列(推荐用共享Executor) @Bean public TomcatExecutorCustomizer executorCustomizer() { return executor -> { // 队列上限 = maxThreads的2倍,超出直接拒绝(fail fast) executor.setMaxQueueSize(1000); }; }这是8个参数里最容易被忽略也最危险的。无限队列=把拒绝压力转成内存压力,最终结果是OOM而不是快速失败。队列必须有上限,让溢出请求快速报错而非悄悄堆积。
8个参数速查表
参数 | 出厂默认 | 建议值 | 不配的后果 |
maxThreads | 200 | 峰值QPS×RT×1.5 | 高并发排队超时 |
minSpareThreads | 10 | maxThreads的10%~20% | 冷启动扩容抖动 |
maxConnections | 8192 | ≥maxThreads,勿低于 | 过低直接拒绝连接 |
acceptCount | 100 | ≤200,勿无限大 | 队列过长慢性堆积 |
connectionTimeout | 20000ms | 5000~10000ms | 慢连接拖垮线程 |
keepAliveTimeout | 继承connectionTimeout | connectionTimeout的1/4 | 长连接占满线程 |
maxKeepAliveRequests | 100 | 内网500~1000 | 频繁重建连接 |
maxQueueSize | Integer.MAX_VALUE | maxThreads×2 | 无限堆积→OOM |
8个参数不是孤立的,是一个联动系统:maxThreads决定并发处理能力,minSpareThreads决定冷启动表现,maxConnections+acceptCount决定连接缓冲策略,connectionTimeout+keepAliveTimeout决定连接生命周期,maxQueueSize决定拒绝策略。只改maxThreads而留其他默认值,等于给漏水的船补了一个洞。
Tomcat线程池检查清单(打印贴工位)
□ maxThreads按峰值QPS×RT×1.5估算(不小于500) □ minSpareThreads设为maxThreads的10%~20% □ maxConnections ≥ maxThreads,不盲目调低 □ acceptCount ≤ 200,禁止无限队列 □ connectionTimeout配5000~10000ms(非默认20秒) □ keepAliveTimeout设为connectionTimeout的1/4 □ maxKeepAliveRequests内网可放到500~1000 □ maxQueueSize必须有上限(maxThreads×2),禁止无限Tomcat线程池参数检查,飞算JavaAI一键扫描
这8个参数在Spring Boot项目里分散在application.yml、
TomcatServletWebServerFactory、server.xml多个地方,人工Code Review极容易漏掉maxQueueSize这种隐蔽项。飞算JavaAI的代码审查器可以自动扫描项目中的Tomcat相关配置,检查线程池参数是否合理:maxThreads是否够用、acceptCount是否过大、connectionTimeout是否过长、maxQueueSize是否设了上限,并给出具体的修改建议和配置示例。30秒跑完8项检查,比人工翻server.xml快10倍。
你的Tomcat线程池,这8个参数里有几个还是出厂默认值?