SpringBoot+Redisson分布式锁

SpringBoot+Redisson分布式锁

文章目录

1.引入依赖

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.kang</groupId>
    <artifactId>redisson</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>redisson-lock-project</name>
    <description>redisson-lock-project</description>
    <properties>
        <java.version>18</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.6.1</version>
        </dependency>

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.10.7</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.编写配置类

java 复制代码
package com.kang.redisson.config;

import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author Emperor Kang
 * @ClassName RedissonConfig
 * @Description RedissonConfig
 * @Date 2024/1/18 16:19
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
@Configuration
public class RedissonConfig {
    /**
     * spring.redis.cluster.nodes=redis://192.168.0.1:6379,redis://192.168.0.2:6379,redis://192.168.0.3:6379,redis://192.168.0.4:6379,redis://192.168.0.5:6379,redis://192.168.0.6:6379
     */
    @Value("${spring.redis.cluster.nodes}")
    private String redisNodes;

    @Value("${spring.redis.password}")
    private String password;

    /**
     * 实例化客户端
     * @return
     */
    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        ClusterServersConfig clusterServersConfig = config.useClusterServers()
                .addNodeAddress(redisNodes.split(","));
        // 密码
        if(StringUtils.isNoneBlank(password)){
            clusterServersConfig.setPassword(password);
        } else {
            clusterServersConfig.setPassword(password);
        }
        return Redisson.create(config);
    }
}

org.redisson.config.Config类是Redisson框架中用于配置Redisson客户端的类。以下是一些常用的配置项:

  1. codec(编码):默认值是org.redisson.codec.JsonJacksonCodec,用于定义与Redis交互时使用的编解码器。
  2. useSingleServer:设置为true时,将使用单节点模式进行连接。
  3. useMasterSlave:设置为true时,将使用主从节点模式进行连接。
  4. useSentinel:设置为true时,将使用哨兵模式进行连接。
  5. useCluster:设置为true时,将使用集群模式进行连接。
  6. useReplicated:设置为true时,将使用复制模式进行连接。
  7. address:Redis服务器的地址和端口号。
  8. database:要连接的Redis数据库的索引号。
  9. password:连接Redis服务器所需的密码。
  10. timeout:连接超时时间(毫秒)。
  11. connectionPoolSize:连接池的大小。
  12. connectionMinimumIdleSize:连接池中的最小空闲连接数。
  13. retryAttempts:重试连接的次数。
  14. retryInterval:重试连接的间隔时间(毫秒)。
  15. autoRetry:是否自动重试连接。
  16. ipAddressToConnectTo:要连接的Redis服务器IP地址。
  17. maxRedirections:重定向的最大次数。
  18. useConnectionPool:是否使用连接池。
  19. useReconnection:是否启用自动重新连接。
  20. useDataSerialization:是否使用数据序列化。
  21. useKeepAlive:是否使用保持活动连接功能。
  22. useOffHeapMemory:是否使用OffHeap内存存储数据。
  23. store:用于存储数据的存储实例或配置。
  24. springCacheName:与Spring框架集成的缓存名称。
  25. autoCreateObjectCaches:是否自动创建对象缓存。
  26. numberOfSlaves:主从模式下的从节点数量。
  27. masterName:主节点名称。
  28. slaveNames:从节点名称列表。
  29. sentinelMasterName:哨兵模式下主节点的名称。
  30. sentinelNames:哨兵节点的名称列表。
  31. clusterNodes:集群模式下节点的地址列表。
  32. replicatedMapName:复制模式下要复制的映射名称。
  33. replicatedMapKeyField:复制模式下映射的键字段名称。
  34. replicatedMapValueField:复制模式下映射的值字段名称。
  35. replicatedMapUseReferenceValues:是否使用引用值作为复制映射的值。
  36. replicatedMapUseReferenceKeys:是否使用引用键作为复制映射的键。
  37. clusterNameNodes:集群模式下节点组的名称列表。
  38. clusterNodeConfigName:集群模式下节点组的配置名称。
  39. clusterSlaveConfigName:集群模式下从节点组的配置名称。
  40. clusterSentinelConfigName:集群模式下哨兵节点组的配置名称。
  41. clusterMapConfigName:集群模式下映射的配置名称。
  42. clusterReplicatedMapConfigName:集群模式下复制映射的配置名称。
  43. clusterReplicatedMapKeyField:复制映射的键字段名称。
  44. clusterReplicatedMapValueField:复制映射的值字段名称。
  45. clusterReplicatedMapUseReferenceValues:是否使用引用值作为复制映射的值。
  46. clusterReplicatedMapUseReferenceKeys:是否使用引用键作为复制映射的键。

org.redisson.config.ClusterServersConfig类是Redisson框架中用于配置Redis集群模式的类。以下是一些常用的配置项:

  1. nodeAddresses:添加节点地址。可以通过host:port的格式来添加Redis集群节点的地址。多个节点可以一次性批量添加。
  2. scanInterval:集群扫描间隔时间。默认值是1000,表示对Redis集群节点状态扫描的时间间隔。单位是毫秒。
  3. readMode:读取操作的负载均衡模式。默认值是SLAVE,表示只在从服务节点里读取。
  4. slots:分片数量。默认值是231,用于指定数据分片过程中的分片数量。

这些配置项可以帮助你更好地管理Redis集群,并确保数据的高可用性和一致性。在Redisson中,可以通过使用ClusterServersConfig类来配置这些参数,以便与Redis集群进行通信和交互。

3.编写工具类

java 复制代码
package com.kang.redisson.utils;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @Author Emperor Kang
 * @ClassName RedissonUtil
 * @Description RedissonUtil
 * @Date 2024/1/18 16:32
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
@Component
@Slf4j
public class RedissonUtil {
    private static final String LOCK_PREFIX = "lock:";

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 获取锁
     * @param lockKey 锁的key
     * @return
     */
    public RLock getLock(String lockKey){
        log.info(">>>>>> 获取锁,lockKey:{} <<<<<<",lockKey);
        return redissonClient.getLock(LOCK_PREFIX + lockKey);
    }

    /**
     * 获取锁
     * @param rLock     当前锁对象
     * @param waitTime  等待时间
     * @param leaseTime 持有锁的时间
     * @param timeUnit  时间单位
     * @return true表示获取锁成功,false表示获取锁失败
     */
    public boolean tryLock(RLock rLock, long waitTime, long leaseTime, TimeUnit timeUnit) {
        log.info(">>>>>> 加锁开始, currentThreadId:{} <<<<<<",Thread.currentThread().getId());
        try {
            boolean lockResult = rLock.tryLock(waitTime, leaseTime, timeUnit);
            log.info(">>>>>> 加锁结束, currentThreadId:{},加锁:{} <<<<<<",Thread.currentThread().getId(),lockResult ? "SUCCESS" : "FAIL");
            return lockResult;
        } catch (InterruptedException e) {
            log.error("加锁出现异常", e);
            Thread.currentThread().interrupt();
            return false;
        }
    }

    /**
     * 加锁
     * @param rLock     当前锁对象
     * @param leaseTime 持有锁的时间
     * @param timeUnit  时间单位
     * @return true表示获取锁成功,false表示获取锁失败
     */
    public boolean tryLock(RLock rLock, long leaseTime, TimeUnit timeUnit) {
        log.info(">>>>>> 加锁开始, currentThreadId:{} <<<<<<",Thread.currentThread().getId());
        try {
            boolean lockResult = rLock.tryLock(0L, leaseTime, timeUnit);
            log.info(">>>>>> 加锁结束, currentThreadId:{},加锁:{} <<<<<<",Thread.currentThread().getId(),lockResult ? "SUCCESS" : "FAIL");
            return lockResult;
        } catch (InterruptedException e) {
            log.error("加锁出现异常", e);
            Thread.currentThread().interrupt();
            return false;
        }
    }

    /**
     * 释放锁
     * @param rLock    当前锁对象
     * @param lockKey  锁的key
     */
    public void unlock(RLock rLock,String lockKey) {
        log.info(">>>>>> 释放锁开始, lockKey:{},currentThreadId:{} <<<<<<",lockKey,Thread.currentThread().getId());
        rLock.unlock();
        log.info(">>>>>> 释放锁结束, lockKey:{},currentThreadId:{} <<<<<<",lockKey,Thread.currentThread().getId());
    }
}

4.编写使用场景示例

java 复制代码
package com.kang.redisson.service.impl;

import com.kang.redisson.service.RedissonService;
import com.kang.redisson.utils.RedissonUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.concurrent.TimeUnit;

/**
 * @Author Emperor Kang
 * @ClassName RedissonServiceImpl
 * @Description RedissonServiceImpl
 * @Date 2024/1/18 17:27
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
@Slf4j
public class RedissonServiceImpl implements RedissonService {

    @Autowired
    private RedissonUtil redissonUtil;

    @Override
    public Object execute() {
        String lockKey = "system:lockKey";
        RLock rLock = redissonUtil.getLock(lockKey);
        boolean tryLock = redissonUtil.tryLock(rLock,30000, TimeUnit.MILLISECONDS);
        try {
            if(tryLock){
                // TODO 业务逻辑
            }
        } catch (Exception e) {
            log.error("交易异常",e);
        } finally {
            // 只释放自己的锁
            if(rLock.isHeldByCurrentThread()){
                redissonUtil.unlock(rLock,lockKey);
            }
        }
        return true;
    }
}
相关推荐
向前看-2 小时前
验证码机制
前端·后端
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
Data跳动4 小时前
Spark内存都消耗在哪里了?
大数据·分布式·spark
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
Java程序之猿5 小时前
微服务分布式(一、项目初始化)
分布式·微服务·架构
来一杯龙舌兰6 小时前
【RabbitMQ】RabbitMQ保证消息不丢失的N种策略的思想总结
分布式·rabbitmq·ruby·持久化·ack·消息确认
AskHarries6 小时前
Spring Cloud OpenFeign快速入门demo
spring boot·后端
isolusion7 小时前
Springboot的创建方式
java·spring boot·后端
Yvemil77 小时前
《开启微服务之旅:Spring Boot Web开发举例》(一)
前端·spring boot·微服务
zjw_rp7 小时前
Spring-AOP
java·后端·spring·spring-aop