In Building Microservices: Designing Fine-Grained Systems, Sam Newman states:
Getting integration right is the single most important aspect of the technology associated with microservices in my opinion. Do it well, and your microservices retain their autonomy, allowing you to change and release them independent of the whole. Get it wrong, and disaster awaits.
With that warning in mind, considerable thought needs to go into choosing the correct communication patterns and technologies in a Microservices architecture. When working with a monolith, things are straight forward and simple. The majority of the communication occurs in-process in the form of method calls with references to data passed around as needed and/or using a shared DB model.
Enter the distributed world of Microservices and things are no longer as simple. The services are typically in their own processes and to communicate, an Interprocess Communication (IPC) mechanism must be used. Services become simpler internally. The complexity is shifted into the interactions between them. Awareness of the Fallacies of Distributed Computing is a must and the design has to account for the constraints placed by the inherent nature of distribution.
Synchronous or Asynchronous ?
Should you use synchronous or asynchronous communication between services ? Well, the answer depends on the context. There are proponents for both, but purists strongly insist on asynchronous only style. In practice though, successful systems have used synchronous RPC calls (especially Google with gRPC), asynchronous mechanisms (Netflix, Gilt) and most often a combination of both styles within the same system. Typically the client facing microservices use synchronous RESTful APIs over HTTP, while internally in between the services, they use asynchronous styles.
Synchronous communication is what everyone is familiar with. A client makes a request to a remote server and most often blocks on the requesting thread until it receives a response from the server. It is a simple and easy style of communication to comprehend. The two common technologies that offer synchronous communication are Remote Procedural call (RPC) and RESTful APIs over HTTP.
Remote Procedure Call (RPC)
If you are going from a monolith to microservice, the in-process method calls can easily translate into synchronous RPC calls when splitting into services as a first pass. If you journey back in time and look at the history of RPC, it has had a turbulent past and some even classified it as an antipattern. The biggest complaint about the RPC is that local calls are nothing like remote calls, which is indeed true. However the earlier RPC frameworks tried hard to make it seem like there was in fact no distinction between the two. This philosophy of ‘hide-complexity-from-user’ led to major issues, since in reality the calls happened over the network and network fallacies will catch up with you sooner or later in surprising and nasty ways. The false promise of remote call being equal to local call could not be kept and consequently RPC got a bad rap.
Fast forward to now and RPC is being adopted widely. Modern implementations of RPC try not to hide the fact that the network is involved between calls. Most notably gRPC from Google, Apache Thrift developed at Facebook and Apache Avro have overcome the infamy and have been successfully embraced by the microservices and the broader software community.
Google is a big proponent of gRPC, no surprise there. The numbers are impressive though. In one of their presentations on gRPC (2-3 years old perhaps) they mentioned that their internal microservices handle O(1010) RPC calls per second. That is phenomenal and I can only imagine that number has gone up since!
For services that require an immediate response synchronously, an RPC based solution may be viable. However whenever a service makes an outside call during it’s own request/response cycle, it has to be aware of all the pitfalls of doing so synchronously. The important benefits of microservices – autonomy, isolation, resilience, scalability have to be considered in light of making RPC calls and the ensuing trade-offs explicitly understood. Some of the things about the RPC technology to consider are:
- The languages that it supports. For instance, Java RMI works only with Java on both the client and server end. This is restrictive in a microservices world where you want to retain freedom to choose language per service. gRPC or Thrift is a better choice here as they both support multiple languages.
- A generic way of defining the service API independent of the internal implementation of the service allowing a decoupling between the internal and external representations. Typically an Interface Definition Language (IDL) is used to define the API and this forms the ‘source of truth’ for service interactions. Avro supports using JSON format for defining the APIs.
- The efficiency of the marshalling/unmarshalling and the latency it adds to the overall RTT of the calls
- The format of the wire protocol. A binary format is used by gRPC and Avro, Thrift gives several options, including binary.
- The ease with which the client/server stubs are generated/deployed from the developer perspective.
- The support for non-breaking changes in the API and evolvability over time. This is a crucial aspect to consider if you want to avoid lock-step releases at the client and server side for every change in the API. If the framework does not support this, it can make your APIs brittle and introduce a tight coupling. You want to avoid that!
- The transport protocol used. Most common are HTTP and TCP. gRPC supports HTTP2 and notable efficiency advantages of HTTP2 are that you can multiplex several requests on to a single connection unlike HTTP and in addition supports bidirectional streams.
The above is by no means a complete list but should serve you as a start. In conclusion, modern RPC has overcome some of the issues of the older generation and so there is no reason to completely shun it (unless you are a purist that is). If you understand the goals of your services and then consider the trade-offs from using RPC and how successfully it aids or hinders meeting your goals, you should be ok!
In the next post, I will cover RESTful APIs and Asynchronous communication.