我如何使用断路器?

时间:2011-10-06 14:11:38

标签: c# design-patterns dependency-injection interceptor circuit-breaker

我正在寻找在我的控制之外远程调用服务的方法,直到连接成功为止。我也不想简单地设置一个计时器,每隔 n 秒/分钟执行一次动作,直到成功为止。经过一系列研究后,看来断路器模式非常合适。

我找到一个使用Castle Windsor拦截器的implementation,它看起来很棒。唯一的问题是我不知道如何使用它。从我发现的有关该主题的几篇文章中,我能够找到的唯一用法示例是简单地使用断路器来仅调用一次动作,这似乎不太有用。从那看起来我需要在while(true)循环中使用断路器简单地运行我的动作。

如何使用Windsor拦截器执行调用外部服务的操作,直到它成功而不会关闭我们的服务器?

有人可以填写缺失的部分吗?

这是我能够提出的

while(true)
{
    try
    {
        service.Subscribe();
        break;
    }
    catch (Exception e)
    {
        Console.WriteLine("Gotcha!");

        Thread.Sleep(TimeSpan.FromSeconds(10));
    }
}

Console.WriteLine("Success!");

public interface IService
{
    void Subscribe();
}

public class Service : IService
{
    private readonly Random _random = new Random();

    public void Subscribe()
    {
        var a = _random.Next(0, 10) % 2421;
        if(_random.Next(0, 10) % 2 != 0)
            throw new AbandonedMutexException();
    }
}

基于此,我认为我现在理解这个概念以及如何应用它。

2 个答案:

答案 0 :(得分:8)

如果你有很多线程遇到同一个资源,这是一个有趣的想法。这种方式的工作方式是汇集所有线程的尝试计数。您可以让断路器跟踪所有击中资源的尝试,而不是担心编写一个循环尝试在实际失败之前尝试击中数据库5次。

在一个例子中,你说5个线程运行这样的循环(伪代码):

int errorCount = 0;
while(errorCount < 10) // 10 tries
{
    if(tryConnect() == false)
      errorCount++;
    else
      break;
}

假设您的错误处理正确且全部,此循环可以运行5次,并且对资源执行总共50次。

断路器试图减少尝试到达资源的总次数。每个线程或请求尝试都将增加一个错误计数器。一旦达到错误限制,断路器将不会尝试连接到它的资源,以便在超时之前对任何线程进行更多调用。轮询资源直到它准备就绪仍然是相同的效果,但是你减少了总负载。

static volatile int errorCount = 0;

while(errorCount < 10)
{
   if(tryConnect() == false)
      errorCount++;
   else
       break;
}

通过这个拦截器实现,拦截器被注册为单例。因此,对于对任何方法的任何调用,您的资源类的所有实例都将首先通过断路器重定向代码。你班上的interceptor is just a proxy。它基本上覆盖了你的方法,并在调用你的方法之前先调用拦截器方法。

如果您没有任何电路理论知识,则打开/关闭位可能会令人困惑。 wiki:

  

如果缺少完整的路径,电路就是“开路”   在其电源的正极和负极之间

理论上,当连接断开时,此电路处于打开状态,当连接可用时,此电路处于关闭状态。您的示例的重要部分是:

public void Intercept(IInvocation invocation)
    {
        using (TimedLock.Lock(monitor))
        {
            state.ProtectedCodeIsAboutToBeCalled(); /* only throws an exception when state is Open, otherwise, it doesn't do anything. */
        }

        try
        {
            invocation.Proceed(); /* tells the interceptor to call the 'actual' method for the class that's being proxied.*/
        }
        catch (Exception e)
        {
            using (TimedLock.Lock(monitor))
            {
                failures++; /* increments the shared error count */
                state.ActUponException(e); /* only implemented in the ClosedState class, so it changes the state to Open if the error count is at it's threshold. */ 
            }
            throw;
        }

        using (TimedLock.Lock(monitor))
        {
            state.ProtectedCodeHasBeenCalled(); /* only implemented in HalfOpen, if it succeeds the "switch" is thrown in the closed position */
        }
    }

答案 1 :(得分:4)

我创建了一个名为CircuitBreaker.Net的库,它封装了所有服务逻辑以安全地执行调用。它易于使用,示例可能如下:

// Initialize the circuit breaker
var circuitBreaker = new CircuitBreaker(
    TaskScheduler.Default,
    maxFailures: 3,
    invocationTimeout: TimeSpan.FromMilliseconds(100),
    circuitResetTimeout: TimeSpan.FromMilliseconds(10000));

try
{
    // perform a potentially fragile call through the circuit breaker
    circuitBreaker.Execute(externalService.Call);
    // or its async version
    // await circuitBreaker.ExecuteAsync(externalService.CallAsync);
}
catch (CircuitBreakerOpenException)
{
    // the service is unavailable, failover here
}
catch (CircuitBreakerTimeoutException)
{
    // handle timeouts
}
catch (Exception)
{
    // handle other unexpected exceptions
}

可通过a nuget package获取。您可以找到the sources on github