Как мне использовать автоматический выключатель? - PullRequest
14 голосов
/ 06 октября 2011

Я ищу способы сделать удаленные вызовы служб вне моего контроля, пока соединение не будет успешным.Я также не хочу просто устанавливать таймер, в котором действие будет выполняться каждые n секунд / минут до его успешного выполнения.После нескольких исследований выяснилось, что схема автоматического выключателя отлично подходит.

Я нашел реализацию , которая использует перехватчик 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();
    }
}

Исходя из этого, я думаю, что теперь я понимаю эту концепцию, а также способы ее применения.

Ответы [ 2 ]

8 голосов
/ 06 октября 2011

Это интересная идея, если у вас много потоков, попадающих на один и тот же ресурс.Это работает путем объединения количества попыток из всех потоков.Вместо того, чтобы беспокоиться о том, чтобы написать цикл, чтобы попытаться выполнить попытку обращения к базе данных 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;
}

В этой реализации перехватчика перехватчик регистрируется как одиночный.Таким образом, все экземпляры вашего класса ресурсов будут перенаправлять код сначала через автоматический выключатель для любого вызова любого метода.Перехватчик является просто прокси для вашего класса.Он в основном переопределяет ваши методы и вызывает метод перехватчика, прежде чем вызывать ваш метод.

Бит Open / Closed может сбивать с толку, если у вас нет знаний по теории цепей. 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 */
        }
    }
4 голосов
/ 10 марта 2016

Я создал библиотеку под названием 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 .

...