1

I am building a microservice app where the client sends requests through REST APIs to the backend server, and the backend will communicate with other services using gRPC. I am aware that this is synchronous and is an anti-pattern for a microservice app due to the tightly-coupled attribute.

My question is if I were to replace gRPC with a message queue for the communication method between the services, which means it's asynchronous, how would I send a response back to the client who's waiting for one synchronously via REST API? Even if the backend communicates asynchronously with the internal services, it has to return a response back to the client in the meantime even before the request has not yet been processed by other service. So the questions become:

  1. What to return back to the client when the request is not fully processed yet? Since it's not processed yet, how do we know whether to return HTTP statusok or HTTP statusinternalservererror.

  2. Since technically the client's request has been "served" (with an acknowledgement message in point 1), the communication between the client and the backend server is already closed. Then how will the client receive the real response / result from the server later once it has finished processing? What is the common practice here?

1
  • How about a GUID that the client can use to poll on an interval to determine if/when the request is finally resolved?
    – Graham
    Jan 5 at 17:56

1 Answer 1

4

It's a stretch to call gRPC communication an “anti-pattern”. Synchronous communication has the tremendous advantage of simplicity, whereas asynchronous communication is more flexible. Do you really need that flexibility, though?

Event-based models for asynchronous operations

Asynchronous communication means thinking in terms of events rather in terms of requests or channels. Translating a request–response communication model to an event-based model could look like the following:

  • process A publishes a Request event
  • process B listens for Request events and eventually publishes a Response event
  • process A listens for Response events. If a matching Response event is created, it will eventually be received by process A which created the original Request event.

The different events can share a correlation ID to indicate that they relate to each other.

I'm talking about Request and Response events generically. In practice, the events should have a meaning within the domain of the software, for example ChatMessageSent, ChatMessageDelivered, and ChatMessageRead events.

Using HTTP for asynchronous operations

If you have a HTTP/REST API that is a gateway to asynchronous microservices, there are different ways for handling the underlying asynchronous operations.

  • The server could wait, in the hopes that a response event is received within some timeout. This can be a good idea if you're pretty sure that the deadline will be met almost always. But in general, waiting is problematic: the client just sees latency and doesn't know if their HTTP request was even properly received yet.

  • The server can respond immediately to indicate that the request was received, but without directly delivering the result. The result can be retrieved later. Specifically, the following HTTP status code can be useful:

    • 202 Accepted is used specifically for asynchronous operations. It means that the request has been received, but it doesn't say that the request will succeed. The body of the response could inform the client how the status could be monitored.

    • Alternatively, the response could use a 303 status and redirect to a newly created resource representing the operation. If such a resource can be created directly, this would be my preferred approach.

The HTTP response can provide a way to monitor the result. For example, it could link to an URL that represents the status of the operation. The HTTP server could maintain a database of all requests and their status. When the HTTP server observes a response event on the message bus, the status can be updated in the database.

In a chat application, the HTTP communication could be as follows:

  1. The client sends a new chat message. The server responds with a redirect to an URL that represents the message

    > POST /messages
    >
    > {"body": "Lorem ipsum."}
    
    < 303 See Other
    < Location: /messages/ff591577-668c-4ffc-88d6-fe160068f93f
    
  2. The client requests the message status.

    > GET /messages/ff591577-668c-4ffc-88d6-fe160068f93f
    
    < 200 OK
    <
    < {"body": "Lorem ipsum."
    < , "sent": 1641405030
    < , "received": null
    < , "read": null}
    
  3. Time passes. The client requests the message status again.

    > GET /messages/ff591577-668c-4ffc-88d6-fe160068f93f
    
    < 200 OK
    <
    < {"body": "Lorem ipsum."
    < , "sent": 1641405030
    < , "received": 1641405032
    < , "read": 1641405079}
    

Of course, having a client poll for the status of a resource is not necessarily a good design. But polling is the only way built-in to HTTP (no, HTTP/2 server push doesn't address this). If real-time updates are desired, there would have to be some communication channel through which updates can be pushed. In a web context, Websockets play this role. The server could send a notification via websockets that an update for a resource exists so that the client would know to request that resource again.

Synchronous communication might be good enough

All of this is complicated. If you're not working on an inherently asynchronous problem, sticking with synchronous communication will be a lot simpler. In this context, synchronous communication just means that every request will receive a response within a reasonable timeout. That can be quite practical if you're only interacting with internal services that are hosted in the same data center.

1
  • I see. So essentially I have 2 options: polling with regular HTTP or maintaining websocket connection until a response is ready. Thanks for the very detailed answer. It really clears things up. Jan 6 at 3:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.