You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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的设计思路。

关键调整点:

  1. 策略顺序:Polly的策略是从外到内执行的,保持「降级 → 超时 → 重试」的顺序,确保超时触发时,能拿到重试阶段的最后结果。
  2. 上下文存储结果:在重试的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 22:07:06