This article will focus on how to do reactive programming with Spring WebFlux. It starts with introductory concepts around reactive programming, reactive streams specification, project reactor and Spring WebFlux. We will work on an example to build a simple loan info microservice to demonstrate the concepts.
Reactive Programming
Reactive programming paradigm is about asynchronous and non-blocking applications. Reactive programming is good for applications that have large streaming data and needs resiliency under high load. In this model, data is pushed to the clients as and when it is available.
Handling streams of data whose volume is unknown (e.g. live data) requires special care in an asynchronous system. The most prominent issue is that resource consumption needs to be controlled such that a fast data source does not overwhelm the stream destination referred as backpressure.
Reactive Streams Specification
Reference:http://www.reactive-streams.org/Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure. The scope of Reactive Streams is to find a minimal set of interfaces, methods and protocols that will describe the necessary operations and entities to achieve the goal—asynchronous streams of data with non-blocking back pressure.
Reactive streams interfaces.
Processor<T,R>
A Processor represents a processing stage—which is both a Subscriber and a Publisher and obeys the contracts of both.public interface Processor<T, R> extends Subscriber<T>, Publisher<R> { }
Publisher<T>
A Publisher is a provider of a potentially unbounded number of sequenced elements, publishing them according to the demand received from its Subscriber(s).public interface Publisher<T> { public void subscribe(Subscriber<? super T> s); }
Subscriber<T>
Will receive call to Subscriber.onSubscribe(Subscription) once after passing an instance of Subscriber to Publisher.subscribe(Subscriber).public interface Subscriber<T> { public void onSubscribe(Subscription s); public void onNext(T t); public void onError(Throwable t); public void onComplete(); }
Subscription
A Subscription represents a one-to-one lifecycle of a Subscriber subscribing to a Publisher.public interface Subscription { public void request(long n); public void cancel(); }
Reactive Streams Publisher Subscriber Flow
The reactive streams publisher subscriber flow starts with a subscribe() request towards the publisher to start streaming the data. This starts a new Subscription. Followed by this a demand is made to the publisher with the number of elements using the request() method. onNext() is the data notification sent from the publisher. onComplete() is the successful terminal state.Project Reactor
Reference: https://projectreactor.io/Project Reactor is a library (reactor-core) based on the Reactive Streams specification, for building non-blocking applications on the JVM. Reactor is fully non-blocking and provides efficient demand management in the form of managing "backpressure".
- Flux object represents a reactive sequence of 0..N items
- Mono object represents a single-value-or-empty (0..1) result
Flux
A Flux is a reactive type, standard Publisher that represents an asynchronous sequence of 0 to N emitted items, optionally terminated by either a completion signal or an error. Simple example below to demonstrate usage and signals of Flux.package com.stackstalk; import java.util.List; import reactor.core.publisher.Flux; public class ReactorTestMain { public Flux<String> loanTypesFlux() { return Flux.fromIterable(List.of("Car Loan", "Education Loan", "Personal Loan")).log(); } public static void main(String[] args) { ReactorTestMain reactorTestMain = new ReactorTestMain(); reactorTestMain.loanTypesFlux().subscribe(name -> { System.out.println(name); }); } }Output:
[ INFO] (main) | onSubscribe([Synchronous Fuseable] FluxIterable.IterableSubscription) [ INFO] (main) | request(unbounded) [ INFO] (main) | onNext(Car Loan) Car Loan [ INFO] (main) | onNext(Education Loan) Education Loan [ INFO] (main) | onNext(Personal Loan) Personal Loan [ INFO] (main) | onComplete()
Mono
Mono object is a reactive type and represents a single value or empty (0..1) result. A Mono is a specialized Publisher that emits atmost one item via onNext signal then terminates with an onComplete signal. Simple example below to demonstrate usage and signals of Mono.package com.stackstalk; import java.util.List; import reactor.core.publisher.Mono; public class ReactorTestMain { public Mono<String> strMono() { return Mono.just("Hello World!!").log(); } public static void main(String[] args) { ReactorTestMain reactorTestMain = new ReactorTestMain(); reactorTestMain.strMono().subscribe(name -> { System.out.println(name); }); } }Output:
[ INFO] (main) | onSubscribe([Synchronous Fuseable] Operators.ScalarSubscription) [ INFO] (main) | request(unbounded) [ INFO] (main) | onNext(Hello World!!) Hello World!! [ INFO] (main) | onComplete()
Spring WebFlux
Reference: https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.htmlOriginal web framework included in Spring Framework is Spring MVC built for servlet API and servlet containers. Spring WebFlux added later, is the reactive-stack web framework which is fully non-blocking, supports reactive streams back pressure and runs on non-blocking servers such as Netty, Undertow etc. Spring WebFlux is built on top of project reactor.
One of the key differences between Spring MVC and Spring WebFlux is the concurrency model.
- In Spring MVC (and servlet applications in general), it is assumed that applications can block the current thread, (for example, for remote calls). For this reason, servlet containers use a large thread pool to absorb potential blocking during request handling.
- In Spring WebFlux (and non-blocking servers in general), it is assumed that applications do not block. Therefore, non-blocking servers use a small, fixed-size thread pool (event loop workers) to handle requests.
Spring WebFlux provides two programming models.
- Annotated Controllers: Consistent with Spring MVC and based on the same annotations from the spring-web module. Both Spring MVC and WebFlux controllers support reactive return types.
- Functional Endpoints: Lambda-based, lightweight, and functional programming model. This is a small library or a set of utilities that an application can use to route and handle requests.
We will see each of these programming models with an example.
In this section we looked at reactive programming in general and understood couple of programming models with Spring WebFlux namely, annotation-based approach and functional endpoints approach.
When to use Spring MVC or Spring WebFlux is a common question.
- If there is a Spring MVC application that works fine, there is no need to change. Imperative programming is the easiest way to write, understand, and debug code. If the application has blocking dependencies it is better to stay with Spring MVC.
- Spring WebFlux is a good fit for uses cases that require ease of streaming up or down. If there is a need for lightweight, functional web framework for use with Java 8 lambdas, we can use the Spring WebFlux functional web endpoints. In a microservice architecture, we can always have a mix of applications with either Spring MVC or Spring WebFlux controllers or with Spring WebFlux functional endpoints.
0 comments:
Post a Comment