本文介绍: Redisson 提供了简单而强大的分布式解决方案通过合理设置锁的持有时间使用看门狗进行续约,可以确保系统在高并发环境下的稳定性和可靠性。在实际应用中,开发人员可以根据业务需求选择合适的锁策略,并进行必要的异常处理,以实现分布式系统的高效运行

引言

在现代分布式系统中,处理并发问题至关重要的。分布式锁是解决这类问题的关键工具之一。本文介绍如何使用 Redisson一个强大的 Redis 客户端库,来实现高性能、高可用性分布式锁。

基本概念

分布式锁是多个进程线程之间协调操作的一种机制。它通常包括三个基本操作加锁、持有锁、释放锁。Redisson 提供了简单而强大的接口管理这些操作

Redisson 分布式锁的使用方法

第一、添加依赖

<!-- redisson分布式锁 -->
<dependency>
    <groupId&gt;org.redisson</groupId&gt;
    <artifactId&gt;redisson</artifactId>
    <version>3.15.5</version>
</dependency>

第二、添加redisson配置

package com.kingmouse.lock;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created with IntelliJ IDEA
 *
 * @author kingMouse
 * @date 2023/11/7 09:50
 * Description: redisson配置类 单机和集群自选
 */

@Configuration
public class RedissonConfig {


    /**
     * 单机模式
     * @return RedissonClient
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonSingle() {
        // 1.创建配置
        Config config = new Config();
        // 单机模式
        config.useSingleServer()
                // 设置地址
                .setAddress("redis://127.0.0.1:6379")
                // 设置连接
                .setConnectionPoolSize(10);
        return Redisson.create(config);
    }


    /**
     * 单机模式
     * @return RedissonClient
     */

//    @Bean(destroyMethod = "shutdown")
//    public RedissonClient redissonCluster() {
//        // 1.创建配置
//        Config config = new Config();
//        // 集群模式-带密码
//        config.useClusterServers()
//                .addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001")
//                .setPassword("12356")
//                // 设置master节点最小连接
//                .setMasterConnectionMinimumIdleSize(10)
//                // 设置slave节点最小连接数
//                .setSlaveConnectionMinimumIdleSize(10);
//        return Redisson.create(config);
//    }

第三添加测试

package com.kingmouse.lock;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA
 *
 * @author kingMouse
 * @date 2023/11/5 09:54
 * Description:
 */

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedissonLockTest {

    @Autowired
    private RedissonClient redissonClient;


    /**
     * 阻塞式
     */
    @Test
    public void testRedissonBlock() {

        // 设置锁的名字,同一个名字为同一把锁,不同名字之间互不干扰
        String lockKey = "kingmouse-001";
        // 获取对象
        RLock lock = redissonClient.getLock(lockKey);
        // 加锁,尝试获取锁,5秒后获取不到则直接放弃
        lock.lock(5, TimeUnit.SECONDS);

        //  实际业务处理
        try {
            System.out.println("阻塞式--加锁成功,线程 ID:" + Thread.currentThread().getId());
        } catch (Exception e) {
            log.error("业务处理异常:{}", e.getMessage());
        } finally {
            // 解锁
            lock.unlock();
            System.out.println("阻塞式--解锁成功,线程 ID:" + Thread.currentThread().getId());
        }
    }

    @Test
    public void testRedissonNonBlock() throws Exception {
        // 设置锁的名字,同一个名字为同一把锁,不同名字之间互不干扰
        String lockKey = "kingmouse-002";
        // 获取锁对象
        RLock lock = redissonClient.getLock(lockKey);

        // 尝试获取锁,最长等待5秒,持有锁最长30秒
        boolean isLock = lock.tryLock(5, 30, TimeUnit.SECONDS);

        if (isLock) {
            //  实际业务处理
            try {
                System.out.println("非阻塞式--获取锁成功,线程 ID:" + Thread.currentThread().getId());
            } catch (Exception e) {
                log.error("业务处理异常:{}", e.getMessage());
            } finally {
                // 解锁
                lock.unlock();
                System.out.println("非阻塞式--解锁成功,线程 ID:" + Thread.currentThread().getId());
            }
        }
    }

}

测试结果

阻塞式--加锁成功,线程 ID:1
阻塞式--解锁成功,线程 ID:1
非阻塞式--获取锁成功,线程 ID:1
非阻塞式--解锁成功,线程 ID:1

扩展知识

redisson锁中lock方法tryLock方法有什么区别

在 Redisson 中,lock 方法tryLock 方法都是用来获取分布式锁的,但它们在获取锁的方式行为上有一些区别

在 Redisson 中,lock 方法和 tryLock 方法都是用来获取分布式锁的,但它们在获取锁的方式和行为上有一些区别

  1. lock 方法:

    • lock 方法是阻塞的,即如果获取锁失败调用该方法的线程会被阻塞,直到获取到锁为止。
    • 如果在获取锁时发生网络分区或其他异常,会一直重试获取锁,直到获取成功或者达到设置的超时时间为止。
    • 使用 lock 方法时,如果获取锁成功后,需要手动调用 unlock 方法来释放锁。

    例如:

    RLock lock = redisson.getLock("myLock");
    lock.lock();
    try {
        // 执行业务逻辑
    } finally {
        lock.unlock();
    }
  1. tryLock 方法:

    例如:

    RLock lock = redisson.getLock("myLock");
    boolean isLocked = lock.tryLock(10, TimeUnit.SECONDS);
    if (isLocked) {
        try {
            // 执行业务逻辑
        } finally {
            lock.unlock();
        }
    } else {
        // 获取锁失败的处理逻辑
    }

总结来说,lock 方法是阻塞的,会一直尝试获取锁直到成功,而 tryLock 方法是非阻塞的,在指定超时时间内尝试获取锁,如果超时返回失败。选择使用哪种方法取决于你的业务需求,以及是否能够容忍等待获取锁的阻塞时间。

锁续约

使用看门狗(Watchdog)可以实现锁的自动续约:

String permitId = lock.acquire(10, TimeUnit.SECONDS);
RScheduledExecutorService executorService = redisson.getExecutorService("myExecutorService");
executorService.schedulePermitExpiration(permitId, 5, TimeUnit.SECONDS);

在这个例子中,我们使用 schedulePermitExpiration 方法每隔5秒续约一次锁的持有时间。

注意事项

在使用分布式锁时,需要注意以下几点:

  1. 合理设置锁的持有时间:不要将锁的持有时间设置得过长,以免影响系统并发性能。根据实际业务需求,选择一个合适的持有时间。

  2. 使用看门狗进行续约:在需要锁的持有时间较长的情况下,使用看门狗可以确保锁不会在持有时间内过期,避免了手动续约的繁琐操作

  3. 异常处理:在加锁、续约、释放锁的过程中,需要考虑可能出现的异常情况,并进行适当的处理,以确保锁能够被正确释放,避免死锁问题

实际应用场景

电商系统中,分布式锁可以用来避免商品超卖问题。在任务调度系统中,可以确保同一个任务在同一时刻只被一个节点执行,避免任务重复执行问题

总结

Redisson 提供了简单而强大的分布式锁解决方案通过合理设置锁的持有时间和使用看门狗进行续约,可以确保系统在高并发环境下的稳定性和可靠性。在实际应用中,开发人员可以根据业务需求选择合适的锁策略,并进行必要的异常处理,以实现分布式系统的高效运行

原文地址:https://blog.csdn.net/qq_22331931/article/details/134546548

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_14961.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注