15

接続が成功するまで、制御できないサービスへのリモート呼び出しを行う方法を探しています。また、アクションが成功するまでn秒/分ごとに実行されるタイマーを単純に設定したくありません。一連の調査の結果、サーキット ブレーカー パターンが最適であることがわかりました。

Castle Windsor インターセプターを使用する実装を見つけました。唯一の問題は、使い方がわからないことです。このトピックに関して見つけたいくつかの記事から、私が見つけることができた唯一の使用例は、サーキットブレーカーを使用してアクションを1だけ呼び出すことでした。これはあまり役に立ちません。そのことから、サーキットブレーカーを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();
    }
}

それに基づいて、この概念とそれを適用する方法を理解したと思います。

4

2 に答える 2

10

多数のスレッドが同じリソースにアクセスしている場合、これは興味深いアイデアです。これが機能する方法は、すべてのスレッドからの試行のカウントをプールすることです。実際に失敗する前にデータベースを 5 回試行してヒットするループを書くことを心配するのではなく、リソースをヒットしようとするすべての試行をサーキット ブレーカーで追跡します。

1 つの例では、次のようなループを実行している 5 つのスレッドがあるとします (疑似コード)。

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

エラー処理がすべて正しいと仮定すると、このループを 5 回実行し、リソースに対して合計 50 回 ping を実行できます。

サーキット ブレーカーは、リソースへの到達を試行する合計回数を減らそうとします。スレッドごと、またはリクエストの試行ごとに、1 つのエラー カウンターがインクリメントされます。エラー制限に達すると、サーキット ブレーカーは、タイムアウトが経過するまで、任意のスレッドでそれ以上の呼び出しのためにそのリソースに接続しようとしません。準備が整うまでリソースをポーリングするのと同じ効果ですが、総負荷を減らします。

static volatile int errorCount = 0;

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

このインターセプターの実装では、インターセプターはシングルトンとして登録されています。そのため、リソース クラスのすべてのインスタンスは、任意のメソッドへの呼び出しに対して、最初にサーキット ブレーカーを介してリダイレクトされるコードを持ちます。インターセプターは、クラスへの単なるプロキシです。基本的に、メソッドをオーバーライドし、メソッドを呼び出す前に最初にインターセプター メソッドを呼び出します。

回路理論の知識がない場合、Open/Closed ビットは混乱する可能性があります。 ウィキ:

電源の正端子と負端子の間に完全な経路がない場合、電気回路は「開回路」です。

理論的には、この回路は、接続がダウンしている場合は開いており、接続が利用できる場合は閉じています。あなたの例の重要な部分はこれです:

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 */
        }
    }
于 2011-10-06T15:04:07.873 に答える
5

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 にあります。

于 2016-03-10T19:48:01.157 に答える