如何使用断路器?

发布于 2024-12-08 05:40:54 字数 1089 浏览 0 评论 0原文

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

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

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

I'm looking for ways to make remote calls to services out of my control until a connect is successful. I also don't want to simply set a timer where an action gets executed every n seconds/minutes until successful. After a bunch of research it appears that the circuit breaker pattern is a great fit.

I found an implementation that uses an Castle Windsor interceptor, which looks awesome. The only problem is I don't know how to use it. From the few articles I found regarding the topic the only usage example I was able to find was to simply use the circuit breaker to call an action only once, which doesn't seem very useful. From that it seems I need to simply run my action using the circuit breaker in a while(true) loop.

How do I use the Windsor interceptor to execute an action making a call to an external service until it is successful without slamming our servers?

Could someone please fill in the missing pieces?

Here is what I was able to come up with

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();
    }
}

Based on that I think I now understand this concept as well as how to apply it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

酒浓于脸红 2024-12-15 05:40:54

如果有很多线程访问同一资源,这是一个有趣的想法。其工作方式是汇集所有线程的尝试计数。您不必担心编写一个循环来在实际失败之前尝试访问数据库 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;
}

通过此拦截器实现,拦截器将被注册为单例。因此,对于对任何方法的任何调用,资源类的所有实例都将首先通过断路器重定向代码。 拦截器只是您的类的代理。它基本上会覆盖您的方法并在调用您的方法之前先调用拦截器方法。

如果您没有任何电路理论知识,开/关位可能会令人困惑。
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 */
        }
    }

This is an interesting idea if you have lots of threads hitting the same resource. The way this works is by pooling the count for attempts from all threads. Rather than worrying about writing a loop to try and hit the database 5 times before actually failing, you have the circuit breaker keep track of all attempts to hit the resource.

In one example, you have say 5 threads running a loop like this (pseudo-code):

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

Assuming your error handling is correct and all, this loop could be run 5 times, and ping the resource a total of 50 times.

The circuit breaker tries to reduce the total number of times it attempts to reach the resource. Each thread, or request attempt, will increment a single error counter. Once the error limit is reached, the circuit breaker will not try to connect to it's resource for any more calls on any threads until the timeout has elapsed. It's still the same effect of polling the resource until it's ready, but you reduce the total load.

static volatile int errorCount = 0;

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

With this interceptor implementation, the interceptor is being registered as a singleton. So, all instances of your resource class will have code redirected through the circuit breaker first for any call made to any method. The interceptor is just a proxy to your class. It basically overrides your methods and calls the interceptor method first before calling your method.

The Open/Closed bit might be confusing if you don't have any circuit theory knowledge.
wiki:

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

In theory, this circuit is Open when the connection is down and Closed when the connection is available. The important part of your example is this:

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 */
        }
    }
爱要勇敢去追 2024-12-15 05:40:54

我创建了一个名为 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
}

它可以通过 nuget 包 获得。您可以在 github 上找到源代码

I've created a library called CircuitBreaker.Net that encapsulates all serving logic to safely perform calls. It's easy to use, an example could look like:

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

It's available via a nuget package. You can find the sources on github.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文