前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、线上实例

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 启动后会预先创建这些线程,确保“热启动”。

  • 推荐值:

    • 一般设为 maxThreads20%~30%

    • 50 是合理的,保证有一定预热线程应对突发流量。

3)maxSpareThreads

  • 含义:最大空闲线程数,在最大空闲时间(maxIdleTime)内活跃过,此时空闲,当空闲时间大于maxIdleTime则被回收,小则继续存活,等待被调度,默认值 50

  • 原理:

    • 防止空闲线程过多浪费内存和系统资源。

    • 当负载下降时,Tomcat 会逐步回收多余线程。

  • 推荐值:通常设为 maxThreads60%~80%

4)maxThreads

  • 含义:最大线程数,大并发请求时,tomcat 能创建来处理请求的最大线程数,超过则放入请求队列中进行排队,默认值为200

    • Windows Tomcat允许每个进程 maxThreads(最大线程数)2000

    • Linux Tomcat 允许每个进程 maxThreads(最大线程数)1000

  • 原理:
  • 每个线程处理一个请求(同步阻塞模式下)。
  • 如果所有线程都在忙,新请求只能排队等待空闲线程。
  • 超过 maxThreads 的请求将被拒绝或排队(取决于 acceptCount)。
  • 为什么不是越大越好?
  • 每个线程默认占用约 1MB 栈内存(可通过 -Xss 调整),200 线程 ≈ 200MB 内存。
  • 更重要的是:CPU 上下文切换开销。8 核 CPU 同时运行 200 个线程,会导致频繁切换,降低整体吞吐。

5)acceptCount

  • 含义:当所有线程都在忙碌时,等待分配线程的请求最大排队数(即 server socket 的 backlog 队列长度

  • 原理:

    • maxThreads 全部占用时,新请求不会立即拒绝,而是进入等待队列。

    • 队列满后,新请求将被拒绝(返回 Connection refusedTCP reset)。

  • 重要细节:

    • 这个队列是 操作系统层的 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

jvm dns 缓存超时的设置,建议小于 30

5)、-Dsun.net.inetaddr.ttl

jvm dns 缓存超时的设置

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:maxThreadsacceptCountmaxConnections

⬤ 情况1:接受一个请求,此时 tomcat 起动的线程数没有到达 maxThreadstomcat 会起动一个线程来处理此请求。

⬤ 情况2:接受一个请求,此时 tomcat 起动的线程数已经到达 maxThreadstomcat 会把此请求放入等待队列,等待空闲线程。

⬤ 情况3:接受一个请求,此时 tomcat 起动的线程数已经到达 maxThreads,等待队列中的请求个数也达到了 acceptCount,此时 tomcat 会直接拒绝此次请求,返回 connection refused

Q2:maxThreadsmaxConnections

答案: 比较容易弄混的是maxThreadsmaxConnections 这两个参数: 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 的虚拟机内存设置和 linuxopen file 限制。

Q4:acceptCount的配置

答案:一般是设置的跟 maxThreads一样大,这个值应该是主要根据应用的访问峰值与平均值来权衡配置的。

⬤ 如果设的较小,可以保证接受的请求较快相应,但是超出的请求可能就直接被拒绝

⬤ 如果设的较大,可能就会出现大量的请求超时的情况,因为我们系统的处理能力是一定的。

问题2: DNS 缓存

  • -Dnetworkaddress.cache.ttl
    • 含义:该参数用于设置成功解析的 DNS 记录在 Java DNS 缓存中的存活时间(Time-To-Live,TTL),单位为秒。

    • 默认值:Java 8 及更高版本:Infinity(无限缓存,但可能受操作系统或网络库限制)。

  • -Dsun.net.inetaddr.ttl

    • 含义:该参数是 Java 早期版本(如 Java 6 及更早)中用于控制 DNS 缓存 TTL 的属性,功能与networkaddress.cache.ttl相同,但属于内部实现(以 sun. 开头的包)。

    • 默认值-1(永久缓存)。

Q1:作为范围是什么?

  • 作用范围

    • 仅影响当前 Java 应用的域名解析行为,不会影响同一机器上其他进程或操作系统的 DNS 缓存。
    • 有通过域名访问的服务才会受到 Java DNS 缓存的影响,而直接使用 IP 地址访问的服务则不会涉及 DNS 解析和缓存问题
  • 缓存内容:存储的是 Java 应用通过 InetAddress类(或其他网络库)解析域名时获取的 IP 地址结果。

  • 影响

    • 域名访问(受 DNS 缓存影响)

      • // 使用域名访问(受 Java DNS 缓存控制)
        String url = "http://payment-service/api/pay";  // Kubernetes Service / 域名
        
    • 首次访问时,Java 会进行 DNS 解析(如 payment-service10.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:线程之间的关系

maxThreadsJava 线程池创建的线程,本质上都是 JVM 内的“用户线程”,共享 CPU 和内存资源,但它们属于不同的“用途层级”:

  • maxThreadsWeb 容器层 的线程,负责处理 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,则必须增加线程数或使用异步模型

ContactAuthor