Building Reactive Microservices with Spring Boot and WebFlux
Create scalable and non-blocking microservices using Spring Boot and WebFlux's reactive programming model
As applications scale and workloads increase, traditional thread-per-request models often hit limitations. Reactive programming offers a non-blocking, event-driven alternative that makes better use of system resources under high load.
Spring WebFlux, introduced in Spring 5, is a reactive-stack web framework built to support non-blocking I/O. It works on top of Project Reactor, enabling Java developers to build reactive microservices that are highly scalable and efficient.
This guide walks you through the foundations of reactive programming and how to build reactive microservices using Spring Boot and WebFlux.
What is Reactive Programming?
Reactive programming is a paradigm focused on asynchronous data streams and event-driven architecture. Instead of blocking threads, reactive systems respond to data events as they occur.
Core principles:
- Responsive: Handle requests in a timely manner
- Resilient: Stay responsive in the face of failure
- Elastic: Scale to handle varying load
- Message-driven: Communicate asynchronously
Introducing Spring WebFlux
Spring WebFlux supports two programming models:
- Annotated Controllers (like
@RestController
) - Functional Endpoints (with
RouterFunction
andHandlerFunction
)
Unlike Spring MVC, WebFlux is built on non-blocking runtimes like Netty, making it ideal for reactive applications.
Add dependencies to pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Working with Reactive Types
Spring WebFlux uses Mono and Flux from Project Reactor:
- Mono: 0 or 1 value
- Flux: 0 to many values
Examples:
@GetMapping("/mono")
public Mono<String> getMono() {
return Mono.just("Hello Reactive World");
}
@GetMapping("/flux")
public Flux<Integer> getFlux() {
return Flux.range(1, 5);
}
These types represent non-blocking, asynchronous streams.
Reactive Repository with Spring Data
Spring Data R2DBC enables reactive access to relational databases.
Add dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
</dependency>
Define a reactive repository:
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
Flux<User> findByRole(String role);
}
Everything from queries to saving records is non-blocking.
Functional Routing Example
Functional endpoints offer more control and are fully reactive:
@Bean
public RouterFunction<ServerResponse> route(UserHandler handler) {
return RouterFunctions
.route(GET("/users"), handler::getAllUsers)
.andRoute(POST("/users"), handler::createUser);
}
Handler implementation:
public Mono<ServerResponse> getAllUsers(ServerRequest request) {
return ServerResponse.ok().body(userRepository.findAll(), User.class);
}
Error Handling in WebFlux
Use onErrorResume
or global exception handling with @ControllerAdvice
:
@GetMapping("/user/{id}")
public Mono<ResponseEntity<User>> getUser(@PathVariable Long id) {
return userRepository.findById(id)
.map(ResponseEntity::ok)
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
}
For global error handling, implement ErrorWebExceptionHandler
.
WebClient for Reactive HTTP Calls
Replace RestTemplate
with WebClient for non-blocking HTTP calls:
WebClient client = WebClient.create("http://localhost:8080");
Mono<User> userMono = client.get()
.uri("/users/1")
.retrieve()
.bodyToMono(User.class);
WebClient integrates seamlessly with reactive flows.
Testing Reactive APIs
Use StepVerifier from Reactor Test to test Mono
and Flux
:
@Test
void testMono() {
Mono<String> mono = Mono.just("Spring");
StepVerifier.create(mono)
.expectNext("Spring")
.verifyComplete();
}
For controller tests, use WebTestClient
to simulate HTTP requests:
@Autowired
private WebTestClient webTestClient;
@Test
void testGetUsers() {
webTestClient.get()
.uri("/users")
.exchange()
.expectStatus().isOk()
.expectBodyList(User.class);
}
Benefits of Building Reactive Microservices
- Better resource utilization under high load
- Improved throughput with fewer threads
- Suitable for event-driven and real-time systems
- Seamless integration with Kafka, MongoDB, R2DBC, and more
Use cases:
- Chat systems
- Live feeds and dashboards
- API gateways and aggregators
- High-concurrency services
Best Practices
- Use reactive APIs end-to-end (database, HTTP, messaging)
- Avoid blocking calls (like JDBC, Thread.sleep)
- Use
Schedulers.boundedElastic()
for bridging non-reactive tasks - Limit response sizes and apply backpressure mechanisms
- Monitor latency and throughput using tools like Micrometer
Conclusion
Reactive programming with Spring WebFlux enables you to build microservices that are lightweight, scalable, and resilient under load. By embracing Mono
, Flux
, and non-blocking patterns, you unlock the full power of modern Java applications built for real-time performance.
Whether you’re scaling APIs or building event-driven pipelines, Spring Boot + WebFlux provides the ideal foundation for reactive microservice architecture.