Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreFollowing previous Reactive Spring and Reactor Core 3.0 blog posts, I would like to explain why Reactive types are useful and how they compare to other asynchronous types, based on what we have learned while working on the Spring Framework 5 upcoming Reactive support.
Reactive types are not intended to allow you to process your requests or data faster, in fact they will introduce a small overhead compared to regular blocking processing. Their strength lies in their capacity to serve more request concurrently, and to handle operations with latency, such as requesting data from a remote server, more efficiently. They allow you to provide a better quality of service and a predictable capacity planning by dealing natively with time and latency without consuming more resources. Unlike traditional processing that blocks the current thread while waiting a result, a Reactive API that waits costs nothing, requests only the amount of data it is able to process and bring new capabilities since it deals with stream of data, not only with individual elements one by one.
Before Java 8, asynchronous non-blocking behavior was not obvious to implement for at least two reasons. The first reason is that callback based API required verbose anonymous classes and are not easy to chain. The second reason is that Future
type is asynchronous but blocks the current thread until the computation completes when you try to get the result with the get()
method. That's why Spring Framework 4.0 introduced ListenableFuture
, a Future
implementation that adds non-blocking callback-based capabilities.
Then Java 8 introduced lambdas and CompletableFuture
. Lambdas allow to write concise callbacks, while CompletionStage
interface and CompletableFuture
class finally allows to deal with future in a non-blocking way and push-based fashion, while providing capabilities to chain such deferred result processing.
Java 8 also introduced Stream
, which has been designed to deal efficiently with stream of data (including primitive types) that can be accessed with no or very little latency. It is pull-based, can only be used once, lacks time-related operations and can perform parallel computations but without being able to specify the thread pool to use. As explained by Brian Goetz,
it has not been designed to deal with operation with latency, such as I/O operations. And that is where Reactive APIs like Reactor or RxJava come in.
Reactive APIs such as Reactor also provide operators like Java 8 Stream, but they work more generally with any stream sequence (not just Collections) and allow to define a pipeline of transforming operations that will apply to the data passing through it thanks to a handy fluent API and using lambdas. They are designed to handle both synchronous or asynchronous operations, and allow you to buffer, merge, concatenate, or apply a wide range of transformations to your data.
Initially Reactive APIs were only designed to deal with streams of data, i.e. N elements, for example, using Reactor's Flux
:
reactiveService.getResults()
.mergeWith(Flux.interval(100))
.doOnNext(serviceA::someObserver)
.map(d -> d * 2)
.take(3)
.onErrorResumeWith(errorHandler::fallback)
.doAfterTerminate(serviceM::incrementTerminate)
.consume(System.out::println);
But during our work on Spring Framework 5, it became apparent that there was a clear need to distinguish between streams of 1 or N elements, and that is why Reactor provides the Mono
type. Mono
is the Reactive equivalent of CompletableFuture
type, and allow to provide a consistent API for handling single and multiple elements in a Reactive way.
Mono.any(reactiveServiceA.findRecent(time), reactiveServiceB.findRecent(time)
.timeout(Duration.ofSeconds(3), errorHandler::fallback)
.doOnSuccess(r -> reactiveServiceC.incrementSuccess())
.consume(System.out::println);
If you have a deeper look to Flux
and Mono
, you will notice these types implement the Publisher
interface from the Reactive Streams specification.
Reactor is built on the Reactive Streams specification. Reactive Streams is composed of 4 simple Java interfaces (Publisher
, Subscriber
, Subscription
and Processor
), a textual specification and a TCK. It is the cornerstone of every modern Reactive library and a must have for interoperability purpose.
The core concern of Reactive Streams is handling backpressure. In a nutshell, backpressure is a mechanism that permits a receiver to ask how much data it wants to receive from the emitter. It allows:
Long.MAX_VALUE
elementsAt first glance, the Publisher
interface seems deceivingly simple to implement; but doing so in complete conformance with the specification turns out to be pretty hard, and users can't do anything with raw Publisher
except subscribing to it! That’s why it’s typically a better idea to rely on a Reactive Streams implementation, such as Reactor, to help you out with this.
Note that Java 9 will include the Reactive Streams interfaces in the java.util.concurrent.Flow
container class, further showing the relevance of Reactive Streams within the JDK.
It is also important to notice that convergence toward Reactive Streams and Reactor conversion capabilities allow easy and efficient conversion from one Reactive type to another at runtime.
I hope this blog post will help you to have a better understanding of Reactive types.
We are working on Reactive support with types like Reactor Mono
and Flux
in various Spring projects like Spring Framework, Spring Boot, Spring Data, Spring Security and Spring Cloud.
But your upcoming Reactive application will also use directly these types too, for example at @Repository
, @Service
or @Controller
methods level, because building a Reactive application means using Reactive semantics where you have to deal with latency or streams (we will also provide some guidance to integrate blocking API).
We will post additional Reactive blog posts in the upcoming months. Feel free to familiarize yourself with this test-driven Lite Rx API Hands-On that will teach you how to use Flux
and Mono
, and as usual your feedbacks are welcome!
If you happen to be in Barcelona mid May (never a bad time to be in Barcelona anyway!), don’t miss the chance to join the Spring I/O conference. Also, the registration for SpringOne Platform (early August, Las Vegas) has opened recently, in case you want to benefit from early bird ticket pricing.