Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redisson分布式锁

Redisson 框架中的分布式锁实现方法

作者:emanjusaka

这篇文章主要介绍了Redisson 框架中的分布式锁,实现分布式锁通常有三种方式:数据库、Redis 和 Zookeeper,我们比较常用的是通过 Redis 和 Zookeeper 实现分布式锁,需要的朋友可以参考下

实现分布式锁通常有三种方式:数据库、Redis 和 Zookeeper。我们比较常用的是通过 Redis 和 Zookeeper 实现分布式锁。Redisson 框架中封装了通过 Redis 实现的分布式锁,下面我们分析一下它的具体实现。

by emanjusaka from https://www.emanjusaka.top/2024/03/redisson-distributed-lock 彼岸花开可奈何
本文欢迎分享与聚合,全文转载请留下原文地址。

关键点

Redis 实现分布式锁的几种部署方式

使用方式

引入依赖

<!--        pom.xml文件-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.17.7</version>
</dependency>

版本依赖:

redisson-spring-data module nameSpring Boot version
redisson-spring-data-161.3.y
redisson-spring-data-171.4.y
redisson-spring-data-181.5.y
redisson-spring-data-2x2.x.y
redisson-spring-data-3x3.x.y

yml配置

spring:
  redis:
    redisson:
      config:
        singleServerConfig:
          address: redis://127.0.0.1:6379
          database: 0
          password: null
          timeout: 3000

直接注入使用

package top.emanjusaka;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
 * @Author emanjusaka www.emanjusaka.top
 * @Date 2024/2/28 16:41
 * @Version 1.0
 */
@Service
public class Lock {
    @Resource
    private RedissonClient redissonClient;
    public void lock() {
        // 写入redis的key值
        String lockKey = "lock-test";
        // 获取一个Rlock锁对象
        RLock lock = redissonClient.getLock(lockKey);
        // 获取锁,并为其设置过期时间为10s
        lock.lock(10, TimeUnit.SECONDS);
        try {
            // 执行业务逻辑....
            System.out.println("获取锁成功!");
        } finally {
            // 释放锁
            lock.unlock();
            System.out.println("释放锁成功!");
        }
    }
}

底层剖析

lock()

关键代码

<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        return commandExecutor.syncedEval(getRawName(), LongCodec.INSTANCE, command,
                "if ((redis.call('exists', KEYS[1]) == 0) " +
                       "or (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]);",
                Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
    }
// 省去了那些无关重要的代码
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
    long threadId = Thread.currentThread().getId();
    // tryAcquire就是上面分析的lua完整脚本
    Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
    // 返回null就代表上锁成功。
    if (ttl == null) {
        return;
    }
    // 如果没成功,也就是锁的剩余时间不是null的话,那么就执行下面的逻辑
    // 其实就是说 如果有锁(锁剩余时间不是null),那就死循环等待重新抢锁。
    try {
        while (true) {
            // 重新抢锁
            ttl = tryAcquire(-1, leaseTime, unit, threadId);
            // 抢锁成功就break退出循环
            if (ttl == null) {
                break;
            }
            // 省略一些代码
        }
    } finally {}
}

上面代码实现了一个分布式锁的功能。它使用了Lua脚本来尝试获取锁,并在成功获取锁后返回锁的剩余时间(ttl)。如果获取锁失败,则进入一个死循环,不断尝试重新获取锁,直到成功为止。

unlock()

关键代码

    protected RFuture<Boolean> unlockInnerAsync(long threadId) {
        return evalWriteAsync(getRawName(), 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(ARGV[4], KEYS[2], ARGV[1]); " +
                        "return 1; " +
                    "end; " +
                    "return nil;",
                Arrays.asList(getRawName(), getChannelName()),
                LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId), getSubscribeService().getPublishCommand());
    }

锁续期

watchDog

核心工作流程是定时监测业务是否执行结束,没结束的话在看你这个锁是不是快到期了(超过锁的三分之一时间),那就重新续期。这样防止如果业务代码没执行完,锁却过期了所带来的线程不安全问题。

Redisson 的 watchDog 机制底层不是调度线程池,而是直接用的 netty 事件轮。

Redisson的WatchDog机制是用于自动续期分布式锁和监控对象生命周期的一种机制,确保了分布式环境下锁的正确性和资源的及时释放。

到此这篇关于Redisson 框架中的分布式锁的文章就介绍到这了,更多相关Redisson分布式锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文