Skip to content

Instantly share code, notes, and snippets.

@prom3theu5
Created January 26, 2020 23:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save prom3theu5/360771a12441b51e97f7bbbe240cbe30 to your computer and use it in GitHub Desktop.
Save prom3theu5/360771a12441b51e97f7bbbe240cbe30 to your computer and use it in GitHub Desktop.
Polly Resiliency for Azure Cognitive Services etc
/// <summary>
/// Creates a Polly-based resiliency strategy that helps deal with transient faults when communicating
/// with the external (downstream) Computer Vision API service.
/// </summary>
/// <returns></returns>
private PolicyWrap<HttpResponseMessage> DefineAndRetrieveResiliencyStrategy()
{
// Retry when these status codes are encountered.
HttpStatusCode[] httpStatusCodesWorthRetrying = {
HttpStatusCode.InternalServerError, // 500
HttpStatusCode.BadGateway, // 502
HttpStatusCode.GatewayTimeout // 504
};
// Define our waitAndRetry policy: retry n times with an exponential backoff in case the Computer Vision API throttles us for too many requests.
var waitAndRetryPolicy = Policy
.HandleResult<HttpResponseMessage>(e => e.StatusCode == HttpStatusCode.ServiceUnavailable ||
e.StatusCode == (System.Net.HttpStatusCode)429)
.WaitAndRetryAsync(10, // Retry 10 times with a delay between retries before ultimately giving up
attempt => TimeSpan.FromSeconds(0.25 * Math.Pow(2, attempt)), // Back off! 2, 4, 8, 16 etc times 1/4-second
//attempt => TimeSpan.FromSeconds(6), // Wait 6 seconds between retries
(exception, calculatedWaitDuration) =>
{
_log.Info($"Computer Vision API server is throttling our requests. Automatically delaying for {calculatedWaitDuration.TotalMilliseconds}ms");
}
);
// Define our first CircuitBreaker policy: Break if the action fails 4 times in a row.
// This is designed to handle Exceptions from the Computer Vision API, as well as
// a number of recoverable status messages, such as 500, 502, and 504.
var circuitBreakerPolicyForRecoverable = Policy
.Handle<HttpResponseException>()
.OrResult<HttpResponseMessage>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))
.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 3,
durationOfBreak: TimeSpan.FromSeconds(3),
onBreak: (outcome, breakDelay) =>
{
_log.Info($"Polly Circuit Breaker logging: Breaking the circuit for {breakDelay.TotalMilliseconds}ms due to: {outcome.Exception?.Message ?? outcome.Result.StatusCode.ToString()}");
},
onReset: () => _log.Info("Polly Circuit Breaker logging: Call ok... closed the circuit again"),
onHalfOpen: () => _log.Info("Polly Circuit Breaker logging: Half-open: Next call is a trial")
);
// Combine the waitAndRetryPolicy and circuit breaker policy into a PolicyWrap. This defines our resiliency strategy.
return Policy.WrapAsync(waitAndRetryPolicy, circuitBreakerPolicyForRecoverable);
}
private async Task<string> MakeOCRRequest(byte[] imageBytes)
{
_log.Info("Making OCR request");
var licensePlate = string.Empty;
// Request parameters.
const string requestParameters = "language=unk&detectOrientation=true";
// Get the API URL and the API key from settings.
// TODO 2: Populate the below two variables with the correct AppSettings properties.
var uriBase = ConfigurationManager.AppSettings["computerVisionApiUrl"];
var apiKey = ConfigurationManager.AppSettings["computerVisionApiKey"];
var resiliencyStrategy = DefineAndRetrieveResiliencyStrategy();
// Configure the HttpClient request headers.
_client.DefaultRequestHeaders.Clear();
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey);
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Assemble the URI for the REST API Call.
var uri = uriBase + "?" + requestParameters;
try
{
// Execute the REST API call, implementing our resiliency strategy.
HttpResponseMessage response = await resiliencyStrategy.ExecuteAsync(() => _client.PostAsync(uri, GetImageHttpContent(imageBytes)));
// Get the JSON response.
var result = await response.Content.ReadAsAsync<OCRResult>();
licensePlate = GetLicensePlateTextFromResult(result);
}
catch (BrokenCircuitException bce)
{
_log.Error($"Could not contact the Computer Vision API service due to the following error: {bce.Message}");
}
catch (Exception e)
{
_log.Error($"Critical error: {e.Message}", e);
}
_log.Info($"Finished OCR request. Result: {licensePlate}");
return licensePlate;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment