logo

Get in touch

Awesome Image Awesome Image

Java Developers August 21, 2023

Guide to Spring Reactive Programming using Spring WebFlux

Written by Mahipalsinh Rana

2.4K

Reactive programming is a programming paradigm that promotes an asynchronous, non-blocking, event driven approach to data processing. Reactive programming involves modelling data and events as observable data streams and implementing data processing routines to react to the changes in those streams. 

In the reactive style of programming, we make a request for the resource and start performing other things. When the data is available, we get the notification along with the data in the form of call back function. In the callback function, we handle the response as per application /user needs. 

Features of Reactive Programming 

  • Asynchronous & Non-blocking 
  • Functional style of coding 
  • Data flow as event driven stream 
  • Backpressure on data streams 

When considering whether to use Spring MVC or Spring WebFlux, there are various factors you must consider.  

Spring-MVC-Webflux

Spring MVC: It’s based on a servlet API and follows an imperative programming model. This means you write code in a step-by-step manner, which is generally easier to follow.   

Spring WebFlux: It offers a reactive programming model. Reactive programming is about handling asynchronous streams of data. This requires a change in thinking and can be more challenging than the traditional imperative model.   

Spring MVC: If you are familiar with traditional web application development, Spring MVC’s imperative model might seem more straightforward. It’s easier to read, write, and understand for developers accustomed to this approach.   

Spring WebFlux: Writing reactive code can initially seem complex because of the shift in mindset. However, for some use cases, like streaming data, it can simplify your code.   

Spring MVC: Debugging is typically more straightforward with an imperative model because the call stacks are more predictable and easier to trace.   

Spring WebFlux: Debugging reactive streams can be tricky, especially for developers new to the reactive paradigm. However, tools and practices are evolving to better support this.   

Spring MVC: Works naturally with blocking resources like traditional RDBMS using JDBC or JPA (Java Persistence API). 

Spring WebFlux: If you have blocking dependencies like traditional databases, you might not get the full benefits of the reactive model. However, reactive databases like MongoDB Reactive, Cassandra Reactive, etc., can be integrated natively with WebFlux.   

Spring MVC: Uses a thread-per-request model. For a high number of simultaneous connections, this can lead to a large number of threads, which may not be efficient. 

Spring WebFlux: Uses an event-loop concurrency model, which can handle a vast number of simultaneous connections with a smaller number of threads. It’s designed for high concurrency. 

Spring MVC: Typically runs on servlet containers like Tomcat, Jetty, etc. 

Spring WebFlux: Runs on reactive runtimes like Netty. This provides non-blocking and highly scalable operations, suitable for high-performance systems. 

Spring MVC: Typically uses annotated controllers. 

Spring WebFlux: In addition to annotated controllers, WebFlux supports functional endpoints which allow for programmatic route definitions. 

Spring WebFlux: 

As we know, Spring provides Web MVC framework to handle the HTTP requests, but it is Blocking & Non-Reactive in nature, so to support reactive programming Spring provides one more web framework in Spring 5 (includes in Spring Boot 2.0) called WebFlux. 

It is a reactive-stack web framework that is fully non-blocking, supports reactive streams back pressure. It uses project Reactor as a reactive library. The Reactor is a Reactive Streams Library and therefore, all of its operators support non-blocking back pressure. 

It uses two publishers: 

  • Mono 
  • Flux 

MONO: 

A mono is a specialized Publisher that emits at most one item and then optionally terminates with an onComplete signal or an onError signal. In short, it returns 0 or 1 element. 

  • Mono is another implementation of Publisher. 
  • It emits at most one item and then (optionally) terminates with an onComplete signal or an onError signal. 
  • Like Flux, Mono is also asynchronous in nature. 

Mono noData = Mono.empty(); 

Mono data = Mono.just(“rishi”); 

FLUX: 

A flux is a standard Publisher representing an asynchronous sequence of 0 to N emitted items, optionally terminated by either a completion signal or an error. These three types of signals translate to calls to a downstream subscriber’s onNext, onComplete, or onError methods. 

  • Flux is an implementation of Publisher. 
  • It will emit 0 to N elements and/or a complete or an error call. 
  • Stream pipeline is synchronous whereas Flux pipeline is completely asynchronous. It will emit values only when there is a downstream subscriber. 

To subscribe, we need to call the subscribe method on Flux. There are different variants of the subscribe method available, which we need to use as per the need: 

Flux flux1 = Flux.just(“foo”, “bar”, “foobar”); 

Flux flux2 = Flux.fromIterable(Arrays.asList(“A”, “B”, “C”)); 

Flux flux3 = Flux.range(5, 3); 

// subscribe 

flux.subscribe(); 

Frequently used operations on Mono/Flux 

  • just(-): Create a new Mono that emits the specified item, which is captured at instantiation time. 
  • fromArray(-): Create a Flux that emits the items contained in the provided array. 
  • fromIterable(-): Create a Flux that emits the items contained in the provided iterable. The Iterable.iterator() method will be invoked at least once and at most twice for each subscriber. 
  • fromStream(-): Create a Flux that emits the items contained in a Stream created by the provided Supplier for each subscription. The Stream is closed automatically by the operator on cancellation, error, or completion. 
  • empty(): Create a Flux that completes without emitting any item. 
  • doOnNext(-): Add behaviour (side-effect) triggered when the Flux emits an item. 
  • doOnComplete(-): Add behaviour (side-effect) triggered when the Flux completes successfully. 
  • doOnError(-): Add behaviour (side-effect) triggered when the Flux completes with an error. 
  • map(-): Transform the items emitted by this Flux by applying a synchronous function to each item. 
  • flatMap(-): Transform the item emitted by this Mono asynchronously, returning the value emitted by another Mono (possibly changing the value type). 
  • subscribe(-, -, -): Subscribe a Consumer to this Flux that will consume all the elements in the sequence. It will request an unbounded demand. 
  • log(): Observe all Reactive Streams signals and trace them using Logger support. Default will use Level.INFO and java.util.logging. If SLF4J is available, it will be used instead. 
  • delayElements(-): Delay each of this Flux elements (Subscriber.onNext signals) by a given Duration. Signals are delayed and continue the parallel default Scheduler, but empty sequences or immediate error signals are not delayed. 
  • block(): Subscribe to this Mono and block indefinitely until a next signal is received. Returns that value, or null if the Mono completes empty. In case the Mono errors, the original exception is thrown (wrapped in a RuntimeException if it was a checked exception) 

Working with Spring Web Flux – Understanding the Reactive nature 

Requirement: Send Promos to all the customers of an e-Commerce website 

Step-1: Create a Spring Boot project using Maven (Choose Spring Boot version 2.0 or later) 

Step-2: Add the below spring-boot-starter-webflux dependency in pom.xml 

 <dependency> 

                             <groupId>org.springframework.boot</groupId> 

                             <artifactId>spring-boot-starter-webflux</artifactId> 

</dependency> 

 This dependency includes the below dependencies 

  • spring-webflux framework 
  • reactor-core that we need for reactive streams 

reactor-netty (the default server that supports reactive streams). Any other servlet 3.1+ containers like Tomcat, Jetty or non-servlet containers like Undertow can be used as well 

Version will be picked from spring-boot-starter-parent dependency version  

Step-3: Create a Customer DTO class with the fields Id, Name & Email Id 

Customer DTO

Step-4: Create a Customer Repo with 2 functions loadCustomers(), loadCustomerStream() as in the below snapshot.

Customer Repo with 2

Customer Repo with 2 - 1

Step-5: Create a Customer Service with 2 functions, one is to send promos to list of customers, another is to send promos to customer stream

Customer Service with 2

Step-6: Create a Customer REST Controller with 2 end points as in the below screenshot

Customer REST Controller

Step-7: Start the Spring Boot Web Flux Application

Error Handling in Spring WebFlux

Step-1: Take the same(above) Spring Boot project

Step-2: Add a function in CustomerDao to find Customer by email Id, return Error Mono if the Customer is not found with the email Id else return the Customer Mono as in below screenshot

CustomerDao to find Customer by email Id

Step-3: Define one Response wrapper class with the fields path, timestamp, status code, error, warning, success, data fields

Response wrapper class

Step-4: Autowire CustomerDao in CustomerController class & add one more end point in class to find the Customer by email Id, handle the error Mono in the Controller itself as in the below screenshot

CustomerController class

Summary:

Spring introduced a Multi-Event Loop model to enable a reactive stack known as WebFlux. It is a fully non-blocking and annotation-based web framework built on Project Reactor which allows building reactive web applications on the HTTP layer. It provides support for popular inbuilt severs like Netty, Undertow, and Servlet 3.1 containers.

Spring WebFlux or Reactive non-blocking applications usually do not make the applications run faster. The essential benefit it serves is the ability to scale an application with a small, fixed number of threads and lesser memory requirements while at the same time making the best use of the available processing power. It often makes a service more resilient under load as they can scale predictably.

WebFlux is also relevant for applications that need scalability or to stream request data in real time. While implementing a micro-service in WebFlux we must consider that the entire flow uses reactive and asynchronous programming and none of the operations are blocking in nature.

Git URL for a basic demo of Spring Reactive Programming: https://github.com/YashKakrechaInexture/flux-demo

 

Bringing Software Development Expertise to Every
Corner of the World

United States

India

Germany

United Kingdom

Canada

Singapore

Australia

New Zealand

Dubai

Qatar

Kuwait

Finland

Brazil

Netherlands

Ireland

Japan

Kenya

South Africa