【问题标题】:How can I use Polly to retry x number of times based on response content and then return the response?如何使用 Polly 根据响应内容重试 x 次,然后返回响应?
【发布时间】:2021-05-04 23:27:58
【问题描述】:

在我的应用中,我使用 Polly 库来调用 API。

API 可以在响应中返回警告和错误。对于其中一些警告,我想重试 2 次,下一次我想将响应返回给调用者

这个可以吗?

编辑:

@StephenCleary 指出我应该只处理 响应 而不要抛出异常。

要检查响应,我需要等待内容。以下将无法编译,任何人都可以看到我如何做到这一点?

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)));
}

【问题讨论】:

  • 您的主线程在 _httpClient.PostAsync() 中被阻塞,等待响应。您需要围绕代码循环重试。所以你还需要改变等待。
  • @jdweng 这里没有什么阻碍,到底是什么让你有这个想法?
  • we throw a custom exception - 通过抛出异常,您将放弃响应。相反,您应该做的是使用确定是否重试的委托来处理 Polly 中的 响应
  • 谢谢@StephenCleary,你能看看我是怎么做到的吗?
  • @DavidG : var httpResponse = await _httpClient.PostAsync(requestUri, new StringContent(requestJson, Encoding.UTF8, "application/json"));

标签: c# .net-core polly


【解决方案1】:

这有几个部分。

首先,如果结果有警告,您不想抛出异常。那时也许你想重试,也许你不想;那里的代码还不能说。但是抛出异常意味着响应被丢弃,所以此时抛出是​​不正确的。

相反,该处理程序应使用“有警告”标志标记响应。这可以使用HttpRequestMessage.Properties(.NET 5 中的HttpRequestMessage.Options)来实现。像这样的:

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);
}

我没有测试过这个;传递给WaitAndRetryAsync2 和用于比较retryCount2 之间可能存在一对一错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-18
    • 2018-03-17
    • 1970-01-01
    • 2022-07-21
    • 1970-01-01
    • 2015-04-19
    • 1970-01-01
    相关资源
    最近更新 更多