【问题标题】:WaitAndRetryPolicy combined with BulkheadPolicy, prioritizing retries. Is it possible?WaitAndRetryPolicy 与 BulkheadPolicy 结合,优先考虑重试。是否可以?
【发布时间】:2020-10-30 07:45:52
【问题描述】:

我正在评估Polly 库的功能和灵活性,作为评估过程的一部分,我尝试将WaitAndRetryPolicyBulkheadPolicy 策略结合起来,以实现弹性和节流的结合。问题是这种组合产生的行为不符合我的期望和偏好。我想要的是优先重试失败的操作,而不是执行新的/未处理的操作。

理由是(根据我的经验)失败的操作再次失败的可能性更大。因此,如果所有失败的操作都被推到整个过程的末尾,那么整个过程的最后一部分将非常缓慢且没有生产力。不仅因为这些操作可能再次失败,而且因为每次重试之间所需的延迟,在每次失败尝试后可能需要逐渐延长。所以我想要的是每次BulkheadPolicy 有空间开始一个新操作时,如果它的队列中有一个重试操作,则选择一个重试操作。

这是一个示例,演示了我想要修复的不良行为。需要处理 10 个项目。第一次尝试失败,第二次尝试成功,总共执行了 20 次。重试项目之前的等待时间为一秒。任何时候都应该只有 2 个操作处于活动状态:

var policy = Policy.WrapAsync
(
    Policy
        .Handle<HttpRequestException>()
        .WaitAndRetryAsync(retryCount: 1, _ => TimeSpan.FromSeconds(1)),

    Policy.BulkheadAsync(
        maxParallelization: 2, maxQueuingActions: Int32.MaxValue)
);

var tasks = new List<Task>();
foreach (var item in Enumerable.Range(1, 10))
{
    int attempt = 0;
    tasks.Add(policy.ExecuteAsync(async () =>
    {
        attempt++;
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} Starting #{item}/{attempt}");
        await Task.Delay(1000);
        if (attempt == 1) throw new HttpRequestException();
    }));
}
await Task.WhenAll(tasks);

输出(实际):

09:07:12 Starting #1/1
09:07:12 Starting #2/1
09:07:13 Starting #3/1
09:07:13 Starting #4/1
09:07:14 Starting #5/1
09:07:14 Starting #6/1
09:07:15 Starting #8/1
09:07:15 Starting #7/1
09:07:16 Starting #10/1
09:07:16 Starting #9/1
09:07:17 Starting #2/2
09:07:17 Starting #1/2
09:07:18 Starting #4/2
09:07:18 Starting #3/2
09:07:19 Starting #5/2
09:07:19 Starting #6/2
09:07:20 Starting #7/2
09:07:20 Starting #8/2
09:07:21 Starting #10/2
09:07:21 Starting #9/2

预期的输出应该是这样的(我手写的):

09:07:12 Starting #1/1
09:07:12 Starting #2/1
09:07:13 Starting #3/1
09:07:13 Starting #4/1
09:07:14 Starting #1/2
09:07:14 Starting #2/2
09:07:15 Starting #3/2
09:07:15 Starting #4/2
09:07:16 Starting #5/1
09:07:16 Starting #6/1
09:07:17 Starting #7/1
09:07:17 Starting #8/1
09:07:18 Starting #5/2
09:07:18 Starting #6/2
09:07:19 Starting #7/2
09:07:19 Starting #8/2
09:07:20 Starting #9/1
09:07:20 Starting #10/1
09:07:22 Starting #9/2
09:07:22 Starting #10/2

例如,在 09:07:14 标记处,失败的项目 #1 的 1 秒等待期已过期,因此它的第二次尝试应优先于对项目 #5 的第一次尝试。

解决这个问题的一个不成功的尝试是颠倒这两个策略的顺序。不幸的是,将BulkheadPolicy 放在WaitAndRetryPolicy 之前会降低并行化。发生的情况是BulkheadPolicy 将项目的所有重试视为单个操作,因此两次重试之间的“等待”阶段计入并行化限制。显然我不想那样。 documentation 也明确了我的示例中两个策略的顺序是正确的:

BulkheadPolicy:通常在最里面,除非包装了最终的TimeoutPolicy。当然在任何WaitAndRetry 内。 Bulkhead 有意限制并行化。您希望并行化专门用于运行委托,而不是等待重试。

有什么方法可以实现我想要的行为,同时留在 Polly 库的领域?

【问题讨论】:

  • 显然你不能开箱即用。我只需下载源代码并自己进行重载,然后发出拉取请求,或在 github 上请求该功能
  • @TheGeneral 是的,这是解决问题的缓慢、痛苦和费力的方法。我希望 Polly 库的一些有经验的用户知道一些简单的方法来解决它。
  • 我每天都在使用它,但是我看不到直接的方法。也许其他人可以想到一个不合时宜的解决方案。祝你好运
  • @TheGeneral 谢谢!如果您有任何想法,请在 cmets 中分享。 :-)

标签: c# asynchronous parallel-processing polly


【解决方案1】:

我找到了一个简单但不完美的解决方案来解决这个问题。解决方案是在WaitAndRetryPolicy 之前包含第二个BulkheadPolicy(在“外部”位置)。这个额外的Bulkhead 将仅用于重新确定工作负载的优先级(通过充当外部队列),并且应该具有比控制并行化的内部Bulkhead 更大的容量(x10 或更多)。原因是外部Bulkhead 也可能以不可预测的方式影响(减少)并行化,我们不希望这样。这就是我认为这个解决方案不完美的原因,因为优先级不是最优的,也不能保证并行化不会受到影响。

这是原始示例的组合策略,使用外部 BulkheadPolicy 进行了增强。它的容量只有 2.5 倍,适合这个人为的例子,但对于一般情况来说太小了:

var policy = Policy.WrapAsync
(
    Policy.BulkheadAsync( // For improving prioritization
        maxParallelization: 5, maxQueuingActions: Int32.MaxValue),

    Policy
        .Handle<HttpRequestException>()
        .WaitAndRetryAsync(retryCount: 1, _ => TimeSpan.FromSeconds(1)),

    Policy.BulkheadAsync( // For controlling paralellization
        maxParallelization: 2, maxQueuingActions: Int32.MaxValue)
);

这是执行的输出:

12:36:02 Starting #1/1
12:36:02 Starting #2/1
12:36:03 Starting #3/1
12:36:03 Starting #4/1
12:36:04 Starting #2/2
12:36:04 Starting #5/1
12:36:05 Starting #1/2
12:36:05 Starting #3/2
12:36:06 Starting #6/1
12:36:06 Starting #4/2
12:36:07 Starting #8/1
12:36:07 Starting #5/2
12:36:08 Starting #9/1
12:36:08 Starting #7/1
12:36:09 Starting #10/1
12:36:09 Starting #6/2
12:36:10 Starting #7/2
12:36:10 Starting #8/2
12:36:11 Starting #9/2
12:36:11 Starting #10/2

虽然这个解决方案并不完美,但我相信在一般情况下它应该是利大于弊,并且应该会带来更好的整体性能。

【讨论】:

    猜你喜欢
    • 2017-01-08
    • 2017-11-01
    • 2018-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-02
    • 1970-01-01
    相关资源
    最近更新 更多