Distributed locks + distributed caching problems use redis (redisson) to implement distributed locks

Distributed lock + distributed cache problem

  • In the microservice mode, multiple identical services need to maintain data consistency for the database. At this time, it needs to evolve from a local lock to a distributed lock.
  • This blog post continuously raises questions and solves ideas in an advanced form, improves the code step by step, and realizes the distributed lock function with high reliability.

Insert picture description here

Use redis set command with NX (not exist) parameter to realize distributed lock

NX: only when it does not exist, can set; successful set will return OK, unsuccessful return null
Insert picture description here

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

    //1、占分布式锁。去redis占坑
    Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");
    if(aBoolean){
    
    
        //加锁成功 执行业务
        Map<String, List<Catelog2Vo>> dataFromDB = this.getDataFromDB();
        //删除锁
        stringRedisTemplate.delete("lock");
        return dataFromDB;
    }else {
    
    
        //加锁失败   重试 自旋
        return getCatalogJsonFromDBWithRedisLock();
    }
}

Phase two independent plus the expiration time of the distributed lock

Insert picture description here

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

    //1、占分布式锁。去redis占坑
    Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");
    if(aBoolean){
    
    
        //加锁成功 执行业务
        //2、设置过期时间
        stringRedisTemplate.expire("lock",30, TimeUnit.SECONDS);
        Map<String, List<Catelog2Vo>> dataFromDB = this.getDataFromDB();
        //删除锁
        stringRedisTemplate.delete("lock");
        return dataFromDB;
    }else {
    
    
        //加锁失败   重试 自旋
        return getCatalogJsonFromDBWithRedisLock();
    }
}

Phase three atomic lock and set expiration time

Insert picture description here

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

    //1、占分布式锁。去redis占坑  并设置过期时间 必须是同步的 原子的
    Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock",30,TimeUnit.SECONDS);
    if(aBoolean){
    
    
        //加锁成功 执行业务
        Map<String, List<Catelog2Vo>> dataFromDB = this.getDataFromDB();
        //删除锁
        stringRedisTemplate.delete("lock");
        return dataFromDB;
    }else {
    
    
        //加锁失败   重试 自旋
        return getCatalogJsonFromDBWithRedisLock();
    }
}

Phase 4 delete lock for permission uuid matching

Insert picture description here

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

    //1、占分布式锁。去redis占坑  并设置过期时间 必须是同步的 原子的
    String uuid = UUID.randomUUID().toString();
    Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock",uuid,30,TimeUnit.SECONDS);
    if(aBoolean){
    
    
        //加锁成功 执行业务
        Map<String, List<Catelog2Vo>> dataFromDB = this.getDataFromDB();
        String lock = stringRedisTemplate.opsForValue().get("lock");
        if(uuid.equals(lock)){
    
    
            //删除自己的锁
            stringRedisTemplate.delete("lock");
        }
        return dataFromDB;
    }else {
    
    
        //加锁失败   重试 自旋
        return getCatalogJsonFromDBWithRedisLock();
    }
}

Phase 5 Lua script delete lock atomic operation

Insert picture description here

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

    //1、占分布式锁。去redis占坑  并设置过期时间 必须是同步的 原子的
    String uuid = UUID.randomUUID().toString();
    Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock",uuid,30,TimeUnit.SECONDS);
    if(aBoolean){
    
    
        //加锁成功 执行业务
        Map<String, List<Catelog2Vo>> dataFromDB = this.getDataFromDB();
        //获取值 + 对比 + 删除 必须是原子操作  lua脚本解锁
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                "then " +
                "   return  redis.call('del', KEYS[1])" +
                "else " +
                "   return 0 " +
                "end";
        Long result = stringRedisTemplate.execute(new DefaultRedisScript<Long>(luaScript, Long.class), Arrays.asList("lock"), uuid);
        
        return dataFromDB;
    }else {
    
    
        //加锁失败   重试 自旋
        return getCatalogJsonFromDBWithRedisLock();
    }
}

Phase 6 final result

Regardless of whether the business is completed correctly, delete the locks established by yourself

    //分布式锁
    public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

        //1、占分布式锁。去redis占坑  并设置过期时间 必须是同步的 原子的
        String uuid = UUID.randomUUID().toString();
        Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock",uuid,300,TimeUnit.SECONDS);
        if(aBoolean){
    
    
            //加锁成功 执行业务
            Map<String, List<Catelog2Vo>> dataFromDB = null;
            try {
    
    
                dataFromDB = this.getDataFromDB();
            }finally {
    
    
                //获取值 + 对比 + 删除 必须是原子操作  lua脚本解锁
                String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                        "then " +
                        "   return  redis.call('del', KEYS[1])" +
                        "else " +
                        "   return 0 " +
                        "end";
                Long result = stringRedisTemplate.execute(new DefaultRedisScript<Long>(luaScript, Long.class), Arrays.asList("lock"), uuid);
            }
            return dataFromDB;
        }else {
    
    
            //加锁失败   重试 自旋
            //睡眠
            try {
    
    
                Thread.sleep(200);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            return getCatalogJsonFromDBWithRedisLock();
        }
    }

Use Redisson framework to implement distributed locks

If you manually use redis to implement distributed locks, you need to write atomic lock and lua script atomic unlock statements before each business process, which is too cumbersome, and the framework of using Redisson distributed locks can be more convenient to implement. The bottom layer of Reddison implements the JUC package Therefore, it can be seamlessly connected to the use of JUC.

Introduce dependencies

<!--引入redis的分布式锁  分布式对象框架redison-->
      <dependency>
          <groupId>org.redisson</groupId>
          <artifactId>redisson</artifactId>
          <version>3.12.0</version>
      </dependency>

Configure redissonClient client

@Configuration
public class MyRedissonConfig {
    
    
    /**
     * 所有对Redisson的使用 都是通过RedissonClient对象
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() throws IOException{
    
    
        //1、创建配置   redisson包下的config
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.231.1:6379");
        //2、根据config创建出RedissonClient示例
        return Redisson.create(config);
    }
}

Use redisson locks to implement distributed locks

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedissonLock() {
    
    

    //1、占分布式锁。redisson
    /**
     * 锁的名字 决定了锁的粒度,越细越快
     * 锁的粒度约定:  具体缓存的是某个数据  例如 11号商品  product-11-lock
     */
    RLock lock = redissonClient.getLock("catalogJson-lock");
    lock.lock();
    Map<String, List<Catelog2Vo>> dataFromDB = null;
    try{
    
    
        //加锁成功 执行业务
        dataFromDB = this.getDataFromDB();
    }finally{
    
    
        lock.unlock();
    }
    return dataFromDB;
}

Guess you like

Origin blog.csdn.net/weixin_44634197/article/details/108308395