我是否应该在调用外部资源的嵌套方法上同时实现重试策略和断路器?

Should I implement both Retry Policy and Circuit Breaker on nested methods calling external resources?

提问人:Maltion 提问时间:9/21/2023 最后编辑:Peter CsalaMaltion 更新时间:9/21/2023 访问量:112

问:

我有一个多层应用程序,其中调用.在内部,我与 Redis 和事件中心等外部资源进行了交互。我已经在 上实施了重试策略。Method1Method2Method2Method1

现在,我正在考虑实现断路器模式以获得更好的容错能力。我主要关心的是在哪里实现这种断路器模式。它应该在访问外部资源的级别吗?Method2

此外,我应该专门在 中实现另一个重试策略,还是因为调用中已经有一个重试策略,所以会矫枉过正?Method2MainMethod1

下面是上下文调用层次结构的简化概述:

using Polly;
using Polly.CircuitBreaker;
using System;

class Program
{
    // Define Circuit Breaker Policy as a static member
    private static readonly CircuitBreakerPolicy circuitBreakerPolicy = Policy
        .Handle<Exception>()
        .CircuitBreaker(2, TimeSpan.FromMinutes(1));

    static void Main(string[] args)
    {
        // Define Retry Policy
        var retryPolicy = Policy
            .Handle<Exception>()
            .Retry(3);

        // Wrap the Retry policy around Method1
        try
        {
            retryPolicy.Execute(Method1);
        }
        catch (Exception e)
        {
            Console.WriteLine($"Failed to execute Method1. Reason: {e.Message}");
        }
    }

    static void Method1()
    {
        Console.WriteLine("Executing Method1");
        //Other calls to external resources
        Method2();
    }

    static void Method2()
    {
        Console.WriteLine("Executing Method2");

        circuitBreakerPolicy.Execute(() =>
        {
            // Simulate external resource call
            Console.WriteLine("Calling external resources like Redis, Event Hub etc.");
            
            // Uncomment to simulate failure
            // throw new Exception("Simulated external resource failure");
        });
    }
}

我很想听听一些关于如何处理这个问题的最佳实践或经验。

c# 。网 波莉 断路器 重试逻辑

评论

1赞 Ralf 9/21/2023
您可能需要添加“Polly”标签。

答:

2赞 Peter Csala 9/21/2023 #1

断路器

此复原模式用于防止已经陷入困境的服务过载。

它通过计算连续/连续故障(由普通断路器完成)或在负载波动的情况下(由高级断路器完成)在采样期间的故障来检测服务遇到困难。

如果下游被认为不正常,则它将不允许新请求,并将使用 .BrokenCircuitException

这里的关键点是,您应该为每个下游服务定义一个 CB。如果你的方法与多个服务通信,那么你也应该有多个断路器。这是有道理的,因为

  • 它们可能以不同的方式表示不健康
  • 它们的故障阈值可能不同
  • 他们的传入请求速率和数量可能不同
  • 等。。

因此,在您的用例中,拥有单个共享 CB 是不够的。

我很高兴看到您已将断路器定义为共享资源。这很重要,因为断路器是一种有状态策略(在这里我详细介绍了它是如何实现的)。换句话说,如果您为每个外部调用重新创建它,那么您将丢失上一个 CB 已经检测到下游不正常的信息。

因此,请将所有断路器保持共享状态,不要按需创建它们。

重试

正如我在评论部分所指出的,并非所有操作都是可重试的。若要使用重试,必须确保满足所有先决条件。总之:

  • 您应该只重试暂时性故障(如果出现永久性故障,例如输入验证失败,则无济于事)
  • 您应仅重试幂等操作(如果您的操作产生副作用,并且您没有适当的重复数据消除逻辑,则可能会造成伤害)
  • 仅当增加的延迟是可以接受的(如果重试次数过多或睡眠持续时间过长,则整个过程可能会超时)时才应重试

两者的结合

我已经多次详细介绍了这个话题,所以请允许我在这里添加一些链接: