【问题标题】:retry using Polly使用 Polly 重试
【发布时间】:2022-07-14 18:57:40
【问题描述】:

这 2 个重试策略是否表示相同?

Policy
    .Handle<SomeExceptionType>()
    .WaitAndRetry(
        new[]
        {
            TimeSpan.FromMinutes(1),
            TimeSpan.FromMinutes(1),
            TimeSpan.FromMinutes(1)
        });
Policy
    .Handle<SomeExceptionType>()
    .WaitAndRetry(
        3,
        retryAttempt => TimeSpan.FromMinutes(1)
    );

【问题讨论】:

    标签: c# polly retry-logic


    【解决方案1】:

    是的,他们也这样做。

    两个代码都定义了一个策略,该策略最多执行 4 次相同的操作:初始尝试 + 3 次额外尝试。

    这两个重载的主要区别如下:

    • 前者以静态方式定义惩罚
      • 它预定义了不同尝试之间的延迟
    • 后者以动态方式定义惩罚
      • 它可以根据即将发生的重试计算延迟

    在您的特定示例中,您的第二种选择可以这样定义:

    Policy
        .Handle<SomeExceptionType>()
        .WaitAndRetry(
            3,
            _ => TimeSpan.FromMinutes(1)
        );
    

    使用丢弃运算符,您明确声明您没有使用 sleepDurationProvider 中的该参数来计算新的延迟。


    为了清楚起见,我在这篇文章中使用了 penaltydelaysleep 可互换的术语。


    更新 #1

    以下是您可以利用动态惩罚计算的两个示例。

    指数退避+抖动

    建议不要在每次尝试之间等待相同的时间,而是使用越来越大的延迟来为下游系统提供自我修复空间。因此,例如:2、4、8 ...

    jitter 只是一个小的随机数,以避免所有客户端同时尝试发送重试尝试。所以它会及时分散/分散客户端的重试尝试。

    const int maxDelayInMilliseconds = 32 * 1000;
    var jitterer = new Random();
    Policy
      .Handle<HttpRequestException>()
      .WaitAndRetryForever(
          retryAttempt =>
          {
              var calculatedDelayInMilliseconds = Math.Pow(2, retryAttempt) * 1000;
              var jitterInMilliseconds = jitterer.Next(0, 1000);
    
              var actualDelay = Math.Min(calculatedDelayInMilliseconds + jitterInMilliseconds, maxDelayInMilliseconds);
              return TimeSpan.FromMilliseconds(actualDelay);
          }
      );
    

    断路器感知重试

    如果您使用断路器来避免在下游系统自我修复时淹没下游系统,您可以在重试时意识到这一点。

    默认情况下,所有策略都是独立的,它们彼此不知道。如果您在 CB 打开时发出重试尝试,那么您将收到BrokenCircuitException(因此它会缩短执行)。但是您可以根据 CB 的状态动态计算延迟,这样就可以跳过这些不必要的重试。

    断路器定义

    Policy<HttpResponseMessage>
        .HandleResult(res => res.StatusCode == HttpStatusCode.InternalServerError)
        .CircuitBreakerAsync(3, TimeSpan.FromSeconds(2),
           onBreak: (dr, ts, ctx) => { ctx[SleepDurationKey] = ts; },
           onReset: (ctx) => { ctx[SleepDurationKey] = null; });
    

    重试定义

    Policy<HttpResponseMessage>
        .HandleResult(res => res.StatusCode == HttpStatusCode.InternalServerError)
        .Or<BrokenCircuitException>()
        .WaitAndRetryAsync(4,
            sleepDurationProvider: (c, ctx) =>
            {
                if (ctx.ContainsKey(SleepDurationKey))
                    return (TimeSpan)ctx[SleepDurationKey];
                return TimeSpan.FromMilliseconds(200);
            });
    

    详细描述此高级用例here

    【讨论】:

    • 谢谢。如果我需要在重试时添加日志,例如“retrying..attempt 1 of {totalnumberofretries}”,我该怎么做?
    • @user989988 您可以在WaitAndRetry 中提供一个委托(称为onRetry),您可以在惩罚睡眠之前执行日志记录。您可以在最后一个链接上看到一个示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多