Reactive programming is currently a trend topic. This is mainly because of the benefits to implementing software using this new paradigm. Spring Framework 5.0 included numerous changes to give the advantage of this programming model and many new components of the Spring family have evolved to support it. In fact new Spring libraries have been created to add additional support to applications interested in what is called the reactive revolution. Additionally, Spring has rewritten the core of the framework, using reactive technologies that will allow a better technology for the applications that use them. In this section, we will understand the basics and principles of reactive programming and how we could apply it to create reactive microservices.
Reactive microservices
Reactive programming
We are quite familiar with imperative programming: in our software, we ask to do something and expect a result and meanwhile, we wait, our action is blocked expecting a result. Consider this small pseudo code as an example:
var someVariable = getData()
print(someVariable)
In this couple of instructions, we will set the content of a variable from the output of a function that will return data; when the data is available, we will print it out. The really important part of this small piece of code is that our program stops until we completely get the data, this is what is called a blocking operation.
There is a way to bypass this limitation that has been used extensively in the past. For example, we could create a separate thread to get our data, but effectively, that thread will be blocked until completion, and if different requests need to handle this, we end up in the situation of creating a thread for each one, possibly using a pool, but finally we will reach a limit of how many of those threads we can handle. This is how most traditional applications work, but now, this could be improved.
Let's see some pseudo code for this in reactive programming:
subscribe(::getData).whenDone(::print)
What we are trying to do here is to subscribe to an operation and when that operation is complete, send the result to another operation. In this example, when we get the data, we will print the results; the important part of this is that after that sentence our program continues, so it could process other things; this is what is called a non-blocking operation. But this could be applied not just to a single result, we could subscribe into a reactive stream of data, and when the stream starts to flow, it will be calling our function that will be progressively printing the data that we receive.
This new programming model allows us to have high-performance applications to process way more requests than in a more traditional blocking model. This approach utilizes resources more effectively and this could reduce the amount of infrastructure required for our applications. But now we need to understand what are the real principles of reactive programming.
Reactive Manifesto
In 2013, a working group of experts from some of the biggest software companies in the world published the Reactive Manifesto that set the basis of how reactive systems are understood and work, this manifesto is available on https://github.com/reactivemanifesto/reactivemanifesto.
Let's review what the Manifesto said:
First, the Manifesto introduces the current landscape of modern applications, focusing on how this demands a new kind of system that needs to respond to way more data and faster than before, and has to be scalable, resilient and fault tolerant. The intention of the Manifesto is to have a coherent approach to those problems and define reactive systems and what benefits we get from them. Many of those topics were discussed in our Microservices principles section, so it probably is a good idea to review them, but now we need to do a deep dive of how reactive systems are defined in the Manifesto.
Responsive
Modern applications should respond in a timely manner, but not only to the users of the system but also to the responsiveness of our problems and errors; we are long way away from those days where our applications would freeze when something was either taking longer to answer or failing for unknown reasons.
We need to help the users have a seamless and predictable experience so they could progressively work in our system, and doing so with consistent quality, so they will be encouraged to use the system and remove our past stigma with randomized user experiences.
Resilient
We cover much of this topic under our build for failure and isolation principles, but the Manifesto indicates as well that if we fail to have resilience, we tend to affect our responsiveness, something that we should handle.
Some of these issues could be handled, as well as applying correctly our scalability principle, since we could archive resilience by replication and replication, depends on our scalability.
Elastic
Reactive systems should be elastic, so they effectively apply the scalability principle to stay responsive under varying workloads, but more internally, the system itself may have the capability of increasing or decreasing the resources that allocate.
In the older architecture, planning resources was part of our architecture; we design thread pools to handle our request with certain capacity, and we prepare our servers to be able to manage this.
In reactive systems, our services could dynamically fetch more resources if required and free them when they are not needed.
Message-driven
Reactive systems use asynchronous messaging to flow information through the different components with very loosely coupling, that allows us to interconnect those systems in isolation. We could think of this as if we are connecting streams through pipes, one service could subscribe to another to get some information and the second service could be subscribed to a couple of additional services to combine the data and return it back to the original service.
Each of those services does not know why or how that information is used, so they have little information about the dependencies. This allows us to replace those pieces easily, but as well as handling errors in case of failure, we could simply just create a stream of errors with other receivers that will handle and process them.
But the manifesto speaks about applying back pressure; we need to understand that concept further.
Back pressure
Back pressure is produced when a reactive system is published at a rate higher than the subscriber could handle, in other words, this is how a consumer of a reactive service says: Please, I am not able to deal with the demand at the moment, stop sending data and do not waste resources (for example, buffer memory).
There is a range of mechanisms for handling this, and they are usually close to the reactive implementation, from batching the messages to dropping them, but right now, we don't need to get into the details, just understand that any reactive system must deal with back pressure.
Reactive frameworks
There are several reactive frameworks that we could use to create reactive applications.
Let's list the more important frameworks:
- Reactive Extensions (ReactiveX or Rx)
- Project Reactor
- Java Reactive Streams
- Akka
Reactive Extensions
Reactive Extensions is probably one of the most popular frameworks to create reactive systems and support a wider set of platforms and programming languages, from JavaScript using RxJS, to Java using RxJava or even in .Net platforms using Rx.Net.
It uses the observable pattern to perform no blocking operations; most of the major reactive systems have been built using Rx.
More details can be found at: http://reactivex.io/.
Project Reactor
Project Reactor is a JVM reactive library that follows the reactive streams specification and provides a high-level library to easily create reactive applications. Spring Framework 5.0 uses Project Reactor extensively.
More details can be found at https://projectreactor.io/.
Java reactive streams
Since Java 9, we now have an implementation of reactive streams in the Java platform, some projects are migrating existing Rx code into the new Java 9 libraries.
More details can be found in: https://community.oracle.com/docs/DOC-1006738.
Akka
Akka was created by Jonas Bonér, one of the main authors of the Reactive Manifesto, to create a toolkit in the JVM, using Scala, to create concurrent and distributed applications. Akka emphasizes in the actor-base model and has been proven to support high scalable distributed applications.
More details can be found in: https://akka.io/.
Reactive microservices
Now that we have a better understanding of reactive systems, we need to consider why we should create reactive microservices. If we look at microservices and remember what drove SoA into microservices, we could view what we need to create more complex applications and produce a better system for our users driving the architecture. With the new reactive programming model, we could create fast and non-blocking software that will utilize better the resources of our infrastructure. We could provide better responsiveness, and we could simplify our development to create highly reusable services that could be connected loosely with each other. And considering how aligned the reactive systems are with our principles and the extensive support of frameworks that they have, we conclude that the way forward for modern microservices is to become reactive.
We will explore more of this topic in Chapter 4, Creating Reactive Microservices.