skip to content

What server runs a Spring WebFlux application by default, and what kind of threads handles incoming requests?

level: juniorimportance: must knowfreq 70%

answer

  1. Reactor Netty = default WebFlux server
  2. reactor-http-nio-* event loops
  3. few threads, many connections (multiplex)
  4. one-thread-per-request is Tomcat/MVC, not this
  5. never block the event loop

basics

~10 s

By default WebFlux runs on Reactor Netty, an async non-blocking server. Requests are handled by a small pool of event-loop threads named reactor-http-nio-* instead of one thread per request.

solid answer

~40 s

Spring WebFlux ships with Reactor Netty as its default embedded server (pull in spring-boot-starter-webflux). Unlike Spring MVC on Tomcat, which dedicates one thread per request from a large pool, Reactor Netty uses a small fixed set of event-loop worker threads named reactor-http-nio-N. Each event loop multiplexes many connections using non-blocking I/O: it registers interest in read/write readiness and processes events as they arrive, never blocking on a socket. By default the number of event loops equals the number of CPU cores (min 4). This is why WebFlux can serve high concurrency with few threads — but also why you must never run blocking code on those threads. You can swap Netty for Jetty, Undertow, or Servlet 3.1+ Tomcat by changing the starter dependency.

code

java · 12 lines
java
@RestController
class PingController {

    // Runs on a reactor-http-nio-* event-loop thread.
    // Log the thread name to prove it.
    @GetMapping("/ping")
    Mono<String> ping() {
        return Mono.fromSupplier(() ->
                "handled on " + Thread.currentThread().getName());
        // -> "handled on reactor-http-nio-3"
    }
}

go deeper

for a junior

Must know: Reactor Netty is the default, threads are named reactor-http-nio-*, and you should not block them.

for a middle

Should articulate event-loop vs thread-per-request and the default sizing (CPU cores, min 4).

for a senior

Explains I/O multiplexing and the operational failure mode of a blocked event loop; knows how to offload.

for a principal

Frames server choice as an SPI decision and reasons about when Netty vs a Servlet container is the right runtime.

## What is Reactor Netty? **Reactor Netty** is a reactive networking library built on top of **Netty** (a mature Java NIO framework) and integrated with **Project Reactor** (the reactive-streams library behind Spring WebFlux). When you add `spring-boot-starter-webflux` and do not add another server, Spring Boot auto-configures Reactor Netty as the embedded HTTP server. This is the default because WebFlux is designed around non-blocking, reactive request handling. ## Event loop vs thread-per-request Traditional Spring MVC on **Tomcat** uses a **thread-per-request** model: a large pool (e.g. 200 threads) hands one thread to each request; that thread is occupied — even while blocked waiting on a database or remote call — until the response is written. Reactor Netty instead uses the **event-loop** model: - A **small, fixed** number of worker threads (the *event loops*), named `reactor-http-nio-1`, `reactor-http-nio-2`, …. - Each event loop owns a set of connections (sockets). Using the OS's I/O readiness mechanism (epoll on Linux, kqueue on macOS, or Java NIO `Selector`), one thread watches many sockets at once — **I/O multiplexing**. - When a socket becomes readable/writable, the loop runs the associated handler *briefly*, then moves on to the next ready socket. The thread is never parked waiting on a single connection. ## Default sizing The default number of event loops is `max(4, number of available processors)` — governed by the system property `reactor.netty.ioWorkerCount` and Reactor Netty's `LoopResources`. A handful of threads can therefore serve thousands of concurrent connections, because threads are only busy while actual work is ready. ## The cardinal rule Because so few threads carry all traffic, **blocking one event-loop thread stalls every connection it owns**. Never call blocking JDBC, `Thread.sleep`, `.block()`, or synchronous HTTP clients on a `reactor-http-nio-*` thread. Offload unavoidable blocking work with `.subscribeOn(Schedulers.boundedElastic())`. ## Swapping the server WebFlux is server-agnostic via the **`ReactiveWebServerFactory`** SPI. Exclude Netty and add `spring-boot-starter-jetty`, `-undertow`, or `-tomcat` to run on those instead — WebFlux adapts each to its reactive `HttpHandler`. ## When to care Knowing the default matters the moment you debug latency (`reactor-http-nio` threads pegged at 100% often means blocking code) or tune throughput.

  • How does the default server differ between Spring MVC and Spring WebFlux?
    MVC defaults to Tomcat with a large thread-per-request pool; WebFlux defaults to Reactor Netty with a small event-loop pool doing non-blocking I/O. MVC blocks a thread per in-flight request; Netty multiplexes many connections per thread.
  • Can you run WebFlux on Tomcat instead of Netty?
    Yes. Exclude Netty and add spring-boot-starter-tomcat (or Jetty/Undertow). WebFlux runs on any Servlet 3.1+ async container or those reactive servers via the ReactiveWebServerFactory abstraction — it does not require Netty.

saying these in an interview costs you the question

  • Saying WebFlux uses Tomcat by default
  • Claiming there is one thread per request in Netty
  • Thinking more event-loop threads always means more throughput
  • Believing blocking on an event-loop thread is harmless

context