Redisson实现分布式锁

Redisson实现分布式锁

  • Redisson是一个Redis客户端的Java实现,它提供了一系列分布式的工具,包括分布式锁,分布式集合,分布式队列,分布式计数器,分布式阻塞队列等。
  • 可以让开发者像使用本地集合一样使用 Redis, 完全感知不到 Redis 的存在.
  • Redisson官网

使用Redisson

  1. 添加Redisson依赖

    • spring boot starter 引入(不推荐,版本迭代太快,容易冲突)
    • 直接引入redisson依赖(推荐)
      1
      2
      3
      4
      5
      6
      
        <!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.35.0</version>
        </dependency>
      
  2. 配置Redisson

    • 新建RedissonConfig类,配置Redis连接信息
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      
      @Configuration
      @ConfigurationProperties(prefix = "spring.data.redis") // 读取配置文件中的redis配置
      @Data    // lombok注解,自动生成getter、setter方法
      public class RedissonConfig {
      
        private String host;
      
        private String port;
      
        private String password;
      
        @Bean
        public RedissonClient redissonClient() {
            // 1. 创建配置
            Config config = new Config();
            String redisAddress = String.format("redis://%s:%s", host, port);
      
            // 使用单个Redis,没有开集群 useClusterServers()  设置地址和使用库
            config.useSingleServer().setAddress(redisAddress).setDatabase(3).setPassword(password);
      
            // 2. 创建实例
            RedissonClient redisson = Redisson.create(config);
            return redisson;
        }
      }
      
  3. 使用Redisson

    • 在xxxService类中注入RedissonClient,并使用Redisson提供的分布式锁, 以定时任务为例:
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      
      @Component
      @Slf4j
      public class PreCacheJob {
      
        @Resource
        private UserService userService;
      
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
      
        @Resource
        private RedissonClient redissonClient;
      
        // 重点用户
        private List<Long> mainUserList = Arrays.asList(1L);
      
        // 每天执行,预热推荐用户
        @Scheduled(cron = "0 59 23 * * *")   //自己设置时间测试
        public void doCacheRecommendUser() {
            RLock rlock = redissonClient.getLock("hjx:precachejob:docache:lock");
            try {
                if (rlock.tryLock(0, 30000L, TimeUnit.MILLISECONDS)) {
                    //查数据库
                    for (Long userId : mainUserList) {
                        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
                        Page<User> userPage = userService.page(new Page<>(1, 20), queryWrapper);
                        String redisKey = String.format("hjx:user:recommend:%s", userId);
                        ValueOperations valueOperations = redisTemplate.opsForValue();
                        //写缓存,30s过期
                        try {
                            valueOperations.set(redisKey, userPage, 30000, TimeUnit.MILLISECONDS);
                        } catch (Exception e) {
                            log.error("redis set key error", e);
                        }
                    }
                }
            } catch (InterruptedException e) {
                log.error("doCacheRecommendUser, redis lock error", e);
            } finally {
                // 只能释放自己的锁
                if (rlock.isHeldByCurrentThread()) {
                    rlock.unlock();
                }
            }
        }
      } 
      
  4. 续期 (看门狗)

    • Redisson提供了Watchdog来监控Redis的运行状态,如果Redis出现故障,Watchdog会自动释放锁,避免死锁。
    • 原理: 监听当前线程, 默认过期时间是 30 秒, 每 10 秒续期一次 (补到 30 秒)
    • 注意: 如果线程挂掉(注意 debug 模式也会被它当成服务器宕机),则不会续期
    • 代码测试:
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      @Test
      void testWatchDog() {
          RLock lock = redissonClient.getLock("hjx:precachejob:docache:lock");
          try {
              // 只有一个线程能获取到锁
              if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                  Thread.sleep(300000);//todo 实际要执行的代码
                  System.out.println("getLock: " + Thread.currentThread().getId());
              }
          } catch (InterruptedException e) {
              System.out.println(e.getMessage());
          } finally {
              // 只能释放自己的锁
              if (lock.isHeldByCurrentThread()) {
                  System.out.println("unLock: " + Thread.currentThread().getId());
                  lock.unlock();
              }
          }
      }
      
使用 Hugo 构建
主题 StackJimmy 设计