Info |
---|
Details in Asynchronous Request-Reply pattern - Azure Architecture Center | Microsoft Learn |
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 :
Make asynchronous call to the API;
API responds synchronously as quickly as possible (http 202 - OK);
Client can poll to check for the result : reponse holds a location reference pointing to an endpoint;
API offloads processing to another component (message queue, …);
API returns HTTP 200. Once the work is complete, endpoint can redirect to another resource URL.
...
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).
Code Block | ||
---|---|---|
| ||
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}");
}
} |