I’m typing this from a sunny room, on a sunny day, to a cacophony of noises outside, a tiny speck somewhere on planet earth. At some point in time, you are reading this from the comfort of your home or office or perhaps in a car…just about anywhere, another tiny speck somewhere on this planet. The fact that we can have this exchange in anonymity, separated in time and space, doesn’t it all seem magical?
Given that you are reading a technology blog that might sound like a stretch, by virtue of the fact that you indeed know that there are no rabbits involved. What I am trying to get to is this:
Any sufficiently advanced technology is indistinguishable from magic.
That is a quote by Arthur C. Clarke and one of my favorite technology quotes – because I believe it to be an absolutely true adage regarding how technology should be designed. A stellar example of that is the World Wide Web, which is unarguably the most prominent and successful distributed system in the world. There are over a billion websites as of last count, with over 3 billion people using it all over the world. It is made of 1000’s of different server implementations and just as many different implementations of clients. It is in a constant state of flux. And yet it all works! To the uninitiated, it seems like nothing short of pure magic. What seems like magic is really due to the standard HTTP protocol and the various web artifacts working together, based on the Representational State Transfer (REST) philosophy.
HTTP/RESTful APIs for Microservices
Given the success of RESTful services over HTTP in distributed systems, it is no surprise that this approach is seen as a viable option for integrating microservices by many. It has several positives going for it – it is a powerful yet simple and familiar style. It has a mature tooling support, security mechanisms, caching, easy to test and supported through firewalls. It has a large community and support base around it, which is vital for the continued success of any technology.
The REST of it
REST lays down a set of six architectural constraints. For a service to call itself RESTful, it has to obey these constraints. In the words of Roy Fielding, the author of REST:
REST provides a set of architectural constraints that, when applied as a whole, emphasizes scalability of component interactions, generality of interfaces, independent deployment of components, and intermediary components to reduce interaction latency, enforce security, and encapsulate legacy systems.
The goal of REST, as stated above, is to help a system realize important non-functional properties such as scalability, reliability, performance, ease of use, deployability, modifiability, simplicity etc. If you look back at the Introduction post that talks about the Why of Microservices, we see that there is a significant overlap between the aims of a RESTful architecture and that of a Microservices architecture. This, in my opinion, is perhaps the justification for the popularity of REST/HTTP as the de facto integration technology for Microservices.
I won’t go into the concepts and protocol details of REST/HTTP itself in this post. They are not new by any means and they have been around long enough that there are abundant resources for it – books, articles, tutorials etc, that I do not want to regurgitate the same here again. The only part that I want to briefly talk about is Hypermedia.
The under-hyped HyperMedia
Hypermedia as the engine of application state (HATEOS) or HyperMedia in short is an important principle defined in REST. It is said that hypermedia is the most important part of REST but also the least understood. Experts in the field lament that it is not as widely adopted by developers as it should be. (My totally unsubstantiated suspicion – the name is scaring off would be adopters. Is it too late for a name change Fielding ?). Despite the name, hypermedia is rather simple conceptually and we use it all the time without being aware of it. Here is the formal definition of hypermedia from Roy Fielding:
Hypermedia is defined by the presence of application control information embedded within, or as a layer above, the presentation of information.
A simpler definition that I like from RESTful Web APIs: Services for a Changing World:
Hypermedia is a way for the server to tell the client what HTTP requests the client might want to make in the future. It’s a menu, provided by the server, from which the client is free to choose. The server knows what might happen, but the client decides what actually happens.
So in essence, if you have used a website with a menu, you have used hypermedia. In the API world, it allows a client to answer the question – ‘What can I do with this API ?‘ and is known by the term API affordances. The websites used by humans are driven by hypermedia. In contrast, the web APIs meant for machine to machine interaction, are starkly devoid of it. However, using it confers many benefits and for an API to be called RESTful it must include hypermedia constructs.
Hypermedia is especially important with microservices as it allows for a higher level of decoupling between the client and the server. It not only allows for loose coupling of data but also that of actions on the data since hypermedia includes both data and the controls (links, forms, metadata etc). It is less brittle and enables evolvability of the API and lets the client incrementally discover the resources and the controls available on it. So if using REST for service integration, do consider the use of hypermedia.
Drawbacks of Synchronous communication
RPC and HTTP only support a synchronous request/response style of communication. (You could of course, emulate an asynchronous call by using callbacks, but it is primarily synchronous in nature in that it can continue with the work related to the request only after it receives the response to it.) A synchronous call results in creating a tight coupling between the client and server in several ways :
- Temporal – The sender and the receiver, both have to be available and active at the same time in order to successfully complete the request/response transaction.
- Spatial – Put simply, the sender of the request has to be aware of how to reach the receiver specifically, by means of some form of addressing. The receiver has to be bound at that specific address.
- Point to Point – The communication is always between the sender and receiver, one to one. A One to many topology is not possible.
In order to realize a complex business function, more than one service is typically involved and hence the creation of the above mentioned types of couplings is unavoidable. Having many such synchronous point to point interactions between services can lead to very complex and haphazard chatty connections resulting in a distributed ball of mud. This is a worse situation to be in than with a monolithic big ball of mud.
Not only that, but you lose the most important tenets of microservices – that of service autonomy and isolation. This results in a less resilient system especially when it comes to handling the failures of individual services. Failure in a downstream service will cause a cascading effect in all the upstream services in the request chain. It also leads to a less scalable and brittle system. It affects performance, elasticity, deployment independence etc. One has to be careful in not ending up with a distributed big ball of mud and aware of the trade-offs being made when choosing to integrate using synchronous communication.
A few good practices for synchronous communication
If you are going with either RPC or REST/HTTP there are some steps you can take to have a better behaved system.
Avoid talking to other services synchronously during your own request/response cycle. If data is needed from other services to fulfill a request, approaches such as replicating that data in your service should be considered. Directly sharing mutable state between services should be avoided at all costs!
Avoid direct point-to-point communication amongst a group of services that need to interact in order to realize a business functionality. One common pattern seen to achieve that, is to have one service control and orchestrate the interactions between all the required services. This simplifies the interactions, but you have to be careful in that you don’t end up with an all powerful GOD service (in the words of Sam Newman) with the controlled services relegated to executing simple data CRUD functionality. The GOD service in turn becomes the dreaded single point of failure and bottleneck.
It is also imperative that you handle the issues that arise from couplings mentioned above. For that there are several patterns that help to try to contain failures from cascading – timeouts, the circuit breaker pattern, flow control etc. This is an interesting topic and requires its own post.
In conclusion, synchronous communication is easy but at the cost of several major benefits. Asynchronous communication is a more natural fit for a microservices architecture and is the topic of my next post.
Scaling Microservices with an Event Stream -This article talks about the issues of an orchestrated service architecture