• 基于Redis实现分布式锁


    基于Redis实现分布式锁
    获取锁的Redis命令
    set resource_name my_random_value NX PX 30000

    resource_name: 资源名称,可根据不同业务区分不同的锁

    my_random_value: 随机值,每个线程的随机值都不同,用于释放锁时的校验

    NX: key不存在时设置成功,key存在则设置不成功

    PX: 自动失效时间,出现异常情况,锁可以过期失效

    30000: 有效时间

    原理: 

    利用NX的原子性,多个线程并发时,只有一个线程可以设置成功

    设置成功即获得锁,可以执行后续的业务处理

    如果出现异常,过了锁的有效期,锁自动释放

    释放锁采用Redis的delete命令

    释放锁时校验之前设置的随机数,相同才能释放锁

    释放锁的LUA脚本:

     if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end

     案例:

    @RestController
    @Slf4j
    public class RedisLockController {
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        @RequestMapping("/redisLock")
        public String redisLock(){
            log.info("我进入了方法!");
            String key = "redisKey" ;
            String value = UUID.randomUUID().toString();
            RedisCallback<Boolean> redisCallback = connection ->{
                //设置NX
                RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();
                //设置过期时间
                Expiration expiration = Expiration.seconds(30);
                //设置序列化key
                byte[] redisKey = redisTemplate.getKeySerializer().serialize(key);
                //序列化value
                byte[] redisValue = redisTemplate.getKeySerializer().serialize(value);
                //执行setnx操作
                Boolean result = connection.set(redisKey, redisValue, expiration, setOption);
                return result;
            };
            //获取分布式锁
            Boolean lock = (Boolean)redisTemplate.execute(redisCallback);
            if (lock){
                log.info("我进入了锁!");
                try {
                    Thread.sleep(15000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally{
                    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                    RedisScript<Boolean> redisScript = RedisScript.of(script,Boolean.class);
                    List<String> keys = Arrays.asList(key);
                    Boolean result = (Boolean) redisTemplate.execute(redisScript, keys, value);
                    log.info("释放锁的结果: " + result);
                }
    
            }
            return "方法执行完成!";
        }
    }

    redisson实现:

    @RequestMapping("redissonLock")
        public String redissonLock(){
            log.info("我进入了方法!");
            Config config = new Config();
            /**
             * <dependency>
             *     <groupId>org.redisson</groupId>
             *     <artifactId>redisson</artifactId>
             *     <version>3.0.1</version>
             * </dependency>
             */
            //注意redisson版本设置setAddress
            config.useSingleServer().setAddress("localhost:6379");
            RedissonClient redisson = Redisson.create(config);
    
            RLock rLock = redisson.getLock("order");
            try {
                rLock.lock(30, TimeUnit.SECONDS);
                log.info("我获得了锁");
                Thread.sleep(10000);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                log.info("我释放了锁!");
                rLock.unlock();
            }
            log.info("方法执行结束!");
            return "方法执行完成";
        }

    Zookeeper:

    @Slf4j
    public class ZkLock implements AutoCloseable, Watcher {
    
        private ZooKeeper zooKeeper;
    
        private String znode;
    
        public ZkLock() throws Exception{
            zooKeeper = new ZooKeeper("localhost:2181",10000,this);
    
        }
    
        public Boolean getLock(String bussinessCode){
            try {
                Stat stat = zooKeeper.exists("/" + bussinessCode, false);
                if (stat == null){
                    zooKeeper.create("/" + bussinessCode,bussinessCode.getBytes(),
                            ZooDefs.Ids.OPEN_ACL_UNSAFE,
                            CreateMode.PERSISTENT);
                }
                //创建瞬时有序节点
                znode = zooKeeper.create("/" + bussinessCode + "/" + bussinessCode + "_",
                        bussinessCode.getBytes(),
                        ZooDefs.Ids.OPEN_ACL_UNSAFE,
                        CreateMode.EPHEMERAL_SEQUENTIAL);
    
                //获取业务节点下的所有子节点
                List<String> childrenNodes = zooKeeper.getChildren("/" + bussinessCode, false);
                //子节点排序
                Collections.sort(childrenNodes);
                String firstNode = childrenNodes.get(0);
                if (znode.endsWith(firstNode)){
                    return true;
                }
    
                //不是第一个子节点,则监听前一个节点
                String lastNode = firstNode;
                for (String node:childrenNodes){
                    if (znode.endsWith(node)){
                        zooKeeper.exists("/" + bussinessCode + "/" + lastNode, true);
                        break;
                    }else {
                        lastNode = node;
                    }
                }
                synchronized (this){
                    wait();
                }
                return true;
            } catch (KeeperException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return false;
        }
    
        @Override
        public void close() throws Exception {
            zooKeeper.delete(znode,-1);
            zooKeeper.close();
            log.info("我已经释放了锁.");
        }
    
        @Override
        public void process(WatchedEvent event) {
            if (event.getType() == Event.EventType.NodeDeleted){
                synchronized (this){
                    notify();
                }
    
            }
        }
    }
    View Code

  • 相关阅读:
    将Linux下python默认版本切换成替代版本
    ubuntu下卸载python2和升级python3.5
    Linux下安装theano
    梯度下降法
    使用Matlab实现对图片的缩放
    matlab 中的删除文件
    解决aws ec2的centos7设置时区无效
    yum安装redis5/mq/consul
    django web应用runserver模式下cpu占用高解决办法
    N1如何完美刷入armbian系统教程
  • 原文地址:https://www.cnblogs.com/yxgmagic/p/15106337.html
Copyright © 2020-2023  润新知