卡飞资源网

专业编程技术资源共享平台

分布式锁、redis缓存、本地缓存维护公众号accessToken

背景

公众平台的 API 调用所需的access_token的使用及生成方式说明:

1、建议公众号开发者使用中控服务器统一获取和刷新access_token,其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务;

2、目前access_token的有效期通过返回的expires_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器可对外继续输出的老access_token,此时公众平台后台会保证在5分钟内,新老access_token都可用,这保证了第三方业务的平滑过渡;

结合文档关于access_token的说明得出如下结论:

1、access_token不同进程的服务器最好维护一个

2、access_token是有有效期的,需要定期去维护

代码实现

//获取公众号accessToken的业务服务
@Autowired
private WxNotifyService wxNotifyService;

//redis应用服务
@Autowired
private RedisClient redisClient;

//redis分布式锁
@Autowired
private RedissonClient redissonClient;

//volatile修饰维护的本地变量
private volatile WxAccessTokenResp localAccessTokenResp;

@Override
public String getWxAccessToken() {
    if( localAccessTokenResp != null && localAccessTokenResp.getEndTime() > System.currentTimeMillis() ){
        //当前的accessToken是有效地
        log.info( "从本地加载微信公众号accesstoken成功" );
        return localAccessTokenResp.getAccess_token();
    }
    //本地的token已经失效,或者本地没有加载redis的token
    WxAccessTokenResp resp =  redisClient.get(RedisConstans.WX_GZH_TOKEN);
    if( resp == null ){
        RLock rLock = null;
        boolean b = false;
        try {
           //获取分布式锁
             rLock =  redissonClient.getLock( RedisConstans.WX_GZH_TOKEN.concat(":lock") );
              b = rLock.tryLock();
             if( b ){
                 log.info( "获取到分布式锁,进入加载微信公众号accesstoken流程==》" );
                //获取到锁
                //调用接口获取
                resp =  wxNotifyService.getAccessToken();
                //设置到redis缓存
                redisClient.set( RedisConstans.WX_GZH_TOKEN , resp , Long.parseLong(resp.getExpires_in().toString()) , TimeUnit.SECONDS );
            }else{
                 log.info( "微信公众号token没有获取到锁 ,进入递归调用==>" );
                 //没有获取到锁
                 TimeUnit.MILLISECONDS.sleep(100 );
                 //睡眠50毫秒,递归调用
                 return getWxAccessToken();
             }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
          //如果获取到锁,需要进行释放
            if(rLock != null && b && rLock.isLocked() && rLock.isHeldByCurrentThread()){
                rLock.unlock();
            }
        }
    }
    //给本地变量赋值
    localAccessTokenResp = resp;
    return resp.getAccess_token();
}

结论

我建立了2级缓存,分别是本地缓存与redis分布式缓存, 在过期时间内我们使用本地缓存的token数据直接使用,如果本地缓存数据过期或者没有初始化,我们使用分布式锁避免多进程同时去修改,拿到分布式锁的线程调用接口刷新Redis的token值与更新本地token值;没有拿到分布式锁的线程通过sleep休眠再进行递归重新获取token值。最后达到文档的要求,并本地缓存进行了一些优化。你们有更好的方法吗?方便一起交流。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言