How To Implement Synchronous Interaction Between Microservices

Oleksii Dushenin
6 min readNov 14, 2021

In a previous post, How To Understand Microservices Architecture, the advantages and disadvantages of Microservices Architecture were reviewed. The interactions between services can be a challenge in the applications built with this kind of architecture. There are different ways to achieve this interaction. In this post, the synchronous interaction (HTTP/HTTPS) is reviewed.

Synchronous Microservices Interactions

Let’s review the example presented above. In this case, we have a Database per Service pattern. It means that services do not share the database. Each of them has its own database (databases) or does not have a database at all.

Synchronous Microservices Interactions Example

A user visits a hotel booking website and selects the dates of stay. The Hotel Service just returns a list with the minimum possible hotel room information. After that, the user selects a specific hotel room. As a result, we have the hotel room and the dates of stay as input. It is expected to return full hotel information:

  • Hotel summary. For instance, the name, address, rating, description, available services, room description etc. Hotel Service uses its own database to fetch required data.
  • Attractions nearby the hotel. It can be anything like restaurants, theaters, cinemas, museums, etc. This information usually is not changed. As a result, it can be saved in the database of Hotel Attractions Service.
  • Events nearby the hotel. These are the events that occur from time to time. Third Party Events Service can be used to fetch the events (e.g. festivals, concerts, exhibitions, etc) nearby the hotel during the period of stay.

To sum up, the response contains data from three different services that communicate synchronously with HTTP/HTTPS.

Synchronous Microservices Patterns

At first glance, it seems that it is enough to make requests from Hotel Service to Hotel Attractions Service and from Hotel Attractions Service to Third-Party Events Service. However, there are common issues with such a method of Microservices Interactions.

Service Discovery

The first issue is with the fact that Hotel Service cannot call Hotel Attractions Service directly (we cannot hard code the service URL). In order to provide stable work of service, multiple instances of the same service should run in parallel. The number of instances can increase or decrease based on the load, unhealthy instances are replaced with healthy ones. As a result, some infrastructure components should be responsible for the list of active services and their active healthy instances. Service Discovery is used for this purpose. The information about running service instances is saved in Service Registry.

For instance, in Spring stack it is Eureka.

Client Side Load Balancing

The first option of working with Service Registry is the Client Side Load Balancing.

What does it mean? We have Service Registry with information about healthy instances. Hotel Service can interact with Service Registry to find out about active service instances of Hotel Attractions Service and use some algorithm to choose the instance to use (e.g. Round Robin).

The call is performed to the chosen service instance.

In Spring it is typically Ribbon.

Gateway

The second option of working with Service Registry is the usage of Gateway (presented in the initial picture).

In this case, a special component is responsible for request redirection to the required service instance. Gateway works with Service Registry to understand which services and their instances can be used.

It can work e.g. like this:

In Spring it is typically Zuul.

Circuit Breaker

Let’s review another problem with Synchronous Microservices Interactions.

In our example, the next request sequence is executed:

  • Hotel Service makes a request to Hotel Attractions Service.
  • Hotel Attractions Service makes a request to Third-Party Events Service.

As you can see, the request depends on three services. Furthermore, Third-Party Events Service is outside of our control.

What happens when one of the services is slow? How does it affect the whole application?

The issue is in the fact that the problem with even one service negatively affects the performance of the whole system.

Imagine the situation, when Third-Party Events Service is down. In this case, the user cannot see even the hotel room information. Yes, information from Third-Party Events Service about events nearby the hotel can be important for some people. However, it is not critical. Without this information, people can still book a hotel room.

The situation is much worse when Third-Party Events Service has poor performance.

Imagine the next scenario:

  • Request is made to Hotel Service.
  • Request is made from Hotel Service to Hotel Attractions Service.
  • Rquest is made from Hotel Attractions Service to Third-Party Events Service.
  • Third-Party Events Service responds in 30 seconds.

As a result:

  • Hotel Attractions Service blocks the thread to wait the response from Third-Party Events Service.
  • Hotel Service blocks the thread to wait the response from Hotel Attractions Service.

In the case of a single request, nothing very serious happens: a user just waits for the response for approximately 30 seconds.

However, there are two major problems here:

  • Not each user will wait for 30 seconds.
  • This information about events neaby the hotel is not so important.

The situation becomes even worse when we look at a real situation: there is more than one request from the users. A lot of users are looking for a hotel room.

It means that this 30 seconds waiting negatively influence the performance of the whole system:

  • The resources of the Hotel Attractions Service are depleted.
  • Hotel Service waits for the response of the Hotel Attractions Service even more.
  • The resources of the Hotel Service are depleted.
  • The Hotel Service cannot event process the requests related to the hotel room list which does not use neither Hotel Attractions Service nor Third-Party Events Service.

The solution is very simple. The circuit breaker is used.

  • The timeouts are set for each request.
  • If the response from one service is not received by anoter service before timeout happens, the initial service responds with the default value.
  • For some configured period of time the requests are not made to the problematic service. Default response is always used.
  • After some time, the requests are made again to the dependant service.

Let’s review the situation when Third-Party Events Service is problematic (the same is applicable for Hotel Attractions Service).

  • The Hotel Service makes a request to Hotel Attractions Service.
  • The Hotel Attractions Service makes a request to Third-Party Events Service and waits for the response for 1 second.
  • If a successful response is received, everything is fine. The user receives all data.
  • If a response is unsuccessful, the user will not receive information about events. Subsequent requests (e.g. next 5 seconds) will skip the call to the problematic Third-Party Events Service.

In Spring it is typically Hystrix.

Caching

In order to optimize performance characteristics, caching can be used in some cases.

It can be used for the next reasons if possible:

  • Skip the call to the target service.
  • In case of the problems with target service, some default (may be outdated or inaccurate) data can be returned to the user.

However, cache invalidation can be a real challenge here.

Summary

In this article, various challenges with Synchronous Microservices Interactions were reviewed. This approach can be applicable in some cases. However, there is another way of microservices communications. Asynchronous Microservices Interactions will be reviewed in the next post.

Originally published at https://datamify.com on November 14, 2021.

--

--