Grasping Reactive Programming in Java: A Basic Guide with Reactor
The Reactor is an implementation of the reactive programming paradigm.
Reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change. This means that it becomes possible to express static (e.g. arrays) or dynamic (e.g. event emitters) data streams with ease via the employed programming language(s).
Over time, a standardization for Java emerged through the Reactive Streams initiative, which is a specification that outlines a set of interfaces and interaction guidelines for reactive libraries on the JVM. These interfaces have been incorporated into Java 9 under the
In reactive streams, there is a
Publisher-Subscriber pair. The
Publisher notifies the
Subscriber of newly available values as they arrive, with this push aspect being crucial to the reactive nature. Additionally, operations applied to pushed values are expressed declaratively rather than imperatively: the programmer conveys the logic of the computation rather than detailing its precise control flow. We will soon look at the communication between
Why do we need an asynchronous reactive library in the first place?
Modern applications often cater to vast numbers of concurrent users, and despite the continuous advancements in modern hardware, software performance remains a critical concern.
There are generally two methods to enhance a program's performance:
1. Parallelize to utilize more threads and additional hardware resources. 2. Pursue greater efficiency in the current resource usage.
Usually, Java developers create programs with blocking code. This works fine until there's a performance problem. Then, they add more threads with similar blocking code. But, using more resources can quickly cause conflicts and issues with multiple users.
Additionally, blocking code wastes resources by causing potentially numerous idle threads to wait for data during latency-prone operations like database requests or network calls.
The parallelization approach, although crucial for maximizing hardware potential, can be complex to comprehend and may result in resource wastage due to contention and concurrency issues.
From Imperative to Reactive Programming
There are two types of coding styles:
Imperative: This involves a sequential series of tasks, with each task running one at a time, following the completion of the previous task. Data is processed in large quantities and cannot be passed on to the next task until the previous task has finished processing the entire dataset.
Reactive: In this coding style, a set of tasks is defined to process data, and these tasks can run concurrently. Each task processes subsets of the data, passing it on to the next task in the sequence while continuing to work on another subset of the data.
Publisher & Subscriber Communication
Step 1: Subscriber seeks to connect
Step 2: The Publisher calls the `
onSubscribe` method with an instance of Subscription
What is a Publisher?
Behind the scene:
onSubscribe method is invoked after calling
Publisher.subscribe(Subscriber). No data will start flowing until
Subscription.request(long) is invoked. It is the responsibility of this Subscriber instance to call
Subscription.request(long) whenever more data is wanted. The Publisher will send notifications only in response to
Step 3: A
Subscription is established between the
Subscriptionrepresents a one-to-one lifecycle of
Subscribersubscribing to a
Publisher. It can only be used once by a single
Subscriber. It is used to both signal the desire for data and cancel demand (and allow resource cleanup).
Subscription has two methods:
public void request(long n);
|No events will be sent by a
Publisher until demand is signaled via this method. It can be called however often and whenever needed—but if the outstanding cumulative demand ever becomes Long.MAX_VALUE or more, it may be treated by the
Publisher as "effectively unbounded". Whatever has been requested can be sent by the
Publisher so only signal the demand for what can be safely handled. A
Publisher can send less than is requested if the stream ends but then must emit either
public void cancel();
Publisher to stop sending data and clean up resources. Data may still be sent to meet previously signaled demand after calling cancel
Publisher pushes data via the
onNext method to the Subscriber
What does onNext do?
Subscription.request(long). Params: t – the element signaled
Step 5: Publisher finished supplying elements and calls
Successful terminal state. No further events will be sent even if the
Subscription.request(long) is invoked again.
Step 6: If there is an error it will send