背景
公众平台的 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值。最后达到文档的要求,并本地缓存进行了一些优化。你们有更好的方法吗?方便一起交流。