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).
—
https://en.wikipedia.org/wiki/Reactive_programming
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 Flow
class.
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 Publisher-Subscriber
.
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 Subscription.request(long)
.Step 3: A Subscription
is established between the Subscriber
and Publisher
.
A
Subscription
represents a one-to-one lifecycle ofSubscriber
subscribing to aPublisher
. It can only be used once by a singleSubscriber
. It is used to both signal the desire for data and cancel demand (and allow resource cleanup).Subscription has two methods:
Methods | Description |
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 Subscriber.onError(Throwable) or Subscriber.onComplete() |
public void cancel(); | Request the Publisher to stop sending data and clean up resources. Data may still be sent to meet previously signaled demand after calling cancel |
Step 4: Publisher
pushes data via the onNext
method to the Subscriber
What does onNext do?
Subscription.request(long)
. Params: t – the element signaledStep 5: Publisher finished supplying elements and calls onComplete()
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 onError(Throwable t)
Subscribe to my newsletter
Read articles from Ish Mishra directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Ish Mishra
Ish Mishra
"Welcome to Bits8Byte. I'm Ish, a passionate software engineer with a deep love for technology and a knack for problem-solving. Through this blog, I aim to share my insights, experiences, and discoveries in the ever-evolving world of software development. Having worked in the industry for 9 years, I have had the opportunity to explore various programming languages, frameworks, and tools. I believe in continuous learning and strive to stay up-to-date with the latest industry trends and best practices. In this blog, you can expect to find practical tips, tutorials, and thought-provoking articles. I will also delve into the challenges faced in software development and share my insights on overcoming them. I encourage you to join the conversation by leaving comments, asking questions, and sharing your own experiences. Together, we can grow and inspire each other in our software development journeys.