Microservices: Must-Have Communication Strategies
Microservices are becoming important for enterprises, but with the growing number of microservices comes the need to understand how they talk.
Join the DZone community and get the full member experience.Join For Free
Microservices is one of the key architectures being discussed and implemented widely. It has many advantages from the aspect of clean programming, design benefits like easy to extend, the simple and small codebases, modular, many deployment benefits, easy to automate, etc.,
However, dealing with an enterprise application that has many microservices, we must give attention to the intercommunication between those microservices.
Instead of getting all data from a single application, Now with microservices, we may need to get data from different microservices through API. It adds additional latency to the response time.
More number of microservices limit the atomic transaction. One business use case might involve more than one Microservices and that limits to extend the transaction
Due to many chatty calls across microservices, there are chances of more communication failures.
I have tried to list the most common complexities that need to be considered while architecting and designing the system.
Possible Solutions or Recommendations
There are various things to consider to reduce latency and improve TPS. It is also based on the type of system that you are designing. One good scenario that I would like to cover for the latency case. In a case, complex business use cases are happening in a transaction, it would be taking say X second.
Now after microservices, if there are more than one microservices needs to be integrated for the same transaction then it might take X Second + Y (Network Latency).
How do we cut down that additional Y second? Assume that Y second is substantial to perform a complex business use case then it would come with lower Apdex.
There are two ways of achieving lower latency:
- Call the microservices in the async way when there is no dependency on the result of the microservice. In most cases, there should not be any dependency considering the best practices of designing the microservices
- Even if you synchronously call microservices, implement the processing of that request through some messaging queue like RabbitMQ. The API will receive the input data and push it to the messaging queue.
In case of implementing the messaging Queue system to process the requests, we have seen a better performance over synchronous monolithic applications. It is because requests queues can be processed instantly and in parallel. They will not hold up the requests until it completes process complex business use cases. RabbitMQ will push messages individually or in batches to the systems subscribing to the messages for processing in parallel.
Transaction and Reliability
Achieving a transaction with a single web application would be easier by implementing a transaction at the service layer. It would wrap all requests under a single request and commit it or rollback the full transaction.
Ex: The .net provides system.transaction to wrap all calls under a transaction
In the case of microservices, it would not be advisable to wrap the API calls within an application transaction. It would time out or lock it and scale down the performance drastically.
In these cases, we must consider compensating logic for ensuring eventual consistency.
Type of Failures
Assume that in a business transaction, partial work is done in the consumer or source service, and to complete the transaction, another microservices has to be invoked. Now, while invoking it there is communication failure and not able to reach the provider microservice. To resolve this kind of problem, implement retry logic to overcome minor network glitch issues.
For major communication issues, Implement proper custom exceptions at the use case level and implement log mining and alerts for any such failures. It is important to have some utilities to retrigger those requests with the same payload to the API. In case of a full transaction is failed then there would not be any data inconsistency issues. However, if the source (consumer) microservice transaction is passed but it failed to do destination (provider) microservices then these retrigger utilities will help to complete the transaction.
Exception in Microservices
In this case, the request landed in Microservices but there is an exception while processing it inside microservice.
Here the implementation of message queuing systems like RabbitMQ will be a boon. The failure would happen while processing the message queues. The failures can be mitigated by pushing that to the second queue and pulling them to process it again with some delay. This way there will be an eventual consistency with microservices.
In case the project is big enough and there are many microservices to integrate then implement service mesh. There are many advantages of service mesh like
- Load balancing
- Fine-grained control of traffic behavior with routing rules, retries, failovers, etc.,
- Good monitoring support for all services
The above list is the most common communication failure in microservices and I have provided some solutions and recommendations to mitigate them.
Opinions expressed by DZone contributors are their own.
Incident Response Guide
Best Practices for Securing Infrastructure as Code (Iac) In the DevOps SDLC
Auto-Scaling Kinesis Data Streams Applications on Kubernetes
Generics in Java and Their Implementation