Friday, June 21, 2019

Basics of Reactive Programming & Project Reactor

 


Objective: 
The objective of this tutorial is to cover the basics of Reactive Programming. The topics that we discuss in the tutorial are:

  • Why Reactive Programming?
  • What is Reactive Programming?
  • Concepts in Reactive Programming
  • What is Reactive Stream Specification?
  • What are the Reactive Libraries?
  • Project Reactor


1. Why Reactive Programming?
Let's discuss why Reactive programming is required first. Let's go back to the evolution of the programming, where 10 to 15 years back, the applications were Monolith, run in Application Server and does not embrace distributed systems. Today the modern applications are developed as Microservices, run in the cloud, embraces distributed systems. In today's world, the expectation of the application are - Scale based on the load, use resources efficiently, latency or response time should be faster.

The following are the draw-backs in the traditional development of REST API's:

a) Thread Per Request model:
The below is the traditional Spring-based application where the Embedded Tomcat (which acts as Server) has in-built Thread pool which will trigger various threads for every client call to an external database. This mode is called "Thread Per Request" model, where each thread in this process takes some memory and the common stack size is 1 MB. Also higher the thread pool size, higher the memory consumption. Applications really perform poor with less memory available.


b) Imperative Programming, Blocking & synchronous:



If you observe the above code, the first a DB call is made (proceRepository.finById(id) method call), which is blocking and then a rest API call is made (restTemplate.getForEntity() method call) and then Item is built. If you look at the code, it is in sequential mode and it is imperative style API - where the flow is Top-Down approach. So in summary, the above code is blocking and synchronous.


c) No Backpressure



Let's imagine the getAllItems() is trying to fetch a huge set of the records, then the application will crash with "Out of memory" error. Also, the client will be overwhelmed with huge data. It would be nice if there is a possibility for the client to say "Slow Down" by applying backpressure, then things will slow down momentarily and will pick up from where they left. So in summary, there is no back pressure possibility for the client to say slow down.

d) Lot of effort required to make the logic asynchronous
 It is possible to make the logic asyncronous in Java with the following concepts, but they have their own limitations.

Callbacks
  • Complex
  • No Return Value
  • Code is hard to read and maintain
Futures
  • Returns Future instance
  • Hard to compose multiple async operations
Completable Future
  • Introduced in Java 8
  • Supports functional style API
  • Not a great fit asynchronous call with multiple items
2. What is Reactive Programming?

The term, “reactive,” refers to programming models that are built around reacting to changes. It is a new programming paradigm, built around publisher-subscriber pattern (observer pattern), asynchronous and Non Blocking, where the Data flows as an Event/Message Driven stream, Functional Style code (not Imperative) and Back Pressure on Data Streams.

3. Data flow as Event Driven Stream in Reactive Programming
Let's discuss in detail about this topic. In Reactive programming, there will always one event or message for every result item from Data Source. The possible Data sources are Database, External File Service, File etc. There will be always one Event or Message emitted for completion or error. Following are the 3 methods that will form the life cycle:

OnNext(item) – Data Stream Events

OnComplete() – Completion or Success Event

OnError() – Error Event

Let's discuss various flows in the process.

a) Happy path - where there is no Error:

In Reactive world, the call to getAllItems() will invoke the data from Database, but the response call will be returned immediately to the client, whereas the data will be sent one after the other using onNext(Item) call. Once all the data is pushed to the client (via onNext(Item)), the onComplete() method will be invoked to say that the transaction is completed.

b) Error case:

Incase of any error during while passing the data to the client in onNext(Item) method call, the onError() method will be triggered and will communicate the client about the error.

c) No-data flow:

Incase of no data found, then the transaction will be made complete with onComplete() call.



4. What is Reactive Streams Specification?

The new Reactive Streams Specification is the specification or Rules for Reactive Streams. It was created by engineers from Netflix, Pivotal, Lightbend, RedHat, Twitter, and Oracle, among others and is now part of Java 9. It defines four interfaces:

a) Publisher: Emits a sequence of events to subscribers according to the demand received from its subscribers. A publisher can serve multiple subscribers.
It has a single method:

Publisher.java
public interface Publisher<T>
{
    public void subscribe(Subscriber<? super T> s);
}


b) Subscriber: Receives and processes events emitted by a Publisher. Please note that no notifications will be received until Subscription#request(long) is called to signal the demand.

It has four methods to handle various kind of responses received.


Subscriber.java
public interface Subscriber<T>
{
    public void onSubscribe(Subscription s);
    public void onNext(T t);
    public void onError(Throwable t);
    public void onComplete();
}
c) Subscription: Defines a one-to-one relationship between a Publisher and a Subscriber. It can only be used once by a single Subscriber. It is used to both signal desire for data and cancel demand (and allow resource cleanup).

Subscription.java
public interface Subscription<T>
{
    public void request(long n);
    public void cancel();
}

d) Processor: Represents a processing stage consisting of both a Subscriber and a Publisher and obeys the contracts of both.

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

Reference links:
https://github.com/reactive-streams/reactive-streams-jvm
https://www.reactive-streams.org/

5. What is Reactive Library?
Reactive Library is the implementation of the above Reactive Stream Specification. The following are the 3 popular Reactive libraries in Java.
The following are the 3 popular libraries used in Project Reactor:
a) Reactor-core
b) Reactor-test
c) Reactor-netty

a) Reactor-core:
It is the core library for Project Reactor, implementation of Reactive Stream Specification and it requires Java 8 minimum.
The following are the various Reactor Types of Project Reactor:
- Flux: Represents 0 to N elements
- Mono: Represents 0 to 1 element

Reference links:
https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html
https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html


Flux:
Flux.just("A""B""C").
          .map(s -> s.concat("flux"))
          .subscribe(System.out::println);


The above example where Flux is accepting 3 elements (A, B and C) and it is using map() function to concat "flux" so that subscribe() method will the output as:
Aflux
Bflux
Cflux


Mono:

Mono.just("A").
          .map(s -> s.concat("mono"))
          .subscribe(System.out::println);
The above example where Mono is accepting 1 element (A) and it is using map() function to concat "flux" so that subscribe() method will the output as:
Amono

No comments:

Post a Comment