В своем приложении я использую библиотеку Polly для вызова API.

API может возвращать предупреждения и ошибки в ответе. Для некоторых из этих предупреждений я хочу повторить попытку 2 раза, а в следующий раз я хотел бы вернуть ответ вызывающему .

Это можно сделать?

Редактировать:

@StephenCleary указал, что я должен просто обрабатывать ответ и не генерировать исключение.

Чтобы проверить ответ, мне нужно дождаться Content. Следующее не будет компилироваться, может ли кто-нибудь увидеть, как я могу это сделать?

static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(async msg =>
            {
               var content  = await msg.Content.ReadAsStringAsync();
               return content.Contains("errorcode123");
            })
            .WaitAndRetryAsync(2, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
3
Vinyl Warmth 4 Май 2021 в 17:11

1 ответ

Лучший ответ

В этом есть пара частей.

Во-первых, вы не хотите создавать исключение, если в результате есть предупреждения. В этот момент, возможно, вы захотите повторить попытку, а может, и нет; код там пока не может сказать. Но выдача исключения означает, что ответ отбрасывается, поэтому бросать в этот момент некорректно.

Вместо этого этот обработчик должен пометить ответ флажком «имеет предупреждения». Это возможно с помощью HttpRequestMessage.Properties (HttpRequestMessage.Options в .NET 5). Что-то вроде этого:

private static readonly string IsInternalServerResponseKey = Guid.NewGuid().ToString("N");

...

var httpResponse = ...
var responseContent = ...
if (InternalServerResponse(responseContent))
{
    httpResponse.RequestMessage.Properties[IsInternalServerResponseKey] = true;
}

Таким образом, к запросу / ответу прикреплен флаг, который может быть прочитан другими частями кода, в частности обработчиком повторов Polly.

Другая часть решения - это количество повторов. Обычно у Polly есть делегаты, которые определяют, следует ли повторять попытку, и эти делегаты являются самодостаточными - повторять исключения этого типа или повторять ответы, которые выглядят так. В этом случае вы хотите повторить ответ, который соответствует определенной форме , но только в том случае, если не было слишком много повторных попыток, и если было слишком много повторных попыток и ответ соответствует форме «повтор» , то вы не хотите генерировать исключение, а вместо этого возвращаете ответ.

Это необычно, но выполнимо. Вам нужно будет зафиксировать «внешние факторы» (в данном случае счетчик повторов) внутри контекста Polly. Затем ваш делегат повторных попыток может извлечь счетчик повторных попыток из контекста и основывать свое решение на этом. Примерно так должно работать:

private static readonly string RetryCountKey = Guid.NewGuid().ToString("N");
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(response =>
        {
            return IsInternalServerResponse() && RetryCount() <= 2;

            bool IsInternalServerResponse()
            {
                if (!response.RequestMessage.Properties.TryGetValue(IsInternalServerResponseKey, out var objValue) ||
                    objValue is not bool boolValue)
                    return false;
                return boolValue;
            }

            int RetryCount()
            {
                if (!response.RequestMessage.GetPolicyExecutionContext().TryGetValue(RetryCountKey, out var objValue) ||
                    objValue is not int intValue)
                    return 0;
                return intValue;
            }
        })
        .WaitAndRetryAsync(2,
            (retryAttempt, _) => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
            (_, _, retryAttempt, context) => context[RetryCountKey] = retryAttempt);
}

Я не проверял это; возможно, есть ошибка на единицу между 2, переданным в WaitAndRetryAsync, и 2, используемым для сравнения retryCount.

3
Stephen Cleary 4 Май 2021 в 15:27