searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Redission在分布式锁中的使用总结

2023-10-16 11:09:35
64
0

前言

锁的功能我想大家已经非常熟悉,是用于控制多个线程对共享资源的访问。锁的主要功能是确保线程安全,以防止多个线程同时访问或修改共享数据。

而分布式锁就是将单机上的锁,推广到分布式系统中。我们常用的分布式锁是基于缓存的分布式锁:在这种锁中,一个分布式缓存(如Redis或Memcached)用于管理锁状态。通常,一个进程或线程会尝试在缓存中设置一个锁键,如果成功,它获得了锁。其他进程或线程在尝试获取锁时会检查锁键是否已被占用。这种方法通常更快速,但需要谨慎处理锁的过期和失效。

在我的实际工作经历中,有自己实现了一套基于redis的分布式锁,但是在实际使用中遇到了许多问题。

下面就稍微介绍下遇到的问题以及为何引入Redission。

为什么使用Redission

在自己实现的分布式锁中,是基于redisTemplate对象的一套接口,其中核心代码是:



String lockKey = "product_001"; 

// 使用 setnx 添加分布式锁 

// 返回 true 代表之前redis中没有key为 lockKey 的值,并已进行成功设置 

// 返回 false 代表之前redis中已经存在 lockKey 这个key了 

Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "lock");


在理想场景中,分布式系统中的某个线程,向redis获取锁,并在任务结束后,手动释放锁。

但是在实际场景中,常常出现线程执行异常,程序异常中断等问题,导致锁没有被正常释放,这样就造成了死锁,导致这个锁无法再被获取。

解决方式:防止死锁 加入expireTime, redis缓存过期时间,使得锁能够在expireTime后被自动释放。

但是也会带来新的问题:

  • 线程A实际执行任务时间超过expireTime,任务未执行完锁便自动释放。
  • 这时候其他线程B可以正常获取锁,而此时线程A执行完任务释放锁,释放的是B的锁。

这样就造成了锁的混乱,不能保证共享资源同一时间只被一个线程占用。

可见,要彻底解决这些问题,需要花费一定功夫。而Redission提供了全面完整的锁功能,我们可以直接引入来解决分布式锁的问题。

Redission是什么

Redisson是一个用于Java编程语言的开源框架,它提供了用于与Redis(一个流行的分布式缓存和消息代理系统)进行交互的简单而强大的API。Redisson使Java开发人员能够更轻松地利用Redis的分布式数据结构和功能,从而简化了分布式系统的开发。

Redisson的主要特点和功能

  1. 分布式数据结构:Redisson提供了多种分布式数据结构,如分布式锁、分布式集合、分布式映射、分布式队列等,这些数据结构可用于构建高度可扩展的分布式应用。

  2. 分布式锁:Redisson允许Java应用程序轻松地使用分布式锁,以协调多个线程或多个应用程序实例之间的并发访问。

  3. 分布式消息队列:Redisson支持发布/订阅模式和队列,允许应用程序在分布式环境中进行异步通信。

  4. 分布式缓存:Redisson提供了缓存功能,允许Java应用程序通过Redis存储和管理缓存数据,以提高性能和减少数据库负载。

  5. 分布式调度器:Redisson包括一个分布式任务调度器,允许开发人员安排和管理分布式任务,以执行定时作业或其他任务。

  6. 高可用性支持:Redisson支持Redis Sentinel和Redis Cluster等Redis高可用配置,确保应用程序能够在Redis出现故障时继续正常运行。

  7. 易于集成:Redisson可以轻松地与Spring框架等Java应用程序框架集成,使开发人员能够快速构建分布式系统。

Redission如何解决刚才的问题

Redisson通过lua脚本实现操作的原子性 

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;

Redission还实现了一种名为 "看门狗"(Watchdog)的功能,用于管理分布式锁的续期(Renewal)。续期是指在一个线程持有分布式锁的情况下,允许其自动延长锁的持有时间,以避免锁过早释放,从而确保锁的有效性。

以下是 Watchdog 的一些特点和用法:

  1. 锁续期:当一个线程成功获得分布式锁后,Watchdog 将启动一个后台任务,定期(默认情况下每10秒)续期锁的有效期。这意味着锁的持有时间将自动延长,直到锁被显式释放或直到线程失去了对锁的控制。

  2. 自动解锁:如果持有锁的线程在锁的有效期内发生崩溃或异常退出,Redisson Watchdog 将确保锁会自动解锁,而不会永久保留锁。这有助于避免由于线程故障而导致的死锁情况。

  3. 手动续期:除了自动续期外,Redisson Watchdog 还允许您手动续期锁的有效期。这对于需要长时间持有锁的场景很有用。

另外Redisson 还提供了可以指定leaseTime参数的加锁方法来指定加锁的时间。超过这个时间后锁便自动解开了,不会延长锁的有效期。

如何使用Redission

首先pom引入Redission依赖

<project>
    <!-- 其他项目配置 -->

    <dependencies>
        <!-- 引入 Redisson 依赖 -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.16.6</version> <!-- 使用你需要的 Redisson 版本 -->
        </dependency>
    </dependencies>
</project>

以下为实际的使用代码
public class RedissonLockRenewalExample {
    public static void main(String[] args) {
        // 创建 Redisson 客户端
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        RedissonClient redisson = Redisson.create(config);

        // 获取分布式锁
        RLock lock = redisson.getLock("myLock");

        try {
            lock.lock();

            // 在锁的持有期间进行操作
            // 锁的有效期将自动续期

            // 手动续期锁的有效期(如果需要)
            lock.extend(30, java.util.concurrent.TimeUnit.SECONDS);
        } finally {
            // 释放锁
            lock.unlock();
        }

        // 关闭 Redisson 客户端
        redisson.shutdown();
    }
}


在上面的示例中,lock.extend(30, TimeUnit.SECONDS) 手动续期了锁的有效期,但通常情况下,Redisson Watchdog 会自动续期锁的有效期,无需手动干预。这有助于确保分布式锁不会在持有线程发生故障时被过早释放。


0条评论
0 / 1000
陈****迪
5文章数
0粉丝数
陈****迪
5 文章 | 0 粉丝
原创

Redission在分布式锁中的使用总结

2023-10-16 11:09:35
64
0

前言

锁的功能我想大家已经非常熟悉,是用于控制多个线程对共享资源的访问。锁的主要功能是确保线程安全,以防止多个线程同时访问或修改共享数据。

而分布式锁就是将单机上的锁,推广到分布式系统中。我们常用的分布式锁是基于缓存的分布式锁:在这种锁中,一个分布式缓存(如Redis或Memcached)用于管理锁状态。通常,一个进程或线程会尝试在缓存中设置一个锁键,如果成功,它获得了锁。其他进程或线程在尝试获取锁时会检查锁键是否已被占用。这种方法通常更快速,但需要谨慎处理锁的过期和失效。

在我的实际工作经历中,有自己实现了一套基于redis的分布式锁,但是在实际使用中遇到了许多问题。

下面就稍微介绍下遇到的问题以及为何引入Redission。

为什么使用Redission

在自己实现的分布式锁中,是基于redisTemplate对象的一套接口,其中核心代码是:



String lockKey = "product_001"; 

// 使用 setnx 添加分布式锁 

// 返回 true 代表之前redis中没有key为 lockKey 的值,并已进行成功设置 

// 返回 false 代表之前redis中已经存在 lockKey 这个key了 

Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "lock");


在理想场景中,分布式系统中的某个线程,向redis获取锁,并在任务结束后,手动释放锁。

但是在实际场景中,常常出现线程执行异常,程序异常中断等问题,导致锁没有被正常释放,这样就造成了死锁,导致这个锁无法再被获取。

解决方式:防止死锁 加入expireTime, redis缓存过期时间,使得锁能够在expireTime后被自动释放。

但是也会带来新的问题:

  • 线程A实际执行任务时间超过expireTime,任务未执行完锁便自动释放。
  • 这时候其他线程B可以正常获取锁,而此时线程A执行完任务释放锁,释放的是B的锁。

这样就造成了锁的混乱,不能保证共享资源同一时间只被一个线程占用。

可见,要彻底解决这些问题,需要花费一定功夫。而Redission提供了全面完整的锁功能,我们可以直接引入来解决分布式锁的问题。

Redission是什么

Redisson是一个用于Java编程语言的开源框架,它提供了用于与Redis(一个流行的分布式缓存和消息代理系统)进行交互的简单而强大的API。Redisson使Java开发人员能够更轻松地利用Redis的分布式数据结构和功能,从而简化了分布式系统的开发。

Redisson的主要特点和功能

  1. 分布式数据结构:Redisson提供了多种分布式数据结构,如分布式锁、分布式集合、分布式映射、分布式队列等,这些数据结构可用于构建高度可扩展的分布式应用。

  2. 分布式锁:Redisson允许Java应用程序轻松地使用分布式锁,以协调多个线程或多个应用程序实例之间的并发访问。

  3. 分布式消息队列:Redisson支持发布/订阅模式和队列,允许应用程序在分布式环境中进行异步通信。

  4. 分布式缓存:Redisson提供了缓存功能,允许Java应用程序通过Redis存储和管理缓存数据,以提高性能和减少数据库负载。

  5. 分布式调度器:Redisson包括一个分布式任务调度器,允许开发人员安排和管理分布式任务,以执行定时作业或其他任务。

  6. 高可用性支持:Redisson支持Redis Sentinel和Redis Cluster等Redis高可用配置,确保应用程序能够在Redis出现故障时继续正常运行。

  7. 易于集成:Redisson可以轻松地与Spring框架等Java应用程序框架集成,使开发人员能够快速构建分布式系统。

Redission如何解决刚才的问题

Redisson通过lua脚本实现操作的原子性 

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;

Redission还实现了一种名为 "看门狗"(Watchdog)的功能,用于管理分布式锁的续期(Renewal)。续期是指在一个线程持有分布式锁的情况下,允许其自动延长锁的持有时间,以避免锁过早释放,从而确保锁的有效性。

以下是 Watchdog 的一些特点和用法:

  1. 锁续期:当一个线程成功获得分布式锁后,Watchdog 将启动一个后台任务,定期(默认情况下每10秒)续期锁的有效期。这意味着锁的持有时间将自动延长,直到锁被显式释放或直到线程失去了对锁的控制。

  2. 自动解锁:如果持有锁的线程在锁的有效期内发生崩溃或异常退出,Redisson Watchdog 将确保锁会自动解锁,而不会永久保留锁。这有助于避免由于线程故障而导致的死锁情况。

  3. 手动续期:除了自动续期外,Redisson Watchdog 还允许您手动续期锁的有效期。这对于需要长时间持有锁的场景很有用。

另外Redisson 还提供了可以指定leaseTime参数的加锁方法来指定加锁的时间。超过这个时间后锁便自动解开了,不会延长锁的有效期。

如何使用Redission

首先pom引入Redission依赖

<project>
    <!-- 其他项目配置 -->

    <dependencies>
        <!-- 引入 Redisson 依赖 -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.16.6</version> <!-- 使用你需要的 Redisson 版本 -->
        </dependency>
    </dependencies>
</project>

以下为实际的使用代码
public class RedissonLockRenewalExample {
    public static void main(String[] args) {
        // 创建 Redisson 客户端
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        RedissonClient redisson = Redisson.create(config);

        // 获取分布式锁
        RLock lock = redisson.getLock("myLock");

        try {
            lock.lock();

            // 在锁的持有期间进行操作
            // 锁的有效期将自动续期

            // 手动续期锁的有效期(如果需要)
            lock.extend(30, java.util.concurrent.TimeUnit.SECONDS);
        } finally {
            // 释放锁
            lock.unlock();
        }

        // 关闭 Redisson 客户端
        redisson.shutdown();
    }
}


在上面的示例中,lock.extend(30, TimeUnit.SECONDS) 手动续期了锁的有效期,但通常情况下,Redisson Watchdog 会自动续期锁的有效期,无需手动干预。这有助于确保分布式锁不会在持有线程发生故障时被过早释放。


文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0