package redis.clients.jedis; import java.net.URI; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.exceptions.JedisException; import redis.clients.util.JedisURIHelper; import redis.clients.util.Pool; public class JedisPool extends Pool<Jedis> { public JedisPool() { this((String)"localhost", 6379); } public JedisPool(GenericObjectPoolConfig poolConfig, String host) { this(poolConfig, host, 6379, 2000, (String)null, 0, (String)null); } public JedisPool(String host, int port) { this(new GenericObjectPoolConfig(), host, port, 2000, (String)null, 0, (String)null); } public JedisPool(String host) { URI uri = URI.create(host); if (JedisURIHelper.isValid(uri)) { String h = uri.getHost(); int port = uri.getPort(); String password = JedisURIHelper.getPassword(uri); int database = JedisURIHelper.getDBIndex(uri); boolean ssl = uri.getScheme().equals("rediss"); this.internalPool = new GenericObjectPool(new JedisFactory(h, port, 2000, 2000, password, database, (String)null, ssl, (SSLSocketFactory)null, (SSLParameters)null, (HostnameVerifier)null), new GenericObjectPoolConfig()); } else { this.internalPool = new GenericObjectPool(new JedisFactory(host, 6379, 2000, 2000, (String)null, 0, (String)null, false, (SSLSocketFactory)null, (SSLParameters)null, (HostnameVerifier)null), new GenericObjectPoolConfig()); } } public JedisPool(String host, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { URI uri = URI.create(host); if (JedisURIHelper.isValid(uri)) { String h = uri.getHost(); int port = uri.getPort(); String password = JedisURIHelper.getPassword(uri); int database = JedisURIHelper.getDBIndex(uri); boolean ssl = uri.getScheme().equals("rediss"); this.internalPool = new GenericObjectPool(new JedisFactory(h, port, 2000, 2000, password, database, (String)null, ssl, sslSocketFactory, sslParameters, hostnameVerifier), new GenericObjectPoolConfig()); } else { this.internalPool = new GenericObjectPool(new JedisFactory(host, 6379, 2000, 2000, (String)null, 0, (String)null, false, (SSLSocketFactory)null, (SSLParameters)null, (HostnameVerifier)null), new GenericObjectPoolConfig()); } } public JedisPool(URI uri) { this(new GenericObjectPoolConfig(), (URI)uri, 2000); } public JedisPool(URI uri, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { this(new GenericObjectPoolConfig(), uri, 2000, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(URI uri, int timeout) { this(new GenericObjectPoolConfig(), uri, timeout); } public JedisPool(URI uri, int timeout, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { this(new GenericObjectPoolConfig(), uri, timeout, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password) { this(poolConfig, host, port, timeout, password, 0, (String)null); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password, boolean ssl) { this(poolConfig, host, port, timeout, password, 0, (String)null, ssl); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password, boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, password, 0, (String)null, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port) { this(poolConfig, host, port, 2000, (String)null, 0, (String)null); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, boolean ssl) { this(poolConfig, host, port, 2000, (String)null, 0, (String)null, ssl); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, 2000, (String)null, 0, (String)null, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout) { this(poolConfig, host, port, timeout, (String)null, 0, (String)null); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, boolean ssl) { this(poolConfig, host, port, timeout, (String)null, 0, (String)null, ssl); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, (String)null, 0, (String)null, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password, int database) { this(poolConfig, host, port, timeout, password, database, (String)null); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password, int database, boolean ssl) { this(poolConfig, host, port, timeout, password, database, (String)null, ssl); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password, int database, boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, password, database, (String)null, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password, int database, String clientName) { this(poolConfig, host, port, timeout, timeout, password, database, clientName, false, (SSLSocketFactory)null, (SSLParameters)null, (HostnameVerifier)null); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password, int database, String clientName, boolean ssl) { this(poolConfig, host, port, timeout, timeout, password, database, clientName, ssl, (SSLSocketFactory)null, (SSLParameters)null, (HostnameVerifier)null); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password, int database, String clientName, boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, timeout, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int connectionTimeout, int soTimeout, String password, int database, String clientName, boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { super(poolConfig, new JedisFactory(host, port, connectionTimeout, soTimeout, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier)); } public JedisPool(GenericObjectPoolConfig poolConfig, URI uri) { this(poolConfig, (URI)uri, 2000); } public JedisPool(GenericObjectPoolConfig poolConfig, URI uri, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { this(poolConfig, uri, 2000, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(GenericObjectPoolConfig poolConfig, URI uri, int timeout) { this(poolConfig, uri, timeout, timeout); } public JedisPool(GenericObjectPoolConfig poolConfig, URI uri, int timeout, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { this(poolConfig, uri, timeout, timeout, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(GenericObjectPoolConfig poolConfig, URI uri, int connectionTimeout, int soTimeout) { super(poolConfig, new JedisFactory(uri, connectionTimeout, soTimeout, (String)null, false, (SSLSocketFactory)null, (SSLParameters)null, (HostnameVerifier)null)); } public JedisPool(GenericObjectPoolConfig poolConfig, URI uri, int connectionTimeout, int soTimeout, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { super(poolConfig, new JedisFactory(uri, connectionTimeout, soTimeout, (String)null, uri.getScheme() != null && uri.getScheme().equals("rediss"), sslSocketFactory, sslParameters, hostnameVerifier)); } public Jedis getResource() { Jedis jedis = (Jedis)super.getResource(); jedis.setDataSource(this); return jedis; } /** @deprecated */ @Deprecated public void returnBrokenResource(Jedis resource) { if (resource != null) { this.returnBrokenResourceObject(resource); } } /** @deprecated */ @Deprecated public void returnResource(Jedis resource) { if (resource != null) { try { resource.resetState(); this.returnResourceObject(resource); } catch (Exception var3) { this.returnBrokenResource(resource); throw new JedisException("Could not return the resource to the pool", var3); } } } }
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class RedisUtil { private JedisPool jedisPool; public void initPool(String host,int port ,int database){ JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(200); poolConfig.setMaxIdle(30); poolConfig.setBlockWhenExhausted(true); poolConfig.setMaxWaitMillis(10*1000); poolConfig.setTestOnBorrow(true); jedisPool=new JedisPool(poolConfig,host,port,20*1000); } public Jedis getJedis(){ Jedis jedis = jedisPool.getResource(); // 正确释放资源 return jedis; } }
public PmsSkuInfo getskuById(String skuId, String remoteAddr) { System.out.println("ip为"+remoteAddr+"的帅哥美女"+Thread.currentThread().getName()+"进入商品详情页面"); PmsSkuInfo pmsSkuInfo = new PmsSkuInfo(); // 链接缓存 Jedis jedis = redisUtil.getJedis(); // 查询缓存 String skuKey = "sku:" + skuId + ":info"; String skuJson = jedis.get(skuKey); if (StringUtils.isNotBlank(skuJson)) {//if(skuJson!=null&&!skuJson.equals("")) System.out.println("ip为"+remoteAddr+"的帅哥美女"+Thread.currentThread().getName()+"进入缓存"); pmsSkuInfo = JSON.parseObject(skuJson, PmsSkuInfo.class); } else { System.out.println("ip为"+remoteAddr+"的帅哥美女"+Thread.currentThread().getName()+"进入开始查询数据库"); // 如果缓存中没有,查询mysql // 设置分布式锁 String token = UUID.randomUUID().toString(); String OK = jedis.set("sku:" + skuId + ":lock", token, "nx", "px", 10*1000); //拿到锁就查询 if(StringUtils.isNotBlank(OK)&&OK.equals("OK")){ System.out.println("ip为"+remoteAddr+"的帅哥美女"+Thread.currentThread().getName()+"拿到锁"); pmsSkuInfo = getItemInfoById(skuId); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } if (pmsSkuInfo != null) { // mysql查询结果存入redis jedis.set("sku:" + skuId + ":info", JSON.toJSONString(pmsSkuInfo)); }else { System.out.println("ip为"+remoteAddr+"的帅哥美女"+Thread.currentThread().getName()+"查询null"); //数据库不存在就设置空/null给redis,防止数据库穿透 jedis.setex("sku:"+skuId+":info",60*3,JSON.toJSONString("")); } System.out.println("ip为"+remoteAddr+"的帅哥美女"+Thread.currentThread().getName()+"释放锁"); //释放锁 根据key匹配vulue,匹配上了就删除锁(防止误删其他线程的锁) String lockToken = jedis.get("sku:" + skuId + ":lock"); // if(StringUtils.isNotBlank(lockToken)&&lockToken.equals(token)){ // jedis.del("sku:" + skuId + ":lock"); // } //对比防重删令牌 lua String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; jedis.eval(script, Collections.singletonList(lockToken), Collections.singletonList(token)); }else { //没拿到锁就自旋 System.out.println("ip为"+remoteAddr+"的帅哥美女"+Thread.currentThread().getName()+"开始自旋"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return getskuById(skuId, remoteAddr); } } jedis.close(); return pmsSkuInfo; }
-----------------------------模板
public List<CategoryVO> queryCategoryByPidCategoryVO(Long parentCid) { String cates = redisTemplate.opsForValue().get(KEY_PREFIX + parentCid); if (StringUtils.isNotBlank(cates)){ return JSON.parseArray(cates,CategoryVO.class); } //开始分布式锁 List<CategoryVO> categoryVOS =null; //唯一标识 防止等会误删锁 String uuid= UUID.randomUUID().toString(); Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.MINUTES); if (ifAbsent){ //查询数据库 categoryVOS = pmsClient.queryCategoryByPidCategoryVO(parentCid).getData(); //存入redis 没有判空,防止缓存穿透(存空值) 设置随机过期时间,防止缓存雪崩 redisTemplate.opsForValue().set(KEY_PREFIX + parentCid,JSON.toJSONString(categoryVOS),5+new Random().nextInt(5), TimeUnit.DAYS); //释放锁 del String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; //获取唯一标识 String lock = redisTemplate.opsForValue().get("lock"); //执行删除 this.redisTemplate.execute(new DefaultRedisScript<>(script), Arrays.asList(lock), Arrays.asList(uuid)); }else { // 每隔1秒钟回调一次,再次尝试获取锁 try { Thread.sleep(500); return queryCategoryByPidCategoryVO(parentCid); } catch (InterruptedException e) { e.printStackTrace(); } } return categoryVOS; }
----------------------------------------------redisson推荐-----------------------
public List<CategoryVO> queryCategoryByPidCategoryVO(Long parentCid) {
//判断缓存是否存在
String cates = redisTemplate.opsForValue().get(KEY_PREFIX + parentCid);
if (StringUtils.isNotBlank(cates)){
return JSON.parseArray(cates,CategoryVO.class);
}
//开始分布式锁
RLock lock = redissonClient.getLock("lock" + parentCid);//只锁当前进入的parentCid
lock.lock();
//判断缓存是否存在
String cates02 = redisTemplate.opsForValue().get(KEY_PREFIX + parentCid);
if (StringUtils.isNotBlank(cates02)){
lock.unlock();//释放锁
return JSON.parseArray(cates02,CategoryVO.class);
}
//查询数据库
List<CategoryVO> categoryVOS = pmsClient.queryCategoryByPidCategoryVO(parentCid).getData();
//存入redis 没有判空,防止缓存穿透(存空值) 设置随机过期时间,防止缓存雪崩
redisTemplate.opsForValue().set(KEY_PREFIX + parentCid,JSON.toJSONString(categoryVOS),5+new Random().nextInt(5), TimeUnit.DAYS);
lock.unlock();//释放锁
return categoryVOS;
}
--------------------------抽取 注解+Aop-----------------------------------------------------------------
import org.springframework.core.annotation.AliasFor; import java.lang.annotation.*;
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RedissonCache { //相互等价 value=prefix @AliasFor("prefix") String value() default ""; // 缓存中key的前缀 @AliasFor("value") String prefix() default ""; // 过期时间 单位:分 int timeout() default 5; // 随机时间 单位:分 int random() default 5; }
import com.alibaba.fastjson.JSON; import com.atguigu.gmall.index.annotation.RedissonCache; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.concurrent.TimeUnit; @Aspect @Component public class RedissonAop { @Autowired private StringRedisTemplate redisTemplate; @Autowired private RedissonClient redissonClient; /** * 1.返回值object * 2.参数proceedingJoinPoint * 3.抛出异常Throwable * 4.proceedingJoinPoint.proceed(args)执行业务方法 */ @Around("@annotation(com.atguigu.gmall.index.annotation.RedissonCache)") public Object cacheAroundAdvice(ProceedingJoinPoint point) throws Throwable { Object result =null; // 获取连接点签名 MethodSignature methodSignature = (MethodSignature) point.getSignature(); // 获取连接点的RedissonCache注解信息 RedissonCache annotation = methodSignature.getMethod().getAnnotation(RedissonCache.class); // 获取缓存的前缀 String prefix = annotation.prefix(); //获取目标方法的参数列表 Object[] args = point.getArgs(); // 组装成key String key = prefix + Arrays.asList(args).toString(); // 1. 查询缓存 result = this.cacheHit(methodSignature, key); if (result!=null){ return result; } // 初始化分布式锁 RLock lock = redissonClient.getLock("lock" + Arrays.asList(args).toString()); lock.lock(); // 防止缓存穿透 加锁 // 再次检查内存是否有,因为高并发下,可能在加锁这段时间内,已有其他线程放入缓存 result= this.cacheHit(methodSignature, key); if (result != null) { lock.unlock(); return result; } // 2. 执行查询的业务逻辑从数据库查询 result = point.proceed(point.getArgs()); // 并把结果放入缓存 int timeout = annotation.timeout(); int random = annotation.random(); this.redisTemplate.opsForValue().set(key, JSON.toJSONString(result),timeout+(int)(Math.random()*random), TimeUnit.MINUTES); // 释放锁 lock.unlock(); return result; } /** * 查询缓存的方法 * * @param signature * @param key * @return */ private Object cacheHit(MethodSignature signature, String key) { // 1. 查询缓存 String cache = this.redisTemplate.opsForValue().get(key); if (StringUtils.isNotBlank(cache)) { // 有,则反序列化,直接返回 Class returnType = signature.getReturnType(); // 获取方法返回类型 // 不能使用parseArray<cache, T>,因为不知道List<T>中的泛型 return JSON.parseObject(cache, returnType); } return null; } }
@RedissonCache(prefix = "index:cates",timeout = 6,random = 10) public List<CategoryVO> queryCategoryByPidCategoryVO(Long parentCid) { //判断缓存是否存在 //开始分布式锁 //判断缓存是否存在 //查询数据库 List<CategoryVO> categoryVOS = pmsClient.queryCategoryByPidCategoryVO(parentCid).getData(); //存入redis 没有判空,防止缓存穿透(存空值) 设置随机过期时间,防止缓存雪崩 return categoryVOS; }
参考:https://github.com/redisson/redisson/wiki