Showing posts with label Reactive Programming. Show all posts
Showing posts with label Reactive Programming. Show all posts

Sunday, December 15, 2019

Spring Webflux with MongoDB

Objective:

The objective of this tutorial is to develop a sample MonogoDB application using Spring Webflux.

Pre-requisites:

The following softwares are to be installed on your machine for this code to work:

  • JDK 11 or Open JDK 11
  • Maven

GitHub links:

The source code covered in this tutorial is available in the following link in GitHub.

Steps:

1. Go to https://start.spring.io/ and enter the following details (as shown in the screenshot below).
Group: com.narayan.reactive
Artifact: reactive-mongo-sample
Java: 11 (under Options)
Dependencies: Spring Data Reactive MongoDB, Spring Reactive Web
and click "Generate Project" button.





2. Now import the project into any IDE like IntelliJ OR Eclipse etc. Once the project is imported, open the pom.xml to make sure that the following dependencies are already listed.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

spring-boot-starter-webflux is to bring all the Spring Webflux library dependencies and 
spring-boot-starter-data-mongodb-reactive is to bring all the Reactive MongoDB dependencies

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>

Additionally add the Lombok dependency to auto-generate the getters and setters for model classes.

Model Classes:
3. Create a new package called "model" under "com.narayan.reactive.reactivemongosample" and create a new class called Employee and EmployeeEvent

4. Employee.java - is the model we want to persist to Mongo DB


Employee Model class has id, name, and salary which we want to persist in MongoDB. The following are the various annotations used in this Model class:

@Document (line 12)- This annotation marks a class as being a domain object that we want to persist to the Mongo database
@Id (line 15)- This annotation specifies the primary key of an entity

Lombok Annotations:
@AllArgsConstructor (line 9) - This annotation will generate a constructor with 1 parameter for each field in your class. 
@NoArgsConstructor (line 10) - This annotation will generate a constructor with no parameters
@Data (line 11) -  This annotation will  generate all the boilerplate that is normally associated with simple POJOs (Plain Old Java Objects) and beans - like getters, setters, appropriate toString, equals and hashCode implementations 

 5. EmployeeEvent,java is an event class which we want to use it for Event Stream, which will have the Employee object and the Date time. This will be used in Flux to generate the event stream, which will be used in /


Repository Class:
7. Now create a new package called "repository" and create a new Java class called EmployeeRepository.java, which will act as Mongo DB Repository interface which will extend ReactiveMongoRepository.java with parameters as Employee and String.


Main Class:
8. This class will insert 4 records during Server Startup. This class has @Bean annotation with CommandLineRunner return type which indicates that the logic inside this method will be executed first during ServerStartup. This method will delete all the records from MongoDB using deleteAll() method, followed by subscribe() method, which in turn create a Stream of 4 employees and save them to the database using save() method and also print the same in subscribe() method. Note that in Webflux, everything is lazy loading, so everything the operator in the reactive chain will be invoked by subscribe() method.


Rest Controller Class:
9. Let's create a new package called "resource" and create a new Java class called EmployeeResource.java, which will act as Rest Controller class.

@RestController (line 21) - This annotation is used to indicate this is Rest Controller for Web 
Service.
@RequestMapping("/rest/employee") - line 22 - This annotation is used to map HTTP requests to handler methods of MVC and REST controllers
@RequiredArgsConstructor - line 23 - This annotation is used to generate a constructor with required arguments, which is applied for EmployeeRepository (in line 26)




lines 28 to 31 - @GetMapping("/all") - This annotation is used to map HTTP GET requests onto specific handler methods. This method will fetch all the records saved in the database using employeeRepository.findAll() method. The return type of this method is of type Flux<Employee> which indicates that there are more than one record will be received.

line 33 to 36 - @GetMapping("/{id}") - will accept id as input and assign it to @PathVariable - which will fetch the specific record matching for the input id using employeeRepository.findById(id) method. The return type of this method is of type Mono<Employee> which indicates that there is only one record will be received.

line 38 to 48 - @GetMapping(value = "/{id}/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE) -  is an event stream API, which will push records continously as an event stream, which is indicated by produces = MediaType.TEXT_EVENT_STREAM_VALUE. This method will receive the id as input, tries to find that record in the database by using employeeRepository.findById(empId) method. Once the record is found, it will apply flatMapMany() operator which will generate two Flux values - where 1st Flux value will generate the timer logic to generate a stream for every 2 seconds and other Flux value will generate EmployeeEvent model class. Both these Flux values are merged together will Flux.zip() and map operator is applied to get the 2nd flux value.



10. docker-compose.yml:
This yaml file will have instructions to pull the monodb docker image (in line 5) and to run on port 27017 (in line 8). Inorder to run the docker-compose, open command prompt and go to the root directory path (/reactive-mongo-sample) and run the following command, which will pull the MongoDB docker image and run the same

docker-compose up


Steps to run the application and see the output:
Once the MongoDB is up and running, follow the below steps to run the application and see the output. 
11. Open another Command prompt and go to the root directory path (/reactive-mongo-sample) and run the following command, which will start the application.

mvnw clean install && java -jar target/reactive-mongo-sample-0.0.1-SNAPSHOT.jar

12. As part of the output of the above command, if everything is working correctly, you should finally see the following output in the console, which indicate that 4 sample records are successfully inserted to MongoDB during Server start up

...
...
Employee(id=6b24f202-180b-4729-8660-1b305fe678f4, name=Aditya, salary=40000)
Employee(id=6e69194f-072b-4399-8381-fa85a9468274, name=Raghu, salary=25000)
Employee(id=91e34aa0-4801-468e-8e9f-b62b5ac18576, name=Praveen, salary=30000)
Employee(id=66a9a625-77fd-4c71-b981-19b3a0f29a6a, name=Shyam, salary=35000)


13. Now open the Browser (or any rest client like Postman) and invoke the following URL which will fetch all the 4 employees (as shown in the screenshot below) that were inserted during Server startup:

http://localhost:8080/rest/employee/all


14. Copy any id value from the above output (in my case, I copied 6e69194f-072b-4399-8381-fa85a9468274). Now open another Browser (or any rest client like Postman) and hit the following URL with the copied id appended at the end of the URL (in my case, I copied 6e69194f-072b-4399-8381-fa85a9468274), which will fetch only one employee detail (as shown in the screenshot below):

http://localhost:8080/rest/employee/6e69194f-072b-4399-8381-fa85a9468274

15. Now open Browser (or any rest client like Postman) and hit the following URL with /events appended at the end of the URL, which will fetch continuous stream of text events (as shown in the screenshot below):
http://localhost:8080/rest/employee/6e69194f-072b-4399-8381-fa85a9468274/events


16. That's all. Hope you learned something from this.

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