• SpringBoot集成Redis主从架构实现读写分离(哨兵模式)


    一、前言

    这里会使用到spring-boot-starter-data-redis包,spring boot 2的spring-boot-starter-data-redis中,默认使用的是lettuce作为redis客户端,也推荐使用lettuce,Redis使用哨兵集群,这里会通过lettuce连接到哨兵获取对应Redis节点地址从而操作Redis。

    Redis主从复制哨兵模式部署:https://blog.csdn.net/weixin_44606481/article/details/133990331

    二、集成配置

    工程结构
    在这里插入图片描述

    2.1、POM

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.12.RELEASE</version>
        </parent>
        
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <!--springboot中的redis依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <!-- lettuce pool 缓存连接池-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
            </dependency>
            <!-- 使用jackson作为redis数据序列化 -->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.11.4</version>
            </dependency>
    		<!-- SpringBoot测试包 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2.2、添加配置文件application.yml

    因为我们用的spring-boot-starter-data-redis包会自动配置redis连接,在配置文件中添加对应配置即可

    spring:
      #redis配置信息
      redis:
        sentinel:
          # 主节点名称 在哨兵配置文件中配置的
          master: mymaster
          # 哨兵ip地址和端口 有多个哨兵就填多个,只填一个其实也能使用
          nodes:
            - 10.0.20.13:26379
            - 10.0.20.13:26380
            - 10.0.20.13:26381
        ## Redis数据库索引(默认为0)
        database: 0
        ## Redis服务连接密码(默认为空)
        password: 123456
        ## 连接超时时间(毫秒)
        timeout: 5000
        lettuce:
          pool:
            ## 连接池最大连接数(使用负值表示没有限制)
            max-active: 8
            ## 连接池最大阻塞等待时间(使用负值表示没有限制)
            max-wait: -1
            ## 连接池中的最大空闲连接
            max-idle: 8
            ## 连接池中的最小空闲连接
            min-idle: 1
          shutdown-timeout: 1000ms
    
    # 打印lettuce debug日志,方便查看读写分离效果
    logging:
      pattern:
        console: '%date{yyyy-MM-dd HH:mm:ss.SSS} | %highlight(%5level) [%green(%16.16thread)] %clr(%-50.50logger{49}){cyan} %4line -| %highlight(%msg%n)'
      level:
        root: info
        io.lettuce.core: debug
        org.springframework.data.redis: debug
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    2.3、编写配置文件

    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import io.lettuce.core.ReadFrom;
    import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.RedisSentinelConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
    import org.springframework.data.redis.core.*;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import java.util.HashSet;
    
    @Configuration
    public class RedisConfig{
    
        /**
         * 配置读写分离
         */
        @Bean
        public RedisConnectionFactory lettuceConnectionFactory(RedisProperties redisProperties) {
            // 配置哨兵节点以及主节点
            RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration(
                    redisProperties.getSentinel().getMaster(), new HashSet<>(redisProperties.getSentinel().getNodes())
            );
    
            // 配置读写分离
            LettucePoolingClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
                    .poolConfig(getPoolConfig(redisProperties.getLettuce().getPool()))
                    // ReadFrom.REPLICA_PREFERRED 优先读取从节点,如果从节点不可用,则读取主节点
                    .readFrom(ReadFrom.REPLICA_PREFERRED)
                    .build();
    
            return new LettuceConnectionFactory(redisSentinelConfiguration, lettuceClientConfiguration);
        }
        private GenericObjectPoolConfig<?> getPoolConfig(RedisProperties.Pool properties) {
            GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();
            config.setMaxTotal(properties.getMaxActive());
            config.setMaxIdle(properties.getMaxIdle());
            config.setMinIdle(properties.getMinIdle());
            if (properties.getTimeBetweenEvictionRuns() != null) {
                config.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRuns().toMillis());
            }
            if (properties.getMaxWait() != null) {
                config.setMaxWaitMillis(properties.getMaxWait().toMillis());
            }
            return config;
        }
    
        /**
         * retemplate相关配置,配置自定义序列化规则为jackson
         */
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
            RedisTemplate<String, Object> template = new RedisTemplate<>();
            // 配置连接工厂
            template.setConnectionFactory(factory);
            //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
            Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jacksonSeial.setObjectMapper(om);
            // 值采用json序列化
            template.setValueSerializer(jacksonSeial);
            //使用StringRedisSerializer来序列化和反序列化redis的key值
            template.setKeySerializer(new StringRedisSerializer());
            // 设置hash key 和value序列化模式
            template.setHashKeySerializer(new StringRedisSerializer());
            template.setHashValueSerializer(jacksonSeial);
            template.afterPropertiesSet();
            return template;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    2.4、编写启动类

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class LettuceApplication {
        public static void main(String[] args) {
            SpringApplication.run(LettuceApplication.class);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.5、编写测试类测试是否连接成功以及读写分离效果

    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(classes =  LettuceApplication.class)
    public class LettuceTest {
        @Autowired
        private RedisTemplate<String,Object> redisTemplate;
        @Test
        public void t1(){
            String key = "key1";
            System.out.println("插入数据到redis");
            redisTemplate.opsForValue().set(key,"value1");
            Object value = redisTemplate.opsForValue().get(key);
            System.out.println("从redis中获取到值为 "+value);
            Boolean delete = redisTemplate.delete(key);
            System.out.println("删除redis中值 "+delete);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
  • 相关阅读:
    后端面试话术集锦第 十六 篇:java锁面试话术
    独立站卖家投放Facebook广告的六大常见误区
    【Unity】Unity C#基础(十四)注释
    Redis 中的原子操作(3)-使用Redis实现分布式锁
    1200*A. Trust Nobody(贪心)
    【Leetcode】1246. Palindrome Removal
    LeetCode每日一题——324. 摆动排序 II
    Dart(2)-变量
    揭秘newSingleThreadExecutor:深度解析与源码探秘
    小功能⭐️Unity静态方法拓展写法
  • 原文地址:https://blog.csdn.net/weixin_44606481/article/details/134017117