Polly熔断与重试策略异常:重试未达配置次数即终止
Polly重试与熔断器组合时未达到配置最大重试次数的问题
问题概述
配置了MaxRetryAttempts = 100的Polly重试策略,结合熔断器后,实际重试在约80次左右就终止,未达到预期的100次。预期重试应持续到以下两种情况之一:
- 熔断器处于打开状态且达到最大重试次数
- 熔断器恢复闭合后请求成功返回200状态码
但日志显示第82次重试后熔断器重新打开,随后重试直接终止,无后续重试记录。
代码示例
services.AddHttpClient<SomeAppClient>() .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://localhost:7143/")) // .AddStandardResilienceHandler(); .AddResilienceHandler("app-pipeline", pipelinebuilder => { pipelinebuilder.AddRetry(new HttpRetryStrategyOptions { BackoffType = DelayBackoffType.Constant, Delay = TimeSpan.FromMilliseconds(100), MaxRetryAttempts = 100, OnRetry = static args => { Serilog.Log.Logger.Information($"Retry {args.AttemptNumber}, after {args.RetryDelay.TotalMilliseconds:F2}ms, due to: {args.Outcome.Result?.StatusCode.ToString() ?? args.Outcome.Exception?.GetType().Name} "); return default; }, ShouldHandle = new PredicateBuilder<HttpResponseMessage>() .Handle<HttpRequestException>() .Handle<BrokenCircuitException>(), UseJitter = false }); pipelinebuilder.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions { BreakDuration = TimeSpan.FromSeconds(5), FailureRatio = 0.1, MinimumThroughput = 2, OnClosed = static args => { Serilog.Log.Logger.Information("Closing circuit now"); return default; }, OnOpened = static args => { Serilog.Log.Logger.Information("Opening circuit now"); return default; }, OnHalfOpened = static args => { Serilog.Log.Logger.Information("Circuit half opened now"); return default; }, SamplingDuration = TimeSpan.FromSeconds(2), ShouldHandle = new PredicateBuilder<HttpResponseMessage>() .Handle<Exception>() .HandleResult(r => r.StatusCode == System.Net.HttpStatusCode.InternalServerError) }); pipelinebuilder.AddTimeout(TimeSpan.FromSeconds(1)); });
日志片段
[08:21:39 INF] Retry 37, after 100.00ms, due to: BrokenCircuitException [08:21:40 INF] Retry 36, after 100.00ms, due to: BrokenCircuitException [08:21:40 INF] Circuit half opened now [08:21:40 INF] Opening circuit now [08:21:40 ERR] Server returned a 500 [08:21:40 INF] Retry 37, after 100.00ms, due to: BrokenCircuitException ... [08:21:45 INF] Retry 82, after 100.00ms, due to: BrokenCircuitException [08:21:45 INF] Circuit half opened now [08:21:45 INF] Opening circuit now [08:21:45 ERR] Server returned a 500
原因分析
核心问题在于重试策略的ShouldHandle未包含对500状态码的处理:
- 当熔断器处于打开状态时,每次重试都会触发
BrokenCircuitException,该异常被重试策略捕获,因此会持续重试。 - 当熔断器的
BreakDuration(5秒)到期后,进入半开状态,允许一次实际请求尝试。这次请求返回500状态码,触发熔断器重新打开,但这个500结果不在重试策略的ShouldHandle范围内(重试仅处理HttpRequestException和BrokenCircuitException)。 - 由于重试策略不认为500状态码需要重试,因此直接终止整个重试流程,不会继续执行剩余的重试次数。
解决方案
修改重试策略的ShouldHandle配置,加入对500状态码的处理,确保与熔断器的失败判定逻辑对齐:
ShouldHandle = new PredicateBuilder<HttpResponseMessage>() .Handle<HttpRequestException>() .Handle<BrokenCircuitException>() .HandleResult(r => r.StatusCode == System.Net.HttpStatusCode.InternalServerError);
这样,当半开状态下的请求返回500时,重试策略会将其视为需要重试的失败,继续执行剩余的重试次数,直到达到MaxRetryAttempts限制或请求成功。
内容的提问来源于stack exchange,提问作者onefootswill
相关产品推荐
相关产品推荐

