Have you ever wondered how modern, high profile web applications like: Netflix, Twitter, Google, Gmail and others are built ?
What are the fundamental principles and patterns these systems rely on, to operate at scale, speed and reliability levels that they do ?
In this article we will try to answer all the questions mentioned above and tackle the topic of “high profile web applications” also known as “reactive systems”.
- What does a “Reactive System” really mean ( in simple terms ) ?
- What is a “Reactive manifesto” ?
- What are the fundamental building blocks of a “Reactive System” ?
- “Whats” and “Hows” of a Responsiveness
- “Whats” and “Hows” of an Elasticity
- “Whats” and “Hows” of a Resiliency
- “Whats” and “Hows” of a Message driven
- Sum up
What a Reactive System really is ( in simple terms ) ?
If you are a really focused reader, by now you would have noticed, that the term “Reactive System” has a word “System” in it.
I’m sure that the wast majority of readers ( most of which i assume are software engineers ) are familiar with this word, but anyways, for the sake of holy gods of engineering, let us agree on the exact meaning of the term: “Software System” ( or just “System” ).
We can define a “System”/“Software System” as a set of software components ( programs ) that communicate, to execute predefined business logic on computer hardware.
Basically saying, it’s a set of contracts, API-s, specifications, Service Level Agreements and much more between clients and the business logic that needs to be served.
We can easily say that web applications like: Amazon, Google, Gmail, or even relatively smaller ones, like Learning Management System of your school or university, are “Software Systems” with client facing UI-s.
Wait a minute, one might say…
Is it fair to talk about Google level platform(s) and University LMS system(s) in a uniform manner ?
How can we categorise both kinds of software systems under one and the same umbrella term ? We know for fact, that the scale, load and requirements on these 2 types of systems are astronomically different ( let me remind you that, Google has 5.4 billion requests per day. I’m pretty sure that none of LMS systems in the world will come anywhere close to that number ).
To complement the idea of distinction between high and low profile systems term “Reactive system” was created.
“Reactive System” is a software system that:
1) Has all the properties of the regular software system
2) Is much more scalable, resilient, flexible and better positioned to meet modern demands on order of millions of users .
What is “Reactive Manifesto”?
All the features and properties of a high profile and scalable web applications, that we’ve defined in the paragraph above, have already been formalised in an official document called: “Reactive Manifesto”.
Few years ago a group of people decided that it was necessary to put together a few non-functional requirements they had learnt during their careers to be essential if you want to build good, scalable and high profile software — then they named this document the Reactive Manifesto.
If you visit the webpage of the manifesto, you’ll see that it’s written in a high-level, abstract ideas, in order to concentrate on it’s essence and not the actual implementation details.
What are the fundamental building blocks of a “Reactive System” ?
There are 4 fundamental building blocks, AKA 4 pillars, mentioned in reactive manifesto. These are 4 characteristics that a system should have to be considered Reactive:
- Responsive: The system responds in a timely manner if at all possible
- Elastic: The system stays responsive under varying workload
- Resilient: The system stays responsive in the face of failure
- Message driven: The System relies on asynchronous message-passing to establish a boundary between components that ensures loose coupling, isolation and location transparency
In the next several paragraphs we will deep dive into “Whats” and “Hows” of each and every of characteristic and try to understand main concepts and principles that underline them.
“Whats” and “Hows” of a Responsiveness
Responsiveness is the ability of a system to respond to each and every request in a timely manner and with reasonable data, even in the face of high load or failures.
I think we can all agree, that nowadays this characteristic is more important than ever. Modern day users anticipate very high standards from web applications. They expect near real time interaction, meaning that they won’t be sitting and waiting for a meaningful response to arrive and they’ll simply go to your competitors platforms. This might be devastating for your business, so responding to users in a timely manner becomes a critical task.
To fully grasp the idea of responsiveness, let’s take an example of 2 services on separate machines: “A” and “B”.
Let’s assume several points:
- they communicate over the network (see diagram No: 1 above).
- “A” is a client facing service, while “B” is for only internal usage ( meaning its API is not accessible to the public, only service “A” itself can consume exposed functionality of “B”).
For the first flow ( on the diagram above ) you see a successful flow when everything, including services and network works fine, so client is satisfied.
One the other hand, for the second flow you see that, after sending a request to “B”, “A” will be waiting for the response indefinitely, because of the network problems. This of course means, it won’t be responding to the client and that their session will be held idle. As a result client is unsatisfied.
This is the exact problem Responsiveness tries to target.
We, software engineers, from theory of computer networks and from our own experiences know, network failures will happen, so we’ll need to prepare for it.
One of the most common “Reactive” solutions, for this problem is “Circuit Braker” pattern. The idea is to instruct “A” to wait for “B” only for some predefined amount of time. After the timeout, trigger some alternative flow of execution ( which can be some predefined message for the client, or some other “fail over” service C which has similar logic as B, or returning some cached data ).
Of course, from the client side perspective, seeing something other than expected result, might be imperfect. like observing a text message: “Please try again later” instead of signing in or seeing a history of messages from your friend instead of a newly sent one, might not be ideal but the main principle is, to give a user something instead of nothing.
“Whats” and “Hows” of an Elasticity
Reactive systems stay up and running on varying workloads. Their ability to adapt to ever changing demands, by growing or shrinking the resource usage, is called “Elasticity”.
Modern day “Cloud Service Providers” like: AWS, GCP, Azure allow us to easily scale in/out our cpu, memory, and storage resources to adapt to the increased/decreased network traffic meaning with applying little to no configuration, you’ll have Elastic behaviour ready.
One of the most interesting details about Elasticity stated in “Reactive Manifesto” is “Reactive Systems should support predictive, as well as dynamic, scaling algorithms by providing relevant live performance measures”. It means that both strategies below are equally valid:
- predicting and pre-configuring our services for deterministic load increase ( something like “Usually our services have much higher load during X period of time” )
- dynamically reacting to non-deterministic load increase and scaling up ( Sudden rush of traffic to our services which couldn’t be pre determined )
For more clarity let’s take en example of a well known system of: “Amazon”-s Online shop ( See the picture below ). On “Black Friday” load on the platform increases dramatically ( Amazon has officially stated that they have somewhere around 60% increased number of requests on that day ). They should definitely monitor the traffic, once noticing the sudden increase of traffic some automatic mechanism ( called “traffic monitor” in the diagram ) will signal the system to adapt to this change via allocating more resources.
After Black Friday is over, on a regular shopping day, load is not that high so they these additionally allocated resources should be brought down automatically, to avoid a total waste of money and other resources.
In a nut shell, elasticity is the ability of software system to adapt to ever changing volume of requests by automatically allocating and later freeing up computing resources.
“Whats” and “Hows” of a Resiliency
The term resiliency means: the ability of a substance or object to spring back into shape, to recover quickly from failures.
To better understand what this term has to do with software systems, let us first discuss what are the potential failures, changing the “shape” of our system, that might appear in software world.
At some point in time in a small scale system we have all experienced failures like: unhandled exceptions, software bugs, hardware outages, human errors and etc, which caused the system to go down.
Compared to small scale systems, in large scale, distributed, systems failures happen.. guess how.. yep you guessed it.. on much larger scale and more frequently.
The reason for it are simply additional failure factors including: more machines, larger software/code base(s) ( thus more bugs ), network outages/delays between the communicating machines and etc.
As we have already stated above, modern business world needs high profile web applications to be always up and running. This fact together with the idea that different kinds of failures will eventually happen, means only one thing: The ability to stay “Responsive” in face of failures is MUST. This is exactly what resilient systems do.
The key notion here is to aim at fault tolerance instead of fault avoidance. It is of course good to plan ahead for many different failure scenarios to make system back up and running without users even noticing it, but it should be the behaviour for not foreseen failures too.
Resilience can be achieved by many different techniques like: replication, containment, redundancy, isolation and delegation. In this article we won’t deep dive into each of these topics, but they all serve one idea: be ready for potentially many kinds of failures, minimise chances that the system will fully fail and in case of partial failures, handle it gracefully, react to it and transition system back to it’s original form ASAP.
“Whats” and “Hows” of a Message driven
The final pillar of a Reactive System is being “Message driven”. This means having components/modules of application decoupled, isolated in separate os processes ( or even machines ) and to have the ability to consume the functionalities exposed by other components via sending asynchronous messages over some medium ( like network ), instead of direct method invocations.
To fully understand the principles of “message driven”, let’s go through a concrete example of both “message driven” and “non-message driven” types of communications.
Non-message driven / Direct method invocation
Let’s say we have two software components: A and B.
If both components are in the same operating system process, A will be able to communicate to B via a simple method invocation. New stack frame will be allocated for the method call and result of computation from B will simply be returned to the caller by being written at some predefined memory location.
Benefits of direct method invocation:
- Simple to reason about and implement
- Low latency, since the whole execution happens in the the same OS process and no additional network calls (or other external delaying factors ) are needed.
Drawbacks of direct method invocation:
The main drawback with this mechanism is tight coupling between the communicating components. Any kind of problem in B will significantly affect A and might even leave our whole system unusable. Which definitely opposes the principles of being Reactive. Since Reactive proposes being decoupled and resilient to failures in different components.
In distributed/reactive systems most of the components are taken apart into separate OS processes ( or even separate machines ). This means that we can no longer communicate from A to B using simple method calls and stack frame allocations. From now on, we’ll have to rely on network and some predefined protocols to deliver sequences of bytes ( also called messages ) from one computational component to the other.
Benefits of message driven systems:
- decoupled: “B” might fail any time without affecting “A”. “A” can still be up and running ready to serve other consumer messages, even in the case of the worst possible failure of “B”.
- location transparency: since most fundamental type of communication here is messaging, you HAVE TO implement message driven communication between “A” and “B”, even if they are on the same machine or even in the same Operating System process.
This kind of communication seems to be kind of over head for computational units located on the same machine ( since direct method invocation between them will be much, much faster ), but the real deal is that it gives us the ability to later separate this communicating components to different OS processes or even different machines without having to change the code base, by simply changing some configurations.
So basically saying: Message driven communication gives us location transparency, which is the ability to communicate to computational units in a location agnostic way.
In this article we tried to understand theory behind the principles of modern Reactive systems. What are the fundamental building blocks that they rely on.
In the upcoming articles we’ll talk more about practical part of this topic and we’ll go in the details of the multi lingual reactive framework called:” “Vert.x”.
Vert.x has module for each and every principle stated above and it makes building Reactive, High load application easy and fun.
Stay tuned and thanks for reading along!