Under The Hood
backend springboot Spring WebFlux / Reactor 3.x / Netty

Spring WebFlux: Project Reactor Mono/Flux & Netty EventLoop

Last updated
Prerequisites:
Spring MVC basics
Understanding of async I/O and event loops
  • spring
  • webflux
  • reactor
  • netty
  • event-loop
  • mono
  • flux
  • reactive

Read at your depth

The practical view

Spring WebFlux is the reactive stack: controllers return Mono<T> (0-1 values) or Flux<T> (0-N values) instead of blocking values. It runs on Netty (default) with a small number of event-loop threads handling many concurrent requests via non-blocking I/O. The reactive pipeline: return Mono.just(data).map(...).flatMap(service::call).map(...); — nothing blocks; the request completes when the publisher emits. Use WebClient (reactive) instead of RestTemplate. WebFlux shines for I/O-bound apps with many concurrent requests; it does NOT speed up CPU-bound work (still needs parallel schedulers).

The same idea in other frameworks

react equivalent
N/A — server-side WebFlux concern.

Legacy vs modern

Blocking MVC controller vs reactive WebFlux controller

MVC blocks a thread per request; WebFlux composes a non-blocking chain that frees the event loop while I/O is in flight.

before → after
Blocking controller (thread per request)
@GetMapping("/order/{id}")
public Order get(@PathVariable long id) {
  User u = userClient.get(id); // ❌ OLD: blocks a thread
  return orderService.findByUser(u); // ❌ OLD: blocks again
}
Reactive WebFlux controller
@GetMapping("/order/{id}")
public Mono<Order> get(@PathVariable long id) {
  return userClient.get(id) // ✅ NEW: non-blocking
    .flatMap(orderService::findByUser); // ✅ NEW: composed, non-blocking
}

Interview gotchas

Context

This is the critical WebFlux concept — interviewers want the event-loop starvation mechanism and why the same blocking call that's merely 'wasteful' in MVC is fatal in the reactive model.

The mechanical answer

In MVC, a blocking call parks one thread; with 200 threads, 200 concurrent blocking calls is the capacity limit but not a deadlock. In WebFlux, the event loop threads are SHARED across all connections: if a handler blocks (Thread.sleep, a blocking JDBC call, a blocking RestTemplate), it stalls that event-loop thread, and because that same thread serves hundreds of other connections, ALL of them are delayed — effectively freezing a large slice of the server. Netty has few event loops (typically 2×cores), so a handful of blocking handlers can starve the entire server. This is why the rule is: end-to-end non-blocking (reactive drivers, WebClient, reactive repositories), and if you must block (e.g. a legacy library), offload it with subscribeOn/publishOn(Schedulers.boundedElastic()) so the event loop is never blocked.

Trap

The naive answer is 'blocking just costs one thread, same as MVC'. It stalls a shared event loop and every connection multiplexed on it. Another trap: assuming WebFlux makes CPU-bound work faster — it doesn't; CPU work still occupies the event loop and should run on the parallel scheduler. Candidates who describe event-loop starvation show real reactive understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.