qidao123.com技术社区-IT企服评测·应用市场

标题: 微服务架构的保卫者:Redisson 分布式锁与看门狗机制实战指南 [打印本页]

作者: 傲渊山岳    时间: 2025-3-26 08:56
标题: 微服务架构的保卫者:Redisson 分布式锁与看门狗机制实战指南
1. 分布式锁简介

1.1 什么是分布式锁

在单机应用中,可以利用 Java 内置的锁机制(如 synchronized、ReentrantLock 等)来实现线程间的同步。但在分布式情况下,由于应用部署在多台服务器上,传统的单机锁无法满足需求,这时就需要分布式锁。
分布式锁是一种跨 JVM、跨服务器的锁机制,它能够在分布式系统中对共享资源举行互斥访问控制,确保在同一时间只有一个客户端可以获得锁并执行操作。
1.2 分布式锁应用场景

1.3 分布式锁的核心要求

2. Redisson 简介

2.1 什么是 Redisson

Redisson 是一个在 Redis 基础上实现的 Java 驻内存数据网格(In-Memory Data Grid)。它提供了分布式和可扩展的 Java 数据结构,包括分布式锁、分布式集合、分布式对象等功能。
2.2 Redisson 与 Jedis、Lettuce 对比

2.3 Redisson 的主要功能

3. Redisson 分布式锁的实现原理

3.1 基于 Redis 的锁实现

Redisson 的分布式锁基于 Redis 的EVAL命令(执行 Lua 脚本)实现。它利用了一个 Redis 键值对来表现锁,键是锁的名称,值包罗锁的持有者信息和过期时间。
基本流程:
3.2 锁的实现方案

Redisson 提供了多种锁的实现方案:
3.3 锁的获取和开释流程

锁的获取
  1. -- KEYS[1]是锁的key,ARGV[1]是线程标识,ARGV[2]是过期时间
  2. if (redis.call('exists', KEYS[1]) == 0) or (redis.call('hexists', KEYS[1], ARGV[1]) == 1) then
  3.     redis.call('hincrby', KEYS[1], ARGV[1], 1);
  4.     redis.call('pexpire', KEYS[1], ARGV[2]);
  5.     return nil;
  6. end;
  7. return redis.call('pttl', KEYS[1]);
复制代码
锁的开释
  1. -- KEYS[1]是锁的key,ARGV[1]是线程标识
  2. if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then
  3.     return nil;
  4. end;
  5. local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1);
  6. if (counter > 0) then
  7.     return 0;
  8. else
  9.     redis.call('del', KEYS[1]);
  10.     return 1;
  11. end;
复制代码
4. Redisson 分布式锁的利用

4.1 Maven 依赖配置
  1. <dependency>
  2.     <groupId>org.redisson</groupId>
  3.     <artifactId>redisson</artifactId>
  4.     <version>3.23.3</version>
  5. </dependency>
复制代码
4.2 基本配置
  1. @Configuration
  2. public class RedissonConfig {
  3.     @Value("${spring.data.redis.host}")
  4.     private String host;
  5.     @Value("${spring.data.redis.port}")
  6.     private int port;
  7.     @Value("${spring.data.redis.password}")
  8.     private String password;
  9.     @Value("${spring.data.redis.database}")
  10.     private int database;
  11.     @Bean
  12.     public RedissonClient redissonClient() {
  13.         Config config = new Config();
  14.         config.useSingleServer()
  15.               .setAddress("redis://" + host + ":" + port)
  16.               .setDatabase(database);
  17.         if (password != null && !password.isEmpty()) {
  18.             config.useSingleServer().setPassword(password);
  19.         }
  20.         return Redisson.create(config);
  21.     }
  22. }
复制代码
4.3 基本锁的利用
  1. @Service
  2. public class LockService {
  3.     @Resource
  4.     private RedissonClient redissonClient;
  5.     public void doSomething() {
  6.         RLock lock = redissonClient.getLock("myLock");
  7.         try {
  8.             // 尝试获取锁,最多等待100秒,锁有效期为30秒
  9.             boolean isLocked = lock.tryLock(100, 30, TimeUnit.SECONDS);
  10.             if (isLocked) {
  11.                 // 业务处理
  12.                 System.out.println("执行业务逻辑");
  13.             }
  14.         } catch (InterruptedException e) {
  15.             Thread.currentThread().interrupt();
  16.         } finally {
  17.             // 释放锁
  18.             if (lock.isHeldByCurrentThread()) {
  19.                 lock.unlock();
  20.             }
  21.         }
  22.     }
  23. }
复制代码
4.4 不同类型锁的利用

  1. RLock lock = redissonClient.getLock("myLock");
  2. lock.lock();
  3. try {
  4.     try {
  5.         lock.lock();
  6.     } finally {
  7.         lock.unlock();
  8.     }
  9. } finally {
  10.     lock.unlock();
  11. }
复制代码
  1. RLock fairLock = redissonClient.getFairLock("myFairLock");
  2. fairLock.lock();
  3. try {
  4.     // 业务处理
  5. } finally {
  6.     fairLock.unlock();
  7. }
复制代码
  1. RReadWriteLock rwLock = redissonClient.getReadWriteLock("myRWLock");
  2. // 读锁(共享)
  3. RLock readLock = rwLock.readLock();
  4. readLock.lock();
  5. try {
  6.     // 读取操作
  7. } finally {
  8.     readLock.unlock();
  9. }
  10. // 写锁(排他)
  11. RLock writeLock = rwLock.writeLock();
  12. writeLock.lock();
  13. try {
  14.     // 写入操作
  15. } finally {
  16.     writeLock.unlock();
  17. }
复制代码
  1. RLock lock1 = redissonClient.getLock("lock1");
  2. RLock lock2 = redissonClient.getLock("lock2");
  3. RLock lock3 = redissonClient.getLock("lock3");
  4. // 组合多个锁
  5. RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2, lock3);
  6. multiLock.lock();
  7. try {
  8.     // 业务处理
  9. } finally {
  10.     multiLock.unlock();
  11. }
复制代码
5. 看门狗机制详解

5.1 什么是看门狗机制

看门狗(Watchdog)机制是 Redisson 为分布式锁提供的一种自动续期功能。它能够在客户端持有锁期间,自动延长锁的有效期,防止因为执行时间过长导致锁过期被其他客户端获取,从而破坏互斥性。
5.2 看门狗的工作原理

5.3 看门狗的关键源码分析

Redisson 中看门狗的核心实如今RedissonLock.java类中:
  1. // 锁的自动续期逻辑
  2. private void scheduleExpirationRenewal(long threadId) {
  3.     ExpirationEntry entry = new ExpirationEntry();
  4.     ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);
  5.     if (oldEntry != null) {
  6.         oldEntry.addThreadId(threadId);
  7.     } else {
  8.         entry.addThreadId(threadId);
  9.         renewExpiration();
  10.     }
  11. }
  12. // 续期定时任务
  13. private void renewExpiration() {
  14.     Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
  15.         @Override
  16.         public void run(Timeout timeout) throws Exception {
  17.             // 续期逻辑
  18.             // ...
  19.             // 每internalLockLeaseTime/3时间后,重新检查并续期
  20.             commandExecutor.getConnectionManager().newTimeout(this,
  21.                 internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
  22.         }
  23.     }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
  24. }
复制代码
5.4 看门狗机制的启用和关闭

启用看门狗机制(默认):
  1. // 不指定过期时间,默认启用看门狗机制
  2. lock.lock();
复制代码
禁用看门狗机制
  1. // 明确指定过期时间,看门狗机制将被禁用
  2. lock.lock(10, 30, TimeUnit.SECONDS);
复制代码
5.5 看门狗配置参数

可以通过配置修改看门狗的默认行为:
  1. // 设置锁的默认过期时间(看门狗续期间隔为该值的1/3)
  2. Config config = new Config();
  3. config.setLockWatchdogTimeout(30000); // 30秒
  4. RedissonClient redisson = Redisson.create(config);
复制代码
6. 分布式锁的最佳实践

6.1 合理利用看门狗机制

6.2 锁的粒度选择

6.3 锁的开释包管

  1. try {
  2.     // 业务逻辑
  3. } finally {
  4.     if (lock.isHeldByCurrentThread()) {
  5.         lock.unlock();
  6.     }
  7. }
复制代码
6.4 处理锁的获取失败

  1. int retryCount = 3;
  2. while (retryCount > 0) {
  3.     boolean locked = lock.tryLock(5, TimeUnit.SECONDS);
  4.     if (locked) {
  5.         try {
  6.             // 业务逻辑
  7.             return result;
  8.         } finally {
  9.             lock.unlock();
  10.         }
  11.     }
  12.     retryCount--;
  13.     Thread.sleep(1000);
  14. }
  15. // 降级处理
  16. return fallbackMethod();
复制代码
7. Redisson 分布式锁与其他实现的对比

7.1 与 Redis 原生命令实现对比

Redis 原生命令(SETNX + EXPIRE):
Redisson:
7.2 与 Zookeeper 实现对比

Zookeeper:
Redisson:
7.3 各种实现的实用场景

8. 常见问题及解决方案

8.1 锁的误删除问题

问题:一个客户端开释了其他客户端持有的锁
解决方案
8.2 锁的过期问题

问题:业务执行时间凌驾锁的有效期
解决方案
8.3 缓存崩溃和恢复

问题:Redis 服务器故障或重启
解决方案
8.4 性能优化


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) Powered by Discuz! X3.4