Why ‘How Much Concurrency Can Spring Boot 3 Handle?’ Is Misleading – A Deep Dive into Max Connections

The article shows that Spring Boot itself does not limit concurrency; the true ceiling is set by Linux TCP parameters, Tomcat's acceptCount and maxConnections, its thread‑pool strategy, and KeepAlive settings, and it walks through source code, default values, and practical experiments to reveal where requests are blocked.

LuTiao Programming
LuTiao Programming
LuTiao Programming
Why ‘How Much Concurrency Can Spring Boot 3 Handle?’ Is Misleading – A Deep Dive into Max Connections

Misconception about Spring Boot concurrency

Stating that Spring Boot can handle only the default 200 Tomcat threads conflates thread count with actual request concurrency and is misleading.

Full stack that determines concurrency

Linux TCP kernel parameters → Tomcat NIO model → connection queue → worker thread pool → Keep‑Alive behavior

Experimental baseline

Tested with:

Spring Boot 2.7.10

Embedded Tomcat 9.0.73

I/O model: NIO

Operating system: Linux

Default Tomcat connector parameters (Spring Boot 2.7.10)

accept-count : 100 – size of the full‑connection backlog

max-connections : 8192 – hard limit of simultaneous connections

min-spare-threads : 10 – initial worker threads

max-threads : 200 – maximum worker threads

connection-timeout : 20 s – timeout while waiting for request data

server:
  tomcat:
    accept-count: 100
    max-connections: 8192
    threads:
      min-spare: 10
      max: 200
    connection-timeout: 20000
    keep-alive-timeout: 20000
    max-keep-alive-requests: 100

Behaviour when connections surge

If concurrent connections exceed max-connections + accept-count + 1, Tomcat does not send a TCP RST. The three‑way handshake never completes, and the client times out after roughly 20 seconds. This is a consequence of TCP flow control, not a Tomcat bug.

AcceptCount semantics

In NIO mode Tomcat passes acceptCount to ServerSocketChannel.bind(..., acceptCount) as the backlog. On Linux the effective backlog is min(tomcat.acceptCount, net.core.somaxconn); Windows imposes no system‑level limit.

ServerSocketChannel serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
// acceptCount becomes the backlog
serverSock.socket().bind(addr, getAcceptCount());

MaxConnections interception point

When the maximum number of connections is reached, the Acceptor thread blocks on a latch before accepting a new socket.

while (!stopCalled) {
    // Block when max connections reached
    connectionLimitLatch.countUpOrAwait();
    socket = endpoint.serverSocketAccept();
    // On socket close: connectionLimitLatch.countDown();
}

New connections then accumulate in the TCP full‑connection queue.

Tomcat’s thread‑pool strategy vs. standard JDK executor

Tomcat uses a custom TaskQueue that prefers creating new threads over queuing tasks. The overridden offer method returns false (causing the executor to spawn a thread) until the pool reaches maxThreads.

@Override
public boolean offer(Runnable task) {
    // If pool size < max, bypass queue and expand threads
    if (parent.getPoolSize() < parent.getMaximumPoolSize()) {
        return false;
    }
    // Only queue when threads are at max
    return super.offer(task);
}

Keep‑Alive impact

Tomcat reads maxKeepAliveRequests from the protocol. When the count is reached, the connection is actively closed.

int maxKeepAliveRequests = protocol.getMaxKeepAliveRequests();
if (maxKeepAliveRequests == 1) {
    keepAlive = false;
} else if (maxKeepAliveRequests > 0 && socketWrapper.decrementKeepAlive() <= 0) {
    keepAlive = false;
}

Setting the value too high keeps connections occupied; setting it too low forces frequent TCP handshakes.

Connection timeout vs. Keep‑Alive timeout

Both default to 20 seconds. If no request data arrives within this window, the connection is closed.

if (timeout > 0 && delta > timeout) {
    readTimeout = true;
}

Acceptor / Poller / Executor three‑stage model

Accept socket and wrap as NioSocketWrapper.

Register the wrapper with NioEndpoint.Poller.

Submit a SocketProcessor to the executor for request processing.

processSocket(socketWrapper, SocketEvent.OPEN_READ);
executor.execute(new SocketProcessor(...));

Extreme test configuration

server:
  port: 8080
  tomcat:
    accept-count: 3
    max-connections: 6
    threads:
      min-spare: 2
      max: 3

Linux verification (recommended)

Run ss -nlt | grep 8080 to inspect:

Recv‑Q : current queue length (connections waiting for a worker thread)

Send‑Q : capacity of the accept‑count backlog

When connections exceed max-connections + accept-count, the server remains in SYN_RECV (half‑open) while the client stays in SYN_SENT. After ~20 seconds the client times out.

Conclusion

The concurrency ceiling of a Spring Boot application is not set by Spring Boot itself. It is governed by five layers:

Linux TCP parameter somaxconn Tomcat accept-count (full‑connection queue length) max-connections (hard connection limit)

Tomcat’s extended thread‑pool strategy that prefers thread creation over queuing

Keep‑Alive and timeout policies

Thus, assessing “how much concurrency Spring Boot can handle” is equivalent to identifying the shortest weak link among the TCP stack, Tomcat configuration, thread‑pool sizing, and application latency.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

concurrencySpring Boottomcatlinux-tcpkeepalivemax-connections
LuTiao Programming
Written by

LuTiao Programming

LuTiao Programming is a friendly community offering free programming lessons. We inspire learners to explore new ideas and technologies and quickly acquire job-ready skills.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.