执行多个策略。

13

如何执行多个策略(或将它们合并为一个策略)?

例如,我有:

var policy1 = Policy.Handle<DivideByZeroException>().WaitAndRetry(5));

var policy2 = Policy.Handle<StackOverflowException>().RetryForever();
如何同时将它们应用于一个方法?
2个回答

25

从 Polly v5.0 开始,新增了一个名为 PolicyWrap 的类,可以让您组合多个策略。

var policy1 = Policy.Handle<DivideByZeroException>().WaitAndRetry(3, i => TimeSpan.FromSeconds(1));
var policy2 = Policy.Handle<StackOverflowException>().RetryForever();
PolicyWrap policyWrap = Policy.Wrap(policy1, policy2);
policyWrap.Execute(someGreatAction);

2

有几种方法可以结合两个策略。为了完整起见,让我列出所有不同的选项。我会针对每个选项进行推荐:何时使用它以及何时避免使用它

根据异常进行分支

ExceptionDispatchInfo edi = null;
int retryCounter = 0;

var combinedPolicy = Policy
       .Handle<DivideByZeroException>()
       .Or<StackOverflowException>()
       .WaitAndRetry(CalculateSleep());

combinedPolicy.Execute(() => {
    try
    {
        //User code...
        throw new DivideByZeroException();
    }
    catch (Exception ex)
    {
        edi = ExceptionDispatchInfo.Capture(ex);
        edi.Throw();
    }
});

IEnumerable<TimeSpan> CalculateSleep()
{
    while (true)
    {
        var wasItADivideByZero = edi.SourceException is DivideByZeroException;
        var attempt = retryCounter++;
        if (wasItADivideByZero)
        {
            if (attempt > 3) break;
            yield return TimeSpan.FromSeconds(1);
        }
        yield return TimeSpan.FromSeconds(0);
    }
}

注意事项

使用方法

  • 如果您有两个策略,最大重试次数相同,但休眠时间和触发器不同,例如:
var combinedPolicy = Policy
       .Handle<DivideByZeroException>()
       .Or<StackOverflowException>()
       .WaitAndRetry(3,
       (_, ex, __) => TimeSpan.FromSeconds(ex is DivideByZeroException ? 1 : 2),
       (_, __, ___, ____) => { });

避免重复造轮子

  • 如果你有其他选择,请不要重复发明已有的东西
  • 虽然上述解决方案非常有效,但它很复杂且难以维护

显式延续

policy1.Execute(() =>
{
    policy2.Execute(() =>
    {
        //User code
    });
});

或者

policy2.Execute(() =>
{
    policy1.Execute(() =>
    {
        //User code
    });
});

注意事项

  • 这里的顺序并不重要,因为这两个策略是独立的。
    • 但请记住,我们在这里实现了一个升级链
  • 因此,如果我们有重试和超时策略,则顺序很重要。
    • 外层:超时;内层:重试 >> 超时是全局性的、总体性的
    • 外层:重试;内层:超时 >> 超时是本地的(每次尝试都会发生)

使用方法

  • 如果您只有两个策略,并且要装饰的代码已经提取到一个单独的方法中,就像这样:
policy1.Execute(policy2.Execute(UserCode()));

避免进入“回调地狱”

Wrap实例方法

var combinedPolicy = policy1.Wrap(policy2);
combinedPolicy.Execute(UserCode());

或者

var combinedPolicy = policy2.Wrap(policy1);
combinedPolicy.Execute(UserCode());

注意事项

  • 我们可以将策略定义与其执行分开
  • 这里的顺序也不重要
  • combinedPolicy的类型是PolicyWrap,它也实现了ISyncPolicy接口

使用方法

  • 当您想要将策略定义与其用法分开时
  • 适合组合不超过两个策略

避免使用

  • 如果您想要组合超过3个策略,则会显得很丑陋(仅个人意见)
var combinedPolicy = globalTimeout.Wrap(retry).Wrap(circuitBreaker).Wrap(localTimeout);
  • 如果您有一个针对void和另一个针对TResult的策略,那么系统会尝试智能处理。
    • combinedPolicy的类型现在是PolicyWrap<string>
  • 这种组合可能是有意的,也可能是无意的。
    • 如果是无意的,则不会警告您。
var policy1 = Policy.Handle<DivideByZeroException>().WaitAndRetry(3, i => TimeSpan.FromSeconds(1));
var policy2 = Policy<string>.Handle<StackOverflowException>().RetryForever();

var combinedPolicy = policy1.Wrap(policy2);
string? result = combinedPolicy.Execute(() => "dummy response");

Wrap 静态方法

var combinedPolicy = Policy.Wrap(policy2, policy1);

或者

var combinedPolicy = Policy.Wrap(policy2, policy1);

注意事项

  • 这个静态方法可以接收任意数量的 ISyncPolicyISyncPolicy<TResult> 策略。

使用方法

  • 使用此方法时,您不能同时组合 ISyncPolicyISyncPolicy<string> 策略,因为所有策略都应该具有相同的类型。
var combinedPolicy = Policy.Wrap<HttpResponseMessage>(globalTimeout,retry,circuitBreaker,localTimeout);

避免使用

  • 当您想要明确地组合两个或多个策略,其中您有一个TResult和一个或多个void
    • 或多个TResult和一个或多个void
  • 请注意:您不能组合具有TResult1TResult2的两个策略

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接