SpringBoot Redis 多数据源集成支持哨兵模式和Cluster集群模式

这篇具有很好参考价值的文章主要介绍了SpringBoot Redis 多数据源集成支持哨兵模式和Cluster集群模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Redis 从入门到精通【应用篇】之SpringBoot Redis 多数据源集成支持哨兵模式Cluster集群模式、单机模式


SpringBoot Redis 多数据源集成支持哨兵模式和Cluster集群模式,Redis从入门到精通2023版,spring boot,redis,后端,java,缓存,sentinel

0.前言

说明

大家都知道Redis在6.0版本之前是单线程工作的,这导致在一个项目中有大量读写操作的情况下,Redis单实例的性能被其他业务长时间占据,导致部分业务出现延迟现象,为了解决这个问题,部分公司项目选择使用多个Redis实例分别存储不同的业务数据和使用场景,比如IoT网关写入的数据,可以单独拆分一个Redis实例去使用,其他业务使用一个Redis实例。用多个Redis实例 可以提高Redis的性能。Redis是一种基于内存的缓存数据库,内存容量是其性能的瓶颈。当项目中的数据量较大时,单个Redis实例可能无法承载所有数据,导致性能下降。而使用多个Redis实例可以将数据分散到多个实例中,从而提高Redis的整体性能。

这就导致在某些业务场景下,一个项目工程,同时要使用这两个Redis实例的数据,这就是本文要解决的问题。

本文通过写一个Redis 多数据源组件 Starter 来解决上面的问题,支持Redis 多数据源,可集成配置哨兵模式、Cluster集群模式、单机模式。如果单实例配置哨兵模式,请参阅我之前的博客《SpringBoot Redis 使用Lettuce和Jedis配置哨兵模式》

项目结构

SpringBoot Redis 多数据源集成支持哨兵模式和Cluster集群模式,Redis从入门到精通2023版,spring boot,redis,后端,java,缓存,sentinel

Pom 依赖

如下,可能有多余的,根据项目具体情况删减。再就是需要使用Springboot parent
<spring-boot-dependencies.version>2.7.12</spring-boot-dependencies.version>

 		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
         <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

1. 配置

1.1 通用配置()

设置主redis的标识

很关键

# 示例
custom.primary.redis.key=user

连接池相关配置

此配置为通用配置所有类型的链接模式都可以配置,不配置走Springboot 默认配置。

spring.redis.xxx.timeout = 3000
spring.redis.xxx.maxTotal = 50
spring.redis.xxx.maxIdle = 50
spring.redis.xxx.minIdle = 2
spring.redis.xxx.maxWaitMillis = 10000
spring.redis.xxx.testOnBorrow = False

1.2 单例模式配置

# 第1个Redis 实例 用于用户体系,我们取标识为user
spring.redis.user.host = 127.0.0.1
spring.redis.user.port = 6380
spring.redis.user.password = 密码
spring.redis.user.database = 0
 

# 第2个Redis 实例用于IoT体系
spring.redis.iot.host = 127.0.0.1
spring.redis.iot.port = 6390
spring.redis.iot.password = 密码
spring.redis.iot.database = 0
 

# 第3个Redis 实例用于xxx
spring.redis.xxx.host = 127.0.0.1
spring.redis.xxx.port = 6390
spring.redis.xxx.password = 密码
spring.redis.xxx.database = 0

1.3 哨兵模式配置

多个Redis数据库实例的情况下,将下面配置项多配置几个。


spring.redis.xxx1.sentinel.master=mymaster1
spring.redis.xxx1.sentinel.nodes=ip:端口,ip:端口
spring.redis.xxx1.password = bD945aAfeb422E22AbAdFb9D2a22bEDd
spring.redis.xxx1.database = 0
spring.redis.xxx1.timeout = 3000


#第二个
spring.redis.xxx2.sentinel.master=mymaster2
spring.redis.xxx2.sentinel.nodes=ip:端口,ip:端口
spring.redis.xxx2.password = bD945aAfeb422E22AbAdFb9D2a22bEDd
spring.redis.xxx2.database = 0
spring.redis.xxx2.timeout = 3000

1.4 集群模式配置(集群模式不支持设置database)

spring.redis.xxx1.cluster.nodes=ip1:端口,ip2:端口,ip3:端口,ip4:端口,ip5:端口,ip6:端口
spring.redis.xxx1.cluster.max-redirects=5
spring.redis.xxx1.password = 密码
spring.redis.xxx1.timeout = 3000

2. 代码实例

2.1.CustomRedisConfig

根据配置文件配置项,创建Redis多个数据源的RedisTemplate
主要思想为,

  1. 在服务启动过程中读取多数据源配置文件,将多数据源的配置读取到
 // 定义静态Map变量redis,用于存储Redis配置参数
    protected static Map<String, Map<String, String>> redis = new HashMap<>();
  1. 根据多数据源配置创建不同类型的Configuration
 private RedisStandaloneConfiguration buildStandaloneConfig(Map<String, String> param){  
//...省略
 }
    private RedisSentinelConfiguration buildSentinelConfig(Map<String, String> param){ 
//...省略
 }
    private RedisClusterConfiguration buildClusterConfig(Map<String, String> param){
	//...省略
 }
  1. 根据 不同类型的创建RedisConnectionFactory
  public RedisConnectionFactory buildLettuceConnectionFactory(String redisKey, Map<String, String> param,GenericObjectPoolConfig genericObjectPoolConfig){
  ...
  }

4.最后遍历上面我们配置的配置文件调用buildCustomRedisService(k, redisTemplate, stringRedisTemplate); 将创建的不同的RedisTemplate Bean 然后注入到Spring容器中

CustomRedisConfig 源码

源码中涉及的Springboot 相关知识在此处就不做赘婿,需要了解,可以参考我的《SpringBoot 源码解析系列》
InitializingBean, ApplicationContextAware, BeanPostProcessor

package com.iceicepip.project.common.redis;

import com.iceicepip.project.common.redis.util.AddressUtils;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.*;

@AutoConfiguration
@ConfigurationProperties(prefix = "spring")
public class CustomRedisConfig implements InitializingBean, ApplicationContextAware, BeanPostProcessor {

    // 定义静态Map变量redis,用于存储Redis配置参数
    protected static Map<String, Map<String, String>> redis = new HashMap<>();

    // 在代码中作为Redis的主数据源的标识
    @Value("${customer.primary.redis.key}")
    private String primaryKey;

    @Override
    // 实现InitializingBean接口的方法,用于在属性被注入后初始化Redis连接工厂和Redis模板
    public void afterPropertiesSet() {
        redis.forEach((k, v) -> {
            // 如果当前的Redis主键等于注入的主键,则将Redis配置参数加入到属性源中
            if(Objects.equals(k,primaryKey)){
                Map<String, Object> paramMap = new HashMap<>(4);
                v.forEach((k1,v1)-> paramMap.put("spring.redis."+k1,v1));
                MapPropertySource mapPropertySource = new MapPropertySource("redisAutoConfigProperty", paramMap);
                ((StandardEnvironment)applicationContext.getEnvironment()).getPropertySources().addLast(mapPropertySource);
            }

            // 创建Redis连接池配置对象和连接工厂对象
            GenericObjectPoolConfig genericObjectPoolConfig = buildGenericObjectPoolConfig(k, v);
            RedisConnectionFactory lettuceConnectionFactory = buildLettuceConnectionFactory(k, v, genericObjectPoolConfig);

            // 创建Redis模板对象和字符串模板对象,并调用方法创建自定义Redis服务对象
            RedisTemplate redisTemplate = buildRedisTemplate(k, lettuceConnectionFactory);
            StringRedisTemplate stringRedisTemplate = buildStringRedisTemplate(k, lettuceConnectionFactory);
            buildCustomRedisService(k, redisTemplate, stringRedisTemplate);
        });
    }

    // 创建Redis主数据源 RedisTemplate
    @Bean
    public RedisTemplate<Object, Object> redisTemplate() {
        Map<String, String> redisParam = redis.get(primaryKey);
        GenericObjectPoolConfig genericObjectPoolConfig = buildGenericObjectPoolConfig(primaryKey, redisParam);
        RedisConnectionFactory lettuceConnectionFactory = buildLettuceConnectionFactory(primaryKey, redisParam, genericObjectPoolConfig);
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(lettuceConnectionFactory);
        return template;
    }

    //  创建Redis主数据源 StringRedisTemplate 
    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate() {
        Map<String, String> redisParam = redis.get(primaryKey);
        GenericObjectPoolConfig genericObjectPoolConfig = buildGenericObjectPoolConfig(primaryKey, redisParam);
        RedisConnectionFactory lettuceConnectionFactory = buildLettuceConnectionFactory(primaryKey, redisParam, genericObjectPoolConfig);
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(lettuceConnectionFactory);
        return template;
    }

    // 创建自定义Redis服务对象
    private void buildCustomRedisService(String k, RedisTemplate redisTemplate, StringRedisTemplate stringRedisTemplate) {
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        constructorArgumentValues.addIndexedArgumentValue(0, stringRedisTemplate);
        constructorArgumentValues.addIndexedArgumentValue(1, redisTemplate);
        // 将来使用的时候Redis对象的beanName,区分多个数据源
        setCosBean(k + "Redis", CustomRedisService.class, constructorArgumentValues);
    }

    // 创建StringRedisTemplate
    private StringRedisTemplate buildStringRedisTemplate(String k, RedisConnectionFactory lettuceConnectionFactory) {
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        constructorArgumentValues.addIndexedArgumentValue(0, lettuceConnectionFactory);
        setCosBean(k + "StringRedisTemplate", StringRedisTemplate.class, constructorArgumentValues);
        return getBean(k + "StringRedisTemplate");
    }

    // 创建Redis模板对象
    private RedisTemplate buildRedisTemplate(String k, RedisConnectionFactory lettuceConnectionFactory) {
        // 如果已经存在Redis模板对象,则直接返回该对象
        if(applicationContext.containsBean(k + "RedisTemplate")){
            return getBean(k + "RedisTemplate");
        }

        // 创建Redis序列化器对象
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);

        // 创建Redis模板对象,并设置连接池配置和序列化器等属性
        Map original = new HashMap<>(2);
        original.put("connectionFactory", lettuceConnectionFactory);
        original.put("valueSerializer", serializer);
        original.put("keySerializer", new StringRedisSerializer());
        original.put("hashKeySerializer", new StringRedisSerializer());
        original.put("hashValueSerializer", serializer);
       // 将来使用RedisTemplate的地方只需要用注解制定beanName 即可获取到每个Redis实例的操作工具类
        setBean(k + "RedisTemplate", RedisTemplate.class, original);
        return getBean(k + "RedisTemplate");
    }
}

    public GenericObjectPoolConfig buildGenericObjectPoolConfig(String redisKey, Map<String, String> param) {
        if(applicationContext.containsBean(redisKey + "GenericObjectPoolConfig")){
            return getBean(redisKey + "GenericObjectPoolConfig");
        }
        Integer maxIdle = StringUtils.isEmpty(param.get(CustomRedisConfigConstant.REDIS_MAXIDLE)) ? GenericObjectPoolConfig.DEFAULT_MAX_IDLE : Integer.valueOf(param.get(CustomRedisConfigConstant.REDIS_MAXIDLE));
        Integer minIdle = StringUtils.isEmpty(param.get(CustomRedisConfigConstant.REDIS_MINIDLE)) ? GenericObjectPoolConfig.DEFAULT_MIN_IDLE : Integer.valueOf(param.get(CustomRedisConfigConstant.REDIS_MINIDLE));
        Integer maxTotal = StringUtils.isEmpty(param.get(CustomRedisConfigConstant.REDIS_MAXTOTAL)) ? GenericObjectPoolConfig.DEFAULT_MAX_TOTAL : Integer.valueOf(param.get(CustomRedisConfigConstant.REDIS_MAXTOTAL));

        Long maxWaitMillis = StringUtils.isEmpty(param.get(CustomRedisConfigConstant.REDIS_MAXWAITMILLIS)) ? -1L:Long.valueOf(param.get(CustomRedisConfigConstant.REDIS_MAXWAITMILLIS));

        Boolean testOnBorrow = StringUtils.isEmpty(param.get(CustomRedisConfigConstant.REDIS_TESTONBORROW)) ? Boolean.FALSE :Boolean.valueOf(param.get(CustomRedisConfigConstant.REDIS_TESTONBORROW));
        Map original = new HashMap<>(8);
        original.put("maxTotal", maxTotal);
        original.put("maxIdle", maxIdle);
        original.put("minIdle", minIdle);
        original.put("maxWaitMillis",maxWaitMillis);
        original.put("testOnBorrow",testOnBorrow);
        setBean(redisKey + "GenericObjectPoolConfig", GenericObjectPoolConfig.class, original);
        return getBean(redisKey + "GenericObjectPoolConfig");
    }

    public RedisConnectionFactory buildLettuceConnectionFactory(String redisKey, Map<String, String> param,GenericObjectPoolConfig genericObjectPoolConfig){
        if(applicationContext.containsBean(redisKey + "redisConnectionFactory")){
            return getBean(redisKey + "redisConnectionFactory");
        }
        String timeout = StringUtils.defaultIfEmpty(param.get(CustomRedisConfigConstant.REDIS_TIMEOUT), "3000");
        LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
                .commandTimeout(Duration.ofMillis(Long.valueOf(timeout)))
                .poolConfig(genericObjectPoolConfig)
                .build();

        Object firstArgument = null;
        if(this.isCluster(param)){
            RedisClusterConfiguration clusterConfiguration = buildClusterConfig(param);
            firstArgument = clusterConfiguration;
        }
        else if(this.isSentinel(param)){
            RedisSentinelConfiguration sentinelConfiguration = buildSentinelConfig(param);
            firstArgument = sentinelConfiguration;
        }
        else{
            RedisStandaloneConfiguration standaloneConfiguration = buildStandaloneConfig(param);
            firstArgument = standaloneConfiguration;
        }

        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        constructorArgumentValues.addIndexedArgumentValue(0, firstArgument);
        constructorArgumentValues.addIndexedArgumentValue(1, clientConfig);
        setCosBean(redisKey + "redisConnectionFactory", LettuceConnectionFactory.class, constructorArgumentValues);
        return getBean(redisKey +"redisConnectionFactory");
    }

    /**
     * 如果配置的是哨兵模式
     * @return
     */
    private boolean isSentinel(Map<String, String> param){
        String sentinelMaster = param.get(CustomRedisConfigConstant.REDIS_SENTINEL_MASTER);
        String sentinelNodes = param.get(CustomRedisConfigConstant.REDIS_SENTINEL_NODES);
        return StringUtils.isNotEmpty(sentinelMaster) && StringUtils.isNotEmpty(sentinelNodes);
    }

    /**
     * 如果配置的是集群模式
     * @return
     */
    private boolean isCluster(Map<String, String> param){
        String clusterNodes = param.get(CustomRedisConfigConstant.REDIS_CLUSTER_NODES);
        return StringUtils.isNotEmpty(clusterNodes);
    }

    private RedisStandaloneConfiguration buildStandaloneConfig(Map<String, String> param){
        String host = param.get(CustomRedisConfigConstant.REDIS_HOST);
        String port = param.get(CustomRedisConfigConstant.REDIS_PORT);
        String database = param.get(CustomRedisConfigConstant.REDIS_DATABASE);
        String password = param.get(CustomRedisConfigConstant.REDIS_PASSWORD);

        RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration();
        standaloneConfig.setHostName(host);
        standaloneConfig.setDatabase(Integer.valueOf(database));
        standaloneConfig.setPort(Integer.valueOf(port));
        standaloneConfig.setPassword(RedisPassword.of(password));
        return standaloneConfig;
    }

    private RedisSentinelConfiguration buildSentinelConfig(Map<String, String> param){

        String sentinelMaster = param.get(CustomRedisConfigConstant.REDIS_SENTINEL_MASTER);
        String sentinelNodes = param.get(CustomRedisConfigConstant.REDIS_SENTINEL_NODES);
        String database = param.get(CustomRedisConfigConstant.REDIS_DATABASE);
        String password = param.get(CustomRedisConfigConstant.REDIS_PASSWORD);

        RedisSentinelConfiguration config = new RedisSentinelConfiguration();
        config.setMaster(sentinelMaster);

        Iterable<AddressUtils.Address> addressIterable = AddressUtils.parseAddresses(sentinelNodes);
        Iterable<RedisNode> redisNodes = transform(addressIterable);

        config.setDatabase(Integer.valueOf(database));
        config.setPassword(RedisPassword.of(password));
        config.setSentinels(redisNodes);
        return config;
    }

    private RedisClusterConfiguration buildClusterConfig(Map<String, String> param){
        String clusterNodes = param.get(CustomRedisConfigConstant.REDIS_CLUSTER_NODES);
        String clusterMaxRedirects = param.get(CustomRedisConfigConstant.REDIS_CLUSTER_MAX_REDIRECTS);
        String password = param.get(CustomRedisConfigConstant.REDIS_PASSWORD);

        RedisClusterConfiguration config = new RedisClusterConfiguration();
        Iterable<AddressUtils.Address> addressIterable = AddressUtils.parseAddresses(clusterNodes);
        Iterable<RedisNode> redisNodes = transform(addressIterable);
        config.setClusterNodes(redisNodes);
        config.setMaxRedirects(StringUtils.isEmpty(clusterMaxRedirects) ? 5 : Integer.valueOf(clusterMaxRedirects));
        config.setPassword(RedisPassword.of(password));
        return config;
    }

    private Iterable<RedisNode> transform(Iterable<AddressUtils.Address> addresses){
        List<RedisNode> redisNodes = new ArrayList<>();
        addresses.forEach( address -> redisNodes.add(new RedisServer(address.getHost(), address.getPort())));
        return redisNodes;
    }


    private static ApplicationContext applicationContext;

    public Map<String, Map<String, String>> getRedis() {
        return redis;
    }

    /**
     * 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        CustomRedisConfig.applicationContext = applicationContext;
    }

    private static void checkApplicationContext() {
        if (applicationContext == null) {
            throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextUtil");
        }
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        checkApplicationContext();
        if (applicationContext.containsBean(name)) {
            return (T) applicationContext.getBean(name);
        }
        return null;
    }

    /**
     * 删除spring中管理的bean
     *
     * @param beanName
     */
    public static void removeBean(String beanName) {
        DefaultListableBeanFactory acf = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
        acf.removeBeanDefinition(beanName);
    }

    /**
     * 同步方法注册bean到ApplicationContext中
     *
     * @param beanName
     * @param clazz
     * @param original bean的属性值
     */
    public synchronized void setBean(String beanName, Class<?> clazz, Map<String, Object> original) {
        checkApplicationContext();
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
        if (beanFactory.containsBean(beanName)) {
            return;
        }
        GenericBeanDefinition definition = new GenericBeanDefinition();
        //类class
        definition.setBeanClass(clazz);
        if(beanName.startsWith(primaryKey)){
            definition.setPrimary(true);
        }
        //属性赋值
        definition.setPropertyValues(new MutablePropertyValues(original));
        //注册到spring上下文
        beanFactory.registerBeanDefinition(beanName, definition);
    }

    public synchronized void setCosBean(String beanName, Class<?> clazz, ConstructorArgumentValues original) {
        checkApplicationContext();
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
        //这里重要
        if (beanFactory.containsBean(beanName)) {
            return;
        }
        GenericBeanDefinition definition = new GenericBeanDefinition();
        //类class
        definition.setBeanClass(clazz);
        if(beanName.startsWith(primaryKey)){
            definition.setPrimary(true);
        }
        //属性赋值
        definition.setConstructorArgumentValues(new ConstructorArgumentValues(original));
        //注册到spring上下文
        beanFactory.registerBeanDefinition(beanName, definition);
    }

}


2.2. CustomRedisConfigConstant

定义常用配置项的键名

package com.iceicepip.project.common.redis;


public class CustomRedisConfigConstant {

    private CustomRedisConfigConstant() {
    }

    public static final String REDIS_HOST = "host";
    public static final String REDIS_PORT = "port";
    public static final String REDIS_TIMEOUT = "timeout";
    public static final String REDIS_DATABASE = "database";
    public static final String REDIS_PASSWORD = "password";
    public static final String REDIS_MAXWAITMILLIS = "maxWaitMillis";
    public static final String REDIS_MAXIDLE = "maxIdle";
    public static final String REDIS_MINIDLE = "minIdle";
    public static final String REDIS_MAXTOTAL = "maxTotal";
    public static final String REDIS_TESTONBORROW = "testOnBorrow";
    public static final String REDIS_SENTINEL_MASTER = "sentinel.master";
    public static final String REDIS_SENTINEL_NODES = "sentinel.nodes";
    public static final String REDIS_CLUSTER_NODES = "cluster.nodes";
    public static final String REDIS_CLUSTER_MAX_REDIRECTS = "cluster.max-redirects";

    public static final String BEAN_NAME_SUFFIX = "Redis";
    public static final String INIT_METHOD_NAME = "getInit";

}

2.3封装一个Redis操作类 CustomRedisService

package com.iceicepip.project.common.redis;


import com.alibaba.ttl.TransmittableThreadLocal;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.types.Expiration;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

public class CustomRedisService {

    private static final Logger logger = LoggerFactory.getLogger(CustomRedisService.class);

    private StringRedisTemplate stringRedisTemplate;

    private RedisTemplate redisTemplate;

    @Value("${distribute.lock.MaxSeconds:100}")
    private Integer lockMaxSeconds;

    private static Long LOCK_WAIT_MAX_TIME = 120000L;

    @Resource
    private ApplicationContext applicationContext;

    /**
     * 保存锁的value
     */
    private TransmittableThreadLocal<String> redisLockReentrant = new TransmittableThreadLocal<>();

    /**
     * 解锁lua脚本
     */
    private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    /**
     * redis锁固定前缀
     */
    private static final String REDIS_LOCK_KEY_PREFIX = "xxx:redisLock";

    /**
     * redis nameSpace
     */
    private static final String REDIS_NAMESPACE_PREFIX = ":";


    @Value("${spring.application.name}")
    private String appName;


    public CustomRedisService() {
    }

    public CustomRedisService(StringRedisTemplate stringRedisTemplate, RedisTemplate redisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.redisTemplate = redisTemplate;
    }

    public StringRedisTemplate getStringRedisTemplate() {
        return stringRedisTemplate;
    }

    public RedisTemplate getRedisTemplate() {
        return redisTemplate;
    }

    //以下是操作

    public void saveOrUpdate(HashMap<String, String> values) throws Exception {
        ValueOperations<String, String> valueOps = stringRedisTemplate
                .opsForValue();
        valueOps.multiSet(values);
    }


    public void saveOrUpdate(String key, String value) throws Exception {
        ValueOperations<String, String> valueOps = stringRedisTemplate
                .opsForValue();
        valueOps.set(key, value);
    }


    public String getValue(String key) throws Exception {
        ValueOperations<String, String> valueOps = stringRedisTemplate
                .opsForValue();
        return valueOps.get(key);
    }


    public void setValue(String key, String value) throws Exception {
        ValueOperations<String, String> valueOps = stringRedisTemplate
                .opsForValue();
        valueOps.set(key, value);
    }

    public void setValue(String key, String value, long timeout, TimeUnit unit) throws Exception {
        ValueOperations<String, String> valueOps = stringRedisTemplate
                .opsForValue();
        valueOps.set(key, value, timeout, unit);
    }

    public List<String> getValues(Collection<String> keys) throws Exception {
        ValueOperations<String, String> valueOps = stringRedisTemplate
                .opsForValue();
        return valueOps.multiGet(keys);
    }


    public void delete(String key) throws Exception {
        stringRedisTemplate.delete(key);
    }


    public void delete(Collection<String> keys) throws Exception {
        stringRedisTemplate.delete(keys);
    }


    public void addSetValues(String key, String... values) throws Exception {
        SetOperations<String, String> setOps = stringRedisTemplate.opsForSet();
        setOps.add(key, values);
    }


    public Set<String> getSetValues(String key) throws Exception {
        SetOperations<String, String> setOps = stringRedisTemplate.opsForSet();
        return setOps.members(key);
    }


    public String getSetRandomMember(String key) throws Exception {
        SetOperations<String, String> setOps = stringRedisTemplate.opsForSet();
        return setOps.randomMember(key);
    }


    public void delSetValues(String key, Object... values) throws Exception {
        SetOperations<String, String> setOps = stringRedisTemplate.opsForSet();
        setOps.remove(key, values);
    }


    public Long getZsetValuesCount(String key) throws Exception {
        return stringRedisTemplate.opsForSet().size(key);
    }


    public void addHashSet(String key, HashMap<String, String> args)
            throws Exception {
        HashOperations<String, String, String> hashsetOps = stringRedisTemplate
                .opsForHash();
        hashsetOps.putAll(key, args);
    }


    public Map<String, String> getHashSet(String key) throws Exception {
        HashOperations<String, String, String> hashsetOps = stringRedisTemplate
                .opsForHash();
        return hashsetOps.entries(key);
    }

    public Map<byte[], byte[]> getHashByteSet(String key) throws Exception {
        RedisConnection connection = null;
        try {
            connection = redisTemplate.getConnectionFactory().getConnection();
            return connection.hGetAll(key.getBytes());
        } catch (Exception e) {
            throw new Exception(e);

        } finally {
            if (Objects.nonNull(connection) && !connection.isClosed()) {
                connection.close();
            }
        }
    }

   
    public List<byte[]> getHashMSet(byte[] key, byte[][] fields) throws Exception {
        return stringRedisTemplate.getConnectionFactory().getConnection().hMGet(key, fields);
    }

    /**
     * 设备hash中的值
     *
     * @param key
     * @param field
     * @param vaule
     * @return
     * @throws Exception
     */
    public Boolean setHashMSet(byte[] key, byte[] field, byte[] vaule) throws Exception {
        return stringRedisTemplate.getConnectionFactory().getConnection().hSet(key, field, vaule);
    }

    /**
     * 采用Pipeline方式获取多个Key的数据
     *
     * @param keys   Key数组
     * @param fields Hash对象的二级Key
     * @return 结果数组,每个Object对象为List<byte[]>,使用时需判断是否为null
     * @throws Exception
     */
    public List<Object> getHashMSet(byte[][] keys, byte[][] fields) throws Exception {
        if (keys == null || keys.length == 0 || fields == null || fields.length == 0) {
            return null;
        }
        RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();
        try {
            connection.openPipeline();
            for (byte[] key : keys) {
                connection.hMGet(key, fields);
            }
            return connection.closePipeline();
        } finally {
            if (!connection.isClosed()) {
                connection.close();
            }
        }
    }

    /**
     * 采用Pipeline方式获取多个Key的数据
     *
     * @param keys  Key数组
     * @param field Hash对象的二级Key
     * @return 结果数组,每个Object对象为List<byte[]>,使用时需判断是否为null
     * @throws Exception
     */
    public List<Object> getHashMSet(byte[][] keys, byte[] field) throws Exception {
        if (keys == null || keys.length == 0 || field == null) {
            return null;
        }
        RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();
        try {
            connection.openPipeline();
            for (byte[] key : keys) {
                connection.hGet(key, field);
            }
            return connection.closePipeline();

        } finally {
            if (!connection.isClosed()) {
                connection.close();
            }
        }
    }

    /**
     * 采用Pipeline方式获取多个Key的数据
     *
     * @param keys Key数组
     * @return 结果数组,每个Object对象为List<byte[]>,使用时需判断是否为null
     * @throws Exception
     */
    public List<Object> getHashMSet(byte[][] keys) throws Exception {
        if (keys == null || keys.length == 0) {
            return null;
        }
        RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();

        try {
            connection.openPipeline();
            for (byte[] key : keys) {
                connection.hGetAll(key);
            }
            return connection.closePipeline();
        } finally {
            if (!connection.isClosed()) {
                connection.close();
            }
        }
    }

    /**
     * 删除批量string
     *
     * @param keys Key数组
     */
    public void deleteAllStringValues(byte[][] keys) {
        if (keys == null || keys.length == 0) {
            return;
        }
        RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();

        try {
            connection.openPipeline();
            for (byte[] key : keys) {
                connection.del(key);
            }
            connection.closePipeline();
        } finally {
            if (!connection.isClosed()) {
                connection.close();
            }
        }
    }

    public List<String> getHashMSet(String key, List<String> fields) throws Exception {
        HashOperations<String, String, String> hashsetOps = stringRedisTemplate
                .opsForHash();
        return hashsetOps.multiGet(key, fields);
    }

    public List<byte[]> getHashByteMSet(String key, byte[]... fields) throws Exception {
//        HashOperations<String, String, byte[]> hashsetOps = stringRedisTemplate
//                .opsForHash();
//        return hashsetOps.multiGet(key, fields);

        RedisConnection connection = null;
        try {
            connection = redisTemplate.getConnectionFactory().getConnection();
            return connection.hMGet(key.getBytes(), fields);
        } catch (Exception e) {
            throw new Exception(e);

        } finally {
            if (Objects.nonNull(connection) && !connection.isClosed()) {
                connection.close();
            }
        }
    }

    public void delHashSetValues(String key, Object... values) throws Exception {
        HashOperations<String, String, String> hashsetOps = stringRedisTemplate
                .opsForHash();
        hashsetOps.delete(key, values);
    }


    public void addZset(String key, String value, double score)
            throws Exception {
        ZSetOperations<String, String> zSetOps = stringRedisTemplate
                .opsForZSet();
        zSetOps.add(key, value, score);
    }


    public Set<String> getZsetValues(String key) throws Exception {
        return null;
    }


    public void delZsetValues(String key, Object... values) throws Exception {
        ZSetOperations<String, String> zSetOps = stringRedisTemplate
                .opsForZSet();
        zSetOps.remove(key, values);
    }


    public String getHashByKey(String redisKey, String mapKey) throws Exception {
        HashOperations<String, String, String> hashsetOps = stringRedisTemplate.opsForHash();
        return hashsetOps.get(redisKey, mapKey);
    }


    public byte[] getHashByteByKey(String redisKey, String mapKey) throws Exception {
        RedisConnection connection = null;
        try {
            connection = redisTemplate.getConnectionFactory().getConnection();
            return connection.hGet(redisKey.getBytes(), mapKey.getBytes());
        } catch (Exception e) {
            throw new Exception(e);

        } finally {
            if (Objects.nonNull(connection) && !connection.isClosed()) {
                connection.close();
            }
        }
//        HashOperations<String, String, byte[]> hashsetOps = stringRedisTemplate.opsForHash();
//        return hashsetOps.get(redisKey, mapKey);
    }


    public Map<byte[], byte[]> getHashByte(String redisKey) throws Exception {
        RedisConnection connection = null;
        try {
            connection = redisTemplate.getConnectionFactory().getConnection();
            return connection.hGetAll(redisKey.getBytes());
        } catch (Exception e) {
            throw new Exception(e);
        } finally {
            if (Objects.nonNull(connection) && !connection.isClosed()) {
                connection.close();
            }
        }
    }

    public void addHashSet(String redisKey, String mapKey, String mapValue)
            throws Exception {
        stringRedisTemplate.opsForHash().put(redisKey, mapKey, mapValue);
    }


    public Set<String> getSet(String key) throws Exception {
        SetOperations<String, String> setOperations = stringRedisTemplate.opsForSet();
        return setOperations.members(key);

    }


    public void addSetValuesPipelined(final String[] keys, final String value) throws Exception {
        stringRedisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) {
                StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
                for (int i = 0; i < keys.length; i++) {
                    stringRedisConn.sAdd(keys[i], value);
                }
                //必须返回null
                return null;
            }
        });
    }


    public void delSetValuesPipelined(final String[] keys, final String value) throws Exception {
        stringRedisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) {
                StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
                for (int i = 0; i < keys.length; i++) {
                    stringRedisConn.sRem(keys[i], value);
                }
                //必须返回null
                return null;
            }
        });
    }


    public void delHashByKey(String redisKey, String mapKey) throws Exception {
        HashOperations<String, String, String> hashMapOps = stringRedisTemplate.opsForHash();
        hashMapOps.delete(redisKey, mapKey);
    }

    public Boolean hasKey(String key) throws Exception {
        return stringRedisTemplate.hasKey(key);
    }

    /**
     * 设置用户其他类的缓存
     *
     * @param key
     * @param field   hash结构的field
     * @param data    需要存的数据
     * @param timeOut 超时时间
     * @param unit    时间单位
     */
    public void setHashOther(String key, String field, String data, long timeOut, TimeUnit unit) {

        stringRedisTemplate.opsForHash().put(key, field, data);
        stringRedisTemplate.expire(key, timeOut, unit);
    }

    /**
     * 返回用户的其他缓存
     *
     * @param key
     * @param field hash结构的field
     * @return String
     * @throws Exception
     */
    public String getHashOther(String key, String field) throws Exception {
        return this.getHashByKey(key, field);
    }


    /**
     * 2019-2-20 changyandong 新增incr方法,设置过期时间
     *
     * @param key
     * @param delta
     * @param timeout
     * @param unit
     * @return
     */
    public Long increment(final String key, final int delta, final long timeout,
                          final TimeUnit unit) {

        if (timeout <= 0 || unit == null) {

            return stringRedisTemplate.opsForValue().increment(key, delta);
        }
        List<Object> result = stringRedisTemplate
                .executePipelined(new SessionCallback<Object>() {
                    @Override
                    public <K, V> Object execute(
                            RedisOperations<K, V> operations)
                            throws DataAccessException {

                        ValueOperations<K, V> ops = operations.opsForValue();
                        ops.increment((K) key, delta);
                        operations.expire((K) key, timeout, unit);
                        return null;
                    }
                });
        return (Long) result.get(0);
    }

    /**
     * 管道增加hash结构
     */
    public void addHashValuesPipelined(Map<String, Map<String, String>> keys) {
        stringRedisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
            keys.forEach(stringRedisConn::hMSet);
            //必须返回null
            return null;
        });
    }

    /**
     * 管道增加hash结构  删除老hash结构
     */
    public void addHashValuesPipelinedRemoveOldHash(Map<String, Map<String, String>> keys) {
        stringRedisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
            stringRedisConn.del(keys.keySet().toArray(new String[0]));
            keys.forEach(stringRedisConn::hMSet);
            //必须返回null
            return null;
        });
    }

    /**
     * 分布式锁模板方法
     *
     * @param businessKey       业务key
     * @param callbackFunction 回调方法
     * @param s                回调方法具体入参
     * @param <S>              回调方法入参类型
     * @param <T>              回调方法返回值类型
     * @return 回调方法返回值
     */
    public <S, T> T redisLockCallback(String businessKey, Function<S, T> callbackFunction, S s) {
        try {
            redisLock(businessKey);
            return callbackFunction.apply(s);
        } finally {
            redisUnLock(businessKey);
        }
    }


    public <T> T redisLockSupplier(String businessKey, Supplier<T> supplier) {
        return redisLockSupplier(businessKey, supplier, lockMaxSeconds, LOCK_WAIT_MAX_TIME, TimeUnit.SECONDS);
    }
    public <T> T redisLockSupplier(String businessKey, Supplier<T> supplier, long lockMaxTime, long tryTimeout, TimeUnit timeUnit) {
        try {
            redisLock(businessKey, lockMaxTime, tryTimeout, timeUnit);
            return supplier.get();
        } finally {
            redisUnLock(businessKey);
        }
    }

    /**
     * 获取锁(不等待,直接返回 是否获取到锁资源)
     *
     * @param businessKey 业务key
     * @return 是否获取到锁资源
     */
    public boolean redisLockSuspend(String businessKey) {
        return redisLockSuspend(businessKey, lockMaxSeconds, TimeUnit.SECONDS);
    }


    /**
     *  获取锁(不等待,直接返回 是否获取到锁资源)
     * @param businessKey 业务key
     * @param lockMaxTime 锁占用时长
     * @param timeUnit 时间单位
     * @return 是否获取锁资源
     */
    public boolean redisLockSuspend(String businessKey, long lockMaxTime, TimeUnit timeUnit) {
        String lockKey = generateLockKey(businessKey);
        long finalLockMaxTime = timeUnit.toMillis(lockMaxTime);
        //可重入锁判断
        if (isReentrantLock(lockKey)) {
            return Boolean.TRUE;
        }

        RedisCallback<Boolean> callback = (connection) -> connection.set(
                lockKey.getBytes(StandardCharsets.UTF_8),
                businessKey.getBytes(StandardCharsets.UTF_8),
                Expiration.milliseconds(finalLockMaxTime),
                RedisStringCommands.SetOption.SET_IF_ABSENT);
        return stringRedisTemplate.execute(callback);
    }

    /**
     * @param keyPrefix  redis锁 key前缀
     * @param key        key
     * @param tryTimeout 超时时间
     * @param timeUnit   时间单位
     * @return 是否获取到锁资源
     */
    @Deprecated
    public boolean redisLock(String keyPrefix, String key, long lockMaxTime, long tryTimeout, TimeUnit timeUnit) {
        String businessKey = getLockKey(keyPrefix, key);
        return redisLock(businessKey, lockMaxTime, tryTimeout, timeUnit);
    }

    public boolean redisLock(String businessKey, long lockMaxTime, long tryTimeout, TimeUnit timeUnit) {
        tryTimeout = System.currentTimeMillis() + timeUnit.toMillis(tryTimeout);
        lockMaxTime = timeUnit.toMillis(lockMaxTime);
        return redisLock(businessKey, lockMaxTime, tryTimeout);
    }

    /**
     * 获取redis分布式锁 (默认超时时间)
     *
     * @param keyPrefix redis锁 key前缀
     * @param key       key
     * @return 是否获取到锁资源
     */
    @Deprecated
    public boolean redisLock(String keyPrefix, String key) {
        String businessKey = getLockKey(keyPrefix, key);
        return redisLock(businessKey);
    }

    public boolean redisLock(String businessKey) {
        long endTime = System.currentTimeMillis() + LOCK_WAIT_MAX_TIME;
        long lockMaxTime = TimeUnit.SECONDS.toMillis(this.lockMaxSeconds);
        return redisLock(businessKey, lockMaxTime, endTime);
    }

    /**
     * 获取redis分布式锁 (默认超时时间)
     * @param businessKey 业务key
     * @param lockMaxTime 锁占用时长
     * @param endTime 结束时间
     * @return 是否获取到锁资源
     */
    private boolean redisLock(String businessKey, long lockMaxTime, long endTime) {
        String lockKey = generateLockKey(businessKey);
        logger.debug("redisLock businessKey:{},  lockKey:{}, lockMaxTime:{}, endTime:{}", businessKey, lockKey, lockMaxTime, endTime);

        //可重入锁判断
        if (isReentrantLock(lockKey)) {
            logger.debug("redisLock lockKey:{}, threadName:{}, isReentrantLock true", lockKey, Thread.currentThread().getName());
            return Boolean.TRUE;
        }

        RedisCallback<Boolean> callback = (connection) -> connection.set(
                lockKey.getBytes(StandardCharsets.UTF_8),
                businessKey.getBytes(StandardCharsets.UTF_8),
                Expiration.milliseconds(lockMaxTime),
                RedisStringCommands.SetOption.SET_IF_ABSENT);

        //在timeout时间内仍未获取到锁,则获取失败
        while (System.currentTimeMillis() < endTime) {
            if (stringRedisTemplate.execute(callback)) {
                redisLockReentrant.set(lockKey);
                logger.debug("redisLock getKey  lockKey:{},  ", lockKey);
                return true;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                logger.error("获取redis分布式锁出错", e);
                Thread.currentThread().interrupt();
            }
        }
        logger.debug("redisLock meiyoukey  lockKey:{},  ", lockKey);
        return false;
    }

    /**
     * 释放分布式锁
     *
     * @param keyPrefix redis锁 key前缀
     * @param key       key
     */
    @Deprecated
    public Boolean redisUnLock(String keyPrefix, String key) {
        String lockKey = getLockKey(keyPrefix, key);
        return redisUnLock(lockKey);
    }

    public Boolean redisUnLock(String businessKey) {
        String lockKey = generateLockKey(businessKey);
        RedisCallback<Boolean> callback = (connection) -> connection.eval(
                RELEASE_LOCK_SCRIPT.getBytes(),
                ReturnType.BOOLEAN, 1,
                lockKey.getBytes(StandardCharsets.UTF_8),
                businessKey.getBytes(StandardCharsets.UTF_8));

        //清空 ThreadLocal
        redisLockReentrant.remove();
        Boolean execute = stringRedisTemplate.execute(callback);
        logger.debug("redisUnLock execute  lockKey:{},  ", lockKey);
        return execute;
    }

    private String getLockKey(String keyPrefix, String key) {
        return keyPrefix + "-" + key;
    }

    /**
     * 是否为重入锁
     */
    private boolean isReentrantLock(String lockKey) {
        String originValue = redisLockReentrant.get();
        String redisValue = stringRedisTemplate.opsForValue().get(lockKey);
        return StringUtils.isNotBlank(originValue) && originValue.equals(redisValue);
    }

    /**
     * 生成规则要求的 key
     *  xxx:redisLock:${appName}:${classSimpleName}:${methodName}:${businessKey}
     * @param businessKey  业务key
     * @return key
     */
    private String generateLockKey(String businessKey) {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        String classSimpleName = StringUtils.EMPTY;
        String methodName = StringUtils.EMPTY;
        for (StackTraceElement traceElement : stackTrace) {
            String itemClassName = traceElement.getClassName();

            //如果是当前类或者stack类 continue;
            if (itemClassName.equals(this.getClass().getName()) || itemClassName.equals(StackTraceElement.class.getName())) {
                continue;
            }

            char[] cs=itemClassName.substring(itemClassName.lastIndexOf(".")+1).toCharArray();
            cs[0]+=32;

            //一直找,找到被spring管理的类。
            Object target;
            try {
                target = applicationContext.getBean(String.valueOf(cs));
            } catch (NoSuchBeanDefinitionException e) {
                continue;
            }

            //如果是代理类,找到实际类
            if (AopUtils.isAopProxy(target) && target instanceof Advised) {
                Advised advised = (Advised) target;
                try {
                    target = advised.getTargetSource().getTarget();
                } catch (Exception e) {
                    continue;
                }
            }
            if (Objects.nonNull(target)) {
                classSimpleName = target.getClass().getSimpleName();
                methodName = traceElement.getMethodName();
                break;
            }
        }

        return REDIS_LOCK_KEY_PREFIX.concat(REDIS_NAMESPACE_PREFIX).concat(appName.toLowerCase())
                .concat(REDIS_NAMESPACE_PREFIX).concat(classSimpleName)
                .concat(REDIS_NAMESPACE_PREFIX).concat(methodName)
                .concat(REDIS_NAMESPACE_PREFIX).concat(businessKey);
    }
}



2.4 将自动配置导入

SpringBoot Redis 多数据源集成支持哨兵模式和Cluster集群模式,Redis从入门到精通2023版,spring boot,redis,后端,java,缓存,sentinel
在工程目录中创建 common-redis-lettuce/src/main/resources/META-INF/spring创建文件名为
org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件
文件内容:

com.iceicepip.project.common.redis.CustomRedisConfig

解释

在工程目录中创建 common-redis-lettuce/src/main/resources/META-INF/spring 目录,并在该目录下创建一个名为 org.springframework.boot.autoconfigure.AutoConfiguration.imports 的文件。该文件的作用是指示 Spring Boot 在自动配置期间需要导入哪些额外的配置类。

org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中,可以添加需要导入的其他配置类的全限定类名。例如,如果我们需要在自动配置期间导入一个名为 CustomRedisConfig 的配置类,可以在该文件中添加以下内容:

com.iceicepip.project.common.redis.CustomRedisConfig

这样,在应用程序启动时,Spring Boot 会自动加载 CustomRedisConfig 类,并将其与自动配置合并,以提供完整的应用程序配置。

3. 使用方式

其中xxx 为在Spring Boot 配置文件中配置的多数据源的标识.如’user’、“iot”

  	@Autowired
    @Qualifier("xxxRedis")
    private CustomRedisService xxxRedisService;
      	@Autowired
    @Qualifier("userRedis")
    private CustomRedisService userRedisService;

或者直接使用RedisTemplate 。

	@Autowired
    @Qualifier("userRedisTemplate")
    private RedisTemplate  userRedisTemplate;

  	@Autowired
    @Qualifier("xxxStringRedisTemplate")
    private StringRedisTemplate  xxxStringRedisTemplate;
  	@Autowired
    @Qualifier("xxxRedisTemplate")
    private RedisTemplate   xxxRedisTemplate;

4. 源码地址

https://github.com/wangshuai67/Redis-Tutorial-2023文章来源地址https://www.toymoban.com/news/detail-611916.html

5. Redis从入门到精通系列文章

  • 《SpringBoot Redis 使用Lettuce和Jedis配置哨兵模式》
  • 《Redis【应用篇】之RedisTemplate基本操作》
  • 《Redis 从入门到精通【实践篇】之SpringBoot配置Redis多数据源》
  • 《Redis 从入门到精通【进阶篇】之三分钟了解Redis HyperLogLog 数据结构》
  • 《Redis 从入门到精通【进阶篇】之三分钟了解Redis地理位置数据结构GeoHash》
  • 《Redis 从入门到精通【进阶篇】之高可用哨兵机制(Redis Sentinel)详解》
  • 《Redis 从入门到精通【进阶篇】之redis主从复制详解》
  • 《Redis 从入门到精通【进阶篇】之Redis事务详解》
  • 《Redis从入门到精通【进阶篇】之对象机制详解》
  • 《Redis从入门到精通【进阶篇】之消息传递发布订阅模式详解》
  • 《Redis从入门到精通【进阶篇】之持久化 AOF详解》
  • 《Redis从入门到精通【进阶篇】之持久化RDB详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构快表QuickList详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构简单动态字符串(SDS)详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构压缩列表(ZipList)详解》
  • 《Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例》
    SpringBoot Redis 多数据源集成支持哨兵模式和Cluster集群模式,Redis从入门到精通2023版,spring boot,redis,后端,java,缓存,sentinel大家好,我是冰点,今天的Redis【实践篇】之SpringBoot Redis 多数据源集成支持哨兵模式和Cluster集群模式,全部内容就是这些。如果你有疑问或见解可以在评论区留言。

到了这里,关于SpringBoot Redis 多数据源集成支持哨兵模式和Cluster集群模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于注解切换、Hikari实现的SpringBoot动态数据源(支持JNDI)

    先说效果,要实现方法级别注解切换当前数据源,不设置注解时走默认数据源,同时支持JNDI源。 Spring框架中存在一个抽象类 AbstractRoutingDataSource ,他是一个可以动态选择当前DataSource的路由类,我们就是要从这里入手,重新实现数据源的切换选择逻辑。然后借助注解和切面,

    2024年02月08日
    浏览(78)
  • 【SpringBoot应用篇】SpringBoot集成atomikos实现多数据源配置和分布式事务管理

    讨论分布式事务之前我们分清两个概念: 本地事务 、 分布式事务 ; 本地事务是解决 单个数据源 上的数据操作的 一致性 问题的话,而分布式事务则是为了解决 跨越多个数据源 上数据操作的 一致性 问题。 百度官方对分布式事务的定义是指事务的参与者、支持事务的服务

    2024年02月16日
    浏览(50)
  • Spring - 配置支持多数据源

    在SpringBoot工程中整合多数据源,和在SSM工程中整合稍微有一点区别。 具体整合步骤如下: 1、在application.properties中配置出多个数据源 2、在代码中创建出mapper目录,在mapper目录下创建出不同数据源的目录 创建出目录 MySQL数据源的Mapper SQL Server数据源的Mapper 3、创建config packa

    2024年02月01日
    浏览(39)
  • 后端项目开发:集成Druid数据源

    Druid作为连接池中间件可以监控数据库访问性能,对数据库密码加密,查看SQL执行日志,扩展JDBC。 添加依赖 参考官方文档:https://github.com/alibaba/druid/wiki 更多配置参考:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter 启动应用:访问http://localhost:8001/druid/login.html查看监

    2024年02月11日
    浏览(43)
  • SpringBoot——动态数据源(多数据源自动切换)

    日常的业务开发项目中只会配置一套数据源,如果需要获取其他系统的数据往往是通过调用接口, 或者是通过第三方工具比如kettle将数据同步到自己的数据库中进行访问。 但是也会有需要在项目中引用多数据源的场景。比如如下场景: 自研数据迁移系统,至少需要新、老两

    2024年02月16日
    浏览(39)
  • springboot整合多数据源的配置以及动态切换数据源,注解切换数据源

    在许多应用程序中,可能需要使用多个数据库或数据源来处理不同的业务需求。Spring Boot提供了简便的方式来配置和使用多数据源,使开发人员能够轻松处理多个数据库连接。如果你的项目中可能需要随时切换数据源的话,那我这篇文章可能能帮助到你 ℹ️:这里对于pom文件

    2024年02月10日
    浏览(49)
  • SpringBoot从数据库读取数据数据源配置信息,动态切换数据源

            首先准备多个数据库,主库smiling-datasource,其它库test1、test2、test3         接下来,我们在主库smiling-datasource中,创建表databasesource,用于存储多数据源相关信息。表结构设计如下         创建好表之后,向表databasesource中存储test1、test2、test3三个数据库的相关配置

    2024年01月16日
    浏览(63)
  • springboot + (mysql/pgsql) + jpa 多数据源(不同类数据源)

     配置文件: datasourceconfig: 数据源一: 数据源二:

    2024年02月14日
    浏览(57)
  • 自动生成数据库设计文档,支持多数据源批量生成(Word文档)

          在做项目时通常使用PowerDesigner设计数据库,但在项目完成交付项目给客户的时候常常需要一份Word版本的数据库文档给客户,你不能指望每个客户都会用PowerDesigner,所以基于当前开发数据库生成数据库文档就是最佳选择,如果手动编写数据库文档那将是一件非常痛苦的

    2024年04月23日
    浏览(46)
  • Springboot多路数据源

    1、多路数据源配置 (1)SpringBoot+MyBatis-Plus+Oracle实现多数据源配置 https://blog.csdn.net/weixin_44812604/article/details/127386828 (2)SpringBoot+Mybatis搭建Oracle多数据源配置简述 https://blog.csdn.net/HJW_233/article/details/129103370 (3)SpringBoot+Mybatis+Oracle 增删改查(简单的案例,超详细) https://blo

    2024年02月12日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包