高并发如何保证微信token的有效
发布于 8 年前 作者 WayneLiang 18098 次浏览 来自 问答

用集群处理微信业务,微信的token存放在redis,当长时间没业务过来,token过期后,突然高并发同时请求到集群,不同服务器上的node同时去微信获取token,由于请求微信的返回的不确定性有可能导致最后存到redis的token无效。请问这种情况该如何避免比较好?

22 回复

单独的线程(定时器)处理token、别发生业务时才去判断和申请token。不然并发必然会导致问题 From Noder

这是个值得关注的问题,开发了这么久的微信,还一直没有想过,估计是还没遇到高并发的情况。

我的解决方法: 1.redis有2个功能:第一是用作token的缓存,第二是作为锁媒介 2.利用事件队列缓存请求 过程: 1.redis作为缓存,缓存住token 2.每次请求判断token是否过期 2.1 如果token未过期,则直接使用 2.2 如果token过期,则利用redis的setnx(只能设置不存在的key)命令获取锁(redis单线程,一次处理1个命令,如果一下子来了N多请求,也只能一条一条处理),设置成功的请求去做获取token的操作,其他的请求缓存到事件队列

var ev = new events.EventEmitter();
var status = '';

emitter.setMaxListeners(0);

var example = function(token, callback) {
  ev.once('token', callback);

  if (token.isExpired() && '' === status) {
    status = 'pending';
    
    getToken(function(_token){
      ev.emit('token', null, _token);
      status = '';
    });
  }
}

@i5ting 这个还是没解决token已经过期后突然高并发过来会出现多个请求刷新token的情况

@limaofeng 感觉开个定时器去维护浪费资源

@qinyang912 我也还没遇到,只是刚好想到

@swfbarhr 请问有没事件队列的demo,不太会队列,还有请求需要设置个超时

@i5ting 而且该项目中并没使用微信返回的expires_in减去部分时间作为有效时间,而是直接写死7000秒,这有点不好

缓存token,微信的token过期时间是7200秒,把缓存过期时间设置为7140秒

@zhengnz 如果7200秒内没请求过来,突然在7300秒时多请求并发过来就可能出现问题

@WayneLiang 使用请求队列,标记一下,当前在请求了,让其他请求不实际发生

@WayneLiang 你所说的这种并发导致的错误情况还有可能很多,过期未刷新,或者缓存中并未存在token,却同时有多条处理需要用到token而导致并发发起获取token,或者token已刷新,但已有其他程序获取老token还没执行完,从逻辑上考虑这些问题是很多,所以应该加一个重试的机制,并对于获取token来说,错误的可能也就是token过期或失效,那么只要重试能获取到可用的token即可

考虑到我不同业务不同node但用到同个token,做队列好像也不行,暂时只能标记或锁上,当请求token中,其他需要token的请求统一响应重试

我觉得比较靠谱的做法有:

  1. 主动定时刷新
  2. 博客中的第五种方法, 存储 key = (value, 过期时间), 在 redis 中 key 不过期, 代码中检测到过期, 则使用@swfbarhr 提到的 setnx , 过期时间短点, 成功 set 的做这个重新获取数据操作

与其他方法区别在于获取数据操作过程中, 取到是过期值, 而不是空值

@swfbarhr 用的 eventemitter 适用于单进程, 多进程可以使用 redis 的 Pub/Sub

@magicdawn redis pub/sub 不是guaranteed delivery吧

@swfbarhr 的做法比较合理, 需要token时, 去redis取, 如果过期了, 就取一个锁, 取到锁的服务器去微信拿token, 其余的设一个timeout后去redis取, 如果还是没取到, 重复.

回到顶部