Book Image

Hands-On Reactive Programming with Reactor

By : Rahul Sharma
Book Image

Hands-On Reactive Programming with Reactor

By: Rahul Sharma

Overview of this book

Reactor is an implementation of the Java 9 Reactive Streams specification, an API for asynchronous data processing. This specification is based on a reactive programming paradigm, enabling developers to build enterprise-grade, robust applications with reduced complexity and in less time. Hands-On Reactive Programming with Reactor shows you how Reactor works, as well as how to use it to develop reactive applications in Java. The book begins with the fundamentals of Reactor and the role it plays in building effective applications. You will learn how to build fully non-blocking applications and will later be guided by the Publisher and Subscriber APIs. You will gain an understanding how to use two reactive composable APIs, Flux and Mono, which are used extensively to implement Reactive Extensions. All of these components are combined using various operations to build a complete solution. In addition to this, you will get to grips with the Flow API and understand backpressure in order to control overruns. You will also study the use of Spring WebFlux, an extension of the Reactor framework for building microservices. By the end of the book, you will have gained enough confidence to build reactive and scalable microservices.
Table of Contents (13 chapters)

Reactive Streams

Reactive Streams is a specification that determines the minimum set of interfaces required to build the asynchronous processing of a large volume of unbounded data. It is a specification aimed at JVM and JavaScript runtime. The main goal of the Reactive Streams specification is to standardize the exchange of stream data across an asynchronous boundary of applications. The API consists of the following four interfaces:

  1. Publisher: The publisher is responsible for the generation of an unbounded number of asynchronous events and pushing those events to the associated subscribers.

  2. Subscriber: The subscriber is a consumer of the events published by a publisher. The subscriber gets events for subscription, data, completion, and error. It can choose to perform actions on any of them.

  3. Subscription: A subscription is a shared context between the publisher and subscriber, for the purpose of mediating the data exchange between the two. The subscription is available with the subscriber only, and enables it to control the flow of events from the publisher. The subscription becomes invalid if there is an error or a completion. A subscriber can also cancel the subscriptions, in order to close its stream.

  4. Processor: The processor represents a stage of data processing between a subscriber and a publisher. Consequently, it is bound by both of them. The processor has to obey the contract between the publisher and the subscriber. If there is an error, it must propagate it back to the subscriber.

The Reactive Streams specification is the result of a collaborative effort of engineers from Kaazing, Netflix, Pivotal, Red Hat, Twitter, Typesafe, and many other companies.

While there are only four interfaces, there are around 30 rules that govern the data exchange between the publisher and the subscriber. These rules are based on the two principles covered in the following sections.

Asynchronous processing

Asynchronous execution refers to the ability to execute tasks without having to wait to finish previously executed tasks first. The execution model decouples tasks, so that each of them can be performed simultaneously, utilizing the available hardware.

The Reactive Streams API delivers events in an asynchronous manner. A publisher can generate event data in a synchronous blocking manner. On the other hand, each of the on-event handlers can process the events in a synchronously blocking manner. However, event publishing must occur asynchronously. It must not be blocked by the subscriber while processing events.

Subscriber backpressure

A subscriber can control events in its queue to avoid any overruns. It can also request more events if there is additional capacity. Backpressure enforces the publisher to bound the event queues according to the subscriber. Furthermore, a subscriber can ask to receive one element at a time, building a stop-and-wait protocol. It can also ask for multiple elements. On the other hand, a publisher can apply the appropriate buffers to hold non-delivered events, or it can just start to drop events if the production rate is more than the consumption rate.

It is important to note that the Reactive Streams API is aimed at the flow of events between different systems. Unlike ReactiveX, it does not provide any operators to perform transformations. The API has been adopted as a part of the java.util.concurrent.Flow package in JDK 9.

David Karnok's classification

David Karnok, a veteran of various reactive projects like Rxjava and Reactor, has categorized the evolution of reactive libraries into the following generations.

Zero generation

The zero generation revolves around the java.util.observable interface and the related callbacks. It essentially uses the observable design pattern for reactive development. It lacks the necessary support of composition, operators, and backpressure.

First generation

The first generation represents Erik Mejer's attempt to address reactive issues by building Rx.NET. This referred to implementations in the form of the IObserver and IObservable interfaces. The overall design was synchronous and lacked backpressure.

Second generation

The first generation deficiencies of backpressure and synchronous handling were handled in the second generation APIs. This generation refers to the first implementations of Reactive Extensions, such as RxJava 1.X and Akka.

Third generation

The third generation refers to the Reactive Streams specification, which enables library implementors to be compatible with each other and compose sequences, cancellations, and backpressure across boundaries. It also enables an end user to switch between implementations at their own will.

Fourth generation

The fourth generation refers to the fact that reactive operators can be combined in an external or internal fashion, leading to performance optimization. A fourth generation reactive API looks like a third generation, but internally, the operators have changed significantly to yield intended benefits. Reactor 3.0 and RxJava 2.x belong to this generation.

Fifth generation

The fifth generation refers to a future work, in which there will be a need for bidirectional reactive I/O operations over the streams.