Moving from a single-server approach to containers is not always easy. But it is possible to set up almost every system to run in containers. A standard web hosting setup, for example, can have PHP and Nginx running in one container and MySQL running in another.
Building a system from scratch that takes full advantage of containers is another story altogether. After using a monolith approach for so long, making the shift both mentally and physically towards building microservices architecture isn’t always as straightforward as it seems. There are a lot of adjustments to be made, including adaptations to the development approach itself.
Still, the benefits offered by microservices are too good to miss. Aside from higher flexibility and efficiency, an application built on top of microservices is more sustainable, reliable, and easier to maintain in the long run.
These benefits can be achieved using the tools and capabilities provided by the microservices architecture, we discuss this in more depth in our article, Microservices: The Good, The Bad, and The Ugly. In this article, however, we are going to focus more on communication between microservices.
Communication and Microservices
Microservices serve specific functions that are essential to building bigger applications. It is only natural then that microservices within such an app need to communicate with each other to complete bigger routines. What’s interesting is the number of options you have when it comes to setting up how microservices communicate with each other.
There are several factors to determine before the right communication flow between microservices can be established. Those factors are:
Microservices communication can be set up as a stateless or stateful process. This means it is possible to configure the communication between microservices as a synchronous and asynchronous process. We will get to that later.
There are a lot of options when it comes to how the payload is formed too, plus you now have a wealth of messaging formats to utilize. You can use REST or SOAP for your app or simply rely on JSON for easier management. You can even integrate XML into the communication process.
Next, you have to determine how communication between microservices should work. Once again, this depends on whether you want to establish synchronous communication or asynchronous protocol.
When you use HTTP or HTTPS, you are utilizing synchronous communication. AMQP, on the other hand, allows you to run asynchronous communication across the entire app, which brings a lot of new benefits to the table.
From this point of view, you usually end up with a combination of architectural style and transport protocol to use. This is exactly why we had popular communication formats even before microservices were widely used. I mean, we all have mixed feelings about RPC, but it is still a reliable means of communication, even today. The same can be said for REST over HTTPS.
The Modern Approach
With microservices, you have another way to determine the right way to design the communication architecture. You start by determining if you want the design of the app to rely on synchronous communication or asynchronous options. Once you identify the right approach – based on the objectives you need to achieve – you continue by finding the most suitable communication protocol to use.
The next step is determining the flow of communication. In some cases, you might want multiple microservices to act as recipients. In others, you simply need to establish a one-to-one communication between microservices. These two situations require different handling. You also have extra layers like the messaging service which every cloud provider has their own implementation of such as Azure Service Bus which helps you design and maintain complex communication flows.
Even with this modern approach, it is easy to see how some of the existing – and highly popular – designs can still work when applied to microservices. A one-to-one communication using HTTPS as the primary protocol is the simplest way to go in many cases. For more complex use cases, using external services like the messaging service RabbitMQ to simplify the use of AMQP provides the most efficient and effective method.
Synchronous vs. Asynchronous
I feel like we have to dig deeper and discuss more about synchronous and asynchronous communications, especially when implemented in microservices. Understanding these two gives you a much better understanding of how to best set up the system.
Naturally, synchronous communication is arranged as a cycle and requires every microservice in that cycle to work in order. When a client sends a request to the first node (the communication basket), that request is processed by the microservices involved in the flow one at a time, before a response is channeled back to the first node through the same microservices.
Asynchronous communication doesn’t work that way. The start of the cycle is the same; a client sends a request to the first node. However, everything after that point is different. The communication basket has the ability to recognize the request, determine the right microservices to contact and react accordingly.
So, that same request can trigger a request to microservices A, and C, but not to microservice B. The basket may also expect a reply from microservice A, but not from microservice C. it is completely flexible and the microservices can be independent of one another.
Since the cycle is no longer synchronous, communications can be performed at a much faster pace. At the same time, responses can be delivered without waiting for all microservices to respond. Even better, you can add microservices that don’t need to respond at all (i.e. a microservice that handles logging or redundancy), making the entire system leaner and more efficient.
Understanding the Challenges
Communication between microservices is not without its challenges. The possibilities are endless with asynchronous communication, but there are still important risks to mitigate. Those risks are:
- Performance: The way the communication architecture is designed needs to take into account the number of calls (requests) that need to be made, how the flow is affecting performance, and whether improvements can be made.
- Error tolerance: It is also necessary to manage how microservices should respond in the event of an error. Let’s say the communication basket doesn’t receive a response from a microservice that should; what routine is in place to handle such a situation?
- Dependencies: With the way microservices are set up, it is easy to fall into the trap of designing a communication architecture that establishes dependency. When not mitigated properly, you may end up with an application full of dependent microservices, eliminating the advantage of using microservices altogether.
- Monitoring and logging: Since communication between microservices can happen independently, logging and monitoring become more challenging. Depending on your situation, a better way to keep track of interactions between mission-critical microservices is needed.
Nevertheless, the flexibility you get when it comes to communication between microservices and the benefits of using such architecture far outweigh the challenges you need to manage along the way. For more on working with microservices, check out our article here on the 30+ Top Tools for Building Microservices on All Levels.
Caylent provides a critical DevOps-as-a-Service function to high growth companies looking for expert support with microservices, containers, cloud infrastructure, and CI/CD deployments. Our managed and consulting services are a more cost-effective option than hiring in-house, and we scale as your team and company grow. Check out some of the use cases, learn how we work with clients, and profit from our DevOps-as-a-Service offering too.