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

基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:47:21