Monolithic to Microservices
Till the late 2000s, I had witnessed and worked extensively on monolithic applications — the era when a single server hosted the entire system. Some of the popular choices for these central servers were WebLogic, WebSphere, and JBoss. However, that age has long passed. Such architectural choices eventually failed to scale, lacked fault tolerance, and struggled to deliver high availability — paving the way for modern, distributed, and cloud-native architectures.
In the modern world of microservices, cloud-native architectures, and real-time systems, performance, scalability, and responsiveness have become critical. Traditional blocking architectures often struggle when faced with thousands of concurrent users or long-running I/O operations.
This is where Spring WebFlux comes in — a powerful, non-blocking, reactive programming framework that enables developers to build highly scalable and resilient APIs with minimal resource usage.
The Problem with Blocking APIs
What “blocking” means in the context of web applications.In traditional Spring MVC (Servlet-based) applications:
- Each request is handled by a dedicated thread from a thread pool.
- When a thread waits for a downstream call (like a database query or REST API), it’s blocked — meaning it can’t handle other requests.
- The server has a limited thread pool (often 200–500 threads). Once all threads are busy, the system starts rejecting new requests.
For small-scale applications, this works fine. But under heavy load — say thousands of concurrent requests — the application becomes sluggish or even unresponsive.
Example:
@GetMapping("/user/{id}")
public User getUser(@PathVariable String id) {
// Simulate blocking call (like DB fetch)
return userRepository.findById(id); // blocking call
}
This method ties up one server thread until the database returns the result.
The Non-Blocking Revolution
In a non-blocking model, threads don’t wait. Instead, they register callbacks or use reactive streams that are notified asynchronously when the result is ready.
This means:
- Fewer threads can handle many concurrent requests.
- Resources are used more efficiently.
- The app remains responsive even under high concurrency.
This concept is inspired by reactive programming, which emphasizes asynchronous data streams and backpressure control.
Spring WebFlux
Spring WebFlux is part of the Spring 5+ ecosystem and was built from the ground up to support reactive programming using Project Reactor.Unlike Spring MVC (which is built on the Servlet API), WebFlux can run on non-blocking runtimes such as:
- Netty (default)
- Undertow
- Jetty
- Servlet 3.1 containers (like Tomcat with async support)
Key Features:
✅ Non-blocking and asynchronous request handling
✅ Reactive Streams support (Publisher, Subscriber model)
✅ Built on Project Reactor (Flux, Mono)
✅ Flexible runtime: Netty, Undertow, Jetty
✅ Compatible with functional and annotation-based programming models
Typical WebFlux Controller Example:
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public Mono<User> getUser(@PathVariable String id) {
return userService.findUserById(id);
}
@GetMapping
public Flux<User> getAllUsers() {
return userService.findAllUsers();
}
}
Here:
Mono<User>represents a single async result (like a promise).Flux<User>represents a stream of results (multiple users).
No thread is blocked while waiting for data to arrive — that’s the beauty of WebFlux.
Project Reactor
Project Reactor is at the core of Spring WebFlux. It implements the Reactive Streams specification, providing a set of interfaces for asynchronous stream processing with non-blocking backpressure.
Reactive Streams Core Interfaces:
- Publisher – Emits data
- Subscriber – Consumes data
- Subscription – Connects publisher and subscriber
- Processor – Acts as both Publisher and Subscriber
Reactor provides two main types of Publishers:
- Mono – Emits 0 or 1 element
- Flux – Emits 0 to N elements
Both are lazy — they don’t do anything until there’s a subscriber.
Flux and Mono Explained
Think of Flux and Mono as the backbone of reactive programming in WebFlux.
🧩 Mono (0 or 1 element)
Mono is used for operations that return a single value or no value at all — like fetching one record, saving data, or completing an operation.
Example:
Mono<String> message = Mono.just("Hello World !");
message.subscribe(System.out::println);
Output:
Hello World!
Example for asynchronous DB call:
Mono<User> userMono = userRepository.findById("123");
userMono.subscribe(user -> System.out.println(user.getName()));
🌪️ Flux (0 to N elements)
Flux represents a stream of data that can emit multiple elements — like reading a list of users, streaming logs, or continuous event streams.
Example:
Flux<Integer> numbers = Flux.range(1, 5);
numbers.subscribe(System.out::println);
Output:
1
2
3
4
5
Reactive Pipeline Example
Reactive pipelines are functional and declarative, allowing you to chain transformations.
Flux<String> names = Flux.just("Jack", "DJ", "Maya", "Rohit");
names
.filter(name -> name.startsWith("D"))
.map(String::toUpperCase)
.subscribe(System.out::println);
Output:
DJ
Here:
- The stream is processed asynchronously.
- Operations are non-blocking.
- The pipeline executes only when
.subscribe()is called.
How Netty Powers Non-Blocking I/O
Spring WebFlux, by default, uses Netty — an asynchronous, event-driven network application framework. Netty is a non-blocking network framework built on top of Java NIO (New I/O). It enables high-performance and low-latency applications.
Instead of assigning a dedicated thread per request, Netty uses a small set of event loops to manage thousands of connections efficiently.
Netty’s Event Loop Model
Here’s how it works:
- EventLoop is a single-threaded loop responsible for handling multiple channels (connections).
- Each channel represents a connection.
- The EventLoopGroup manages multiple EventLoops.
- Each EventLoop handles:
- Accepting new connections
- Reading incoming data
- Writing responses
- Triggering callbacks when operations complete
This allows Netty to scale efficiently with minimal thread usage.
Why Event Loops Matter
In traditional blocking I/O:
- Each request → one thread.
- Thousands of connections → thousands of threads → context switching overhead.
In Netty’s non-blocking I/O:
- Thousands of requests → handled by a handful of event loop threads.
- No blocking or waiting → just event-driven callbacks.
- Reduced context switching → faster performance and better scalability.
In short: One event loop can handle thousands of concurrent connections using asynchronous callbacks.
How WebFlux Uses Netty
Spring WebFlux uses Reactor Netty — a reactive programming adaptation of Netty.
It integrates seamlessly with Project Reactor, using Flux and Mono to represent asynchronous HTTP requests and responses.
Example:
RouterFunction<ServerResponse> route = RouterFunctions
.route(GET("/hello"),
request -> ServerResponse.ok().body(Mono.just("Hello Reactive World!"), String.class)
);
This functional style is fully non-blocking. Each request is processed asynchronously by Netty’s event loop, and responses are streamed reactively.
Functional Endpoints vs Annotation-Based Controllers
Spring WebFlux offers two programming models:
1. Annotation-based (similar to Spring MVC)
@RestController
public class HelloController {
@GetMapping("/hello")
public Mono<String> sayHello() {
return Mono.just("Hello WebFlux!");
}
}
2. Functional Routing (more reactive style)
@Configuration
public class RouterConfig {
@Bean
public RouterFunction<ServerResponse> route() {
return RouterFunctions
.route(GET("/hello"), req -> ServerResponse.ok().bodyValue("Hello Reactive World!"));
}
}
Functional routing gives you greater control and flexibility in defining reactive pipelines.
Thread Model Comparison
| Model | Framework | Threading | Scalability | Nature |
|---|---|---|---|---|
| Blocking | Spring MVC | One thread per request | Limited (depends on thread pool size) | Synchronous |
| Non-Blocking | Spring WebFlux | Event-loop based | High (handles thousands of concurrent users) | Asynchronous |
Backpressure Handling
A crucial aspect of reactive systems is backpressure — the ability to handle producers that emit data faster than consumers can process it.
Project Reactor implements backpressure automatically, ensuring system stability.
Example:
Flux.range(1, 1000)
.log()
.limitRate(10)
.subscribe(System.out::println);
Here, only 10 elements are requested at a time, preventing overload.
Practical Example: Non-Blocking Service Integration
Let’s say you’re calling two external APIs and want to combine their results reactively.
public Mono<ResponseDTO> getCombinedData() {
Mono<User> userMono = webClient.get().uri("/user/123").retrieve().bodyToMono(User.class);
Mono<Order> orderMono = webClient.get().uri("/order/123").retrieve().bodyToMono(Order.class);
return Mono.zip(userMono, orderMono)
.map(tuple -> new ResponseDTO(tuple.getT1(), tuple.getT2()));
}
WebClientis the non-blocking alternative toRestTemplate.Mono.zipcombines results when both calls complete.- No threads are blocked waiting for API responses.
Even if the remote API takes time to respond, the server thread isn’t blocked — it’s free to handle other requests.
System Design Perspective
As a System Designer, WebFlux makes perfect sense for:
- High-concurrency microservices
- Real-time streaming (e.g., stock feeds, chat, analytics)
- API Gateways
- IoT systems where thousands of devices connect simultaneously
- Serverless and cloud-native apps
When designed properly, a WebFlux-based system can handle tens of thousands of concurrent connections with just a few hundred threads.
When Not to Use WebFlux
While WebFlux is powerful, it’s not a silver bullet.
Avoid it if:
- Your system is heavy computation and does not have any blocking IO calls.
- You rely on blocking APIs (traditional JDBC, legacy code).
- You have low concurrency requirements and simplicity is preferred.
In those cases, traditional Spring MVC may be simpler and equally effective.
Summary: Why Choose WebFlux?
| Feature | Spring MVC | Spring WebFlux |
|---|---|---|
| I/O Model | Blocking | Non-blocking |
| Concurrency | Thread per request | Event loop |
| Scalability | Limited | High |
| Suitable For | Traditional apps | Reactive systems |
| Built On | Servlet API | Project Reactor / Reactive Streams |
| Default Runtime | Tomcat | Netty |
Conclusion
The shift toward non-blocking, reactive programming is no longer just a trend — it’s the foundation for modern, scalable system design.
Spring WebFlux, powered by Project Reactor and Netty’s event loop, offers an elegant way to build asynchronous, resilient APIs without compromising code readability or maintainability.
As backend systems move towards event-driven, microservices-based architectures, adopting WebFlux ensures your applications are future-ready — capable of handling massive concurrency with minimal resource footprint.
So if you’re designing the next generation of real-time, cloud-native systems — it’s time to go reactive.
References
Spring & WebFlux
- Spring Framework Reference — WebFlux Section
Provides official documentation about WebFlux, its threading model, server support, and concurrency considerations.
https://docs.spring.io/spring-framework/reference/web/webflux/new-framework.html Home - Spring Reactive (spring.io)
Overview of reactive processing in the Spring ecosystem, and how Project Reactor is the foundation.
https://spring.io/reactive Home - Spring WebFlux Internals: How Netty’s Event Loop & Threads Power Reactive Apps
Deep dive into Netty, event loops, and how WebFlux uses them internally.
https://medium.com/%40gourav20056/spring-webflux-internals-how-nettys-event-loop-threads-power-reactive-apps-4698c144ef68 Medium
Netty, Reactor, Event Loop & Pattern
- Reactor Netty Reference Guide
Official documentation showing default event loop group setup and how Reactor Netty configures threading.
https://projectreactor.io/docs/netty/1.1.17/reference Project Reactor
(Also see snapshot version) Project Reactor - Reactor Pattern — Wikipedia
Good for explaining the design pattern behind event loops and multiplexed I/O.
https://en.wikipedia.org/wiki/Reactor_pattern Wikipedia
Concurrency, Threading & Practical Concerns
- Threading Model of Spring WebFlux and Reactor — GeeksforGeeks
Explains how Netty / Undertow event loops delegate I/O and reactive pipelines.
https://www.geeksforgeeks.org/threading-model-of-spring-webflux-and-reactor/ GeeksforGeeks - Concurrency in Spring WebFlux — Baeldung
How WebClient shares the event loop, scheduling, and thread contexts.
https://www.baeldung.com/spring-webflux-concurrency Baeldung on Kotlin - Blocking I/O operation with WebFlux — StackOverflow
Example discussion of integrating blocking APIs within a reactive pipeline (usingpublishOn, etc.).
https://stackoverflow.com/questions/52945696/blocking-i-o-operation-with-webflux Stack Overflow - Spring WebFlux: Refactoring Blocking API with Reactive API — StackOverflow
On when you might need to offload blocking work, usingSchedulers.boundedElastic(), etc.
https://stackoverflow.com/questions/72914528/spring-webflux-refactoring-blocking-api-with-reactive-api-or-should-i Stack Overflow