在 Polly 库中使用 httpclient 的指数退避

gro*_*eto 5 c# httpresponse dotnet-httpclient polly retry-logic

我刚刚读到Polly 库,当从桌面代理到服务器通信时,我需要处理 3 次重试。

目前我喜欢指数退避。

但是,我很难理解如何在我的代码中实现这一点。这是我到目前为止所做的:

using (HttpClient client = new HttpClient())
{
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
    client.DefaultRequestHeaders.Add("TenantId", tenantId);
#if DEBUG
    var url = "http://localhost:5001/api/v1/RetrievePaymentDetails?orderpaymentid={orderPaymentId";
#else
    //TODO: add URL once the application is in production!
    var url = "intent2.api";
#endif
    Policy
        .Handle<HttpRequestException>()
        .WaitAndRetry(new[]
        {
            TimeSpan.FromSeconds(5),
            TimeSpan.FromSeconds(15),
            TimeSpan.FromSeconds(30)
        });

    using (HttpResponseMessage response = await client.GetAsync($"{url}"))
    {
        using (HttpContent content = response.Content)
        {
            HttpContentHeaders headers = content.Headers;
            var result = JsonConvert.DeserializeObject<PaymentDetailsDto>(response.Content.ReadAsStringAsync().Result);
            Currency currencyFromDb = (Currency)Enum.Parse(typeof(Currency), result.Currency);
            var result2 = await this.posManager.PayPos(result.Amount, currencyFromDb, result.SumUpUsername, result.SumUpPassword);

            if (result2.TransactionCode == null)
            {
                return this.Request.CreateResponse(HttpStatusCode.BadRequest, result2);
            }
            return this.Request.CreateResponse(HttpStatusCode.OK, result2);
            //return this.Request.CreateResponse<object>(result2);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我关闭了网络服务器,以便可以模拟此练习。现在,每次访问该 URL 时,它都会抛出 aHttpRequestException并且根本不会重试。我该如何使用这个?

Cro*_*der 6

您没有使用该政策。定义一个并不会将其应用于任何事物。下面是来自 HttpPolicyExtensions 的专门瞬时错误策略的示例。但这种一般模式适用于任何政策:

static AsyncRetryPolicy<HttpResponseMessage> transientRetry3Policy = HttpPolicyExtensions
          .HandleTransientHttpError()
          .RetryAsync(3);
Run Code Online (Sandbox Code Playgroud)

以下是将其应用于请求的方法:

var someresult = await transientRetry3Policy
                .ExecuteAndCaptureAsync(async () => await client.SendAsync(msg));
Run Code Online (Sandbox Code Playgroud)


Pet*_*ala 5

您的代码可以(并且应该)以多种不同的方式进行改进。
因为我们不进行代码审查,所以我只关注与 Polly 相关的内容。

一般重试

重试有一个非常重要的前提条件:该操作(可能被调用多次)应该是等且无副作用的。作为基于所提供代码的有根据的猜测,您正在尝试检索有关已发生的购买的信息。我想这样的查询是以幂等的方式编写的。

我不太确定你的是否posManager是幂等且无副作用的(我想不是)。所以我的观点是,你应该明智地选择你的弹性策略的范围,因为如果不小心执行它可能会造成损害。

重试和它的朋友

重试本身只是一种策略,而不是弹性策略。当我们结合多种策略以获得弹性行为时,我们可以谈论策略。

例如,想象一下当您的支付服务严重超载并且您的响应时间变得疯狂时的情况。比方说 99 秒。你愿意等待吗?HttpClient的默认超时时间是100秒,这个时间很多。如果您不加以控制,您的系统将挂起一段时间。这就是Timeout出现的地方。

它可以用于每个单独的查询,也可以用作整个过程(包括重试)的全局超时。例如:

  • 本地超时:5秒
  • 全局超时:90 秒

另一个相关的策略被调用CircuitBreaker,它可以帮助您防止下游系统(在您的情况下是支付服务)泛滥。如果您的支付服务已经很难满足请求,那么我们不应该向其发送新的请求。

CircuitBreaker 监视连续的故障,如果超过给定的阈值,它将在预定义的时间内阻止所有传出请求,以帮助下游系统再次启动。

如果将所有这些放在一起,那么您将获得一个相当有弹性的解决方案:

全局超时

重试

断路器

本地超时

瞬时 Http 错误

HandleTransientHttpErrorCrowcoder 提到的方法处理408HttpRequestException或 5xx 状态代码。如果您的支付服务返回另一个响应状态,例如 429(请求过多),那么它将不会处理该状态。值得牢记。