Microservices: Asynchronous Request Response Pattern

Pulkit Swarup
5 min readMay 15, 2018

--

In continuation to my previous post and talking more in detail about microservices, today I would be sharing about a design strategy that helped me achieve a nicely designed microservice or at least come close enough.

Most of the communication that we do on the web is “synchronous”. Mainly attributed to the protocol that we use for this communication i.e. HTTP (which uses request-response or request-reply pattern)

Request–response is a message exchange pattern in which a requestor sends a request message to a replier system which receives and processes the request, ultimately returning a message in response. This is a simple, but powerful messaging pattern which allows two applications to have a two-way conversation with one another over a channel. This pattern is especially common in client–server architectures. — Wikipedia

However, being the most powerful and the most commonly used pattern doesn’t mean that it comes without a list of it’s own challenges. There are in fact many, two of the most obvious ones are:

  1. System availability/performance tightly-coupled with the availability/performance of the backend(server) application
  2. A lot of outstanding request(s) / slow response times in case communicating with a backend(server) applications with high latency

While in case you go ahead and search the internet for the topic, you would find distinct/multiple ways of implementing asynchronous communication between microservices, the one that I will discuss here today is the one using message brokers.

Like every traditional conversation involves participant, even the asynchronous request-response conversation has following participants:

  1. The Requester, initiator of the communication that begins with sending of Request message and then waits for corresponding Response message
  2. The Provider, which is waiting for the Request messages and replies with Response messages.
Request Response (Synchronous) Pattern

However, due to the asynchronous nature of the communication that we are talking about The Requestor / Provider can engage in multiple communication without the guarantee that the order of responses would be maintained in the order of requests (which is due to the fact that some requests might get processed faster than the others), the Requester would have to equip each request with a unique identifier in order to correlate it back to the original communication i.e. correlationId.

Request Response (Asynchronous) Pattern

Let’s see how we actually implement this; So, assuming we have an application that generates a PDF (which is a definitely a CPU intensive and a time consuming job) from the HTML content provided to it as the input, in this case a url.

Say for example, the generate pdf function is as follows,

Which is pretty straightforward, it uses puppeteer to convert the url (e.g. https://en.wikipedia.org/wiki/Request-response) into a downloadable PDF file stored at the path.

And, our server code looks something like,

Which starts by establishing a connection, creating a channel and declaring a queue.

We use channel.consume to read messages from the queue, enter callback where we convert the html to its equivalent pdf and return the response back.

And, finally the client that looks something like,

Where we do two things

  1. Send the request to the queue from where it can be picked up by the server and processed
  2. Listen back on the request_queue for the response corresponding to the correlationId, as initially posted to the queue.

Voila! And, that’s how we setup Asynchronous Request Response in our applications (just like we in this example).

Advantages

As evident from the beginning of this post, there are various advantages of this pattern over traditional “Synchronous” Request Response Pattern which are as follows:

  1. The design has minimal or no impact inherent to networked interaction, such as latency, poor performance, etc.
  2. The server can be easily scaled up in case of increased load, we just have to spawn up a few more instances of the server and share the load equally across the instances.
  3. The client is loosely coupled with the service, which brings us to the point that in case of temporal unavailability the client has minimal to no impact. However, in case of prolonged unavailability the only non-functional part of the system would be the service, while other parts of the system can continue to work as is.
  4. Converting/Inheriting properties of a “resilient” microservice is quite achievable.

Disadvantages

Classifying undermentioned point(s) as disadvantages is clearly with the intent that in case not properly handled these situation(s) might lead to major problem(s) in the system

  1. A response may never arrive or be delayed beyond expected time interval. The client in this case would have to be intelligent enough to either time-out after a stipulated time, or resend the message.
  2. Since the message is posted to the broker it might ill-formed or invalid, preventing the service from processing it. Which would either have to be handled by implementing additional validation on the client end and/or a proper response from the service in case of ill-formed or invalid message.
  3. In case, of excessive load on the service there could a long queue of request messages resulting in timeout(s) on the client. Which again needs to be handled by effective load distribution and process management.
  4. Since, the pattern is using a message broker as its communication medium, availability of message broker is crucial. Which in turn could have its own set of problems.

Question: Is Asynchronous Request Response & RPC (Remote Procedure Call) synonymous?

While this code setup might indicate that it is an implementation of RPC (Remote Procedure Call), it actually is not. The reason for this is that in case of RPC, the application is tied synchronously to a client-side programming model, which makes code look like a method call on the client side.

While in our case we might have a method call on the client side just like in case of RPC, however we are not tied to specific semantics while sharing messages in Asynchronous Request Response pattern. The messages shared can be used to invoke a method, or simply acknowledge the receipt and respond with a message which indicates the location or future availability of the processed information.

  1. This is not the only possible implementation of Asynchronous Request Response pattern, it can vary depending on specific use cases.
  2. This code is simplistic and doesn’t cover all the possible exceptions and error situations.
  3. Although, asynchronous request response pattern is not specifically tied to microservices ecosystem, it is a nice approach towards making the microservices asynchronous and have various elements of resiliency.

*The information shared in this post, are strictly based on my personal experiences and a compilation of facts/texts from different sources for the purpose of explaining/documenting the approach.

--

--