Hello! Welcome back to our module on "Advanced Concurrency & Performance."
In our last lesson, we established the core principles of reactive programming: non-blocking I/O, the Publisher-Subscriber model for data streams, and the critical role of backpressure. We understood why the traditional thread-per-request model, common in Spring MVC, becomes a bottleneck under high load due to wasteful, blocking waits for I/O operations.
Today, we transition from theory to practice. Our goal is to implement non-blocking REST controllers using Spring WebFlux (Mono/Flux) for high-concurrency scenarios. You'll learn how to build endpoints that are highly scalable and responsive by applying the reactive principles we've discussed. This is a crucial skill for building modern, production-ready microservices and a frequent topic in senior-level interviews.
1. Spring WebFlux: The Reactive Web Stack
Spring WebFlux is the reactive-stack web framework in the Spring ecosystem, designed to handle massive concurrency with a small number of threads. It's an alternative to Spring MVC, not a replacement, and the choice between them is a key architectural decision.

The core advantage of WebFlux for high-concurrency workloads comes from its non-blocking foundation. To understand this, let's briefly review the motivation and the underlying engine.
This article from Baeldung provides a concise overview of why reactive programming is necessary and how the event loop model works. It will connect the theory from our last lesson directly to the architecture of WebFlux.
Please read sections '2. The Motivation for Reactive Programming' and '3. Concurrency in Reactive Programming'. These sections reinforce the limitations of the thread-per-request model and frame asynchronicity as the key to better resource utilization.
As the article mentions, the key is asynchronicity. WebFlux achieves this using a server like Netty, which is built around the event loop model. Instead of dedicating one thread per request, a small number of event loop threads (often equal to the number of CPU cores) handle events from many connections. When an I/O operation is needed, the event loop thread dispatches the task and immediately moves on to handle another event, rather than blocking.
2. Building a Non-Blocking Controller with Mono and Flux
Now, let's write some code. In WebFlux, instead of returning a List<T> or a single object T, our controllers return reactive publishers:
Mono<T>: A publisher that emits 0 or 1 item. Use this for endpoints that return a single resource (e.g.,findById) or just a status (voidoperations).Flux<T>: A publisher that emits 0 to N items. Use this for endpoints that return a collection or a stream of data.
Let's see how to build a basic reactive application from scratch.
Reactive Programming with Spring Boot | A Beginner's Guide
The following video by Ali Bouali provides a clear, step-by-step guide to creating a reactive Spring Boot application. We'll focus on the setup and controller implementation.
Watch the following segments: Project Setup (07:05 - 10:05): Pay attention to the dependencies. We need Spring Reactive Web (which includes WebFlux and Netty) instead of the standard Spring Web. Entity and Repository (10:18 - 25:25): Skim this part. The key takeaway is the use of reactive counterparts like @Table instead of @Entity and ReactiveCrudRepository instead of JpaRepository when using reactive databases (like R2DBC). Service Layer (25:25 - 28:59): Notice how the service methods now return Mono<Student> and Flux<Student>. Controller Layer (28:59 - 31:38): This is the most important part for our lesson. Observe how the RestController methods are defined to return Mono and Flux. The structure is very similar to Spring MVC, but the return types are reactive.
As you saw, the controller implementation looks familiar. We still use @RestController and mapping annotations like @GetMapping. The main difference is that our methods return Mono or Flux.
@RestController
@RequestMapping("/api/v1/students")
@RequiredArgsConstructor
public class StudentController {
private final StudentService service;
// Returns a stream of 0-N students
@GetMapping
public Flux<Student> findAll() {
return service.findAll();
}
// Returns a single student (0 or 1)
@GetMapping("/{id}")
public Mono<Student> findById(@PathVariable Integer id) {
return service.findById(id);
}
}
3. Seeing Non-Blocking in Action: A Deeper Look
So, what does this reactive controller actually do differently? How does it handle high concurrency?
The true power of this model is demonstrated when dealing with latency. Imagine an endpoint that fetches data from a slow downstream service.
- In Spring MVC, a request to this endpoint would tie up a thread for the entire duration of the wait. If 200 requests come in at once, all 200 threads in your pool could be blocked, and your service would stop responding to new requests.
- In Spring WebFlux, the event loop thread that receives the request would initiate the call to the slow service and then immediately be free to handle other requests. When the slow service finally responds, that response is treated as a new event, and an event loop thread picks it up to continue processing.
This allows a handful of threads to manage thousands of concurrent requests that are in various stages of waiting for I/O.
Let's watch a detailed demonstration that shows this non-blocking behavior and inspects the underlying threads.
Spring Boot WebFlux Complete Flow | Netty Event Loop Explained ! | Reactive Programming
This video from Selenium Express gives an excellent deep-dive into the WebFlux execution model. We will focus on the part where he builds a consumer service that calls a slow producer, demonstrating the non-blocking nature of WebFlux.
Watch from 1:11:48 to 1:21:45. In this segment, the presenter does the following: Creates a WebClient to make a non-blocking call to a slow endpoint (which has an artificial 20-second delay). Creates a second, simple endpoint that returns data instantly. Restricts the application to use only one event loop thread to prove the concept. He then hits the slow endpoint, and while it's 'waiting' for the 20-second response, he repeatedly hits the fast endpoint, which continues to respond instantly. This is a powerful visual proof that the single event loop thread is not blocked by the slow I/O operation.
This demonstration is critical for your understanding. Being able to explain this behavior—how a single thread can serve multiple requests concurrently without blocking—is exactly what interviewers at companies like Paypal and FAANG look for. It shows you understand the why behind the technology.
4. Practice for Your Interview
Let's put this into practice with a common interview-style problem.
Test your understanding! (Coding Challenge)
You have a traditional Spring MVC controller for an e-commerce application that fetches a list of products. The productService.getProducts() call is slow because it fetches data from a legacy system.
Blocking Controller (Spring MVC):
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductService productService; // A traditional, blocking service
@GetMapping
public List<Product> getAllProducts() {
// This call blocks the thread for several seconds
return productService.getProducts();
}
}
Your task is to refactor this endpoint into a non-blocking Spring WebFlux controller. Assume you've already refactored the ProductService to be reactive:
Reactive Service Interface:
public interface ProductService {
// This method now returns a Flux, streaming products as they become available
Flux<Product> getProducts();
}
How would you implement the new ProductController using WebFlux to stream the products to the client?
Show answer
Here is the non-blocking implementation using Spring WebFlux.
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductService reactiveProductService;
@GetMapping(produces = MediaType.APPLICATION_NDJSON_VALUE) // or TEXT_EVENT_STREAM_VALUE
public Flux<Product> getAllProducts() {
// The controller immediately returns a Flux.
// WebFlux subscribes to it and starts sending data to the client
// as soon as the service emits each product.
return reactiveProductService.getProducts();
}
}
Explanation:
- The controller's method signature is changed to return
Flux<Product>. - We return the
Fluxdirectly from the service. We are not blocking and waiting for all products to be collected into aList. - The
produces = MediaType.APPLICATION_NDJSON_VALUEattribute is important. It tells the client that we will be sending a stream of JSON objects (Newline Delimited JSON) rather than a single JSON array. This allows the client (like a web browser) to start rendering the data as it arrives.TEXT_EVENT_STREAM_VALUEis another common option for Server-Sent Events (SSE). - Behind the scenes, the WebFlux framework subscribes to the
Fluxreturned by the controller. As thereactiveProductServiceemits eachProduct, WebFlux writes it to the HTTP response stream, flushing it to the client. The request thread is never blocked.
Conclusion
In this lesson, we bridged the gap between reactive theory and practice. You now have the foundational knowledge to build scalable, non-blocking REST APIs with Spring WebFlux.
Key Takeaways:
- Spring WebFlux is the reactive alternative to Spring MVC, built for high-concurrency and non-blocking I/O.
- Controllers in WebFlux return reactive types:
Monofor single items andFluxfor streams of items. - WebFlux runs on servers like Netty that use an event loop model, allowing a few threads to handle thousands of concurrent requests without blocking.
- For a fully reactive system, all components in the chain must be non-blocking. This includes using
WebClientfor downstream HTTP calls and reactive database drivers (like R2DBC).
Next Up
We've seen how reactive programming helps manage concurrency. Another critical technique for building high-performance services is caching. In our next lesson, we'll dive into practical caching strategies, where you will implement the cache-aside pattern using Redis with Spring caching annotations (@Cacheable, @CacheEvict). We'll explore how caching can drastically reduce latency and protect your downstream services from excessive load.
Can't find a good explanation? Sign up and we'll make it for you
Sign up