基于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

原文地址:https://www.cnblogs.com/yxgmagic/p/15106337.html