Tomcat 8中NIO工作原理解析及Spring Boot应用测试疑问
Tomcat NIO 工作原理与你的场景解析
Hey there! Let's break down what's going on with your Tomcat NIO setup, your initial thread observations, and what to expect during your wrk load test.
首先,Tomcat NIO的核心工作原理
Tomcat的NIO模式基于多路复用IO模型,核心是用少量线程处理大量连接,关键角色有三个:
- Acceptor线程:负责监听端口,接收新的TCP连接。默认情况下Tomcat会启动1个Acceptor线程(一般无需手动调整)。
- Selector:一个多路复用器,负责监控所有注册进来的连接的读写事件。Tomcat会为每个Worker线程绑定一个Selector,或让多个Worker共享(取决于配置)。
- Worker线程(就是你看到的
http-nio-exec-*):这些是实际处理请求的线程。它们通过Selector监听多个连接的事件,当某个连接有读写需求时(比如客户端发来请求数据、需要返回响应),Worker线程才会处理该连接的对应操作。
和传统BIO模式(一个连接对应一个线程)不同,NIO的Worker线程不需要一直绑定在单个连接上,一个Worker线程可以同时处理多个处于"等待事件"状态的连接——这就是它能高效支撑高并发的核心原因。
启动后线程现象的解释
你观测到启动后有51个活跃线程,其中仅10个是Tomcat的Worker线程,这完全正常:
- Tomcat的Worker线程池默认是动态扩容的,默认最小空闲线程数(
min-spare-threads)为10,所以启动后无请求时,线程池会保持10个空闲Worker线程待命,也就是你看到的http-nio-exec-1到http-nio-exec-10。 - 剩下的41个活跃线程来自Spring Boot和JVM本身:比如Spring上下文初始化线程、事件广播线程、Tomcat的Acceptor/Selector线程、JVM GC线程、主线程,还有Spring Boot内置的健康检查、Metrics管理线程等,加起来就会有几十条。
你的Tomcat配置参数解析
先明确你设置的两个核心参数的含义:
server.tomcat.max-connections=80:这是Tomcat在NIO模式下能同时管理的最大连接数(即注册到Selector上的连接总数)。注意这不是线程数——因为NIO是多路复用,一个Worker线程可以处理多个连接。server.tomcat.max-threads=70:这是Worker线程池的最大线程数,当请求负载足够大时,Tomcat会把Worker线程从默认的10个扩容到最多70个,来处理更多并发请求事件。
你的wrk压测场景分析
你用wrk -t12 -c400 -d30s http://localhost:8080发起压测,先理清几个参数的作用:
-t12:是wrk自身用来生成请求的线程数,和Tomcat的Worker线程数无直接关系,只是wrk模拟并发请求的工具线程。-c400:是模拟400个并发连接到Tomcat。
结合你的Tomcat配置,压测时会发生这些情况:
- Tomcat先处理前80个连接,将它们注册到Selector上,交给Worker线程处理。
- 剩下的400-80=320个连接,会进入Tomcat的连接等待队列(由
server.tomcat.accept-count控制,默认值为100)。 - 当队列满了之后(即第80+100=181个连接之后),新的连接请求会被Tomcat拒绝,客户端会收到
Connection Refused或503 Service Unavailable响应。
压测过程中你可以用JVisualVM观测:
http-nio-exec-*线程数会从10逐渐扩容到70(如果负载足够),因为Tomcat会根据请求量动态增加Worker线程。- 当Worker线程数达到70后,Tomcat不会再创建新的Worker线程,剩余请求事件会由这70个线程通过Selector多路复用处理。
补充几个关键注意点
- 如果你想调整Tomcat Worker线程的初始数量,可以设置
server.tomcat.min-spare-threads,比如改成20,启动后就会有20个http-nio-exec-*线程。 max-connections在NIO模式下的数值可以远大于BIO模式,因为NIO无需为每个连接分配线程。- 压测时如果发现大量连接被拒绝,可以调整
max-connections或accept-count,但要注意服务器CPU、内存的资源限制,避免设置过大导致资源耗尽。
内容的提问来源于stack exchange,提问作者Almas Abdrazak




