本文介绍了WaitAndRetryPolicy 与 BulkheadPolicy 相结合,优先重试.是否可以?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am evaluating the Polly library in terms of features and flexibility, and as part of the evaluation process I am trying to combine the WaitAndRetryPolicy with the BulkheadPolicy policies, to achieve a combination of resiliency and throttling. The problem is that the resulting behavior of this combination does not match my expectations and preferences. What I would like is to prioritize the retrying of failed operations over executing fresh/unprocessed operations.

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

The rationale is that (from my experience) a failed operation has greater chances of failing again. So if all failed operations get pushed to the end of the whole process, that last part of the whole process will be painfully slow and unproductive. Not only because these operations may fail again, but also because of the required delay between each retry, that may need to be progressively longer after each failed attempt. So what I want is that each time the BulkheadPolicy has room for starting a new operation, to choose a retry operation if there is one in its queue.

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

Here is an example that demonstrates the undesirable behavior I would like to fix. 10 items need to be processed. All fail on their first attempt and succeed on their second attempt, resulting to a total of 20 executions. The waiting period before retrying an item is one second. Only 2 operations should be active at any moment:

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

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

The expected output should be something like this (I wrote it by hand):

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 的第一次尝试.

For example at the 09:07:14 mark the 1-second wait period of the failed item #1 has been expired, so its second attempt should be prioritized over doing the first attempt of the item #5.

解决此问题的失败尝试是颠倒两个策略的顺序.不幸的是,将 BulkheadPolicy 放在 WaitAndRetryPolicy 之前会导致并行化减少.发生的情况是 BulkheadPolicy 将项目的所有重试视为单一操作,因此等待"不会发生.两次重试之间的阶段计入并行化限制.显然我不想那样.文档 也清楚地说明了我的示例中两个策略的顺序正确:

An unsuccessful attempt to solve this problem is to reverse the order of the two policies. Unfortunately putting the BulkheadPolicy before the WaitAndRetryPolicy results to reduced parallelization. What happens is that the BulkheadPolicy considers all retries of an item to be a singe operation, and so the "wait" phase between two retries counts towards the parallelization limit. Obviously I don't want that. The documentation also makes it clear the the order of the two policies in my example is correct:

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

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

Is there any way to achieve the behavior I want, while staying in the realm of the Polly library?

推荐答案

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

I found a simple but not perfect solution to this problem. The solution is to include a second BulkheadPolicy positioned before the WaitAndRetryPolicy (in an "outer" position). This extra Bulkhead will serve only for reprioritizing the workload (by serving as an outer queue), and should have a substantially larger capacity (x10 or more) than the inner Bulkhead that controls the parallelization. The reason is that the outer Bulkhead could also affect (reduce) the parallelization in an unpredictable way, and we don't want that. This is why I consider this solution imperfect, because neither the prioritization is optimal, nor it is guaranteed that the parallelization will not be affected.

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

Here is the combined policy of the original example, enhanced with an outer BulkheadPolicy. Its capacity is only 2.5 times larger, which is suitable for this contrived example, but too small for the general case:

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

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

Although this solution is not perfect, I believe that it should do more good than harm in the general case, and should result in a better performance overall.

这篇关于WaitAndRetryPolicy 与 BulkheadPolicy 相结合,优先重试.是否可以?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-22 09:55