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

Polly超时策略单独使用未触发,与重试策略结合正常的问题排查

为什么Polly超时策略单独使用时不触发,与重试策略结合却正常工作?

我尝试让Polly超时策略生效,但单独使用时无法触发,与重试策略结合使用时却能正常工作。策略定义如下:

声明:此为纯测试代码,忽略代码异味

var httpRetryPolicy = Policy.Handle<TimeoutRejectedException>()
    .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
    .WaitAndRetryAsync(
        5,
        retryAttempt => TimeSpan.FromSeconds(1),
        (_, _, retryAttempt, _) => Debug.WriteLine($"Retrying ({retryAttempt})")
        );

var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
        4,
        TimeoutStrategy.Optimistic,
        (_, _, _, _) => { Debug.WriteLine("Timeout"); return Task.CompletedTask; }
    );

PollyTestController中的策略测试端点

[HttpGet("{policyKey}/{delay}")]
public async Task<IActionResult> RunPolicyTest(string policyKey, int delay)
{
    AsyncPolicyWrap<HttpResponseMessage> policy = null;
    if(policyKey == "T")
        policy = Policy.WrapAsync(timeoutPolicy, Policy.NoOpAsync<HttpResponseMessage>());
    else if(policyKey == "TR")
        policy = Policy.WrapAsync(timeoutPolicy, httpRetryPolicy);

    HttpClient httpClient = new HttpClient();
    var url = $"http://localhost:5000/test/{delay}";
    try
    {
       var response = await policy.ExecuteAsync(() => httpClient.GetAsync(url));
        return Ok(await response.Content.ReadAsStringAsync());
    }
    catch (Exception ex)
    {
        return BadRequest(ex.Message); 
    }
}

TestController中的测试端点

[HttpGet("{delay}")]
public IActionResult GetWithDelay(int delay)
{
    if (delay == 10) return NotFound("Not found");

    Thread.Sleep(delay*1000);
    return Ok("Success");
}

场景1 - 调用 http://localhost:5000/pollytest/T/6

此调用会选择“T”策略(仅超时)执行对http://localhost:5000/test/6的请求。根据代码,该响应会延迟6秒,而策略超时时间为4秒,本应抛出TimeoutRejectedException,但实际情况是请求挂起6秒后返回“Success”响应,输出窗口无任何内容。

场景2 - 调用 http://localhost:5000/pollytest/TR/10

此调用会选择“TR”策略(超时包装重试)执行对http://localhost:5000/test/10的请求。由于delay值为10,端点会返回404,因此重试策略会尝试5次,每次间隔1秒。但此次超时策略在第4次尝试后触发,第5次尝试未执行,输出窗口显示:

Retrying (1)
Retrying (2)
Retrying (3)
Retrying (4)
Timeout

响应内容为“The delegate executed asynchronously through TimeoutPolicy did not complete within the timeout.”

我忽略了什么?为什么场景1中的超时策略未触发?


问题原因

你使用的是**TimeoutStrategy.Optimistic(乐观超时),这种超时策略的生效前提是:被执行的异步委托必须支持协作取消**——也就是委托内部要监听CancellationToken,并在收到取消信号时主动终止执行。

在场景1中,你调用policy.ExecuteAsync(() => httpClient.GetAsync(url))时,没有传入Polly超时策略生成的CancellationToken。HttpClient.GetAsync的无参重载不会响应外部取消请求,所以即使超时策略触发了取消信号,HTTP请求仍会继续执行直到完成,导致超时逻辑完全不生效。

而场景2中,重试策略内部会自动传递CancellationToken,当超时策略触发时,重试流程会响应这个取消信号,终止后续重试并抛出TimeoutRejectedException,因此超时策略正常工作。

修复方案

修改场景1中执行委托的代码,传入Polly提供的取消令牌:

将原代码中的:

var response = await policy.ExecuteAsync(() => httpClient.GetAsync(url));

改为:

var response = await policy.ExecuteAsync(ct => httpClient.GetAsync(url, ct));

这样HttpClient.GetAsync会监听超时策略生成的CancellationToken,当超时时间到达时,请求会被主动取消,超时策略的回调和异常也会正常触发。

另外补充:如果无法修改委托以支持取消,可以考虑改用**TimeoutStrategy.Pessimistic(悲观超时)**。它会在独立线程中执行委托,超时后直接终止线程,但这种方式有额外线程开销,还可能引发资源泄漏问题,需谨慎使用。


内容的提问来源于stack exchange,提问作者Sam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 22:55:55