/
Asynchronous Request-Reply Pattern

Asynchronous Request-Reply Pattern

Problem

  • Design APIs for a client app to respond quickly (100 ms or less), code running in a web-client (browser).

  • Manage the response latency : factors such as current load, network infra, size of a request payload, etc.

  • We prefer the owrk done by the frontend and not by the backend (which could be lon-running and done in seconds).

 

Solution

  • Use HTTP polling which is useful to client-side code :

    1. Make asynchronous call to the API;

    2. API responds synchronously as quickly as possible (http 202 - OK);

    3. Client can poll to check for the result : reponse holds a location reference pointing to an endpoint;

    4. API offloads processing to another component (message queue, …);

    5. API returns HTTP 200. Once the work is complete, endpoint can redirect to another resource URL.

image-20240822-192547.png
Diagram from Microsoft Documentation

Asynchornous Request-Reply In Practice

API call with Azure Function which can listen to changes in Azure resources : decouple backend processing from a frontend host where backend processing needs to be asynchronous, but the frontend still needs a clear response : function implements an endpoint that accepts work from a client application and puts it on a queue for processing (see below).

public static class AsyncProcessingWorkAcceptor { [FunctionName("AsyncProcessingWorkAcceptor")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] CustomerPOCO customer, [ServiceBus("outqueue", Connection = "ServiceBusConnectionAppSetting")] IAsyncCollector<ServiceBusMessage> OutMessages, ILogger log) { if (String.IsNullOrEmpty(customer.id) || string.IsNullOrEmpty(customer.customername)) { return new BadRequestResult(); } string reqid = Guid.NewGuid().ToString(); string rqs = $"http://{Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME")}/api/RequestStatus/{reqid}"; var messagePayload = JsonConvert.SerializeObject(customer); var message = new ServiceBusMessage(messagePayload); message.ApplicationProperties.Add("RequestGUID", reqid); message.ApplicationProperties.Add("RequestSubmittedAt", DateTime.Now); message.ApplicationProperties.Add("RequestStatusURL", rqs); await OutMessages.AddAsync(message); return new AcceptedResult(rqs, $"Request Accepted for Processing{Environment.NewLine}ProxyStatus: {rqs}"); } }

 

image-20240826-182719.png
In Practice with Azure Function