基于Async/Await与锁机制解决Node.js并发请求重复认证问题
优化Node.js反向代理的Token并发刷新机制
问题根源分析
原代码的核心问题出在authenticate函数的状态管理上:refreshingToken = false被提前放在了获取Basic Token的finally块中,此时真正的token请求(axios.post)还在异步执行。后续并发请求会误以为刷新流程已结束,从而重复触发认证操作。
优化方案
通过缓存刷新Promise替代轮询逻辑,同时确保刷新状态在整个认证流程完成后才重置,彻底避免重复发起认证请求。
修改后的完整代码
// api.js let access_token = null; let tokenExpiration = null; let refreshPromise = null; // 用Promise缓存正在进行的刷新请求 function isAccessTokenValid() { return access_token && Date.now() < tokenExpiration; } async function authenticate() { // 已有刷新请求在处理,直接等待该Promise完成 if (refreshPromise) { console.log("Token刷新中,等待结果..."); await refreshPromise; return access_token; } // 创建刷新Promise并缓存,后续请求直接复用 refreshPromise = (async () => { try { const basicToken = getBearerToken(); const headers = { "Content-Type": "application/x-www-form-urlencoded", "Authorization": `Basic ${basicToken}` }; const url = new URL('https://myapi.com/auth'); const body = new URLSearchParams(); body.append("grant_type", "client_credentials"); const res = await axios.post(url.toString(), body, { headers }); access_token = res.data.access_token; tokenExpiration = Date.now() + (res.data.expires_in * 1000); console.log('Token刷新完成'); return access_token; } catch (error) { console.error('Token刷新失败:', error); // 刷新失败时清空无效token,保证下次请求会重新触发认证 access_token = null; tokenExpiration = null; throw error; } finally { // 无论成功失败,都清空缓存的Promise,允许后续新的刷新请求 refreshPromise = null; } })(); return refreshPromise; } const energyUsage = async (req, res, next) => { try { if (!isAccessTokenValid()) { await authenticate(); } const url = new URL('https://myapi.com'); const headers = { "Authorization": `Bearer ${access_token}` }; const apiResponse = await axios.get(decodeURIComponent(url.toString()), { headers }); const data = handleResponse(apiResponse.data); res.status(apiResponse.status).json(data); } catch (error) { next(new ErrorHandler('获取数据失败', 400)); } }
关键优化点说明
- Promise缓存机制:第一个请求触发刷新时生成Promise并缓存,后续并发请求直接等待该Promise结果,无需轮询,性能更高效。
- 状态生命周期管理:刷新Promise的清空放在整个认证流程的finally块中,确保只有当刷新完全结束(成功/失败)后,才允许新的刷新请求触发。
- 失败容错处理:刷新失败时主动清空无效token,避免后续请求持续使用过期/错误token。
- 简化有效性判断:合并
access_token存在性与过期时间的判断,逻辑更严谨。
内容的提问来源于Stack Exchange,提问作者Trey6
相关产品推荐
相关产品推荐

