封装Redis工具类

package com.hmdp.utils; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Function; import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY; import static com.hmdp.utils.RedisConstants.LOCK_SHOP_KEY; @Slf4j @Component //将来这个bean由Spring维护 public class CacheClient { private final StringRedisTemplate stringRedisTemplate; public CacheClient(StringRedisTemplate stringRedisTemplate) { this.stringRedisTemplate = stringRedisTemplate; } //方法1 public void set(String key, Object value, Long time, TimeUnit unit){ stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,unit); } //方法2 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 stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData)); } //方法三 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 json = stringRedisTemplate.opsForValue().get(key); //判断是否存在 if(StrUtil.isNotBlank(json)){ //存在,直接返回 return JSONUtil.toBean(json,type); } //判断是否为空值 if(json!=null){ //返回一个错误信息 return null; } //不存在,根据id查询数据库 //传入函数有参有返回值的函数Function 参数类型ID 返回值类型R //函数式编程 就是定义的时候参数传了个接口,用到时传入接口的实现类 R r = dbFallback.apply(id); //不存在,返回错误 if(r==null){ //将空值写入redis stringRedisTemplate.opsForValue().set(key,"",time,unit); //返回错误信息 return null; } this.set(key,r,time,unit); return r; } //线程池 private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10); public <R,ID> R queryWithLogicalExpire(String keyPrefix,ID id,Class<R>type,Function<ID,R>dbFallback,Long time, TimeUnit unit){ String key = keyPrefix + id; //1.从redis查询商铺缓存 也可以用springcache String json = stringRedisTemplate.opsForValue().get(key); //2.判断是否存在 if (StrUtil.isBlank(json)) { //3.存在,直接返回 return null; } //4.命中,需要先把json反序列化为对象 RedisData redisData = JSONUtil.toBean(json, RedisData.class); JSONObject data = (JSONObject) redisData.getData(); R r = JSONUtil.toBean(data,type); LocalDateTime expireTime = redisData.getExpireTime(); //5.判断是否过期 if(expireTime.isAfter(LocalDateTime.now())){ //5.1 未过期 直接返回店铺信息 return r; } //5.2 已过期,需要缓存重建 //6.重建缓存 //6.1 获取互斥锁 String lockKey = LOCK_SHOP_KEY + id; boolean isLock = tryLock(lockKey); //6.2 判断是否获取锁成功 //注意: 获取锁成功应该再次检测redis缓存是否过期,做DoubleCheck.如果存在则无需重建缓存 if(isLock){ //6.3 成功,开启独立线程,实现缓存重建 CACHE_REBUILD_EXECUTOR.submit(() ->{ //查询数据库 R apply = dbFallback.apply(id); //写入redis this.setWithLogicalExpire(key,apply,time,unit); }); } //6.4 返回过期商铺信息 return r; } private boolean tryLock(String key){ Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key,"1",10,TimeUnit.SECONDS); //需要转化为基本类型返回 如果直接return flag是有可能发生拆箱的,可能出现空指针 return BooleanUtil.isTrue(flag); } private void unlock(String key){ stringRedisTemplate.delete(key); } }
上一篇:python爬虫入门(实践)


下一篇:Shell控监Kafka积压