Understanding Tomcat Configuration and Thread Management in Spring Boot 2.7.10
This article explains the default Tomcat settings bundled with Spring Boot 2.7.10, details core parameters such as connection queues, thread pools, keep‑alive limits, and timeouts, and demonstrates how to monitor and test these settings with code examples and network tools.
Overview
The author, a self‑described "architect who writes code and poetry", presents a deep dive into the Tomcat server embedded in Spring Boot 2.7.10, covering default configurations, internal thread architecture, key parameters, and practical testing methods.
Default Tomcat Settings in Spring Boot 2.7.10
Spring Boot 2.7.10 ships with Tomcat 9.0.73. The default values are:
Connection accept queue length (accept‑count): 100
Maximum connections: 8192
Minimum worker threads: 10
Maximum worker threads: 200
Connection timeout: 20 seconds
Configuration snippet (application.yml):
server:
tomcat:
# Maximum length of the queue when all request‑handling threads are busy
accept-count: 100
# Maximum number of connections the server will accept at any time
max-connections: 8192
threads:
# Minimum number of spare threads created at startup
min-spare: 10
# Maximum number of worker threads (IO‑intensive workloads usually need ~10×CPU cores)
max: 200
connection-timeout: 20000
keep-alive-timeout: 20000
max-keep-alive-requests: 100Core Parameters Explained
AcceptCount
Defines the size of the connection backlog (similar to the Linux somaxconn value). It limits how many pending connections can wait before being accepted.
MaxConnections
Maximum simultaneous connections that Tomcat will handle. When the limit is reached, new connections are queued or rejected.
MinSpareThread / MaxThread
Thread pool configuration: min-spare is the number of idle threads kept ready; max is the upper bound of worker threads. Tomcat uses a custom ThreadPoolExecutor that extends the JDK pool with additional metrics.
MaxKeepAliveRequests
After this many requests on a persistent connection, Tomcat will close the connection. Setting it to -1 disables the limit.
ConnectionTimeout
Time (in ms) a connection can stay idle after being established before the server closes it.
KeepAliveTimeout
Time to wait for the next HTTP request on a keep‑alive connection; defaults to connectionTimeout unless overridden. -1 means no timeout.
Internal Thread Model
Acceptor
Accepts incoming socket connections, configures them via setSocketOptions() , and registers them with the poller.
public void run() {
while (!stopCalled) {
socket = endpoint.serverSocketAccept();
endpoint.setSocketOptions(socket);
poller.register(socketWrapper);
}
}Poller
Continuously polls the NIO Selector for ready events, extracts SelectionKey objects, and dispatches them to the executor thread pool.
public void run() {
while (true) {
Iterator
iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey sk = iterator.next();
iterator.remove();
NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
if (socketWrapper != null) {
processKey(sk, socketWrapper);
executor.execute(new SocketProcessor(socketWrapper, SocketEvent.OPEN_READ/SocketEvent.OPEN_WRITE));
}
}
}
}TomcatThreadPoolExecutor
Extends java.util.concurrent.ThreadPoolExecutor with a submittedCount metric and a custom TaskQueue that can force tasks into the queue when the pool is saturated.
public class ThreadPoolExecutor extends java.util.concurrent.ThreadPoolExecutor {
private final AtomicInteger submittedCount = new AtomicInteger(0);
@Override
protected void afterExecute(Runnable r, Throwable t) {
if (!(t instanceof StopPooledThreadException)) {
submittedCount.decrementAndGet();
}
}
@Override
public void execute(Runnable command) {
submittedCount.incrementAndGet();
try {
super.execute(command);
} catch (RejectedExecutionException rx) {
// force task into queue or re‑throw
}
}
}TaskQueue
A custom LinkedBlockingQueue that can force insertion of tasks, report remaining capacity, and stop idle threads when the pool shuts down.
Testing the Configuration
Example minimal configuration used for testing:
server:
port: 8080
tomcat:
accept-count: 3
max-connections: 6
threads:
min-spare: 2
max: 3Network inspection commands:
ss -nltp
ss -nlt | grep 8080Observations for different concurrent connection counts (6, 9, 10, 11, 12):
When the total connections exceed maxConnections + acceptCount + 1 , new clients remain in SYN_RECV state and eventually time out after the 20 s handshake timeout.
Client‑side sockets stay in SYN_SENT until the server drops the handshake.
Increasing the accept queue shows the same pattern, confirming the importance of tuning acceptCount and maxConnections .
References
ZhangBJ article
Tomcat monitoring metrics
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.