我们通过伪代码来讲解整个设计思想,具体实现细节不再赘述。
设计思想:
双缓存机制:主token + 备用token,实现平滑过渡
Redis分布式锁:防止多个线程同时刷新token
预刷新策略:token有效期80%时就刷新,避免临期失效
容错机制:主token失效时可使用备用token继续服务
1、多个任务并行执行,调用mgr.run函数实现对资源的访问,访问时要求携带有效的token
List<MyJob> jobList = job.getJobList();
if (CollectionUtils.isEmpty(jobList)) {
return ;
}
jobList.parallelStream()
.forEach(job -> {
mgr.run(job);
}
});
2、mgr.run函数的伪代码如下
public void run(MyJob job) {
try {
String token = getToken(jobFish.getId());
// 拿着token请求资源获取结果
} catch (Exception e) {
return;
}
}
3、getToken函数的伪代码如下
public String getToken(String id) {
// 从缓存中获取token
Object token = redisTemplate.opsForValue().get(tokenKey);
if (Objects.nonNull(token)) {
return token.toString();
}
// 如果token为空,则刷新token(使用redis的nx锁实现仅允许一个线程去刷新token)
boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockThreadKey, "lock", Duration.ofSeconds(30)));
if(lock){
// 锁定成功的线程去刷新token
return getTokenByHttp(id,tokenKey,tokenKeyBack);
}else{
// 锁定失败的线程继续使用旧的token(旧的token有5分钟的缓冲时间)
Object tokenBack = redisTemplate.opsForValue().get(tokenKeyBack);
if (Objects.nonNull(tokenBack)) {
return tokenBack.toString();
}else{
return "error";
}
}
}
4、getTokenByHttp函数的伪代码如下
private String getTokenByHttp(String id,String tokenKey,String tokenKeyBack) {
TokenResp tokenResp resp=null;//正式的从http请求中拿到token,这里以null代替
// token默认有效期7200秒,这里设定redis缓存有效期为token的80%,提前24分钟让redis过期但token还有24分钟有效期,如此可以规避token临近过期时被拿去使用,http请求发过去时token已失效
int expiresIn = (int) (tokenResp.getExpires_in() * 0.8);
// 保存新的token到redis中,并设置有效期
redisTemplate.opsForValue().set(tokenKey, tokenResp.getAccess_token(), expiresIn, TimeUnit.SECONDS);
// 保存新的token到redis的备份key中,并设置有效期+300秒即5分钟(为什么+5分钟?token刷新会导致之前一次获取的token有效期缩短为 5 分钟,这5分钟内新旧token都可以使用,完成token的平滑过度)
redisTemplate.opsForValue().set(tokenKeyBack, tokenResp.getAccess_token(), expiresIn+300, TimeUnit.SECONDS);
return tokenResp.getAccess_token();
}

249

被折叠的 条评论
为什么被折叠?



