·更新于 2026年10月25日· 10 次浏览 Node.jsRedis微信小程序BUG解决工程实践
Node.js 给小程序生成二维码总失败:我把 accessToken 改成了 Redis 单点续期
二维码接口偶发 40001/42001,不一定是参数问题,很可能是 accessToken 被并发刷新互踢。我用 Redis 锁、过期缓冲和失败重试,把刷新收敛到一个点。

作者
ZHOUYI
前阵子服务里给小程序生成二维码的接口开始偶发失败,日志里常见 40001 或 42001。一开始我以为是二维码参数、域名或网络问题,但复现后发现:失败多发生在 accessToken 刚刷新不久,尤其是多个接口同时请求 token 的时候。
问题:并发刷新把 token 互踢了
微信小程序的 accessToken 是全局凭据,多个服务实例如果各自判断过期、各自刷新,就会出现新 token 覆盖旧 token。前一个请求刚拿到旧 token,还没来得及用,旧 token 可能已经失效;后一个请求又拿到另一个新 token。结果就是大家都觉得自己没错,但二维码生成偶发失败。
方案:Redis 单点续期
- token 统一存 Redis,所有实例读同一份。
- 过期时间不要卡满,提前 5 分钟续期,避免临界点失败。
- 刷新时加 Redis 锁,只让一个请求去微信换 token。
- 其他请求短暂等待后重读缓存,避免重复刷新。
- 接口返回 token 失效时,再触发一次带锁强刷。
最小实现
js
async function getToken(force = false) {
const key = 'wx:token';
const lock = 'wx:token:lock';
const cached = await redis.get(key);
if (cached && !force) return cached;
if (await redis.set(lock, '1', 'EX', 10, 'NX')) {
try {
const res = await fetchToken();
await redis.set(key, res.token, 'EX', Math.max(res.expiresIn - 300, 60));
return res.token;
} finally { await redis.del(lock); }
}
await new Promise(r => setTimeout(r, 200));
return getToken(force);
}这里有几个细节容易踩坑:锁一定要带过期时间,防止刷新进程挂掉后永远拿不到锁;提前量别设得太短,否则微信接口慢一点就会用旧 token;失败重试也不能无限强刷,不然一旦业务参数错误,会把 token 接口刷爆。微信字段和频次限制以官方文档为准。
“这类问题本质上不是某个接口不稳,而是共享状态没有收敛。把 accessToken 的读取、续期和强刷放到同一个 Redis 单点后,二维码生成失败就基本消失了。”