SpringBoot--整合Lettuce redis&哨兵&集群

一、SpringBoot--整合Lettuce redis

首先解释一下Lettuce客户端:

  Lettuce 和 Jedis 的都是连接Redis Server的客户端程序。Jedis在实现上是直连redis server,多线程环境下非线程安全,除非使用连接池,为每个Jedis实例增加物理连接。Lettuce基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问,同时它是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。

1、添加依赖

        <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>

2、添加redis配置

spring:
  redis:
      host: ****
      password:****
      port: 6379
  # 连接超时时间(毫秒)
      timeout: 1000
  # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
      database: 0
  # 连接池配置
      lettuce:
        pool:
  # 连接池最大连接数(使用负值表示没有限制) 默认 8
          max-active: 8
  # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
          max-wait: -1
  # 连接池中的最大空闲连接 默认 8
          max-idle: 8
  # 连接池中的最小空闲连接 默认 0
          min-idle: 0

3、实现逻辑

@Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public String testRedis(){

        ExecutorService executorService = Executors.newFixedThreadPool(1000);
        IntStream.range(0, 1000).forEach(i -> executorService.execute(() -> stringRedisTemplate.opsForValue().increment("lcl",1)));

        System.out.println("lcl1=============" + stringRedisTemplate.opsForValue().get("lcl"));

        stringRedisTemplate.opsForValue().set("lcl1","val1");

        String val1 = stringRedisTemplate.opsForValue().get("lcl1");

        System.out.println("lcl1=============" + val1);

        String key = "redis:test:demo1";


        User user = new User();
        user.setId(100L);
        user.setUsername("u2");
        user.setPassword("p2");
        stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(user));

        String valUser = stringRedisTemplate.opsForValue().get(key);
        System.out.println("redis:test:demo1=============" + valUser);

        User getUser = JSON.parseObject(valUser, User.class);

        System.out.println("redis:test:demo1=============" + getUser.getUsername()+ "========" + getUser.getPassword());

        return null;
    }

测试结果:

 

 

 

 

 

 由于redis有String、list、set、zset、hash、geo等类型,因此使用时不止使用opsForValue()方法,具体的对应方法如下:

    opsForValue: 对应 String(字符串)

    opsForZSet: 对应 ZSet(有序集合)

    opsForHash: 对应 Hash(哈希)

    opsForList: 对应 List(列表)

    opsForSet: 对应 Set(集合)

    opsForGeo: 对应 GEO(地理位置)

二、Luttuce连接哨兵

  Luttuce连接哨兵,主要是需要做哨兵的配置以及CacheManager、RedisTemplate、RedisConnectionFactory这三个Bean实例。

  1、哨兵配置:主要是配置哨兵集合和master节点名称

spring:
  redis:
      password:
  # 连接超时时间(毫秒)
      timeout: 1000
  # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
      database: 0
  # 连接池配置
      lettuce:
      #lettuce连接池
        pool:
  # 连接池最大连接数(使用负值表示没有限制) 默认 8
          max-active: 8
  # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
          max-wait: -1
  # 连接池中的最大空闲连接 默认 8
          max-idle: 8
  # 连接池中的最小空闲连接 默认 0
          min-idle: 0
          #哨兵增加的配置
      sentinel:
        nodes: 8.131.245.53:26379,8.131.245.53.234:26380,8.131.245.53:26381
        master: mymaster

  ###################以下为springcache增加的配置###########################
  cache:
    redis:
      use-key-prefix: true
      key-prefix: dev
      cache-null-values: false
      time-to-live: 20s

  2、配置哨兵(Bean:RedisConnectionFactory)

  主要是使用上一步的配置信息,创建一个Luttuce的连接工厂,其中有连接池相关的配置,同时有哨兵相关的配置。

@Configuration
@ConfigurationProperties(prefix = "spring.redis.sentinel")
@Data
public class RedisSentinelConfig {
    private Set<String> nodes;
    private String master;

    @Value("${spring.redis.timeout}")
    private long timeout;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.lettuce.pool.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.lettuce.pool.min-idle}")
    private int minIdle;
    @Value("${spring.redis.lettuce.pool.max-wait}")
    private long maxWait;
    @Value("${spring.redis.lettuce.pool.max-active}")
    private int maxActive;


    @Bean
    public RedisConnectionFactory lettuceConnectionFactory() {
        RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration(master, nodes);
        redisSentinelConfiguration.setPassword(RedisPassword.of(password.toCharArray()));
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxIdle(maxIdle);
        genericObjectPoolConfig.setMinIdle(minIdle);
        genericObjectPoolConfig.setMaxTotal(maxActive);
        genericObjectPoolConfig.setMaxWaitMillis(maxWait);
        LettucePoolingClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
                .poolConfig(genericObjectPoolConfig)
                .build();
        return new LettuceConnectionFactory(redisSentinelConfiguration, lettuceClientConfiguration);
    }
}

  3、数据源配置(Bean:CacheManager)

  主要是根据上一步配置来创建一个RedisCacheManager,其实主要就是使用redis来作为缓存。同时将查询缓存转换异常的问题做了解决,并改变了序列化方式。

/*
 * 修改 CacheManager 接口,将默认序列化改为JSON序列化,并使用redis作为缓存
 * */
@Configuration
@EnableCaching//开启缓存
@ConfigurationProperties(prefix = "spring.cache.redis")
public class SpringCacheRedisConfig {

    private Duration timeToLive = Duration.ZERO;
    public void setTimeToLive(Duration timeToLive) {
        this.timeToLive = timeToLive;
    }

    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        //创建Json序列化对象
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // 将默认序列化改为Jackson2JsonRedisSerializer序列化
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(timeToLive)
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        // 使用redis作为缓存,进入 CacheManager 接口按Control+H查看 CacheManager 的实现类,其中 RedisCacheManager 与redis有关
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

  4、Redis模板配置(Bean:RedisTemplate)

  这里仍然是使用第2步中的连接工厂对象,来创建一个RedisTemplate对象

@Configuration
public class RedisTemplateConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
        //创建Json序列化对象
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);

        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // 将默认序列化改为Jackson2JsonRedisSerializer序列化
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setKeySerializer(jackson2JsonRedisSerializer);// key序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
        template.setHashKeySerializer(jackson2JsonRedisSerializer);// Hash key序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化
        template.setConnectionFactory(factory);
        template.afterPropertiesSet();
        return template;
    }
}

  5、测试

  至此,使用Springboot+Luttuce+sentinel的缓存框架已经搭建完毕,使用时,仍然与单机版一致

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public void testRedis(){

        stringRedisTemplate.opsForValue().set("lcl1","val1");

        String val1 = stringRedisTemplate.opsForValue().get("lcl1");

        System.out.println("lcl1=============" + val1);

    }

  但是有个问题需要注意:

  (1)由于使用哨兵模式不需要在配置文件中配置redis的host,只需要配置哨兵集合及监控master的名称,在使用redis时,redis会从哨兵中获取master名称对应的host,因此当matser切换时,我们仍然可以直接访问新的master,而不需要调整代码。

  (2)有时我们只是使用的单机服务器模拟的哨兵,因此哨兵中监控的master配置ip为127.0.0.1,如果这样,实际上在代码中,其也是直接访问的127.0.0.1,那么就会造成项目虽然可以启动成功,但是redis不能访问的情况。

三、Luttuce连接集群

  和使用哨兵一致,需要配置集群以及CacheManager、RedisTemplate、RedisConnectionFactory这三个Bean实例。

  其中CacheManager、RedisTemplate完全一致,所以这里主要写一下集群的配置参数和RedisConnectionFactory的创建

  

spring:
  redis:

      password:

  # 连接超时时间(毫秒)
      timeout: 1000
  # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
      database: 0
  # 连接池配置
      lettuce:
      #lettuce连接池
        pool:
  # 连接池最大连接数(使用负值表示没有限制) 默认 8
          max,active: 8
  # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 ,1
          max,wait: -1
  # 连接池中的最大空闲连接 默认 8
          max,idle: 8
  # 连接池中的最小空闲连接 默认 0
          min,idle: 0
      cluster:
        nodes: 8.131.245.53:8001, 8.131.245.53:8002, 8.131.245.53:8003, 8.131.245.53:8004, 8.131.245.53:8005, 8.131.245.53:8006
        # 获取失败 最大重定向次数
        max,redirects: 3
    @Bean
    public LettuceConnectionFactory myLettuceConnectionFactory() {
        Map<String, Object> source = new HashMap<String, Object>();
        source.put("spring.redis.cluster.nodes", environment.getProperty("spring.redis.cluster.nodes"));
        source.put("spring.redis.cluster.timeout", environment.getProperty("spring.redis.cluster.timeout"));
        source.put("spring.redis.cluster.max-redirects", environment.getProperty("spring.redis.cluster.max-redirects"));
        RedisClusterConfiguration redisClusterConfiguration;
        redisClusterConfiguration = new RedisClusterConfiguration(new MapPropertySource("RedisClusterConfiguration", source));
        return new LettuceConnectionFactory(redisClusterConfiguration);
    }

 

posted @ 2019-10-17 21:15  李聪龙  阅读(2088)  评论(0编辑  收藏  举报