zoukankan      html  css  js  c++  java
  • spring boot:使用多个redis数据源(spring boot 2.3.1)

    一,什么情况下需要使用多个redis数据源?

    为了缓存数据,通常我们会在线上使用多个redis的cluster,

    每个cluster中缓存不同的数据,以方便管理.

    例如:我们缓存了杂志文章/商品信息/分类页面

    同时我们又使用一个redis cluster作为分布式session

    这里就会有多个redis数据源在项目中

    说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest

             对应的源码可以访问这里获取: https://github.com/liuhongdi/

    说明:作者:刘宏缔 邮箱: 371125307@qq.com

    二,演示项目说明:

    1,项目地址:

    https://github.com/liuhongdi/multiredissource

    2, 项目原理

        存储session使用redis集群,

        另外使用两个redis实例做缓存

       关于redis集群的搭建请参见:

    https://www.cnblogs.com/architectforest/p/13196749.html

    3, 项目结构:

    如图:

    三,项目配置文件说明:

    application.properties

    #default redis,for session
    spring.redis.cluster.nodes=172.17.0.2:6379,172.17.0.3:6379,172.17.0.4:6379,172.17.0.5:6379,172.17.0.6:6379,172.17.0.7:6379 spring.redis.cluster.max-redirects=3 spring.redis.password=lhddemo spring.redis.database=0 spring.session.store-type=redis spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=1 spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0 #redis1 spring.redis1.host=127.0.0.1 spring.redis1.port=6379 spring.redis1.password=lhddemo spring.redis1.database=0 spring.redis1.lettuce.pool.max-active=8 spring.redis1.lettuce.pool.max-wait=1 spring.redis1.lettuce.pool.max-idle=8 spring.redis1.lettuce.pool.min-idle=0 #redis2 spring.redis2.host=127.0.0.1 spring.redis2.port=6380 spring.redis2.password=lhddemo spring.redis2.database=0 spring.redis2.lettuce.pool.max-active=8 spring.redis2.lettuce.pool.max-wait=1 spring.redis2.lettuce.pool.max-idle=8 spring.redis2.lettuce.pool.min-idle=0

    说明:共3个redis,

            存储session的集群有6个节点(3主3从):

             172.17.0.2:6379,

             172.17.0.3:6379,

             172.17.0.4:6379,

             172.17.0.5:6379,

             172.17.0.6:6379,

             172.17.0.7:6379

            redis1是非集群结点:

            127.0.0.1:6379

            redis2是非集群结点:

            127.0.0.1:6380

    四,java代码说明

    1,RedisConfig.java

    @Configuration
    public class RedisConfig {
        @Bean
        @Primary
        public LettuceConnectionFactory redissessionLettuceConnectionFactory(RedisClusterConfiguration redisSessionRedisConfig,
                                                                       GenericObjectPoolConfig redisSessionPoolConfig) {
            LettuceClientConfiguration clientConfig =
                    LettucePoolingClientConfiguration.builder().commandTimeout(Duration.ofMillis(100))
                            .poolConfig(redisSessionPoolConfig).build();
            return new LettuceConnectionFactory(redisSessionRedisConfig, clientConfig);
        }
    
        @Bean
        public RedisTemplate<String, String> redisSessionTemplate(
                @Qualifier("redissessionLettuceConnectionFactory") LettuceConnectionFactory redissessionLettuceConnectionFactory) {
            RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(new StringRedisSerializer());
            //使用StringRedisSerializer来序列化和反序列化redis的ke
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(new StringRedisSerializer());
            //开启事务
            redisTemplate.setEnableTransactionSupport(true);
            redisTemplate.setConnectionFactory(redissessionLettuceConnectionFactory);
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
        @Bean
        @ConditionalOnBean(name = "redis1RedisConfig")
        public LettuceConnectionFactory redis1LettuceConnectionFactory(RedisStandaloneConfiguration redis1RedisConfig,
                                                                        GenericObjectPoolConfig redis1PoolConfig) {
            LettuceClientConfiguration clientConfig =
                    LettucePoolingClientConfiguration.builder().commandTimeout(Duration.ofMillis(100))
                            .poolConfig(redis1PoolConfig).build();
            return new LettuceConnectionFactory(redis1RedisConfig, clientConfig);
        }
    
        @Bean
        public RedisTemplate<String, String> redis1Template(
                @Qualifier("redis1LettuceConnectionFactory") LettuceConnectionFactory redis1LettuceConnectionFactory) {
            RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(new StringRedisSerializer());
            //使用StringRedisSerializer来序列化和反序列化redis的ke
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(new StringRedisSerializer());
            //开启事务
            redisTemplate.setEnableTransactionSupport(true);
            redisTemplate.setConnectionFactory(redis1LettuceConnectionFactory);
            redisTemplate.afterPropertiesSet();
    
            return redisTemplate;
        }
    
        @Bean
        @ConditionalOnBean(name = "redis2RedisConfig")
        public LettuceConnectionFactory redis2LettuceConnectionFactory(RedisStandaloneConfiguration redis2RedisConfig,
                                                                      GenericObjectPoolConfig redis2PoolConfig) {
            LettuceClientConfiguration clientConfig =
                    LettucePoolingClientConfiguration.builder().commandTimeout(Duration.ofMillis(100))
                            .poolConfig(redis2PoolConfig).build();
            return new LettuceConnectionFactory(redis2RedisConfig, clientConfig);
        }
    
        @Bean
        @ConditionalOnBean(name = "redis2LettuceConnectionFactory")
        public RedisTemplate<String, String> redis2Template(
                @Qualifier("redis2LettuceConnectionFactory") LettuceConnectionFactory redis2LettuceConnectionFactory) {
            RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
            redisTemplate.setValueSerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(new StringRedisSerializer());
            //使用StringRedisSerializer来序列化和反序列化redis的ke
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            //开启事务
            redisTemplate.setEnableTransactionSupport(true);
            redisTemplate.setConnectionFactory(redis2LettuceConnectionFactory);
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
        @Configuration
        public static class RedisSessionConfig {
            @Value("${spring.redis.cluster.nodes}")
            private String nodes;
            @Value("${spring.redis.cluster.max-redirects}")
            private Integer maxRedirects;
            @Value("${spring.redis.password}")
            private String password;
            @Value("${spring.redis.database}")
            private Integer database;
            
            @Value("${spring.redis.lettuce.pool.max-active}")
            private Integer maxActive;
            @Value("${spring.redis.lettuce.pool.max-idle}")
            private Integer maxIdle;
            @Value("${spring.redis.lettuce.pool.max-wait}")
            private Long maxWait;
            @Value("${spring.redis.lettuce.pool.min-idle}")
            private Integer minIdle;
    
            @Bean
            public GenericObjectPoolConfig redisSessionPoolConfig() {
                GenericObjectPoolConfig config = new GenericObjectPoolConfig();
                config.setMaxTotal(maxActive);
                config.setMaxIdle(maxIdle);
                config.setMinIdle(minIdle);
                config.setMaxWaitMillis(maxWait);
                return config;
            }
    
            @Bean
            public RedisClusterConfiguration redisSessionRedisConfig() {
                RedisClusterConfiguration config = new RedisClusterConfiguration();
                String[] sub = nodes.split(",");
                List<RedisNode> nodeList = new ArrayList<>(sub.length);
                String[] tmp;
                for (String s : sub) {
                    tmp = s.split(":");
                    nodeList.add(new RedisNode(tmp[0], Integer.valueOf(tmp[1])));
                }
                config.setClusterNodes(nodeList);
                config.setMaxRedirects(maxRedirects);
                config.setPassword(RedisPassword.of(password));
                return config;
            }
        }
    
        @Configuration
        public static class Redis1Config {
            @Value("${spring.redis1.host}")
            private String host;
            @Value("${spring.redis1.port}")
            private Integer port;
            @Value("${spring.redis1.password}")
            private String password;
            @Value("${spring.redis1.database}")
            private Integer database;
            @Value("${spring.redis1.lettuce.pool.max-active}")
            private Integer maxActive;
            @Value("${spring.redis1.lettuce.pool.max-idle}")
            private Integer maxIdle;
            @Value("${spring.redis1.lettuce.pool.max-wait}")
            private Long maxWait;
            @Value("${spring.redis1.lettuce.pool.min-idle}")
            private Integer minIdle;
    
            @Bean
            public GenericObjectPoolConfig redis1PoolConfig() {
                GenericObjectPoolConfig config = new GenericObjectPoolConfig();
                config.setMaxTotal(maxActive);
                config.setMaxIdle(maxIdle);
                config.setMinIdle(minIdle);
                config.setMaxWaitMillis(maxWait);
                return config;
            }
    
            @Bean
            public RedisStandaloneConfiguration redis1RedisConfig() {
                RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
                config.setHostName(host);
                config.setPassword(RedisPassword.of(password));
                config.setPort(port);
                config.setDatabase(database);
                return config;
            }
        }
    
        @Configuration
        @ConditionalOnProperty(name = "host", prefix = "spring.redis2")
        public static class Redis2Config {
            @Value("${spring.redis2.host}")
            private String host;
            @Value("${spring.redis2.port}")
            private Integer port;
            @Value("${spring.redis2.password}")
            private String password;
            @Value("${spring.redis2.database}")
            private Integer database;
    
            @Value("${spring.redis2.lettuce.pool.max-active}")
            private Integer maxActive;
            @Value("${spring.redis2.lettuce.pool.max-idle}")
            private Integer maxIdle;
            @Value("${spring.redis2.lettuce.pool.max-wait}")
            private Long maxWait;
            @Value("${spring.redis2.lettuce.pool.min-idle}")
            private Integer minIdle;
    
            @Bean
            public GenericObjectPoolConfig redis2PoolConfig() {
                GenericObjectPoolConfig config = new GenericObjectPoolConfig();
                config.setMaxTotal(maxActive);
                config.setMaxIdle(maxIdle);
                config.setMinIdle(minIdle);
                config.setMaxWaitMillis(maxWait);
                return config;
            }
    
            @Bean
            public RedisStandaloneConfiguration redis2RedisConfig() {
                RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
                config.setHostName(host);
                config.setPassword(RedisPassword.of(password));
                config.setPort(port);
                config.setDatabase(database);
                return config;
            }
        }
    }

    说明:

    分别构造了三个redisTemplate:

    redisSessionTemplate:访问redis session cluster

    redis1Template:访问redis1

    redis2Template:访问redis2

    因为要供session默认使用,
    所以给第一个LettuceConnectionFactory加上@Primary注解

    cluster的配置要使用RedisClusterConfiguration类,

    注意与:RedisStandaloneConfiguration区分

    2,CacheController.java

    @RestController
    @RequestMapping("/cache")
    public class CacheController {
    
        @Resource
        RedisTemplate<String, String> redis1Template;
        @Resource
        RedisTemplate<String, String> redis2Template;
    
        /*
        * get redis1 cache
        */
        @RequestMapping("/redis1get")
        public String redis1Get(HttpServletRequest request){
            String goodsname = redis1Template.opsForValue().get("goodsname1");
            return goodsname;
        }
    
        /*
         * write redis1 cache
         */
        @RequestMapping("/redis1set/{name}")
        public String redis1Set(@PathVariable String name) {
            //request.getSession().setAttribute("goods", name);
            redis1Template.opsForValue().set("goodsname1",name);
            return "ok";
        }
    
        /*
         * get redis2 cache
         * */
        @RequestMapping("/redis2get")
        public String redis2Get(HttpServletRequest request){
            String goodsname2 = redis2Template.opsForValue().get("goodsname2");
            return goodsname2;
        }
    
        /*
         * write redis2 cache
         * */
        @RequestMapping("/redis2set/{name}")
        public String redis2Set(@PathVariable String name) {
            //request.getSession().setAttribute("goods", name);
            redis2Template.opsForValue().set("goodsname2",name);
            return "ok";
        }
    }

    说明:对redis1和redis2分别读取和写入

    3,SessionController.java

    @RestController
    @RequestMapping("/session")
    public class SessionController {
        /*
        * read session
        * */
        @RequestMapping("/get")
        public Object getSession(HttpServletRequest request){
    
            Map<String, Object> map = new HashMap<>();
            map.put("sessionId", request.getSession().getId());
            map.put("user", request.getSession().getAttribute("user"));
            map.put("maxInactiveInterval", request.getSession().getMaxInactiveInterval());
            //map.put("ttl", request.getSession().);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String time = sdf.format(new Date(request.getSession().getCreationTime()));
            map.put("creationTime", time);
            return map;
        }
    
        /*
        * write session
        * */
        @RequestMapping("/set/{name}")
        public String setSession(@PathVariable String name, HttpServletRequest request) {
            request.getSession().setAttribute("user", name);
            return "ok";
        }
    }

    说明:对session的读取和写入

    五,多redis数据源效果测试

    1,查看三个redis的数据:

    redissession

    [root@redis4 /]# /usr/local/soft/redis-6.0.5/bin/redis-cli -a lhddemo -c --cluster call 172.17.0.2:6379 keys *
    Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
    >>> Calling keys *
    172.17.0.2:6379: 
    172.17.0.4:6379: 
    172.17.0.3:6379: 
    172.17.0.6:6379: 
    172.17.0.7:6379: 
    172.17.0.5:6379: 

    redis1:

    127.0.0.1:6379> keys *
    (empty list or set)

    redis2:

    127.0.0.1:6380> keys *
    (empty list or set)

    2,测试session:

    访问:http://127.0.0.1:8080/session/set/thislaoliu

    设置一个session值为thislaoliu:

    访问:http://127.0.0.1:8080/session/get

    查询我们设置的session值是否生效?

    从redis的控制台检查写入情况:

    [root@redis4 /]# /usr/local/soft/redis-6.0.5/bin/redis-cli -a lhddemo -c --cluster call 172.17.0.2:6379 keys *
    Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
    >>> Calling keys *
    172.17.0.2:6379: spring:session:sessions:expires:97a889f8-7122-4248-a5fc-4e559cb429e5
    172.17.0.4:6379: spring:session:sessions:97a889f8-7122-4248-a5fc-4e559cb429e5
    172.17.0.3:6379: spring:session:expirations:1593321720000
    172.17.0.6:6379: spring:session:sessions:expires:97a889f8-7122-4248-a5fc-4e559cb429e5
    172.17.0.7:6379: spring:session:expirations:1593321720000
    172.17.0.5:6379: spring:session:sessions:97a889f8-7122-4248-a5fc-4e559cb429e5

    我们连接到172.17.0.4,查询session的值:

    [root@redis3 /]# /usr/local/soft/redis-6.0.5/bin/redis-cli -c -h 172.17.0.4

    看session的内容:

    172.17.0.4:6379> hgetall spring:session:sessions:97a889f8-7122-4248-a5fc-4e559cb429e5
    1) "sessionAttr:user"
    2) "xacxedx00x05tx00
    thislaoliu"
    3) "creationTime"
    4) "xacxedx00x05srx00x0ejava.lang.Long;x8bxe4x90xccx8f#xdfx02x00x01Jx00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x01rxf9CWxb7"
    5) "maxInactiveInterval"
    6) "xacxedx00x05srx00x11java.lang.Integerx12xe2xa0xa4xf7x81x878x02x00x01Ix00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00a"
    7) "lastAccessedTime"
    8) "xacxedx00x05srx00x0ejava.lang.Long;x8bxe4x90xccx8f#xdfx02x00x01Jx00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x01rxf9D)n"

    可以看到user的值是我们所设置的:thislaoliu

    3,测试redis1

    访问:http://127.0.0.1:8080/cache/redis1set/cup1

    设置一个key,值为:cup1

    访问:http://127.0.0.1:8080/cache/redis1get

    返回我们设置的值:

    连接到redis控制台:

    [liuhongdi@localhost ~]$ /usr/local/soft/redis/bin/redis-cli

    从redis控制台查看kv

    127.0.0.1:6379> get goodsname1
    "cup1"

    4,测试redis2

    访问:http://127.0.0.1:8080/cache/redis2set/phone1

    设置一个key,值为:phone1

     

    访问:http://127.0.0.1:8080/cache/redis2get

    返回我们设置的值:

    连接到redis控制台

    [root@localhost etc]# /usr/local/soft/redis/bin/redis-cli -p 6380

    从控制台查看值

    127.0.0.1:6380> get goodsname2
    "phone1"

    六,查看spring boot的版本

      .   ____          _            __ _ _
     /\ / ___'_ __ _ _(_)_ __  __ _    
    ( ( )\___ | '_ | '_| | '_ / _` |    
     \/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.3.1.RELEASE)
  • 相关阅读:
    李时珍的皮肤衣【快速幂】
    ClickHouse深度解析
    Hadoop&HDFS知识点整理
    实时日志数据写入Clickhouse
    Kafka consumer Job异常重置offset
    Flink WaterMark原理与实现
    Spark定期合并Hive表小文件
    Flink 1.11 Table & SQL深度解读
    Docker 安装Elasticsearch、Kibana实战——避免踩坑
    Flink 异步IO实践
  • 原文地址:https://www.cnblogs.com/architectforest/p/13202550.html
Copyright © 2011-2022 走看看