c# - 如何使用断路器?

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

在连接成功之前,我正在寻找不受我控制的远程调用服务的方法。我也不想简单地设置一个计时器,让一个 Action 每 n 秒/分钟执行一次,直到成功。经过大量研究,断路器模式似乎非常适合。

我找到了一个 implementation它使用 CaSTLe Windsor 拦截器,看起来很棒。唯一的问题是我不知道如何使用它。从我找到的关于该主题的几篇文章中,我能找到的唯一用法示例是简单地使用断路器仅调用一个操作一次,这似乎不是很有用。由此看来,我似乎只需要在 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();
    }
}

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

最佳答案

如果您有很多线程访问同一个资源,这是一个有趣的想法。它的工作方式是汇集所有线程的尝试计数。与其担心在实际失败之前编写一个循环来尝试访问数据库 5 次,不如让断路器跟踪所有访问资源的尝试。

在一个示例中,假设有 5 个线程运行这样一个循环(伪代码):

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

假设您的错误处理是正确的,这个循环可以运行 5 次,并且总共 ping 资源 50 次。

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

static volatile int errorCount = 0;

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

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

如果您没有任何电路理论知识,开/关位可能会造成混淆。 wiki:

An electric circuit is an "open circuit" if it lacks a complete path between the positive and negative terminals of its power source

理论上,该电路在连接断开时打开,在连接可用时关闭。您示例的重要部分是:

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 */
        }
    }

关于c# - 如何使用断路器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7675535/

相关文章:

c# - 关闭密码表单 c#

mysql - 库存管理数据库: definition vs.实例

iOS 如何最好地将用户对象传递到多个 View Controller (依赖注入(inject))

c# - 正则表达式从 img 标签获取 src 值

c# - 用于将双数组转换为逗号分隔字符串的 lambda 表达式

java - 为什么jaxb会生成这样的代码?

dependency-injection - 我如何构建既可以作为 OSGI bundle 又可以作为 WAR 运行的应用程序

php - Symfony 4 服务依赖注入(inject)——构造函数与方法

c# - 绑定(bind)到 Nullable<DateTime> 控件属性

wpf - 对于 MVVM,viewmodel 将模型中的属性导出到 View 的最佳做法是什么?