HomeAbout
 
  

Introduction to Reactive Programming with Spring Webflux

February 18, 2023

Reactive programming allows for asynchronous, non-blocking, and efficient execution of tasks without waiting for each other, thereby achieving more with less resources. Today, reactive programming has become very valuable and is one of the trendiest topics. If you are unfamiliar with reactive programming and do not know what it is, I highly recommend reading my previous article before proceeding.

In today's world, especially when developing applications that cater to large audiences and receive high traffic, it is important for these applications to efficiently handle a high volume of requests using limited resources, respond promptly, be scalable, and easily deal with errors. One of the best ways to address these challenges is by implementing the development phase with reactive programming.

Spring offers a great component to put the reactive programming paradigm into practice. For those familiar with Spring, especially in the Java world, we can quickly develop our web applications in a reactive manner using Spring Webflux.

What is Spring Webflux?

Spring Webflux is a framework built on Project Reactor that supports reactive, asynchronous, and non-blocking programming for developing web applications. Unlike the traditional SpringMVC, which allocates a thread for each request (typically with Tomcat), Spring WebFlux uses event streams with backpressure support for asynchronous and non-blocking programming (typically with Netty) and utilizes resources more efficiently. Backpressure is a way to handle large data flows that cannot be processed with limited resources.

In reactive programming, threads can complete their tasks without waiting for previous tasks to finish and continue without being blocked. To achieve this, the entire process must be reactive and should not contain any blocking operations. If any part of the application performs blocking operations, it will move away from being reactive. In Spring Webflux, the whole stack of the application from the database to the web server must be reactive.

Spring Web Flux Structure

The Reactive streams in Spring Web Flux are based on the publisher-subscriber model.

  • Publisher - publishes based on the requests from subscribers. A producer can serve multiple subscribers.
  • Subscriber - captures events (messages/events) published by the producer. The subscriber has four methods to handle the received events: onSubscribe, onNext, onError, and onComplete.
  • Subscription - represents the relationship between subscriber and publisher. It has a request(long n) method to request data and a cancel method to cancel events.
  • Processor - is a rare structure that can act as both subscriber and publisher.

Two different publisher structures are very important for webflux:

  • Mono: used for Publishers that can contain 0 or 1 event.
Mono<String> stringMono = Mono.just("Hey Gokhan");
Mono<Integer> integerMono = Mono.just(1);
Mono<Object> monoError = Mono.error(new RuntimeException());
Mono<Object> monoEmpty = Mono.empty();
  • Flux: used for Publishers that can contain 0 to N events. Once a stream is created, we need to subscribe to it to produce its elements.
Flux<String> stringFlux = Flux.just("Gokhan", "Reader", "World");
Flux<Integer> fluxRange = Flux.range(1,5);
Flux<Long> longFlux = Flux.interval(Duration.ofSeconds(1));

Once a stream has been created, we need to subscribe to it in order to produce its elements.

List<String> streamData = new ArrayList<>();
Flux<String> items = Flux.just("Gokhan", "Reader", "World");
items.log().subscribe(streamData::add);
streamData.forEach(System.out::println);

By using the .log() method, we can observe and follow the entire flow.

log method

Building a Simple Web Application with Reactive Programming

First, we need to manage the dependencies of the project by adding the spring-boot-starter-webflux dependency for gradle or maven. Now we can write a simple application with reactive objects using WebFlux.

implementation 'org.springframework.boot:spring-boot-starter-webflux'

There are two paradigms to develop the controller layer with WebFlux:

  • Annotation-based controller (Like Spring MVC)
  • Functional Endpoints which enables the development of functional endpoints and the continuation of processes through routing.

REST API - Annotation-Based Controller

You can develop the controller layer using the same structures as Spring MVC. The similarity is quite high, except for the return type being reactive objects, so someone familiar with Spring MVC can easily adapt to this controller structure once they understand the basic Webflux structures explained above.

public Mono<User> getUserById(int id) {
return Mono.just(new User(id, "Gokhan", new Random().nextInt(100) + 1));
}

To quickly create our Rest layer, we can use the @RestController and @RequestMapping structures just like in Spring MVC.

@RestController
@RequestMapping("api/v1/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public Mono<ResponseEntity<User>> getUserById(@PathVariable int id) {
return userService.getUserById(id)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
}

When writing a reactive endpoint to handle a GET request, you can see that the return type is Mono. Upon examining the service layer, the method called returns the Mono Publisher with the return values. The work done is to process the received Mono Publisher using the map method and create the response based on the received value. In addition to the example we have made, we can also create an endpoint that returns multiple events/messages using Flux structures.

// User Service
@Override
public Flux<User> getUsers() {
return Flux.just(
new User(1, "Gokhan", new Random().nextInt(100) + 1),
new User(2, "Gokhan", new Random().nextInt(100) + 1)
);
}
// Controller layer
@GetMapping
public Flux<User> all() {
return userService.getUsers();
}

As shown in the example, we should pay attention to the fact that the return types of the methods in the service layer are one of the Mono or Flux reactive publishers.

Mono and Flux

With Flux, we can create streams. Just like in the example below, it's pretty cool :)

@GetMapping(path = "/flux", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Flux<User> getFlux(){
return userService.getUsers()
.delayElements(Duration.ofSeconds(1)).log();
}

Rest API — Functional Endpoint

We can develop the API layer in a functional programming-friendly manner using Spring Functional Endpoint. With this structure, we can create a system for handling routing and requests. These statements might not make sense at first, so let's move on to the example.

Instead of using an annotation-based model, we use structures called HandlerFunction and RouterFunctions.

HandlerFunction is a method that takes a "ServerRequest" variable and returns a Mono of "ServerResponse" type. To perform an example similar to the annotation-based structure, we create a HandlerFunction based on the User Controller structure.

@Component
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService) {
this.userService = userService;
}
public Mono<ServerResponse> getUsers(ServerRequest request) {
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(userService.getUsers(), User.class);
}
}

By creating Handler functions, the requests received by these functions can be directed by Router functions. Router functions can be written using the RouterFunctions.route() method. Multiple routers can be defined within the same Router Function method using .andRoute(...)

Functional programming and Reactive programming make a great pair. So even if we are familiar with annotation-based programming, we can use Router Functions for developments towards functional programming. This way, we can manage all routing management ourselves instead of relying on the framework.


"With the above examples, you can access the complete simple reactive Rest API containing all methods on my Github."


TL; DR;

Spring WebFlux is a suitable option for applications that require concurrency, efficiency in handling multiple requests with less resources, and scalability.

When deciding to use Spring WebFlux, it is important to keep in mind that all aspects of the application, including all flows, methods, and components, must be reactive and asynchronous, and that no operation should be blocking. If a database is being used, the database driver must also be reactive.


"We have searched for answers to questions such as what is Spring Webflux, how can an API layer be created, and what are its basic structures, and made an introduction to reactive programming with Spring Webflux. From now on, we will look at topics that can be considered as "Advance" and learn how data processing can be done and how a database can be made reactive, thus continuing this series that will enhance our expertise in reactive programming."

 

Gökhan Ayrancıoğlu

Gökhan Ayrancıoğlu

Sr. Software Engineer @Heycar | Tech Blogger

 

Copyright © 2023 All rights reserved

Made with Coffee ☕