The single biggest problem in communication is the illusion that it has taken place.
This astute observation is attributed to the great playwright George Bernard Shaw (although there is some ambiguity there, as far as this post is concerned, Shaw it is.) I don’t think I need to explain what Shaw meant. People with kids, with significant others – you know what I’m talking about, our conversations are, for the most part , futile one way illusory tracks!
Human communication skills haven’t improved much from Shaw’s time. In fact I’ll go so far as to say that we have regressed if anything. Isn’t it ironic though, given that we are more connected today than at any other point of time in history? As an aside, I wonder what he would have to say if he were to see us all communicating virtually, bent over our devices addictively, the most successful illusionistic invention by humankind to date. Now that would be an interesting conversation to have!
There is no easy fix for this problem in the human realm, greater beings have tried and failed. That’s why in this post I’ll stick to something far far easier – the illusion of communication in the computer realm and how asynchronous style can help mitigate that illusion.
Illusion lifted – Asynchronous Communication
The opening quote describes so succinctly, the mindset that distributed synchronous communication encourages among developers – one of ignoring the network fallacies. We can conveniently fall into the trap of believing that synchronous calls over the network are the same as calls within a process and that they always succeed. It all works well until it doesn’t and then fails spectacularly.
A strength of Asynchronous style that is often overlooked, is that it brings these network fallacies or the constraints to the forefront and thus can be addressed as part of the original design rather than as patching errors after the fact. Asynchronous style thus results in a much more resilient and robust system – which is especially important if you are building microservices and/or a reactive system.
Definition and basics of asynchronous communication
Let’s briefly look at the basics of asynchronous communication. In asynchronous style, a sending process sends a message (a piece of data) to a particular destination. This destination is typically identified by a virtual address. The message is sent asynchronously in that the sender, once it sends the message, continues with its execution without blocking in waiting for a response.
Asynchronous communication is achieved by way of messaging. Messaging is a technology that enables high-speed, asynchronous, program-to-program communication with reliable delivery (Source – Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions). A messaging system is the infrastructure component that enables messaging. It is important to note that messaging supports reliable delivery – by that I mean that the messaging system stores and forwards the messages on. If forwarding fails then it attempts retries until the message reaches its intended destination. Thus the ‘store and forward‘ is the primary role of the messaging system. The philosophy adopted by the microservices architecture in choosing a messaging system is that of having ‘smart endpoints and dumb pipes’. This is one of the lessons learnt from the SOA era. Heavy weight, overloaded enterprise service buses were used for integrating services in SOA architectures. That didn’t work out so well and hence the dump piping now. What it means is that the messaging system ought be lightweight and only truly focus on transmitting messages. All of the service interaction logic and control stays within the microservices boundary.
Why choose Asynchronous style ?
Another way of looking at asynchronous messaging is to think of the temporal decoupling that is created – the message will be processed by a receiver at some later point in time. The sender has no relation whatsoever in real time as to when this occurs or by who the request will be processed, if at all. This is why asynchronous communication is also called the ‘fire and forget’ approach. Once the message is out of the sender’s scope, the sender has no say in how, when or by who it will be processed further.
Using virtual addresses allows us to achieve spatial decoupling which allows individual services to be mobile. This is also known as Location transparency and is a necessary feature to achieve elasticity so that the system can scale resources dynamically as per the load.
So the fact that we have achieved both temporal and spatial (by way of virtual addressing) decoupling allows for greater freedom in implementing a service independently even when it interacts with others. Note that in asynchronous style, the service has to just connect to the messaging system irrespective of how many services it interacts with. Contrast that to a synchronous style where a service will have to connect to all the other services it interacts with.
Asynchronous style also allows dynamic changes in the topology. In addition to supporting one to one communication, it can also support one to many communications. This can be achieved by using a publish-subscribe mechanism. More on this when I get to event-driven systems.
Messaging supports reliable delivery unlike in synchronous calls. This takes some of the burden off the services themselves in handling failure scenarios. It can also apply flow control measures such as throttling in order to handle situations when the receiver is unable to cope with the incoming messages. All of this makes for a more robust system.
What I’ve covered here is not an exhaustive list, but a few of the important ones that I could think of for microservices architecture. For more look at the Further Readings below.
Considerations before taking the plunge
Although asynchronous messaging offers numerous benefits and seems a natural fit for microservices, there are a few points to consider before diving into it. It is definitely a more complex and unfamiliar style of programming. It is also harder to test and debug than simple synchronous calls. The indeterminism introduced by temporal decoupling has to be handled in the service making the code more complex. Messages can arrive out of order and if the business logic needs ordering then it has to be handled in the services themselves or in the messaging system. For business processes spanning several services there has to be way to co-ordinate all the messaging and that is a non-trivial task with asynchronous calls. Careful thought and design has to be done upfront to hash out all the interactions.
If done well, then the benefits that asynchronous style confers is vastly superior to that of synchronous calls – especially for complex domains like microservices.
This article is part of the Microservices series. You can the previous ones here : Prelude, Introduction, Evolution, Guiding Principles, Ubiquitous Language, Bounded Contexts, Communication Part 1, Communication Part2.
The Enterprise Integration patterns website for the book. Contains more updated information.
Reactive Systems advocate asynchronous style and a good place to read all about it : Reactive Manifesto