简体   繁体   English

如何对断路器的所有重试中调用的断路器进行故障预置

[英]How to make fallback for circuit breaker invoked on all retries on the broken circuit

I have the following policies: 我有以下政策:

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

Now, fallbackForCircuitBreaker is only invoked if all retries fail, and if the last retry fails with BrokenCircuitException . 现在, fallbackForCircuitBreaker如果所有试失败时才会激活,而如果最后重试失败, BrokenCircuitException What changes should be made in order for fallbackForCircuitBreaker to be invoked every time a retry is made on a broken circuit? 为了使每次在断路电路上重试时调用fallbackForCircuitBreaker ,应该进行哪些更改?

Also, I am using a sharedBulkHead which is an instance field in the service and is initialized in the constructor. 另外,我正在使用sharedBulkHead ,它是服务中的实例字段,并在构造函数中初始化。 Is that a good practise? 这是一个好习惯吗? What is to be done ideally on onBulkheadRejectedAsync ? 理想情况下在onBulkheadRejectedAsynconBulkheadRejectedAsync什么? Can I modify the retry policy to handle bulk head rejection as well? 我可以修改重试策略来处理批量头拒绝吗?

Now, fallbackForCircuitBreaker is only invoked if all retries fail, and if the last retry fails with BrokenCircuitException. 现在,仅当所有重试均失败且上一次重试因BrokenCircuitException而失败时,才调用fallbackForCircuitBreaker。 What changes should be made in order for fallbackForCircuitBreaker to be invoked every time a retry is made on a broken circuit? 为了使每次在断路电路上重试时调用fallbackForCircuitBreaker,应该进行哪些更改?

See the PolicyWrap documentation , especially the diagrams and description of operation . 请参阅PolicyWrap文档 ,尤其是操作图和说明 Policies in a PolicyWrap act like a sequence of nested middleware around the call: PolicyWrap中的策略就像调用周围的一系列嵌套中间件一样:

  • the outermost (leftmost in reading order) policy executes the next inner, which executes the next inner, etc; 最外层(按读取顺序最左)策略执行下一个内层,该策略执行下一个内层,依此类推; until the innermost policy executes the user delegate; 直到最里面的策略执行用户委托;
  • a thrown exception bubbles back outwards (until handled) through the layers 抛出的异常通过层向外冒泡(直到处理)

So, to make (an equivalent to) fallbackForCircuitBreaker invoked per try, move it inside the retry policy. 因此,要使(等于)每次尝试调用fallbackForCircuitBreaker ,请将其移到重试策略中。

The current fallbackForCircuitBreaker however substitutes the thrown exception with a return value false , whereas it sounds like what you are seeking from fallback-per-try is a 'log, then make the next try'. 但是,当前fallbackForCircuitBreaker会将抛出的异常替换为返回值false ,但这听起来像您从fallback-per-try寻求的是“日志,然后进行下一次尝试”。 The technique for that is to use fallback as log then rethrow , so that your retry policy can still respond to the (rethrown) exception. 这样做的技术是使用fallback作为日志,然后重新抛出 ,以便您的重试策略仍然可以响应( 重新抛出 )异常。 So: 所以:

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

Can I modify the retry policy to handle bulk head rejection as well? 我可以修改重试策略来处理批量头拒绝吗?

The Polly bulkhead documentation states the policy throws BulkheadRejectedException , so: Polly隔板文档说明该策略引发BulkheadRejectedException ,因此:

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

What is to be done ideally on onBulkheadRejectedAsync? 理想情况下在onBulkheadRejectedAsync上要做什么?

You can log. 您可以登录。 Broadly speaking, you can shed the excess load, or use the bulkhead rejection as a trigger to scale horizontally to increase capacity. 广义上讲,您可以减轻多余的负载,也可以将隔板抑制作为触发来水平扩展以增加容量。 The Polly documentation provides more discussion here and here . Polly文档在此处此处提供了更多讨论。

Also, I am using a sharedBulkHead which is an instance field in the service and is initialized in the constructor. 另外,我正在使用sharedBulkHead,它是服务中的实例字段,并在构造函数中初始化。 Is that a good practise? 这是一个好习惯吗?

It depends. 这取决于。 The lifetime of the Bulkhead policy instance must be long-lived across the governed calls , not per call, in order for the state of the Bulkhead policy instance to govern the number of calls executing concurrently. 为了使Bulkhead策略实例的状态能够控制并发执行的呼叫数,Bulkhead策略实例的生存期必须在受控制的调用 (而不是每个调用)中具有较长的寿命

  • If the service exists as a long-lived singleton, holding the bulkhead policy in an instance field would be appropriate as the bulkhead policy instance would also be long lived. 如果该服务作为一个长期存在的单例存在,则将隔板策略保留在实例字段中将是适当的,因为隔板策略实例也将是长期生存的。
  • If instances of the service class are created as transient/per-request by a DI container, you would need to ensure the bulkhead policy instance was still long-lived and shared across concurrent requests (eg by making it static ), not per-request. 如果服务容器的实例是由DI容器创建为临时/每个请求的实例,则您需要确保隔板策略实例仍然有效并在并发请求之间共享(例如,通过使其变为static ),而不是每个请求。
  • If instances of the bulkhead are configured on an HttpClient by HttpClientFactory, follow the notes on scoping stateful policies with HttpClientFactory in the Polly and HttpClientFactory documentation . 如果通过HttpClientFactory在HttpClient上配置了隔板的实例,请遵循Polly和HttpClientFactory文档中有关使用HttpClientFactory范围化有状态策略的说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM