缓存(Redis)工具类,包含缓存击穿、缓存穿透、生成全局唯一id的解决方法
public class CacheManipulate {
public static final long BEGIN_TIMESTEMP =
LocalDateTime.of(2000,1,1,0,0).toEpochSecond(ZoneOffset.UTC);
public static final int MOVE_BIT = 32;
private final StringRedisTemplate stringRedisTemplate;
private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);
public CacheManipulate(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
private boolean getLock(String key){
try {
Boolean valid = stringRedisTemplate.opsForValue().setIfAbsent(key,"1",10,TimeUnit.SECONDS);
return BooleanUtil.isTrue(valid);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void unlock(String key){
try {
stringRedisTemplate.delete(key);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void set(String key, Object val, Long time, TimeUnit unit){
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(val),time,unit);
}
public void setLogicalExpire(String key, Object val, Long time, TimeUnit unit){
RedisData redisData = new RedisData();
redisData.setData(val);
redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData),time,unit);
}
public<T,R> R quaryWithExpire(String keyPrefix, T id, Class<R> type,
Function<T,R> dbRollBack,Long time,TimeUnit unit){
R res;
String key = keyPrefix+ id.toString();
String json = stringRedisTemplate.opsForValue().get(key);
if(!StrUtil.isBlank(json)){
res = JSONUtil.toBean(json,type);
return res;
}
if(json == null){
return null;
}
res = dbRollBack.apply(id);
if(res==null){
this.set(key,"",time,unit);
}else{
this.set(key,res,time,unit);
}
return res;
}
public<T,R> R quaryWithLogicalExpire(String keyPrefix, T id, Class<R> type,
Function<T,R> dbRollBack,Long time,TimeUnit unit) {
R res=null;
String key = keyPrefix+ id.toString();
res = isExistKey(key,type);
if(res != null)return res;
String lockKey = RedisLockConstants.GLOBAL_LOCK_KEY+id.toString();
Boolean isLock = getLock(lockKey);
if(isLock){
res = isExistKey(key,type);
if(res != null)return res;
Future<R> future = CACHE_REBUILD_EXECUTOR.submit(()->{
R r;
try {
r = dbRollBack.apply(id);
if(r==null){
this.setLogicalExpire(key,"",time,unit);
}else{
this.setLogicalExpire(key,r,time,unit);
}
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
unlock(lockKey);
}
return r;
});
try {
res = future.get();
return res;
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
return res;
}
public <R> R isExistKey(String key,Class<R> type){
R res = null;
String json = stringRedisTemplate.opsForValue().get(key);
if(!StrUtil.isBlank(json)){
RedisData redisData = JSONUtil.toBean(json, RedisData.class);
LocalDateTime expireTime = redisData.getExpireTime();
res = JSONUtil.toBean((JSONObject) redisData.getData(),type);
if(!expireTime.isAfter(LocalDateTime.now())){
res=null;
}
}
if(json == null){
res=null;
}
return res;
}
public long nextId(String prefixKey){
LocalDateTime now = LocalDateTime.now();
long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
long timeStemp = nowSecond-BEGIN_TIMESTEMP;
String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
long count =stringRedisTemplate.opsForValue().increment("icr"+prefixKey+":"+date);
long id = (timeStemp<<MOVE_BIT)|count;
return id;
}
}