【实践篇】4.13 SpringBoot Redis 多数据源集成支持哨兵模式和Cluster集群 ...

打印 上一主题 下一主题

主题 226|帖子 226|积分 678

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


  

0.媒介

说明

各人都知道Redis在6.0版本之前是单线程工作的,这导致在一个项目中有大量读写利用的情况下,Redis单实例的性能被其他业务长时间占据,导致部分业务出现耽误现象,为了解决这个问题,部分公司项目选择利用多个Redis实例分别存储不同的业务数据和利用场景,比如IoT网关写入的数据,可以单独拆分一个Redis实例去利用,其他业务利用一个Redis实例。用多个Redis实例 可以提高Redis的性能。Redis是一种基于内存的缓存数据库,内存容量是其性能的瓶颈。当项目中的数据量较大时,单个Redis实例可能无法承载所有数据,导致性能降落。而利用多个Redis实例可以将数据分散到多个实例中,从而提高Redis的整体性能。
   这就导致在某些业务场景下,一个项目工程,同时要利用这两个Redis实例的数据,这就是本文要解决的问题。
  本文通过写一个Redis 多数据源组件 Starter 来解决上面的问题,支持Redis 多数据源,可集成配置哨兵模式、Cluster集群模式、单机模式。如果单实例配置哨兵模式,请参阅我之前的博客 《SpringBoot Redis 利用Lettuce和Jedis配置哨兵模式》
项目布局


Pom 依赖

如下,可能有多余的,根据项目具体情况删减。再就是需要利用Springboot parent
<spring-boot-dependencies.version>2.7.12</spring-boot-dependencies.version>
  1.                 <dependency>
  2.             <groupId>org.springframework.boot</groupId>
  3.             <artifactId>spring-boot-starter-json</artifactId>
  4.         </dependency>
  5.         <dependency>
  6.             <groupId>org.springframework.boot</groupId>
  7.             <artifactId>spring-boot-configuration-processor</artifactId>
  8.             <optional>true</optional>
  9.         </dependency>
  10.         <dependency>
  11.             <groupId>org.apache.commons</groupId>
  12.             <artifactId>commons-lang3</artifactId>
  13.         </dependency>
  14.         <dependency>
  15.             <groupId>com.alibaba</groupId>
  16.             <artifactId>transmittable-thread-local</artifactId>
  17.         </dependency>
  18.         <dependency>
  19.             <groupId>org.springframework.boot</groupId>
  20.             <artifactId>spring-boot-starter-data-redis</artifactId>
  21.         </dependency>
  22.          <dependency>
  23.             <groupId>org.apache.commons</groupId>
  24.             <artifactId>commons-pool2</artifactId>
  25.         </dependency>
复制代码
1. 配置

1.1 通用配置()

设置主redis的标识

很关键
  1. # 示例
  2. custom.primary.redis.key=user
复制代码
毗连池相关配置

此配置为通用配置所有类型的链接模式都可以配置,不配置走Springboot 默认配置。
  1. spring.redis.xxx.timeout = 3000
  2. spring.redis.xxx.maxTotal = 50
  3. spring.redis.xxx.maxIdle = 50
  4. spring.redis.xxx.minIdle = 2
  5. spring.redis.xxx.maxWaitMillis = 10000
  6. spring.redis.xxx.testOnBorrow = False
复制代码
1.2 单例模式配置

  1. # 第1个Redis 实例 用于用户体系,我们取标识为user
  2. spring.redis.user.host = 127.0.0.1
  3. spring.redis.user.port = 6380
  4. spring.redis.user.password = 密码
  5. spring.redis.user.database = 0
  6. # 第2个Redis 实例用于IoT体系
  7. spring.redis.iot.host = 127.0.0.1
  8. spring.redis.iot.port = 6390
  9. spring.redis.iot.password = 密码
  10. spring.redis.iot.database = 0
  11. # 第3个Redis 实例用于xxx
  12. spring.redis.xxx.host = 127.0.0.1
  13. spring.redis.xxx.port = 6390
  14. spring.redis.xxx.password = 密码
  15. spring.redis.xxx.database = 0
复制代码
1.3 哨兵模式配置

多个Redis数据库实例的情况下,将下面配置项多配置几个。
  1. spring.redis.xxx1.sentinel.master=mymaster1
  2. spring.redis.xxx1.sentinel.nodes=ip:端口,ip:端口
  3. spring.redis.xxx1.password = bD945aAfeb422E22AbAdFb9D2a22bEDd
  4. spring.redis.xxx1.database = 0
  5. spring.redis.xxx1.timeout = 3000
  6. #第二个
  7. spring.redis.xxx2.sentinel.master=mymaster2
  8. spring.redis.xxx2.sentinel.nodes=ip:端口,ip:端口
  9. spring.redis.xxx2.password = bD945aAfeb422E22AbAdFb9D2a22bEDd
  10. spring.redis.xxx2.database = 0
  11. spring.redis.xxx2.timeout = 3000
复制代码
1.4 集群模式配置(集群模式不支持设置database)

  1. spring.redis.xxx1.cluster.nodes=ip1:端口,ip2:端口,ip3:端口,ip4:端口,ip5:端口,ip6:端口
  2. spring.redis.xxx1.cluster.max-redirects=5
  3. spring.redis.xxx1.password = 密码
  4. spring.redis.xxx1.timeout = 3000
复制代码
2. 代码实例

2.1.CustomRedisConfig

根据配置文件配置项,创建Redis多个数据源的RedisTemplate 。
重要头脑为,

  • 在服务启动过程中读取多数据源配置文件,将多数据源的配置读取到
  1. // 定义静态Map变量redis,用于存储Redis配置参数
  2.     protected static Map<String, Map<String, String>> redis = new HashMap<>();
复制代码

  • 根据多数据源配置创建不同类型的Configuration
  1. private RedisStandaloneConfiguration buildStandaloneConfig(Map<String, String> param){  
  2. //...省略
  3. }
复制代码
  1.     private RedisSentinelConfiguration buildSentinelConfig(Map<String, String> param){
  2. //...省略
  3. }
复制代码
  1.     private RedisClusterConfiguration buildClusterConfig(Map<String, String> param){
  2.         //...省略
  3. }
复制代码

  • 根据 不同类型的创建RedisConnectionFactory
  1.   public RedisConnectionFactory buildLettuceConnectionFactory(String redisKey, Map<String, String> param,GenericObjectPoolConfig genericObjectPoolConfig){
  2.   ...
  3.   }
复制代码
4.最后遍历上面我们配置的配置文件调用buildCustomRedisService(k, redisTemplate, stringRedisTemplate); 将创建的不同的RedisTemplate Bean 然后注入到Spring容器中
CustomRedisConfig 源码

源码中涉及的Springboot 相关知识在此处就不做赘婿,需要了解,可以参考我的《SpringBoot 源码解析系列》
InitializingBean, ApplicationContextAware, BeanPostProcessor
  1. 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配置参数
  2.     protected static Map<String, Map<String, String>> redis = new HashMap<>();
  3.     // 在代码中作为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

定义常用配置项的键名
  1. package com.iceicepip.project.common.redis;
  2. public class CustomRedisConfigConstant {
  3.     private CustomRedisConfigConstant() {
  4.     }
  5.     public static final String REDIS_HOST = "host";
  6.     public static final String REDIS_PORT = "port";
  7.     public static final String REDIS_TIMEOUT = "timeout";
  8.     public static final String REDIS_DATABASE = "database";
  9.     public static final String REDIS_PASSWORD = "password";
  10.     public static final String REDIS_MAXWAITMILLIS = "maxWaitMillis";
  11.     public static final String REDIS_MAXIDLE = "maxIdle";
  12.     public static final String REDIS_MINIDLE = "minIdle";
  13.     public static final String REDIS_MAXTOTAL = "maxTotal";
  14.     public static final String REDIS_TESTONBORROW = "testOnBorrow";
  15.     public static final String REDIS_SENTINEL_MASTER = "sentinel.master";
  16.     public static final String REDIS_SENTINEL_NODES = "sentinel.nodes";
  17.     public static final String REDIS_CLUSTER_NODES = "cluster.nodes";
  18.     public static final String REDIS_CLUSTER_MAX_REDIRECTS = "cluster.max-redirects";
  19.     public static final String BEAN_NAME_SUFFIX = "Redis";
  20.     public static final String INIT_METHOD_NAME = "getInit";
  21. }
复制代码
2.3封装一个Redis利用类 CustomRedisService

  1. package com.iceicepip.project.common.redis;
  2. import com.alibaba.ttl.TransmittableThreadLocal;
  3. import org.apache.commons.lang3.StringUtils;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.aop.framework.Advised;
  7. import org.springframework.aop.support.AopUtils;
  8. import org.springframework.beans.factory.NoSuchBeanDefinitionException;
  9. import org.springframework.beans.factory.annotation.Value;
  10. import org.springframework.context.ApplicationContext;
  11. import org.springframework.dao.DataAccessException;
  12. import org.springframework.data.redis.connection.RedisConnection;
  13. import org.springframework.data.redis.connection.RedisStringCommands;
  14. import org.springframework.data.redis.connection.ReturnType;
  15. import org.springframework.data.redis.connection.StringRedisConnection;
  16. import org.springframework.data.redis.core.*;
  17. import org.springframework.data.redis.core.types.Expiration;
  18. import javax.annotation.Resource;
  19. import java.nio.charset.StandardCharsets;
  20. import java.util.*;
  21. import java.util.concurrent.TimeUnit;
  22. import java.util.function.Function;
  23. import java.util.function.Supplier;
  24. public class CustomRedisService {
  25.     private static final Logger logger = LoggerFactory.getLogger(CustomRedisService.class);
  26.     private StringRedisTemplate stringRedisTemplate;
  27.     private RedisTemplate redisTemplate;
  28.     @Value("${distribute.lock.MaxSeconds:100}")
  29.     private Integer lockMaxSeconds;
  30.     private static Long LOCK_WAIT_MAX_TIME = 120000L;
  31.     @Resource
  32.     private ApplicationContext applicationContext;
  33.     /**
  34.      * 保存锁的value
  35.      */
  36.     private TransmittableThreadLocal<String> redisLockReentrant = new TransmittableThreadLocal<>();
  37.     /**
  38.      * 解锁lua脚本
  39.      */
  40.     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";
  41.     /**
  42.      * redis锁固定前缀
  43.      */
  44.     private static final String REDIS_LOCK_KEY_PREFIX = "xxx:redisLock";
  45.     /**
  46.      * redis nameSpace
  47.      */
  48.     private static final String REDIS_NAMESPACE_PREFIX = ":";
  49.     @Value("${spring.application.name}")
  50.     private String appName;
  51.     public CustomRedisService() {
  52.     }
  53.     public CustomRedisService(StringRedisTemplate stringRedisTemplate, RedisTemplate redisTemplate) {
  54.         this.stringRedisTemplate = stringRedisTemplate;
  55.         this.redisTemplate = redisTemplate;
  56.     }
  57.     public StringRedisTemplate getStringRedisTemplate() {
  58.         return stringRedisTemplate;
  59.     }
  60.     public RedisTemplate getRedisTemplate() {
  61.         return redisTemplate;
  62.     }
  63.     //以下是操作
  64.     public void saveOrUpdate(HashMap<String, String> values) throws Exception {
  65.         ValueOperations<String, String> valueOps = stringRedisTemplate
  66.                 .opsForValue();
  67.         valueOps.multiSet(values);
  68.     }
  69.     public void saveOrUpdate(String key, String value) throws Exception {
  70.         ValueOperations<String, String> valueOps = stringRedisTemplate
  71.                 .opsForValue();
  72.         valueOps.set(key, value);
  73.     }
  74.     public String getValue(String key) throws Exception {
  75.         ValueOperations<String, String> valueOps = stringRedisTemplate
  76.                 .opsForValue();
  77.         return valueOps.get(key);
  78.     }
  79.     public void setValue(String key, String value) throws Exception {
  80.         ValueOperations<String, String> valueOps = stringRedisTemplate
  81.                 .opsForValue();
  82.         valueOps.set(key, value);
  83.     }
  84.     public void setValue(String key, String value, long timeout, TimeUnit unit) throws Exception {
  85.         ValueOperations<String, String> valueOps = stringRedisTemplate
  86.                 .opsForValue();
  87.         valueOps.set(key, value, timeout, unit);
  88.     }
  89.     public List<String> getValues(Collection<String> keys) throws Exception {
  90.         ValueOperations<String, String> valueOps = stringRedisTemplate
  91.                 .opsForValue();
  92.         return valueOps.multiGet(keys);
  93.     }
  94.     public void delete(String key) throws Exception {
  95.         stringRedisTemplate.delete(key);
  96.     }
  97.     public void delete(Collection<String> keys) throws Exception {
  98.         stringRedisTemplate.delete(keys);
  99.     }
  100.     public void addSetValues(String key, String... values) throws Exception {
  101.         SetOperations<String, String> setOps = stringRedisTemplate.opsForSet();
  102.         setOps.add(key, values);
  103.     }
  104.     public Set<String> getSetValues(String key) throws Exception {
  105.         SetOperations<String, String> setOps = stringRedisTemplate.opsForSet();
  106.         return setOps.members(key);
  107.     }
  108.     public String getSetRandomMember(String key) throws Exception {
  109.         SetOperations<String, String> setOps = stringRedisTemplate.opsForSet();
  110.         return setOps.randomMember(key);
  111.     }
  112.     public void delSetValues(String key, Object... values) throws Exception {
  113.         SetOperations<String, String> setOps = stringRedisTemplate.opsForSet();
  114.         setOps.remove(key, values);
  115.     }
  116.     public Long getZsetValuesCount(String key) throws Exception {
  117.         return stringRedisTemplate.opsForSet().size(key);
  118.     }
  119.     public void addHashSet(String key, HashMap<String, String> args)
  120.             throws Exception {
  121.         HashOperations<String, String, String> hashsetOps = stringRedisTemplate
  122.                 .opsForHash();
  123.         hashsetOps.putAll(key, args);
  124.     }
  125.     public Map<String, String> getHashSet(String key) throws Exception {
  126.         HashOperations<String, String, String> hashsetOps = stringRedisTemplate
  127.                 .opsForHash();
  128.         return hashsetOps.entries(key);
  129.     }
  130.     public Map<byte[], byte[]> getHashByteSet(String key) throws Exception {
  131.         RedisConnection connection = null;
  132.         try {
  133.             connection = redisTemplate.getConnectionFactory().getConnection();
  134.             return connection.hGetAll(key.getBytes());
  135.         } catch (Exception e) {
  136.             throw new Exception(e);
  137.         } finally {
  138.             if (Objects.nonNull(connection) && !connection.isClosed()) {
  139.                 connection.close();
  140.             }
  141.         }
  142.     }
  143.    
  144.     public List<byte[]> getHashMSet(byte[] key, byte[][] fields) throws Exception {
  145.         return stringRedisTemplate.getConnectionFactory().getConnection().hMGet(key, fields);
  146.     }
  147.     /**
  148.      * 设备hash中的值
  149.      *
  150.      * @param key
  151.      * @param field
  152.      * @param vaule
  153.      * @return
  154.      * @throws Exception
  155.      */
  156.     public Boolean setHashMSet(byte[] key, byte[] field, byte[] vaule) throws Exception {
  157.         return stringRedisTemplate.getConnectionFactory().getConnection().hSet(key, field, vaule);
  158.     }
  159.     /**
  160.      * 采用Pipeline方式获取多个Key的数据
  161.      *
  162.      * @param keys   Key数组
  163.      * @param fields Hash对象的二级Key
  164.      * @return 结果数组,每个Object对象为List<byte[]>,使用时需判断是否为null
  165.      * @throws Exception
  166.      */
  167.     public List<Object> getHashMSet(byte[][] keys, byte[][] fields) throws Exception {
  168.         if (keys == null || keys.length == 0 || fields == null || fields.length == 0) {
  169.             return null;
  170.         }
  171.         RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();
  172.         try {
  173.             connection.openPipeline();
  174.             for (byte[] key : keys) {
  175.                 connection.hMGet(key, fields);
  176.             }
  177.             return connection.closePipeline();
  178.         } finally {
  179.             if (!connection.isClosed()) {
  180.                 connection.close();
  181.             }
  182.         }
  183.     }
  184.     /**
  185.      * 采用Pipeline方式获取多个Key的数据
  186.      *
  187.      * @param keys  Key数组
  188.      * @param field Hash对象的二级Key
  189.      * @return 结果数组,每个Object对象为List<byte[]>,使用时需判断是否为null
  190.      * @throws Exception
  191.      */
  192.     public List<Object> getHashMSet(byte[][] keys, byte[] field) throws Exception {
  193.         if (keys == null || keys.length == 0 || field == null) {
  194.             return null;
  195.         }
  196.         RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();
  197.         try {
  198.             connection.openPipeline();
  199.             for (byte[] key : keys) {
  200.                 connection.hGet(key, field);
  201.             }
  202.             return connection.closePipeline();
  203.         } finally {
  204.             if (!connection.isClosed()) {
  205.                 connection.close();
  206.             }
  207.         }
  208.     }
  209.     /**
  210.      * 采用Pipeline方式获取多个Key的数据
  211.      *
  212.      * @param keys Key数组
  213.      * @return 结果数组,每个Object对象为List<byte[]>,使用时需判断是否为null
  214.      * @throws Exception
  215.      */
  216.     public List<Object> getHashMSet(byte[][] keys) throws Exception {
  217.         if (keys == null || keys.length == 0) {
  218.             return null;
  219.         }
  220.         RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();
  221.         try {
  222.             connection.openPipeline();
  223.             for (byte[] key : keys) {
  224.                 connection.hGetAll(key);
  225.             }
  226.             return connection.closePipeline();
  227.         } finally {
  228.             if (!connection.isClosed()) {
  229.                 connection.close();
  230.             }
  231.         }
  232.     }
  233.     /**
  234.      * 删除批量string
  235.      *
  236.      * @param keys Key数组
  237.      */
  238.     public void deleteAllStringValues(byte[][] keys) {
  239.         if (keys == null || keys.length == 0) {
  240.             return;
  241.         }
  242.         RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();
  243.         try {
  244.             connection.openPipeline();
  245.             for (byte[] key : keys) {
  246.                 connection.del(key);
  247.             }
  248.             connection.closePipeline();
  249.         } finally {
  250.             if (!connection.isClosed()) {
  251.                 connection.close();
  252.             }
  253.         }
  254.     }
  255.     public List<String> getHashMSet(String key, List<String> fields) throws Exception {
  256.         HashOperations<String, String, String> hashsetOps = stringRedisTemplate
  257.                 .opsForHash();
  258.         return hashsetOps.multiGet(key, fields);
  259.     }
  260.     public List<byte[]> getHashByteMSet(String key, byte[]... fields) throws Exception {
  261. //        HashOperations<String, String, byte[]> hashsetOps = stringRedisTemplate
  262. //                .opsForHash();
  263. //        return hashsetOps.multiGet(key, fields);
  264.         RedisConnection connection = null;
  265.         try {
  266.             connection = redisTemplate.getConnectionFactory().getConnection();
  267.             return connection.hMGet(key.getBytes(), fields);
  268.         } catch (Exception e) {
  269.             throw new Exception(e);
  270.         } finally {
  271.             if (Objects.nonNull(connection) && !connection.isClosed()) {
  272.                 connection.close();
  273.             }
  274.         }
  275.     }
  276.     public void delHashSetValues(String key, Object... values) throws Exception {
  277.         HashOperations<String, String, String> hashsetOps = stringRedisTemplate
  278.                 .opsForHash();
  279.         hashsetOps.delete(key, values);
  280.     }
  281.     public void addZset(String key, String value, double score)
  282.             throws Exception {
  283.         ZSetOperations<String, String> zSetOps = stringRedisTemplate
  284.                 .opsForZSet();
  285.         zSetOps.add(key, value, score);
  286.     }
  287.     public Set<String> getZsetValues(String key) throws Exception {
  288.         return null;
  289.     }
  290.     public void delZsetValues(String key, Object... values) throws Exception {
  291.         ZSetOperations<String, String> zSetOps = stringRedisTemplate
  292.                 .opsForZSet();
  293.         zSetOps.remove(key, values);
  294.     }
  295.     public String getHashByKey(String redisKey, String mapKey) throws Exception {
  296.         HashOperations<String, String, String> hashsetOps = stringRedisTemplate.opsForHash();
  297.         return hashsetOps.get(redisKey, mapKey);
  298.     }
  299.     public byte[] getHashByteByKey(String redisKey, String mapKey) throws Exception {
  300.         RedisConnection connection = null;
  301.         try {
  302.             connection = redisTemplate.getConnectionFactory().getConnection();
  303.             return connection.hGet(redisKey.getBytes(), mapKey.getBytes());
  304.         } catch (Exception e) {
  305.             throw new Exception(e);
  306.         } finally {
  307.             if (Objects.nonNull(connection) && !connection.isClosed()) {
  308.                 connection.close();
  309.             }
  310.         }
  311. //        HashOperations<String, String, byte[]> hashsetOps = stringRedisTemplate.opsForHash();
  312. //        return hashsetOps.get(redisKey, mapKey);
  313.     }
  314.     public Map<byte[], byte[]> getHashByte(String redisKey) throws Exception {
  315.         RedisConnection connection = null;
  316.         try {
  317.             connection = redisTemplate.getConnectionFactory().getConnection();
  318.             return connection.hGetAll(redisKey.getBytes());
  319.         } catch (Exception e) {
  320.             throw new Exception(e);
  321.         } finally {
  322.             if (Objects.nonNull(connection) && !connection.isClosed()) {
  323.                 connection.close();
  324.             }
  325.         }
  326.     }
  327.     public void addHashSet(String redisKey, String mapKey, String mapValue)
  328.             throws Exception {
  329.         stringRedisTemplate.opsForHash().put(redisKey, mapKey, mapValue);
  330.     }
  331.     public Set<String> getSet(String key) throws Exception {
  332.         SetOperations<String, String> setOperations = stringRedisTemplate.opsForSet();
  333.         return setOperations.members(key);
  334.     }
  335.     public void addSetValuesPipelined(final String[] keys, final String value) throws Exception {
  336.         stringRedisTemplate.executePipelined(new RedisCallback<Object>() {
  337.             @Override
  338.             public Object doInRedis(RedisConnection connection) {
  339.                 StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
  340.                 for (int i = 0; i < keys.length; i++) {
  341.                     stringRedisConn.sAdd(keys[i], value);
  342.                 }
  343.                 //必须返回null
  344.                 return null;
  345.             }
  346.         });
  347.     }
  348.     public void delSetValuesPipelined(final String[] keys, final String value) throws Exception {
  349.         stringRedisTemplate.executePipelined(new RedisCallback<Object>() {
  350.             @Override
  351.             public Object doInRedis(RedisConnection connection) {
  352.                 StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
  353.                 for (int i = 0; i < keys.length; i++) {
  354.                     stringRedisConn.sRem(keys[i], value);
  355.                 }
  356.                 //必须返回null
  357.                 return null;
  358.             }
  359.         });
  360.     }
  361.     public void delHashByKey(String redisKey, String mapKey) throws Exception {
  362.         HashOperations<String, String, String> hashMapOps = stringRedisTemplate.opsForHash();
  363.         hashMapOps.delete(redisKey, mapKey);
  364.     }
  365.     public Boolean hasKey(String key) throws Exception {
  366.         return stringRedisTemplate.hasKey(key);
  367.     }
  368.     /**
  369.      * 设置用户其他类的缓存
  370.      *
  371.      * @param key
  372.      * @param field   hash结构的field
  373.      * @param data    需要存的数据
  374.      * @param timeOut 超时时间
  375.      * @param unit    时间单位
  376.      */
  377.     public void setHashOther(String key, String field, String data, long timeOut, TimeUnit unit) {
  378.         stringRedisTemplate.opsForHash().put(key, field, data);
  379.         stringRedisTemplate.expire(key, timeOut, unit);
  380.     }
  381.     /**
  382.      * 返回用户的其他缓存
  383.      *
  384.      * @param key
  385.      * @param field hash结构的field
  386.      * @return String
  387.      * @throws Exception
  388.      */
  389.     public String getHashOther(String key, String field) throws Exception {
  390.         return this.getHashByKey(key, field);
  391.     }
  392.     /**
  393.      * 2019-2-20 changyandong 新增incr方法,设置过期时间
  394.      *
  395.      * @param key
  396.      * @param delta
  397.      * @param timeout
  398.      * @param unit
  399.      * @return
  400.      */
  401.     public Long increment(final String key, final int delta, final long timeout,
  402.                           final TimeUnit unit) {
  403.         if (timeout <= 0 || unit == null) {
  404.             return stringRedisTemplate.opsForValue().increment(key, delta);
  405.         }
  406.         List<Object> result = stringRedisTemplate
  407.                 .executePipelined(new SessionCallback<Object>() {
  408.                     @Override
  409.                     public <K, V> Object execute(
  410.                             RedisOperations<K, V> operations)
  411.                             throws DataAccessException {
  412.                         ValueOperations<K, V> ops = operations.opsForValue();
  413.                         ops.increment((K) key, delta);
  414.                         operations.expire((K) key, timeout, unit);
  415.                         return null;
  416.                     }
  417.                 });
  418.         return (Long) result.get(0);
  419.     }
  420.     /**
  421.      * 管道增加hash结构
  422.      */
  423.     public void addHashValuesPipelined(Map<String, Map<String, String>> keys) {
  424.         stringRedisTemplate.executePipelined((RedisCallback<Object>) connection -> {
  425.             StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
  426.             keys.forEach(stringRedisConn::hMSet);
  427.             //必须返回null
  428.             return null;
  429.         });
  430.     }
  431.     /**
  432.      * 管道增加hash结构  删除老hash结构
  433.      */
  434.     public void addHashValuesPipelinedRemoveOldHash(Map<String, Map<String, String>> keys) {
  435.         stringRedisTemplate.executePipelined((RedisCallback<Object>) connection -> {
  436.             StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
  437.             stringRedisConn.del(keys.keySet().toArray(new String[0]));
  438.             keys.forEach(stringRedisConn::hMSet);
  439.             //必须返回null
  440.             return null;
  441.         });
  442.     }
  443.     /**
  444.      * 分布式锁模板方法
  445.      *
  446.      * @param businessKey       业务key
  447.      * @param callbackFunction 回调方法
  448.      * @param s                回调方法具体入参
  449.      * @param <S>              回调方法入参类型
  450.      * @param <T>              回调方法返回值类型
  451.      * @return 回调方法返回值
  452.      */
  453.     public <S, T> T redisLockCallback(String businessKey, Function<S, T> callbackFunction, S s) {
  454.         try {
  455.             redisLock(businessKey);
  456.             return callbackFunction.apply(s);
  457.         } finally {
  458.             redisUnLock(businessKey);
  459.         }
  460.     }
  461.     public <T> T redisLockSupplier(String businessKey, Supplier<T> supplier) {
  462.         return redisLockSupplier(businessKey, supplier, lockMaxSeconds, LOCK_WAIT_MAX_TIME, TimeUnit.SECONDS);
  463.     }
  464.     public <T> T redisLockSupplier(String businessKey, Supplier<T> supplier, long lockMaxTime, long tryTimeout, TimeUnit timeUnit) {
  465.         try {
  466.             redisLock(businessKey, lockMaxTime, tryTimeout, timeUnit);
  467.             return supplier.get();
  468.         } finally {
  469.             redisUnLock(businessKey);
  470.         }
  471.     }
  472.     /**
  473.      * 获取锁(不等待,直接返回 是否获取到锁资源)
  474.      *
  475.      * @param businessKey 业务key
  476.      * @return 是否获取到锁资源
  477.      */
  478.     public boolean redisLockSuspend(String businessKey) {
  479.         return redisLockSuspend(businessKey, lockMaxSeconds, TimeUnit.SECONDS);
  480.     }
  481.     /**
  482.      *  获取锁(不等待,直接返回 是否获取到锁资源)
  483.      * @param businessKey 业务key
  484.      * @param lockMaxTime 锁占用时长
  485.      * @param timeUnit 时间单位
  486.      * @return 是否获取锁资源
  487.      */
  488.     public boolean redisLockSuspend(String businessKey, long lockMaxTime, TimeUnit timeUnit) {
  489.         String lockKey = generateLockKey(businessKey);
  490.         long finalLockMaxTime = timeUnit.toMillis(lockMaxTime);
  491.         //可重入锁判断
  492.         if (isReentrantLock(lockKey)) {
  493.             return Boolean.TRUE;
  494.         }
  495.         RedisCallback<Boolean> callback = (connection) -> connection.set(
  496.                 lockKey.getBytes(StandardCharsets.UTF_8),
  497.                 businessKey.getBytes(StandardCharsets.UTF_8),
  498.                 Expiration.milliseconds(finalLockMaxTime),
  499.                 RedisStringCommands.SetOption.SET_IF_ABSENT);
  500.         return stringRedisTemplate.execute(callback);
  501.     }
  502.     /**
  503.      * @param keyPrefix  redis锁 key前缀
  504.      * @param key        key
  505.      * @param tryTimeout 超时时间
  506.      * @param timeUnit   时间单位
  507.      * @return 是否获取到锁资源
  508.      */
  509.     @Deprecated
  510.     public boolean redisLock(String keyPrefix, String key, long lockMaxTime, long tryTimeout, TimeUnit timeUnit) {
  511.         String businessKey = getLockKey(keyPrefix, key);
  512.         return redisLock(businessKey, lockMaxTime, tryTimeout, timeUnit);
  513.     }
  514.     public boolean redisLock(String businessKey, long lockMaxTime, long tryTimeout, TimeUnit timeUnit) {
  515.         tryTimeout = System.currentTimeMillis() + timeUnit.toMillis(tryTimeout);
  516.         lockMaxTime = timeUnit.toMillis(lockMaxTime);
  517.         return redisLock(businessKey, lockMaxTime, tryTimeout);
  518.     }
  519.     /**
  520.      * 获取redis分布式锁 (默认超时时间)
  521.      *
  522.      * @param keyPrefix redis锁 key前缀
  523.      * @param key       key
  524.      * @return 是否获取到锁资源
  525.      */
  526.     @Deprecated
  527.     public boolean redisLock(String keyPrefix, String key) {
  528.         String businessKey = getLockKey(keyPrefix, key);
  529.         return redisLock(businessKey);
  530.     }
  531.     public boolean redisLock(String businessKey) {
  532.         long endTime = System.currentTimeMillis() + LOCK_WAIT_MAX_TIME;
  533.         long lockMaxTime = TimeUnit.SECONDS.toMillis(this.lockMaxSeconds);
  534.         return redisLock(businessKey, lockMaxTime, endTime);
  535.     }
  536.     /**
  537.      * 获取redis分布式锁 (默认超时时间)
  538.      * @param businessKey 业务key
  539.      * @param lockMaxTime 锁占用时长
  540.      * @param endTime 结束时间
  541.      * @return 是否获取到锁资源
  542.      */
  543.     private boolean redisLock(String businessKey, long lockMaxTime, long endTime) {
  544.         String lockKey = generateLockKey(businessKey);
  545.         logger.debug("redisLock businessKey:{},  lockKey:{}, lockMaxTime:{}, endTime:{}", businessKey, lockKey, lockMaxTime, endTime);
  546.         //可重入锁判断
  547.         if (isReentrantLock(lockKey)) {
  548.             logger.debug("redisLock lockKey:{}, threadName:{}, isReentrantLock true", lockKey, Thread.currentThread().getName());
  549.             return Boolean.TRUE;
  550.         }
  551.         RedisCallback<Boolean> callback = (connection) -> connection.set(
  552.                 lockKey.getBytes(StandardCharsets.UTF_8),
  553.                 businessKey.getBytes(StandardCharsets.UTF_8),
  554.                 Expiration.milliseconds(lockMaxTime),
  555.                 RedisStringCommands.SetOption.SET_IF_ABSENT);
  556.         //在timeout时间内仍未获取到锁,则获取失败
  557.         while (System.currentTimeMillis() < endTime) {
  558.             if (stringRedisTemplate.execute(callback)) {
  559.                 redisLockReentrant.set(lockKey);
  560.                 logger.debug("redisLock getKey  lockKey:{},  ", lockKey);
  561.                 return true;
  562.             }
  563.             try {
  564.                 Thread.sleep(100);
  565.             } catch (InterruptedException e) {
  566.                 logger.error("获取redis分布式锁出错", e);
  567.                 Thread.currentThread().interrupt();
  568.             }
  569.         }
  570.         logger.debug("redisLock meiyoukey  lockKey:{},  ", lockKey);
  571.         return false;
  572.     }
  573.     /**
  574.      * 释放分布式锁
  575.      *
  576.      * @param keyPrefix redis锁 key前缀
  577.      * @param key       key
  578.      */
  579.     @Deprecated
  580.     public Boolean redisUnLock(String keyPrefix, String key) {
  581.         String lockKey = getLockKey(keyPrefix, key);
  582.         return redisUnLock(lockKey);
  583.     }
  584.     public Boolean redisUnLock(String businessKey) {
  585.         String lockKey = generateLockKey(businessKey);
  586.         RedisCallback<Boolean> callback = (connection) -> connection.eval(
  587.                 RELEASE_LOCK_SCRIPT.getBytes(),
  588.                 ReturnType.BOOLEAN, 1,
  589.                 lockKey.getBytes(StandardCharsets.UTF_8),
  590.                 businessKey.getBytes(StandardCharsets.UTF_8));
  591.         //清空 ThreadLocal
  592.         redisLockReentrant.remove();
  593.         Boolean execute = stringRedisTemplate.execute(callback);
  594.         logger.debug("redisUnLock execute  lockKey:{},  ", lockKey);
  595.         return execute;
  596.     }
  597.     private String getLockKey(String keyPrefix, String key) {
  598.         return keyPrefix + "-" + key;
  599.     }
  600.     /**
  601.      * 是否为重入锁
  602.      */
  603.     private boolean isReentrantLock(String lockKey) {
  604.         String originValue = redisLockReentrant.get();
  605.         String redisValue = stringRedisTemplate.opsForValue().get(lockKey);
  606.         return StringUtils.isNotBlank(originValue) && originValue.equals(redisValue);
  607.     }
  608.     /**
  609.      * 生成规则要求的 key
  610.      *  xxx:redisLock:${appName}:${classSimpleName}:${methodName}:${businessKey}
  611.      * @param businessKey  业务key
  612.      * @return key
  613.      */
  614.     private String generateLockKey(String businessKey) {
  615.         StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
  616.         String classSimpleName = StringUtils.EMPTY;
  617.         String methodName = StringUtils.EMPTY;
  618.         for (StackTraceElement traceElement : stackTrace) {
  619.             String itemClassName = traceElement.getClassName();
  620.             //如果是当前类或者stack类 continue;
  621.             if (itemClassName.equals(this.getClass().getName()) || itemClassName.equals(StackTraceElement.class.getName())) {
  622.                 continue;
  623.             }
  624.             char[] cs=itemClassName.substring(itemClassName.lastIndexOf(".")+1).toCharArray();
  625.             cs[0]+=32;
  626.             //一直找,找到被spring管理的类。
  627.             Object target;
  628.             try {
  629.                 target = applicationContext.getBean(String.valueOf(cs));
  630.             } catch (NoSuchBeanDefinitionException e) {
  631.                 continue;
  632.             }
  633.             //如果是代理类,找到实际类
  634.             if (AopUtils.isAopProxy(target) && target instanceof Advised) {
  635.                 Advised advised = (Advised) target;
  636.                 try {
  637.                     target = advised.getTargetSource().getTarget();
  638.                 } catch (Exception e) {
  639.                     continue;
  640.                 }
  641.             }
  642.             if (Objects.nonNull(target)) {
  643.                 classSimpleName = target.getClass().getSimpleName();
  644.                 methodName = traceElement.getMethodName();
  645.                 break;
  646.             }
  647.         }
  648.         return REDIS_LOCK_KEY_PREFIX.concat(REDIS_NAMESPACE_PREFIX).concat(appName.toLowerCase())
  649.                 .concat(REDIS_NAMESPACE_PREFIX).concat(classSimpleName)
  650.                 .concat(REDIS_NAMESPACE_PREFIX).concat(methodName)
  651.                 .concat(REDIS_NAMESPACE_PREFIX).concat(businessKey);
  652.     }
  653. }
复制代码
2.4 将主动配置导入


在工程目次中创建 common-redis-lettuce/src/main/resources/META-INF/spring创建文件名为
org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件
文件内容:
  1. 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 的配置类,可以在该文件中添加以下内容:
  1. com.iceicepip.project.common.redis.CustomRedisConfig
复制代码
这样,在应用程序启动时,Spring Boot 会主动加载 CustomRedisConfig 类,并将其与主动配置归并,以提供完整的应用程序配置。
3. 利用方式

其中xxx 为在Spring Boot 配置文件中配置的多数据源的标识.如’user’、“iot”
  1.           @Autowired
  2.     @Qualifier("xxxRedis")
  3.     private CustomRedisService xxxRedisService;
  4.               @Autowired
  5.     @Qualifier("userRedis")
  6.     private CustomRedisService userRedisService;
复制代码
或者直接利用RedisTemplate 。
  1.         @Autowired
  2.     @Qualifier("userRedisTemplate")
  3.     private RedisTemplate  userRedisTemplate;
  4.           @Autowired
  5.     @Qualifier("xxxStringRedisTemplate")
  6.     private StringRedisTemplate  xxxStringRedisTemplate;
  7.           @Autowired
  8.     @Qualifier("xxxRedisTemplate")
  9.     private RedisTemplate   xxxRedisTemplate;
复制代码
4. 源码地点

https://github.com/wangshuai67/Redis-Tutorial-2023
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详解和利用示例》
    各人好,我是冰点,今天的Redis【实践篇】之SpringBoot Redis 多数据源集成支持哨兵模式和Cluster集群模式,全部内容就是这些。如果你有疑问或见解可以在批评区留言。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

李优秀

高级会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表