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

并发场景下定时刷新第三方API Token的重复请求问题求助

并发场景下Token重复刷新问题的解决方案

问题描述

从第三方API获取带过期时间的Token,要求按设定间隔(比如每分钟1次)仅刷新一次Token,但服务接收数百并发请求时,到达刷新时间会发起大量重复的Token请求。

测试现象:

  • 先执行一次Token请求,再发起100次并发请求,所有请求返回同一Token;
  • 直接发起100次并发请求,会得到100个不同的Token。

当前实现代码:

// _timeGetToken - 获取Token的时间
// tokenUpdateTime - Token需要刷新的间隔时间(double类型,单位:分钟,示例值为1)
public async Task<string> GetToken()
{
    if(DateTime.Now.Subtract(_timeGetToken) > TimeSpan.FromMinutes(tokenUpdateTime))
    {
        try
        {
            token = await _api.GetToken(new TokenUser());
            _timeGetToken = DateTime.Now;
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex.Message);
        }
    }
    if(token != null) 
        return token.Token;
    return string.Empty;
}

优化方案

1. 用SemaphoreSlim实现异步锁,避免并发刷新

核心逻辑是通过信号量控制只有一个请求能进入Token刷新流程,其他请求等待刷新完成后直接复用新Token,而非各自发起请求。同时加入双重检查,防止等待期间Token已被其他请求刷新。

修改后的代码:

private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private TokenModel _token; // 替换为你的Token实体类
private DateTime _timeGetToken;
private readonly double _tokenUpdateTime;

public async Task<string> GetToken()
{
    // 第一重检查:Token未过期直接返回
    if (_token != null && DateTime.Now.Subtract(_timeGetToken) <= TimeSpan.FromMinutes(_tokenUpdateTime))
    {
        return _token.Token;
    }

    // 进入异步锁,确保单线程刷新
    await _semaphore.WaitAsync();
    try
    {
        // 第二重检查:防止等待期间Token已被刷新
        if (_token != null && DateTime.Now.Subtract(_timeGetToken) <= TimeSpan.FromMinutes(_tokenUpdateTime))
        {
            return _token.Token;
        }

        // 执行Token刷新
        _token = await _api.GetToken(new TokenUser());
        _timeGetToken = DateTime.Now;
        return _token.Token;
    }
    catch (Exception ex)
    {
        _logger.LogWarning(ex.Message);
        // 刷新失败时返回旧Token(如果存在),避免服务雪崩
        return _token?.Token ?? string.Empty;
    }
    finally
    {
        _semaphore.Release();
    }
}

2. 可选优化:提前刷新Token

为了避免Token刚好过期时的请求阻塞,可以提前触发刷新逻辑,比如在Token剩余10%有效期时发起刷新,让用户请求无需等待刷新过程。

示例调整判断逻辑:

var validDuration = TimeSpan.FromMinutes(_tokenUpdateTime);
var refreshThreshold = validDuration * 0.1; // 提前10%时间刷新
if (_token == null || DateTime.Now.Subtract(_timeGetToken) > validDuration - refreshThreshold)
{
    // 进入刷新逻辑
}

3. 关键注意事项

  • 确保_token、_timeGetToken是类级别的私有变量,多线程场景下保证线程安全;
  • 刷新失败时优先返回旧Token,避免第三方API故障导致自身服务不可用;
  • 分布式服务场景下,需改用分布式缓存(如Redis)存储Token和刷新时间,配合分布式锁实现跨实例的Token复用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 18:35:12