gcs*_*cso 14 c# design-patterns dependency-injection interceptor circuit-breaker
我正在寻找在我的控制之外远程调用服务的方法,直到连接成功为止.我也不想简单地设置一个定时器,每隔n秒/分钟执行一次动作,直到成功为止.经过一系列研究后,似乎断路器模式非常适合.
我发现了一个实现,它使用温莎城堡拦截弹,这看起来真棒.唯一的问题是我不知道如何使用它.从几篇文章,我发现关于该主题的唯一使用的例子,我能找到的就是简单地使用断路器只调用一个动作一次,这似乎不是很有用.从那看起来我需要简单地在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();
}
}
Run Code Online (Sandbox Code Playgroud)
基于此,我想我现在理解这个概念以及如何应用它.
如果你有很多线程命中相同的资源,这是一个有趣的想法.这种方式的工作方式是汇集所有线程的尝试计数.您可以让断路器跟踪所有击中资源的尝试,而不是担心编写一个循环来尝试在实际失败之前尝试击中数据库5次.
在一个例子中,你说5个线程运行这样的循环(伪代码):
int errorCount = 0;
while(errorCount < 10) // 10 tries
{
if(tryConnect() == false)
errorCount++;
else
break;
}
Run Code Online (Sandbox Code Playgroud)
假设你的错误处理是正确的,那么这个循环可以运行5次,并且总共ping资源50次.
断路器试图减少它尝试到达资源的总次数.每个线程或请求尝试都将增加一个错误计数器.一旦达到错误限制,断路器将不会尝试连接到它的资源,以便在超时之前对任何线程进行更多调用.轮询资源直到它准备就绪仍然是相同的效果,但是你减少了总负载.
static volatile int errorCount = 0;
while(errorCount < 10)
{
if(tryConnect() == false)
errorCount++;
else
break;
}
Run Code Online (Sandbox Code Playgroud)
通过这种拦截器实现,拦截器被注册为单例.因此,对于对任何方法的任何调用,您的资源类的所有实例都将首先通过断路器重定向代码.该拦截器只是一个代理上您的课.它基本上覆盖了你的方法,并在调用你的方法之前先调用拦截器方法.
如果您没有任何电路理论知识,则打开/关闭位可能会令人困惑. 维基:
如果电路在其电源的正极和负极之间缺少完整的路径,则该电路是"开路"
从理论上讲,当连接断开时,该电路是打开的,当连接可用时,该电路是闭合的.您的示例的重要部分是:
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 */
}
}
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
It's available via a nuget package. You can find the sources on github.
| 归档时间: |
|
| 查看次数: |
5308 次 |
| 最近记录: |