卡飞资源网

专业编程技术资源共享平台

Redisson 源码解析分布式锁的实现原理与实践

在分布式系统中,多个服务节点需要对共享资源进行协调访问。Redisson 是一个基于 Redis 的 Java 客户端库,它不仅提供了丰富的数据结构支持,还实现了多种分布式锁机制,如可重入锁、读写锁、联锁等。


本文将从源码角度分析 Redisson 中 `RLock` 接口的核心实现类 `RedissonLock`,深入探讨其可重入、自动续期、公平锁、Redlock 算法支持等特性,并结合实际应用场景给出使用建议。




一、Redisson 分布式锁核心接口:RLock


Redisson 提供了 `RLock` 接口,继承自 JDK 的 `
java.util.concurrent.locks.Lock`,并扩展了支持 Redis 的特性:


public interface RLock extends Lock, RExpirable {
    void lock(long leaseTime, TimeUnit unit);
    boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
    ...
}



主要实现类包括:


`RedissonLock`:基础可重入锁;

`RedissonReadLock` / `RedissonWriteLock`:读写锁;

`RedissonMultiLock`:多锁组合(Redlock 实现);

`RedissonFairLock`:公平锁。




二、RedissonLock 的基本结构


`RedissonLock` 是 Redisson 分布式锁的核心实现类,其关键字段如下:


private final CommandAsyncExecutor commandExecutor;
private final String id; // UUID + threadId,唯一标识持有线程
private final String name; // 锁名称 key
private final int internalLockLeaseTime; // 默认加锁时间(毫秒)


底层通过 Lua 脚本操作 Redis 来保证原子性,确保加锁和解锁过程的线程安全。




三、加锁流程详解:tryLockInnerAsync 方法


加锁的核心方法是 `tryLockInnerAsync`,使用 Lua 脚本执行:



<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
        "if (redis.call('exists', KEYS[1]) == 0) then " +
            "redis.call('hset', KEYS[1], ARGV[2], 1); " +
            "redis.call('pexpire', KEYS[1], ARGV[1]); " +
            "return nil; " +
        "end; " +
        "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
            "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
            "redis.call('pexpire', KEYS[1], ARGV[1]); " +
            "return nil; " +
        "end; " +
        "return redis.call('pttl', KEYS[1]);",
        Arrays.<Object>asList(getName()), 
        unit.toMillis(leaseTime), getLockName(threadId));
}



脚本逻辑说明:


1. 如果锁不存在(`exists key == 0`),则设置 hash 表中的当前线程 ID 计数为 1,并设置过期时间;

2. 如果当前线程已持有锁,则增加计数器(实现可重入);

3. 否则返回当前锁的剩余生存时间(TTL)。


该脚本保证了 Redis 中锁的原子性获取与重入逻辑




四、自动续期机制:Watchdog


Redisson 支持自动续期功能,默认加锁时间为 30 秒(可通过 `lockWatchdogTimeout` 配置),并在后台启动 Watchdog 线程定期刷新锁的 TTL。


private void scheduleExpirationRenewal(long threadId) {
    Timeout task = commandExecutor.getConnectionManager().newTimeout(timeout -> {
        RFuture<Boolean> future = renewExpirationAsync(threadId);
        future.onComplete((res, e) -> {
            if (e != null) {
                log.error("Can't update lock expiration", e);
                return;
            }
            if (res) {
                // 成功续期后继续调度
                scheduleExpirationRenewal(threadId);
            }
        });
    }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}


每 1/3 锁超时时间检查一次,若锁仍被当前线程持有,则调用 `renewExpirationAsync` 续期。




五、释放锁流程:unlockInnerAsync


释放锁也通过 Lua 脚本实现:


protected RFuture<Boolean> unlockInnerAsync(long threadId) {
    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
        "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
            "return nil;" +
        "end; " +
        "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
        "if (counter > 0) then " +
            "redis.call('pexpire', KEYS[1], ARGV[2]); " +
            "return 0; " +
        "else " +
            "redis.call('del', KEYS[1]); " +
            "redis.call('publish', KEYS[2], ARGV[1]); " +
            "return 1; " +
        "end;",
        Arrays.<Object>asList(getName(), getChannelName()), 
        LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}


逻辑如下:


1. 判断当前线程是否持有锁;

2. 减少重入计数器;

3. 若计数器大于 0,仅更新 TTL;

4. 若为 0,删除锁并发布解锁消息通知等待线程。




六、公平锁实现:RedissonFairLock


Redisson 还提供了公平锁的实现 `RedissonFairLock`,其核心在于维护一个队列,确保线程按顺序获取锁。


关键机制包括:


使用 Redis List 结构模拟 FIFO 队列;

加锁前检查是否轮到自己;

自动清理无效节点避免死锁。




七、Redlock 算法支持:RedissonMultiLock


Redisson 支持 Redlock 算法,通过 `RedissonMultiLock` 将多个独立的 Redis 实例上的锁组合成一个锁,提高可用性和一致性。



RLock lock1 = redisson.getLock("lock1");
RLock lock2 = redisson.getLock("lock2");
RLock lock3 = redisson.getLock("lock3");
 
RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
lock.lock();



其内部实现采用 N/2+1 个节点成功即认为加锁成功 的策略,符合 Redlock 算法要求。




八、适用场景与最佳实践





最佳实践建议:


合理设置锁超时时间,避免死锁;

避免长时间持有锁,防止阻塞其他线程;

优先使用异步 API,提升性能;

监控 Redis 性能与连接状态,确保锁服务可用性。




九、总结


Redisson 的分布式锁设计充分考虑了可重入、公平性、自动续期、集群支持等多个维度,是一个成熟、稳定的分布式协调工具。其核心特点如下:



通过对 Redisson 源码的深入理解,开发者可以更好地掌握其内部机制,在实际项目中高效地使用分布式锁,提升系统的稳定性与一致性。




> 参考资料


> - [Redisson GitHub](https://github.com/redisson/redisson)

> - Redisson 官方文档

> - 《Redis 设计与实现》

> - Martin Fowler 对 Redlock 的讨论文章

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言