Understanding Reactive types

Engineering | Sébastien Deleuze | April 19, 2016 | ...

Following 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.

Why using Reactive types?

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

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.

Lambdas, CompletableFuture and Stream

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

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.

Reactive Streams

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:

  • The receiver to start receiving data only when it is ready to process it
  • To control the inflight amount of data
  • Efficient handling of slow emitter/fast receiver or fast emitter/slow receiver use cases
  • To switch from a dynamic push-pull strategy to a push-based only strategy if you request Long.MAX_VALUE elements

At 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.

Conclusion

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.

Get the Spring newsletter

Thank you for your interest. Someone will get back to you shortly.

Get ahead

VMware offers training and certification to turbo-charge your progress.

Learn more

Get support

Tanzu Spring Runtime offers support and binaries for OpenJDK™, Spring, and Apache Tomcat® in one simple subscription.

Learn more

Upcoming events

Check out all the upcoming events in the Spring community.

View all