并发场景下定时刷新第三方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
相关产品推荐
相关产品推荐

