Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
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 :

    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.

...

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
languagec#
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}");
    }
}