Redisson(二)SpringBoot集成Redisson

目录

一、Redis单例模式

二、Redis哨兵模式

三、Redis集群模式

 四、主从模式 

五、兼容多种模式的配置(重点)

1、pom

2、配置文件

(1)application.properties

(2)application-dev.properties

(3) application-prod.properties

3、Java配置类

4、应用


<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.5</version>
</dependency>

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

支持Redis多种连接模式,这里以RedissonClient工具为例

一、Redis单例模式

#单机模式
#Redis url should start with redis:// or rediss:// (for SSL connection)
my.redisson.address = redis://127.0.0.1:6379
my.redisson.password = wtyy

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

@Configuration
public class RedissionConfig {

    @Value("${my.redisson.address}")
    private String address;

    @Value("${my.redisson.password}")
    private String password;

    /**
     * 所有对Redisson的使用都是通过RedissonClient对象
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient(){
        // 创建配置 指定redis地址及节点信息
        Config config = new Config();
        config.useSingleServer().setAddress(address);
                //.setPassword(password);
        // 根据config创建出RedissonClient实例
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}

二、Redis哨兵模式

my.redisson.sentinel.schema=
my.redisson.sentinel.address = redis://xxx.xx.xxx1:19601,redis://xxx.xx.xxx2:19601,redis://xxx.xx.xxx3:19601
my.redisson.sentinel.password = N5WAXX4z9qw
my.redisson.sentinel.master = master-test
my.redisson.sentinel.database = 10

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

@Configuration
public class RedissionConfig {

    @Value("${my.redisson.sentinel.address}")
    private String address;

    @Value("${my.redisson.sentinel.password}")
    private String password;

    @Value("${my.redisson.sentinel.master}")
    private String master;

    @Value("${my.redisson.sentinel.database}")
    private Integer dataBase;

    @Value("${my.redisson.sentinel.schema}")
    private String schema;

    /**
     * 所有对Redisson的使用都是通过RedissonClient对象
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.useSentinelServers()
                .setScanInterval(2000) // cluster state scan interval in milliseconds
                .addSentinelAddress(address.split(","))
                .setCheckSentinelsList(false)
                .setMasterName(master)
                .setPassword(password)
                .setDatabase(dataBase);
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}

三、Redis集群模式

    Config config = new Config();
	config.useClusterServers()
    .setScanInterval(2000) // cluster state scan interval in milliseconds
    .addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001")
    .addNodeAddress("redis://127.0.0.1:7002");
    RedissonClient redisson = Redisson.create(config);

 四、主从模式 

    Config config = new Config();
	config.useMasterSlaveServers()
	.setMasterAddress("redis://127.0.0.1:6379")
    .addSlaveAddress("redis://127.0.0.1:6389", "redis://127.0.0.1:6332", "redis://127.0.0.1:6419")
    .addSlaveAddress("redis://127.0.0.1:6399");
    RedissonClient redisson = Redisson.create(config);

五、兼容多种模式的配置(重点)

从上面可以看到,不同的redis模式有不同的连接配置方式,如果连接变更,配置代码也要跟着变更,不够灵活,再比如有多套环境,测试环境和生产环境使用的是不同的redis模式,所以需要考虑配置兼容问题。下面的配置适用于所有模式。

1、pom

因为使用到了 RedisProperties,所以需要引入 spring-boot-starter-data-redis

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>redis-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/>
    </parent>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.26.0</version>
        </dependency>

        <!--连接池依赖-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.11.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>


        <!--hutool工具-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.15</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>


        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.10.1</version>
        </dependency>
    </dependencies>

</project>
2、配置文件

 按照 org.springframework.boot.autoconfigure.data.redis.RedisProperties 规则配置。源码:

package org.springframework.boot.autoconfigure.data.redis;

@ConfigurationProperties(
    prefix = "spring.data.redis"
)
public class RedisProperties {
    private int database = 0;
    private String url;
    private String host = "localhost";
    private String username;
    private String password;
    private int port = 6379;
    private Duration timeout;
    private Duration connectTimeout;
    private String clientName;
    private ClientType clientType;
    private Sentinel sentinel;
    private Cluster cluster;
    private final Ssl ssl = new Ssl();
    private final Jedis jedis = new Jedis();
    private final Lettuce lettuce = new Lettuce();

    ......
}

这里有两套环境:dev和prod

(1)application.properties
spring.profiles.active=dev
(2)application-dev.properties
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
spring.data.redis.password=
spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-wait=-1
spring.data.redis.lettuce.pool.max-idle=8
spring.data.redis.lettuce.pool.min-idle=0
spring.data.redis.lettuce.pool.enabled=true
spring.data.redis.lettuce.pool.time-between-eviction-runs=30s
(3) application-prod.properties
spring.data.redis.cluster.nodes=xxx.xxx.xx.xxx:6379
spring.data.redis.cluster.max-redirects=3
spring.data.redis.timeout=60S
spring.data.redis.lettuce.pool.max-wait=2S
spring.data.redis.lettuce.pool.max-active=1200
spring.data.redis.lettuce.pool.max-idle=400
spring.data.redis.lettuce.pool.min-idle=50
# spring.redis.lettuce.pool.time-between-eviction-runs=200
spring.data.redis.lettuce.pool.enabled=true
spring.data.redis.lettuce.pool.time-between-eviction-runs=30s
spring.data.redis.lettuce.cluster.refresh.adaptive=true
spring.data.redis.lettuce.cluster.refresh.period=60s
3、Java配置类

读取配置文件中 spring.data.redis 开头的配置,并加以判断。

package org.example.config;

import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Configuration
public class RedissonConfig {

    @Autowired
    RedisProperties redisProperties;

    private static final String REDIS_PROTOCOL_PREFIX = "redis://";

    private static final String REDISS_PROTOCOL_PREFIX = "rediss://";

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson() {
        Config config;
        Method clusterMethod = ReflectionUtils.findMethod(RedisProperties.class, "getCluster");
        Method timeoutMethod = ReflectionUtils.findMethod(RedisProperties.class, "getTimeout");
        Object timeoutValue = ReflectionUtils.invokeMethod(timeoutMethod, redisProperties);
        int timeout;
        if (null == timeoutValue) {
            timeout = 10000;
        } else if (!(timeoutValue instanceof Integer)) {
            Method millisMethod = ReflectionUtils.findMethod(timeoutValue.getClass(), "toMillis");
            timeout = ((Long) ReflectionUtils.invokeMethod(millisMethod, timeoutValue)).intValue();
        } else {
            timeout = (Integer) timeoutValue;
        }

        if (redisProperties.getSentinel() != null) {
            Method nodesMethod =
                    ReflectionUtils.findMethod(RedisProperties.Sentinel.class, "getNodes");
            Object nodesValue =
                    ReflectionUtils.invokeMethod(nodesMethod, redisProperties.getSentinel());

            String[] nodes;
            if (nodesValue instanceof String) {
                nodes = convert(Arrays.asList(((String) nodesValue).split(",")));
            } else {
                nodes = convert((List<String>) nodesValue);
            }

            config = new Config();
            config.useSentinelServers()
                    .setMasterName(redisProperties.getSentinel().getMaster())
                    .addSentinelAddress(nodes)
                    .setDatabase(redisProperties.getDatabase())
                    .setConnectTimeout(timeout)
                    .setPassword(redisProperties.getPassword());
        } else if (clusterMethod != null
                && ReflectionUtils.invokeMethod(clusterMethod, redisProperties) != null) {
            Object clusterObject = ReflectionUtils.invokeMethod(clusterMethod, redisProperties);
            Method nodesMethod = ReflectionUtils.findMethod(clusterObject.getClass(), "getNodes");
            List<String> nodesObject =
                    (List) ReflectionUtils.invokeMethod(nodesMethod, clusterObject);

            String[] nodes = convert(nodesObject);

            config = new Config();
            config.useClusterServers()
                    .addNodeAddress(nodes)
                    .setConnectTimeout(timeout)
                    .setPassword(redisProperties.getPassword());
        } else {
            config = new Config();
            String prefix = REDIS_PROTOCOL_PREFIX;
            Method method = ReflectionUtils.findMethod(RedisProperties.class, "isSsl");
            if (method != null && (Boolean) ReflectionUtils.invokeMethod(method, redisProperties)) {
                prefix = REDISS_PROTOCOL_PREFIX;
            }

            String password =
                    StringUtils.isBlank(redisProperties.getPassword())
                            ? null
                            : redisProperties.getPassword();
            config.useSingleServer()
                    .setAddress(
                            prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
                    .setConnectTimeout(timeout)
                    .setDatabase(redisProperties.getDatabase())
                    .setPassword(password);
        }

        return Redisson.create(config);
    }

    private String[] convert(List<String> nodesObject) {
        List<String> nodes = new ArrayList<String>(nodesObject.size());
        for (String node : nodesObject) {
            if (!node.startsWith(REDIS_PROTOCOL_PREFIX)
                    && !node.startsWith(REDISS_PROTOCOL_PREFIX)) {
                nodes.add(REDIS_PROTOCOL_PREFIX + node);
            } else {
                nodes.add(node);
            }
        }
        return nodes.toArray(new String[nodes.size()]);
    }
}

 tips:以下的反射是从官网copy的,不建议使用反射,其实RedisProperties对象已经拿到了,直接从 redisProperties中get也是可以的,如获取cluster:

RedisProperties.Cluster cluster = redisProperties.getCluster();
4、应用
import com.alibaba.fastjson.JSON;
import org.example.dto.UserDTO;
import org.example.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest(classes = {Main.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
public class RedissonTest {

    @Autowired
    private RedissonClient redissonClient;

    @Test
    public void  testString(){
        RBucket<Object> bucket = redissonClient.getBucket("test");
        bucket.set("这是value");
        System.out.println(bucket.get());
    }
}
5、与RedisTemplate并存使用

这个demo引入了  spring-boot-starter-data-redis,所以可以直接使用RedisTemplate:

@SpringBootTest(classes = {Main.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
public class MyTest {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private UserService userService;

    @Test
    public void  testString(){
        String key = "string-name";
        //存
        String value = "这是value123";
        stringRedisTemplate.opsForValue().set(key,value);
        //取
        Object valueObj = stringRedisTemplate.opsForValue().get(key);
        System.out.println("value为:" + valueObj);
    }
}

### 集成 Redisson 分布式锁 为了在 Spring Boot 项目中集成并使用 Redisson 实现分布式锁,需按照如下说明进行设置。 #### 添加依赖项 首先,在项目的 `pom.xml` 文件中加入必要的 Maven 依赖: ```xml <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>${最新版本号}</version> </dependency> ``` 此依赖允许开发者轻松地将 Redisson 整合至 Spring Boot 应用程序之中[^2]。 #### 修改配置文件 接着修改 `application.yml` 来指定 Redisson 的连接参数和其他选项。例如: ```yaml spring: redisson: config: classpath:redisson.yaml ``` 其中 `classpath:redisson.yaml` 表明实际的 Redisson 连接配置位于类路径下的 `redisson.yaml` 文件内[^1]。 对于简单的单节点 Redis 设置,可以直接在此处定义基本属性而无需额外创建 YAML 文件;而对于更复杂的集群环境则推荐采用外部化配置文件的方式处理。 #### 创建自定义注解与切面逻辑 为了让开发人员能够便捷地标记哪些方法需要启用分布式锁定机制,建议设计一个名为 `@DistributedLock` 的自定义注解,并编写对应的 AOP 切片来拦截这些标记的方法调用前自动获取锁实例并尝试占有它直到业务流程结束为止。 ```java import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; @Aspect public class DistributedLockAspect { private final RedissonClient redissonClient; public DistributedLockAspect(RedissonClient redissonClient) { this.redissonClient = redissonClient; } @Around("@annotation(distributedLock)") public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable { String lockName = distributedLock.value(); RLock lock = redissonClient.getLock(lockName); try { boolean isLocked = lock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), TimeUnit.SECONDS); if (!isLocked){ throw new RuntimeException("Failed to acquire the lock"); } return joinPoint.proceed(); } finally { lock.unlock(); } } } ``` 上述代码片段展示了如何利用 AspectJ 编写环绕通知(`around`)以控制目标函数执行期间对特定命名空间下所持有的锁的操作过程。 #### 测试案例展示 下面给出一段测试服务类的例子,用于验证整个方案的有效性: ```java @Service public class TestService { @Autowired private OrderRepository orderRepo; @Transactional(propagation=Propagation.REQUIRES_NEW) @DistributedLock(value="order:${id}", waitTime=5L, leaseTime=10L) public void placeOrder(Long id) { Optional<OrderEntity> optionalOrder = orderRepo.findById(id); // 假设这里有一些更新订单状态或其他敏感操作... System.out.println(Thread.currentThread().getName() + " processing orderId:" + id); Thread.sleep((long)(Math.random()*3)*1000); //模拟耗时任务 if(!optionalOrder.isPresent()){ throw new EntityNotFoundException("No such order found!"); } OrderEntity entity = optionalOrder.get(); entity.setStatus(OrderStatus.PROCESSED); orderRepo.save(entity); } } ``` 这段代码里声明了一个带有事务特性的 `placeOrder()` 方法,并为其加上了之前提到过的 `@DistributedLock` 注解以便于在线程间同步访问共享数据结构——即数据库记录[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_t_y_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值