Как сделать запасной вариант для автоматического выключателя, который вызывается при всех повторных попытках обрыва цепи - PullRequest
0 голосов
/ 27 декабря 2018

У меня есть следующие политики:

var sharedBulkhead = Policy.BulkheadAsync(
            maxParallelization: maxParallelizations, 
            maxQueuingActions: maxQueuingActions,
            onBulkheadRejectedAsync: (context) =>
            {
                Log.Info($"Bulk head rejected => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}");
                return TaskHelper.EmptyTask;
            }
        );

var retryPolicy = Policy.Handle<HttpRequestException>()
.Or<BrokenCircuitException>().WaitAndRetryAsync(
            retryCount: maxRetryCount,
            sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)),
            onRetryAsync: (exception, calculatedWaitDuration, retryCount, context) =>
            {
                Log.Error($"Retry => Count: {retryCount}, Wait duration: {calculatedWaitDuration}, Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}, Exception: {exception}.");
                return TaskHelper.EmptyTask;
            });

            var circuitBreaker = Policy.Handle<Exception>(e => (e is HttpRequestException)).CircuitBreakerAsync(
            exceptionsAllowedBeforeBreaking: maxExceptionsBeforeBreaking, 
            durationOfBreak: TimeSpan.FromSeconds(circuitBreakDurationSeconds), 
            onBreak: (exception, timespan, context) =>
            {
                Log.Error($"Circuit broken => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}, Exception: {exception}");
            },
            onReset: (context) =>
            {
                Log.Info($"Circuit reset => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}");
            }
        );

var fallbackForCircuitBreaker = Policy<bool>
         .Handle<BrokenCircuitException>()
         .FallbackAsync(
             fallbackValue: false,
             onFallbackAsync: (b, context) =>
             {
                 Log.Error($"Operation attempted on broken circuit => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}");
                 return TaskHelper.EmptyTask;
             }
         );

var fallbackForAnyException = Policy<bool>
            .Handle<Exception>()
            .FallbackAsync(
                fallbackAction: (ct, context) => { return Task.FromResult(false); },
                onFallbackAsync: (e, context) =>
                {
                    Log.Error($"An unexpected error occured => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}");
                    return TaskHelper.EmptyTask;
                }
            );


var resilienceStrategy = Policy.WrapAsync(retryPolicy, circuitBreaker, sharedBulkhead);
        var policyWrap = fallbackForAnyException.WrapAsync(fallbackForCircuitBreaker.WrapAsync(resilienceStrategy));

Теперь fallbackForCircuitBreaker вызывается только в случае сбоя всех попыток и если последняя попытка не удалась с BrokenCircuitException.Какие изменения необходимо внести для того, чтобы fallbackForCircuitBreaker вызывался каждый раз при повторной попытке прерывистой цепи?

Кроме того, я использую sharedBulkHead, которое является полем экземпляра в службе и являетсяинициализируется в конструкторе.Это хорошая практика?Что в идеале должно быть сделано на onBulkheadRejectedAsync?Могу ли я изменить политику повторных попыток, чтобы обрабатывать также массовые отклонения головки?

1 Ответ

0 голосов
/ 30 декабря 2018

Теперь fallbackForCircuitBreaker вызывается только в случае сбоя всех повторных попыток и если последняя повторная попытка завершится неудачей с BrokenCircuitException.Какие изменения необходимо внести для того, чтобы fallbackForCircuitBreaker вызывался каждый раз, когда повторная попытка выполняется на разорванной цепи?

См. Документацию PolicyWrap , особенно диаграммы иописание операции .Политики в PolicyWrap действуют как последовательность вложенного промежуточного программного обеспечения вокруг вызова:

  • самая внешняя (самая левая в порядке чтения) выполняет следующую внутреннюю часть, которая выполняет следующую внутреннюю часть и т. Д .;пока внутренняя политика не выполнит пользовательский делегат;
  • выброшенное исключение пузырится назад (пока не будет обработано) через слои

Таким образом, чтобы (эквивалентный) fallbackForCircuitBreaker вызывалсяпри попытке переместите его в политику повторных попыток.

Текущий fallbackForCircuitBreaker, однако, заменяет выброшенное исключение возвращаемым значением false, в то время как звучит так, как будто вы ищете резервный вариант на«войти, затем сделать следующую попытку».Техника для этого заключается в использовании отката в качестве log, а затем повторного выброса , чтобы ваша политика повторных попыток все еще могла реагировать на (повторно выброшенное) исключение.Итак:

var sharedBulkhead = /* as before */;
var retryPolicy = /* as before */;
var fallbackForCircuitBreaker = /* as before */;
var logCircuitBreakerBrokenPerTry = Policy<bool>
     .Handle<BrokenCircuitException>()
     .FallbackAsync(
         fallbackValue: false,
         onFallbackAsync: (outcome, context) =>
         {
             Log.Error($"Operation attempted on broken circuit => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}");
             throw outcome.Exception;
         }
     );
var fallbackForAnyException = /* as before */;

var resilienceStrategy = Policy.WrapAsync(retryPolicy, logCircuitBreakerBrokenPerTry, circuitBreaker, sharedBulkhead);
var policyWrap = fallbackForAnyException.WrapAsync(fallbackForCircuitBreaker.WrapAsync(resilienceStrategy));

Могу ли я изменить политику повторных попыток, чтобы обрабатывать также отклонение больших объемов?

Документация Переборка Polly заявляет, что политики выбрасывает BulkheadRejectedException, поэтому:

var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .Or<BrokenCircuitException>()
    .Or<BulkheadRejectedException>()
    /* etc */

Что в идеале должно быть сделано на onBulkheadRejectedAsync?

Вы можете войти.Вообще говоря, вы можете сбросить лишнюю нагрузку или использовать отклонение переборки в качестве триггера для горизонтального масштабирования для увеличения пропускной способности.Документация Polly обеспечивает более подробное обсуждение здесь и здесь .

Кроме того, я использую sharedBulkHead, который является полем экземпляра в службе и инициализируется вконструкторЭто хорошая практика?

Это зависит.Время жизни экземпляра политики перегородки должно быть долгоживущим по управляемым вызовам , а не по вызову, чтобы состояние экземпляра политики переборки определяло число одновременных вызовов.

  • Если служба существует как долгоживущий одноэлементный, хранение политики переборки в поле экземпляра будет целесообразным, так как экземпляр политики переборки также будет долгоживущим.
  • Если экземпляры класса обслуживания создаются как переходные / на запрос в контейнере DI, вам необходимо убедиться, что экземпляр политики переборки все еще является долгоживущим и используется совместно для одновременных запросов (например, делая его static), не по запросу.
  • Если экземпляры переборки настроены на HttpClient с помощью HttpClientFactory, следуйте указаниям по определения политики с сохранением состояния с помощью HttpClientFactory в документации Polly и HttpClientFactory .
...