CacheClient类
//解决缓存击穿1 --- 用互斥锁的方式
public <R,ID> R queryWithMutex(String keyPrefix, ID id,Class<R> type,Function<ID,R> dbFallBACK
,Long time, TimeUnit unit){
String key = keyPrefix + id;//cache:shop
//1、尝试从redis查询缓存
String json = stringRedisTemplate.opsForValue().get(key);
//2、判断是否存在
if(StrUtil.isNotBlank(json)){
//3、存在 直接返回
R r = JSONUtil.toBean(json, type);
return r;
}
// 判断命中的是否是空值
if(json != null){
//为空字符串 返回错误信息 命中空值
return null;
}
//4、实现缓存重建
//4、1获取互斥锁
String lockKey = LOCK_SHOP_KEY + id;
R r = null;
try {
boolean isLock = tryLock(lockKey);
//4、2判断是否获取成功
if(!isLock){
//4、3失败 休眠并重试
Thread.sleep(50);
return queryWithMutex(keyPrefix,id,type,dbFallBACK,time,unit);
}
//4、4成功 则根据id查询数据库
r = dbFallBACK.apply(id);
//模拟重建延迟
Thread.sleep(200);
//5、不存在 直接返回错误
if(r == null){
//改进22 解决缓存穿透问题 --将空值写入redis中 过期时间设置短一些
stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);
//返回错误信息
return null;
}
//6、存在 则写入redis
this.set(key, r, time, unit);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally{
//7、释放互斥锁
unLock(lockKey);
}
//8、返回
return r;
}
ShopServiceImpl
//解决缓存击穿1 --- 用互斥锁的方式
Shop shop = cacheClient.queryWithMutex(CACHE_SHOP_KEY,id,Shop.class,this::getById,CACHE_SHOP_TTL,TimeUnit.MINUTES);