[英]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
? 理想情况下在onBulkheadRejectedAsync
上onBulkheadRejectedAsync
什么? 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中的策略就像调用周围的一系列嵌套中间件一样:
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策略实例的生存期必须在受控制的调用 (而不是每个调用)中具有较长的寿命 。
static
), not per-request. 如果服务容器的实例是由DI容器创建为临时/每个请求的实例,则您需要确保隔板策略实例仍然有效并在并发请求之间共享(例如,通过使其变为static
),而不是每个请求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.