In this article, we will take a look at how the services within a system communicate with one another. We will cover various microservices inter-service communication strategies for either synchronous communication or asynchronous communication.
In previous my article, Software Architecture Patterns, we learned about monolithic and microservice based architectural patterns & designs and discussed their benefits and drawbacks. Also, in the Microservice Decomposition Services article, we looked at the various ways of decomposing a monolithic application into a microservice-based application, and we also built an application based on a microservice architecture. In my other article, Microservices Deployment Patterns, I have discussed various strategies for deploying microservice-based applications.
In a monolithic application, there is no need to inter-service communication or any remote call for internal business functions. The components invoke to another component by calling the language-level method or simple function calls.
But in case on the microservices-based application, all components might not be a part of a same service or machine, so, all components run in the several machines or on the clouds, and each service is typically a process, you have to call other processes or services by using various patterns that provide inter-communication between the services over the networks.
In the Microservice architecture pattern, a distributed system is running on several different machines, and each service is a component or process of an enterprise application. That means these services at the multiple machines must handle requests from the clients of this enterprise application. Sometimes all these services collaborate to handle those requests. So all services interact using an inter-service communication mechanism.
But in case of the Monolithic application, all components are the part of the same application and run on the same machine. So, Monolithic application doesn’t require microservices inter-service communication mechanism.
Let’s see the following diagram about the microservices inter-service communication between service components of an application:
As you can see in the preceding diagram, a monolithic application has all of its components combined as a single artifact and deployed to a single machine. One component calls another using language-level method calls.
However, in the microservice architecture, all components of the application run on multiple machines as a process or service and they use inter-process communication to interact with each other.
In Microservice Architecture, we can classify our inter-service communication into two approaches like the following:
Let’s have a look at these communication styles in detail.
In this communication style, the client service expects a response within time and wait for a response by blocking a while. This style can be used by simple using HTTP protocol usually via REST. It is the simplest possible solution for microservices inter-service communication to interact with services. The client can make a REST call to interact with other services. The client sends a request to the server and waits for a response from the service (Mostly JSON over HTTP). For example, Spring Cloud Netflix provides the most common pattern for synchronous REST communication such as Feign or Hystrix.
In the above diagram, Order-Service calls Book-Service and waits for a response returned by Book-Service. Order-Service can then process Book-Service’s response in the same transaction that triggered the communication.
The synchronous communication approach does have some drawbacks, such as timeouts and strong coupling. For example, the Order Service needs to wait for the response from the Book Service, and the strong coupling means that the Order Service can’t work without the Book Service is available. We can avoid this coupling by using the Hystrix library, which enables us to use fallbacks in case the service is not available at that time.
There are numerous protocols, such as REST, gRPC, and Apache Thrift, that can be used to interact with services synchronously.
Most synchronous communications are one-to-one. In synchronous one-to-one communication, you can also use multiple instances of a service to scale the service. However, if you do, you have to use a load-balancing mechanism on the client side. Each service contains meta-information about all instances of the calling service. This information is provided by the service discovery server, an example of which is Netflix Eureka.
There are several load-balancing mechanisms you can use. One of these is Netflix Ribbon, which carries out load-balancing on the client side, as illustrated in the following diagram:
As you can see in the preceding diagram, we have multiple instances of a particular service, but the services are still communicating one-to-one. That means that each service communicates to an instance of another service. The load balancer chooses which method should be called. The following is a list of some of the most common load-balancing methods available:
You can use the Spring Cloud, it provides support to Netflix libraries to balance the load on the client side; you can also use Spring’s RestTemplate or Feign client. Netflix Feign implements load-balancing internally. Let’s see the following code snippet:
@FeignClient(value = "ACCOUNT-SERVICE", fallback = AccountFallback.class) public interface AccountClient { @GetMapping("/accounts/customer/{customerId}") List getAccounts(@PathVariable("customerId") Integer customerId); } @Component public class AccountFallback implements AccountClient { @Override public List getAccounts(Integer customerId) { List acc = new ArrayList<>(); return acc; } }
In the preceding code, we also configured Hystrix as a circuit breaker pattern.
In this communication style, the client service doesn’t wait for the response coming from another service. So, the client doesn’t block a thread while it is waiting for a response from the server. Such type of communications is possible by using lightweight messaging brokers. The message producer service doesn’t wait for a response. It just generates a message and sends message to the broker, it waits for the only acknowledgement from the message broker to know the message has been received by a message broker or not. Let’s see in the following diagram:
As you can see in the preceding diagram, the Order Service generates a message to A Message Broker and then forgets about it. The Book Service that subscribes to a topic is fed with all the messages belonging to that topic. The services don’t need to know each other at all, they just need to know that messages of a certain type exist with a certain payload.
There are various tools to support lightweight messaging, you just choose one of the following message brokers that is delivering your messages to consumers running on respective microservices:
The above tools are based on the AMQP (Advanced Message Queuing Protocol). This protocol provides messaging based inter-service communication. Spring Cloud Stream also provides mechanisms for building message-driven microservices using either the RabbitMQ or the Apache Kafka.
So, let’s now look at two properties of the asynchronous communication style; they are as follows:
Let’s take a closer look at these properties.
In this communication approach, each request of a service client is processed by one instance of a service. There are the following kinds of one-to-one interactions:
The following diagram demonstrates one-to-one asynchronous service communication:
As you can see in the preceding diagram, each service has only one instance and the services are communicating through the message broker queue.
In this communication approach, each request of a service client is processed by multiple service instances. There are the following kinds of one-to-many interactions:
Take a look at the following diagram, which demonstrates one-to-many asynchronous service communication:
In the preceding diagram, you can see that there are multiple instances of each service. Here, a client service publishes a notification message as a topic and this topic is consumed by one or more instances of the interested services.
You can learn more about these microservices inter-service communication patterns, in Chris Richardson‘s blog, https://microservices.io/.
Strategy Design Patterns We can easily create a strategy design pattern using lambda. To implement…
Decorator Pattern A decorator pattern allows a user to add new functionality to an existing…
Delegating pattern In software engineering, the delegation pattern is an object-oriented design pattern that allows…
Technology has emerged a lot in the last decade, and now we have artificial intelligence;…
Managing a database is becoming increasingly complex now due to the vast amount of data…
Overview In this article, we will explore Spring Scheduler how we could use it by…