Introduction to Java Reactive Programming

Introduction to Java Reactive Programming

What is Reactive Programming?

Reactive programming is a declarative programming paradigm which focuses on developing an event-driven and asynchronous application. In other words, this way of programming reacts to the events as and when required in a non-blocking fashion.

By definition,

Reactive programming is a programming paradigm where the focus is on developing asynchronous and non-blocking applications in an event-driven form.

Traditional Programming vs Reactive Programming

Fig.1. Traditional Programming (Thread-per-Request Programming)

Figure.1 describes the traditional way of programming,

  • Client sends a request to the application, which is hosted in a webserver.

  • On receiving the request (R1), the webserver assigns it to the thread (T1).

  • Thread T1 will be occupied with the request R1 until, R1 finishes the process. This process can be, calling a 3rd Party API or a database call and so on.

  • If the webserver gets another request (R2), before the completion of R1 then R2 will be assigned to an alternative available thread T2 as T1 is occupied with R1.

  • So, each thread will be occupied with its corresponding request until the process is completed. This imposes a limitation in handling the number of concurrent requests by a webserver based on its thread pool size.

  • Example: By default, Tomcat webserver has maximum pool size of 200. When 500 concurrent requests are sent to the server, Tomcat can handle 200 simultaneous request and the remaining 300 requests will get queued until a thread becomes available to process.

Fig.2. Reactive Programming (Event-Driven Programming)

  • Figure.2 shows the reactive way of programming in which the request comes to the application and the application’s webserver assign the request to an available thread say, T1.

  • Now, the thread T1 sends the request as an event to the database wherein T1 will inform the database about the requested action and gets free from the process.

  • In this case, T1 will not wait for the response from database instead it becomes available for processing the next request.

  • The database will then use the next available thread to publish its response.

The advantage in reactive programming, is the asynchronous and non-blocking way of execution i.e., threads will not be blocked for the process completion. Threads will accept the request, sends the event to the target destination, and becomes available for next processing. By this way, applications can use less resource to serve more requests.

Need for Reactive Programming

  • Reactive programming helps in building applications which adheres to the four principles of reactive system such as,

    • Event-driven: Following a non-blocking and asynchronous way of code execution. This way of execution leads to an efficient handling of concurrent requests which results in improved performance and responsiveness of the application.

    • Scalable: Using event-driven approach rather than thread-per-request.

    • Responsive: Able to react to the events or messages in a good and uniform means, under more load and less load.

    • Resilient: Being responsive even in case of failures like time-outs, system crash or exceptions etc. This is achieved by taking advantage of methods like onError(Throwable).

  • Reactive programming provides a stream-based processing model, where data flows in a continuous stream of events. This enables developers to easily compose and transform data in a declarative and concise manner, allowing for efficient data processing and manipulation.

Reactive Stream Specification

Reactive stream specification is a set of rules or specification that needs to be followed when building reactive code. Reactive specification has four interfaces such as,

  • Publisher

  • Subscriber

  • Subscription

  • Processor

Publisher:

Publisher interface publishes the event or data to the subscribed entities. The publisher interface has a single method, “subscribe()” which is used to register subscriber object.

public interface Publisher<T> {
    public void subscribe(Subscriber<? super T> s);
}
  • The subscribe method of the Publisher interface is a factory method and can be called multiple times. Each time of the method invocation, it creates new Subscription.

  • Each Subscription will work only for a single Subscriber.

  • A Subscriber should subscribe once to a single Publisher.

  • If the Publisher rejects the subscriber’s request or if there is any error during subscription, Publisher will send the error signal via Subscriber.OnError(Throwable).

Subscriber:

Subscriber subscribes to (or) consumes the event(s) published by Publisher. The Subscriber interface have four abstract methods,

onSubscribe(Subscription s):

  • This method will be invoked by Publisher after Subscriber calling the Publisher.subscribe(Subscriber s) method.

  • The Subscription object is passed from Publisher to Subscriber via method parameter.

  • Using Subscription object’s request method (Refer Subscription section for request method), Subscriber will request data from Publisher.

  • Publisher will send notification (or) event data to Subscriber on response to Subscription.request(long).

 public void onSubscribe(Subscription s);

onNext(T t):

  • This method will be invoked to publish the next data (iterate over data) from Publisher.
 public void onNext(T t);
  • onError(Throwable t): Failed Terminal State

    • This method will be called when there are any errors while publishing the data.

    • Publisher will not send any further events, after invoking this method.

public void onError(Throwable t);

onComplete(): Successful Terminal State

  • This method will be called by Publisher, once all the event/data is published successfully to the Subscriber.

  • Publisher will not send any further events after invoking this method.

public void onComplete();

Subscriber.class

public interface Subscriber<T> {
    public void onSubscribe(Subscription s);
    public void onNext(T t);
    public void onError(Throwable t);
    public void onComplete();
}

Subscription:

Subscription interface represents a unique relationship between Subscriber and Publisher. Subscriber subscribes to Publisher via Subscription object.

  • As discussed in “onSubscribe(Subscription s)” method, when the Subscriber invokes the subscribe() method, Publisher will send a Subscription object via onSubscribe method parameter.

  • This Subscription object is used to request the data and/or cancel the subscription with Publisher. To facilitate this functionality, Subscription interface provides two methods,

  • request(long n):

    • This method will be called by Subscriber to request data from Publisher.

    • Method parameter (n) indicates the requested amount of data from Publisher. It is used for back pressure (Refer section Back Pressure).

  • cancel():

    • The cancel method will be called by the Subscriber if it wants to end the subscription with Publisher i.e., request to stop sending the data and clean up the resources.
public interface Subscription {
    public void request(long n);
    public void cancel();
}

Processor:

  • Processor represents a processing stage i.e., it can act as both Publisher and Subscriber.

  • Processor stays in between Publisher and Subscriber. It consumes the message from Publisher, manipulates the message and send the processed message to its Subscribers.

  • Multiple Processors can be chained between Publisher and Subscriber.

Example:

  • From the above example, A1 and B1 are the two input cells. We want to perform, square of the sum of A1 and B1 value i.e., (A1+B1)2 .

  • C1 is performing summation of A1 and B1 and D1 is calculating the square of C1.

  • So, whenever A1 or B1 value gets updated, C1 also gets updated which in turn updates D1.

  • Here, C1 is the processor. C1 gets the data from Publisher (A1 and B1) manipulates the data (Sum of A1 and B1) and send the processed data to Subscriber (D1).

public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}

Back Pressure:

  • When Publisher publishes the data, its emitting rate might be higher than Subscriber’s processing rate.

  • In that case, Subscriber will send some feedback to the Publisher on the rate at which it would require the data. This mechanism is achieved through request method parameter n.

  • The back pressure can be understood with few real-time scenarios,

    • We keep the volume up (or) down based on the current volume while listening to music.

    • We speed up (or) slow down the speed of the YouTube videos based on the speaker’s speech rate.

Reactive Stream Specification Communication Flow:

Publisher and Subscriber Dataflow:

This section explains, how the requested items (Demand) from Subscriber is fulfilled by Publisher and what happens, if Publisher does not hold the requested number of items (Outstanding Demand) at the time of request.

  • If a subscriber makes a request for N items and no items are available with Publisher, the subscriber will wait until at least one item is available and is pushed to the subscriber.

  • Suppose a subscriber requests 10 items and 15 are currently available in the publisher. The outstanding demand for the subscriber is 10 so 10 of the 15 items are pushed to the subscriber.

  • The remaining 5 items are maintained by the publisher, awaiting the subscriber to request more items.

  • If the subscriber then requests 20 more items, the 5 remaining items are pushed to the subscriber, resulting in an outstanding demand of 15. If 5 more items become available in the publisher, these 5 items are pushed to the subscriber, leaving an outstanding demand of 10.

  • The outstanding demand will remain at 10 unless the subscriber requests N more items, in which case the outstanding demand will increase to 10 + N, (or) more K items are pushed to the subscriber, in which case the outstanding demand will decrease to 10 - K (to a minimum of 0).

Implementation of Reactive Specification Interfaces

Reactive library is the implementation of the above discussed reactive specification interfaces (Publisher, Subscriber, Subscription and Processor). Some of the reactive libraries available in the market is listed below.

References

We would be discussing the core concepts of Project Reactor which is one of the prominent reactive libraries and the method of their implementation in the upcoming articles.