微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

WaitAndRetryPolicy与BulkheadPolicy结合使用,优先级重试可能吗?

如何解决WaitAndRetryPolicy与BulkheadPolicy结合使用,优先级重试可能吗?

我正在评估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库的范围内,有什么方法可以实现我想要的行为?

解决方法

我找到了解决此问题的简单但不完美的解决方案。解决方案是在BulkheadPolicy之前(在“外部”位置)包含第二个WaitAndRetryPolicy。额外的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

尽管此解决方案并不完美,但我认为在一般情况下,它应该比弊大于利,并且总体上应该会带来更好的性能。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。