@Data public class RedisData { private LocalDateTime expireTime; private Object data; }
package com.martin.utils;/* * @Author Martin·Antonio * @Description TODO * @DateTime 2022/11/18 20:02 * */ import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import org.springframework.data.redis.core.StringRedisTemplate; import java.time.LocalDateTime; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Function; @Component public class CacheClient { private final StringRedisTemplate template; public CacheClient(StringRedisTemplate template) { this.template = template; } public void set(String key, Object value, Long time, TimeUnit unit){ template.opsForValue().set(key, JSONUtil.toJsonStr(value),time,unit); } public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit){ //设置逻辑过期 RedisData redisData = new RedisData(); redisData.setData(value); redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time))); //写入redis template.opsForValue().set(key, JSONUtil.toJsonStr(redisData)); } private boolean tryLock(String key) { Boolean flag = template.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS); return BooleanUtil.isTrue(flag); } private void unlock(String key) { template.delete(key); } //缓存穿透 public <R,ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallBack, Long time, TimeUnit unit) { String key = keyPrefix + id; String lockKey = "lock:shop:" + id; String json = template.opsForValue().get(key); //2.判断是否存在 if (StrUtil.isNotBlank(json)) { return JSONUtil.toBean(json,type); } if (json != null){ return null; } R r = dbFallBack.apply(id); if (r == null) { template.opsForValue().set(key, JSONUtil.toJsonStr(r), time,unit); return r; } this.set(key, JSONUtil.toJsonStr(r), time,unit); return r; } private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10); public <R,ID> R logicExpire(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallBack, Long time, TimeUnit unit) { String key = keyPrefix + id; String lockKey = "lock:shop:" + id; String json = template.opsForValue().get(key); //2.判断是否存在 if (StrUtil.isBlank(json)) { return null; } //3.命中 RedisData redisData = JSONUtil.toBean(json, RedisData.class); R r = JSONUtil.toBean((JSONObject) redisData.getData(), type); LocalDateTime expireTime = redisData.getExpireTime(); //4.判断是否过期 if (expireTime.isAfter(LocalDateTime.now())) { //4.1未过期直接返回 return r; } //4.2已过期,需要缓存重建 boolean isLock = tryLock(lockKey); if (isLock) { //5.获取互斥锁 CACHE_REBUILD_EXECUTOR.submit(() -> { //6.缓存重建 try { R apply = dbFallBack.apply(id); this.setWithLogicalExpire(key, JSONUtil.toJsonStr(r), time,unit); } catch (Exception e) { throw new RuntimeException(e); } finally { //7.释放锁 unlock(lockKey); } }); } return r; } }