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

如何让HttpClient请求排队等待Polly重试策略完成Token刷新?

问题场景与现有实现

我正在使用Polly处理HTTP请求返回401错误时的授权Token刷新,现有方案基于Polly结合命名客户端实现,相关代码如下:

重试策略代码

static IAsyncPolicy<HttpResponseMessage> GetAuthRefreshPolicy(IServiceProvider serviceProvider)
{
        return Policy<HttpResponseMessage>
            .HandleResult(response => response.StatusCode == HttpStatusCode.Unauthorized)
            .RetryAsync(async (handler, retry) => await serviceProvider.GetRequiredService<IAuthorisationService>().RefreshTokenAsync());
}

授权服务代码

public class AuthorisationService : IAuthorisationService
{
    public String? AuthToken { get; private set; }

    private readonly IAuthenticationClient _AuthenticationClient;
    private readonly AuthOptions _AuthOptions;

    public AuthorisationService(IAuthenticationClient authenticationClient, IOptions<AuthOptions> options)
    {
        _AuthenticationClient = authenticationClient;
        _AuthOptions = options.Value;
    }

    public async Task RefreshTokenAsync()
    {
        this.AuthToken = await _AuthenticationClient.LoginAsync(_AuthOptions.User, _AuthOptions.Password);
    }
}

存在的问题

当授权Token无效且多个请求同时发起时,由于异步执行特性,所有请求都会使用无效Token触发Polly重试策略,导致多次不必要的Token刷新请求。

希望实现:某个请求因401失败后,后续所有请求等待Token刷新成功后再执行;或者将重试尝试排队,若Token已刷新则跳过刷新请求。同时想了解是否可以用Polly的舱壁断路器等特性,或者更简单的Token刷新锁定方式。

解决方案

方法一:授权服务添加异步锁定(最直接简单)

给AuthorisationService添加SemaphoreSlim实现互斥刷新,确保同一时间只有一个请求执行Token刷新,其他请求等待刷新完成后直接复用新Token:

public class AuthorisationService : IAuthorisationService
{
    public String? AuthToken { get; private set; }

    private readonly IAuthenticationClient _AuthenticationClient;
    private readonly AuthOptions _AuthOptions;
    private readonly SemaphoreSlim _refreshLock = new SemaphoreSlim(1, 1); // 初始化信号量,仅允许1个并发刷新操作

    public AuthorisationService(IAuthenticationClient authenticationClient, IOptions<AuthOptions> options)
    {
        _AuthenticationClient = authenticationClient;
        _AuthOptions = options.Value;
    }

    public async Task RefreshTokenAsync()
    {
        await _refreshLock.WaitAsync();
        try
        {
            // 双重检查:等待期间若已有其他请求完成刷新,直接返回
            if (!string.IsNullOrEmpty(AuthToken))
            {
                // 可选:添加Token有效性校验,避免极端场景下的过期问题
                return;
            }
            this.AuthToken = await _AuthenticationClient.LoginAsync(_AuthOptions.User, _AuthOptions.Password);
        }
        finally
        {
            _refreshLock.Release(); // 释放锁,允许其他请求继续
        }
    }
}

这个方案无需修改Polly策略,仅改造授权服务即可解决问题:

  • 第一个触发401的请求会获取锁并执行刷新
  • 后续请求会等待锁释放,通过双重检查直接复用已刷新的Token,避免重复发起刷新请求

方法二:Polly层面结合共享状态控制

若需在Polly策略中实现逻辑,可借助共享的刷新状态对象配合异步锁定,但本质和方法一逻辑一致,复杂度更高,不如直接在授权服务中处理清晰。

关于舱壁断路器的说明

舱壁断路器的核心作用是限制并发请求数量、防止服务过载,无法直接解决Token并发刷新的问题,因此不适合当前场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:00:41