JMeter与Spring Boot并发线程2000限制原因排查
本地同时运行JMeter与简易Spring Boot Web服务器时,出现异常:JMeter设置2000个并发线程时所有请求正常,设置为2001个时多数请求失败,报错HttpHostConnectException,请求未到达Spring Boot应用。需要明确2000这个临界值的来源及相关限制配置。
环境配置
- Windows 11
- Java 21
- Spring Boot 3.2.0-RC1(3.1.5版本存在同样问题)
- Apache JMeter 5.6.2
- 16核CPU
- 64GB内存
Spring Boot代码
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/person") public class PersonController { @GetMapping public String getPerson() { return "John"; } }
application.properties配置
server.port=9001 server.tomcat.threads.max=10000 server.tomcat.max-connections=10000 server.tomcat.accept-count=10000
Spring Boot启动日志
MyApplication : Starting MyApplication using Java 21 with PID 71328 MyApplication : No active profile set, falling back to 1 default profile: "default" o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 9001 (http) o.apache.catalina.core.StandardService : Starting service [Tomcat] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.15] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 671 ms o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 9001 (http) with context path '' MyApplication : Started MyApplication in 1.302 seconds (process running for 1.812) o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
JMeter相关信息
- JMeter请求配置:单HTTP GET请求,目标地址为
localhost:9001/person - 2000并发线程结果:所有请求成功,无失败
- 2001并发线程结果:多数请求失败,报错连接被拒绝
JMeter报错信息
org.apache.http.conn.HttpHostConnectException: Connect to localhost:9001 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:156) at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl$JMeterDefaultHttpClientConnectionOperator.connect(HTTPHC4Impl.java:409) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.executeRequest(HTTPHC4Impl.java:940) at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.sample(HTTPHC4Impl.java:651) at org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy.sample(HTTPSamplerProxy.java:66) at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1311) at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1300) at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:651) at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:570) at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:501) at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:268) at java.base/java.lang.Thread.run(Thread.java:1583) Caused by: java.net.ConnectException: Connection refused: connect at java.base/sun.nio.ch.Net.connect0(Native Method) at java.base/sun.nio.ch.Net.connect(Net.java:589) at java.base/sun.nio.ch.Net.connect(Net.java:578) at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:583) at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) at java.base/java.net.Socket.connect(Socket.java:751) at org.apache.http.conn.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:75) at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142) ... 19 more
原因分析与解决方案
1. 核心原因:Windows系统层面的TCP限制
2000的临界值并非来自Spring Boot/Tomcat配置(你已将Tomcat的连接、线程、等待队列设为10000),而是Windows系统默认的TCP连接相关限制,具体有两个可能:
(1)本地临时端口耗尽
JMeter与Spring Boot在同一台机器上,每个JMeter线程会占用一个本地临时端口去连接服务端的9001端口。Windows默认临时端口范围是1024-5000,共3976个可用端口,但TCP连接关闭后会进入TIME_WAIT状态(默认保留240秒),短连接场景下大量端口被占用无法复用,当并发达到2000时,可用端口接近耗尽,新连接无法创建,导致Connection refused。
验证方式:运行netstat -ano | findstr :9001,查看是否有大量TIME_WAIT状态的连接。
解决方法:扩大临时端口范围,以管理员身份运行命令:
netsh int ipv4 set dynamicport tcp start=1024 num=64511 netsh int ipv6 set dynamicport tcp start=1024 num=64511
执行后重启机器生效。
(2)TCP全连接队列限制
Windows系统默认的MaxConnectionsBacklog(TCP全连接队列大小)为2000,即使Tomcat设置了accept-count=10000,系统层面的限制会优先生效。当并发请求超过2000时,系统直接拒绝新连接,服务端无法接收请求,导致报错。
验证方式:查看注册表项HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\MaxConnectionsBacklog(若不存在则为默认值2000)。
解决方法:
- 打开注册表编辑器,定位到上述路径;
- 新建或修改
DWORD类型的MaxConnectionsBacklog,设置值为10000(与Tomcat的accept-count保持一致); - 重启机器生效。
2. 辅助优化:JMeter连接复用
开启JMeter的连接复用,减少端口占用:
- 在HTTP请求的「高级」选项中勾选「Use keepalive」;
- 调整JMeter的HTTP连接池配置,在
jmeter.properties中修改:httpclient4.max_total=20000 httpclient4.max_per_route=20000
3. 确认Tomcat配置生效
通过JMX工具(如JConsole)连接Spring Boot应用,查看Tomcat的maxConnections、maxThreads等指标,确认配置已正确加载。
内容的提问来源于stack exchange,提问作者Honza Zidek

