首页 > 其他 > 详细

分布式锁-redis

时间:2020-07-22 09:40:58      阅读:84      评论:0      收藏:0      [点我收藏+]
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

分布式锁-redis

原文:https://www.cnblogs.com/lvym/p/13355377.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!