如何在Polly的外部超时策略中获取重试策略的最后结果?
在Polly中结合超时与重试策略时,获取超时前最后一次重试的结果
我在C#/.NET中用Polly框架时碰到个问题:同时使用自定义超时策略和内部重试策略的情况下,超时过期后怎么拿到最后一次重试的结果?
目前我用外部变量捕获结果的方式解决,但写法很别扭。要是没有超时和降级策略,ExecuteAsync本身会直接返回最后一次尝试的结果。
现有实现代码:
var pipelineBuilder = new ResiliencePipelineBuilder<(Orders.Contracts.OrderView?, RequestResult)>(); (Orders.Contracts.OrderView?, RequestResult) outerTempResult = new(null, new()); // 降级用的默认结果 var pipeline = pipelineBuilder .AddFallback(new() { ShouldHandle = new PredicateBuilder<(Orders.Contracts.OrderView?, RequestResult)>().Handle<TimeoutRejectedException>(), FallbackAction = args => Outcome.FromResultAsValueTask(outerTempResult), }) .AddTimeout(TimeSpan.FromMilliseconds(options.MaxPollingTimeout)) // 整个流程的全局超时 .AddRetry(new RetryStrategyOptions<(Orders.Contracts.OrderView?, RequestResult)> { ShouldHandle = new PredicateBuilder<(Orders.Contracts.OrderView?, RequestResult)>() .HandleResult(newOrder => newOrder.Item1 == null || newOrder.Item1.Status == S4.Orders.Contracts.OrderStatus.InProgress), Delay = TimeSpan.FromMilliseconds(OrderSettings.RetryInterval), MaxRetryAttempts = OrderSettings.RetryCount, BackoffType = DelayBackoffType.Linear, UseJitter = true, OnRetry = (args) => { outerTempResult = args.Outcome.Result; // 把结果赋值给外部变量,供降级回调使用 return ValueTask.CompletedTask; } }) .Build();
请求执行代码:
var orderView = await pipeline.ExecuteAsync(async token => { return await this.orderServiceClient.GetOrderAsync(id); }, CancellationToken.None); return orderView.Item2.ToApiResult();
优化方案:用Polly上下文替代外部变量
不用外部变量,利用Polly内置的ResilienceContext就能传递最后一次重试的结果,写法更优雅,也符合Polly的设计思路。
关键调整点:
- 策略顺序:Polly的策略是从外到内执行的,保持「降级 → 超时 → 重试」的顺序,确保超时触发时,能拿到重试阶段的最后结果。
- 上下文存储结果:在重试的
OnRetry回调里,把当前结果存入ResilienceContext的属性集合;降级回调时再从上下文取出这个结果。
优化后的代码:
var pipelineBuilder = new ResiliencePipelineBuilder<(Orders.Contracts.OrderView?, RequestResult)>(); var pipeline = pipelineBuilder .AddFallback(new FallbackStrategyOptions<(Orders.Contracts.OrderView?, RequestResult)> { ShouldHandle = new PredicateBuilder<(Orders.Contracts.OrderView?, RequestResult)>().Handle<TimeoutRejectedException>(), FallbackAction = args => { // 从上下文取出最后一次重试的结果 if (args.Context.Properties.TryGetValue("LastRetryResult", out var lastResult)) { return Outcome.FromResultAsValueTask((((Orders.Contracts.OrderView?, RequestResult))lastResult)); } // 没有结果时返回兜底值 return Outcome.FromResultAsValueTask((null, new RequestResult())); } }) .AddTimeout(TimeSpan.FromMilliseconds(options.MaxPollingTimeout)) .AddRetry(new RetryStrategyOptions<(Orders.Contracts.OrderView?, RequestResult)> { ShouldHandle = new PredicateBuilder<(Orders.Contracts.OrderView?, RequestResult)>() .HandleResult(newOrder => newOrder.Item1 == null || newOrder.Item1.Status == S4.Orders.Contracts.OrderStatus.InProgress), Delay = TimeSpan.FromMilliseconds(OrderSettings.RetryInterval), MaxRetryAttempts = OrderSettings.RetryCount, BackoffType = DelayBackoffType.Linear, UseJitter = true, OnRetry = args => { // 将当前重试结果存入上下文 args.Context.Properties.Set("LastRetryResult", args.Outcome.Result); return ValueTask.CompletedTask; } }) .Build();
执行代码无需改动,保持原有逻辑即可。
补充说明
如果重试策略已经覆盖了所有需要重试的场景,还可以通过Outcome的失败信息进一步处理,但用上下文存储是最直接且易维护的方式。这种方式避免了外部变量带来的副作用,也更符合Polly的状态传递设计。
内容的提问来源于stack exchange,提问作者Esben Bach
相关产品推荐
相关产品推荐

