Building Reactive REST APIs in Java: Part 1
Introduction
This article is focused on building RESTFul APIs using Java while benefiting from the reactive programming model. But unlike most other articles on this topic, this article would not rush to code straight away. It will guide you through the backbone of this programming paradigm to give you solid understanding of it. Then use that knowledge to build APIs.
This series consists of two parts. First part introduce reactive systems and reactive programming and clear the confusion between these terms.
Then it covers the fundamentals of reactive programming, and compares the traditional concurrency model with message/event driven concurrency.
Second part is about getting our hands dirty and building RESTFul APIs using Spring WebFlux and introduce reader to the fourth generation reactive framework.
Reactive Systems
Reactive system architecture is an architectural style to build responsive systems. This is defined in the reactive manifesto, we will go through each of the key items in the manifesto briefly, while explaining their meaning using day-to-day system behaviour.
Reactive systems are responsive: The system stays responsive in a timely manner, delivers consistent quality of service.
This means systems behave in a consistent manner when the load is high and low. The result is users starting to build confidence in your systems and keep continue do business with the system.
Reactive systems are resilient: The system stays responsive in the face of failure.
This means the system has the capability to isolate failures, contained those and use replication if necessary to mitigate the failure and keep serving the users.
Things go wrong, quite frequently. But if we have a system which is resilient that is how it can become responsive.
Reactive systems are elastic: The system stays responsive under varying workload.
This means the systems can react to changes in the load. Again this links back to responsiveness, as you can see responsiveness can not be achieved without resiliency and elasticity.
Reactive systems are message driven: The system rely on asynchronous message passing. It passes failures as messages. And it applies back-pressure when necessary to control the message flow.
This means the system react to the events/messages and only consumes the resources while it is active.
Reactive systems and reactive programming are two different things. One is an architectural style and other is programming paradigm which can be used to achieve some characteristics of a reactive system, but not all.
Reactive Programming
Now, we have good understanding of reactive systems. It’s time for us to go bit deeper in to reactive programming concepts.
Reactive programming is a branch of asynchronous programming paradigm which allow information to drive the logic rather than rely on thread of execution
Now, this sounds like Wikipedia or some academic research paper. It throws term that seems bit scary.
In simple language ,
Reactive programming allow applications to be operate based on message/event happens in arbitrary time, rather than thread of execution driven.
Now you can see that the message driven and responsive characteristics can be benefited from reactive programming. But not all, to achieve all of it we need much more wider scope of tools. Those are not in the scope of this article.
Events Events Events..
Are we familier with these types of events ? Yes, we are. As we deal with real world software applications we do deal with these types of events. Here are some examples.
Click stream: Clicks happen in different time points on the time line and it is a sequence of events. There is no guarantee that it will be properly interval when it occurs.
Notifications of your news app: News items appear on your phone screen as push notification in arbitrary time. No one knows when breaking news happens.
HTTP responses from remote endpoints: Network have their own problems, like delays and connection failures , hence the responses arrive in arbitrary time intervals.
Reactive programming and events
The common factor of these types of events is that these events happen in arbitrary time hence if we have a thread of execution in our programs , those threads have to wait for these events to complete.
If the system has more clients who are waiting for results from these actions, then we need more threads to server clients.
This is where reactive programming stands out. Instead of waiting for these events to complete, it offers observer like mechanism where it let these events to drive execution.
Result is less number of threads and resources to process large number of these events.
The most important fact that we need to understand is that
Reactive programming does not make application faster, but it allows the application to serve more clients with less resource.
If we need to scale, we can scale out horizontally, (with less resources) which makes it is perfect paradigm to build modern microservices. That is how reactive programming reinforces the elastic characteristic of the reactive system.
Characteristics of Reactive Programming libraries.
Understanding the characteristics of reactive programming is the key to unlock possibilities.
The execution is asynchronous and non-blocking.
This means the calling thread does not block on I/O events and wait for it to be completed. We will discuss this in great details later in this article.
Non-blocking back-pressure
Non-blocking back pressure is a mechanism that allow the event subscriber to control the rate of event flow in a non-blocking way.
In a blocking scenario the blocking call it self act as a back pressure which forces publisher to wait until subscriber comes back.
This allows dealing smartly in the scenarios of slow publisher/ fast receiver and fast publisher/slow receiver.
Supports reactive streams.
An unbounded event/message stream with asynchronous passing of elements between components, with mandatory non-blocking bask-pressure.
Combining all above we can get a good picture of application developed using reactive programming principles.
These application support to handle unbounded number of events, event-driven and aware of the environment they are dealing with and can react to changes in these environment.
Why is it important , What is it good for, Still not clear ?
Let’s dive into deeper details and discuss.
I remember once I had a conversation about reactive programming with a fellow developer over a coffee, and his questions were.
“What are the benefits of using it ?”
“What are the advantages it brings to the table compared to what we use today ?”
And those are totally valid questions to ask about any new technology. To answer these we need to take step back and think about the tools we used to build applications using Java.
Web APIs written in Java usually get deployed to servlet containers like Tomcat, WebSphere etc.. These containers use Servlet API for their operations and those operations provides blocking I/O. Hence popular frameworks like Spring, Spring Web MVC also inherits this blocking behaviours.
The database operations are blocking I/O calls, JPA, JDBC both behave this way.
These blocking operation keep the request threads blocked until that operation get completed. Hence, more requests leads to more blocked threads waiting for the I/O operations to complete. Results are as follows.
- The system has to pay more CPU time for the context switching between thread.
- The system has to allocate more memory to support increasing number of threads and execution stacks of those threads.
- More memory means , more GC time and GC overhead on CPU.
It’s just not the I/O operations, common citizens of Java concurrent tool are also possess blocking behaviour.
- java.util.concurrent.Future, we can use Future to represent results of an asynchronous computation.
FutureTask<String> future =
new FutureTask<String>(new Callable<String>() {
public String call() {
return searcher.search(target);
}});
executor.execute(future);
But when you want the results we have to call
//Waits if necessary for the computation to complete, and then retrieves its result.String result = future.get();
2. synchronized methods also force the thread to stop and check before enter the logic block.
And if we take scenario by scenario, there are many more.
And the problem with the blocking I/O approach is that with blocking APIs we can not support the reactive systems. It is thread of execution driven, synchronous. These two reasons makes it heavy in resource consumption.
Next step would be to find a way to find more efficient way to execute operations asynchronous non- blocking manner.
Java 8 brought CompletableFuture which is truly asynchronous non- blocking capability for asynchronous programming. But if you want to compose more results and do stream processing the code becomes difficult to read and it lacks of fluent manipulation API.
The non-blocking approach
Java introduced java.nio package later to address this blocking behaviour by introducing a concept call Selector, which can monitor multiple channels.
This allows single thread to monitor many channels of input. And a key concept of loading data in to ByteBuffers instead of Streams which are blocking. As a result the ByteBuffers will have data as they available.
The following is a demonstration of a server using NIO capabilities. It is a simple implementation of an echo server using NIO implementation but a sufficient example to get the fundamentals of non-blocking approach.
The basic principle behind this approach is , Selector can register its interest in multiple channels and when those events occur, the main thread responds to those events by invoking matching processing logic.
The only blocking code in there is at the line #39
// get a channel from selector, this will block until a channel get selected
selector.select();
select() method blocks until a channel get selected. For an example until a new connection occurs.
The event loop
Above pattern is equal to what we call event loop in JavaScript world. Javascript is a single threaded run time, hence it has to find ways to support the multiple tasks while not having to create multiple threads.
[If you do not know what is event loop, there is a great talk by Philip Roberts at here]
And when NodeJS comes to the picture and start servicing heavy loads with less memory foot print and CPU time, Java community realised it is more scalable approach to this set of problems. And everyone knows multi threaded applications are hard to develop, hard to maintain.
Reactive libraries.
Now that we have a good understanding of synchronous blocking behaviour of old Java world and new approaches using event loops to be non-blocking, we can start getting in to reactive world.
First, Microsoft created reactive extension to .NET framework. And follow up by JavaScript the single thread , non-blocking , asynchronous language that has genuine needs for reactive library and hence RxJs came in to existence.
The javaScript implementation of reactive library is RxJS
Based on that RxJava implementation done by Netflix
You can find Rx library in most of the popular programming languages. The current libraries of the reactive programming provides following.
- Unbroken pipe line of non-blocking operations as data get available.
- Rich set-of operators to manipulate these event streams.
- Backpressure, ability to control the producer event emission rate.
- Ability to orchestrate multiple asynchronous tasks with good readability in the code.
For the purpose of the article, I have selected project Reactor to provide the introduction and answer common questions that every new developer go through when they start programming with reactive libraries.
The second part of this article, Building Reactive REST APIs in Java : Part 2 will guide you through developing reactive RESTFul API using Reactor.