Java使用Redis

Java使用Redis有三种技术分别是:Jedis、lettuce和RedisTemplate,其中目前主流的是RedisTemplate,其它两个面临被淘汰

一、Jedis

Jedis是Redis官方推荐的Java连接开发工具,创建springboot项目,直接导入 maven 坐标,如下pom.xml所示:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.9</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.redis7</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <jedis.version>4.3.1</jedis.version>
        <lettuce-core.version>6.2.3.RELEASE</lettuce-core.version>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
    </properties>

    <dependencies>
        <!--springboot通用依赖模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>${jedis.version}</version>
        </dependency>

        <!--通用基础配置-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

设置application.yml文件

server:
  port: 7777

spring:
  application:
    name: com.redis7

引入Redis 的驱动程序后,接下来就是连接 Redis 服务器。最简单的连接方式,就是通过 Jedis 对象连接。在项目创建 Demo.jedisDemo 类代码如下:

package com.redis7.demo.Demo;

import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;

import java.util.List;

@Slf4j
public class JedisDemo {
    public static void main(String[] args) {


        // 创建一个jedis连接对象
        Jedis jedis = new Jedis("192.168.42.132", 6379);

        // 指定连接的密码
        jedis.auth("123456");

        log.info("redis conn status:{}","连接成功");
        log.info("redis ping retvalue:{}",jedis.ping());

        //设置一个字符串
        jedis.set("k9090", "jedis");
        log.info("k1 value:{}",jedis.get("k9090"));

        //列表
        jedis.lpush("names", "jery");
        jedis.lpush("names", "augus");
        jedis.lpush("names", "tom");

        // 获取存储的数据并输出
        List<String> list_name = jedis.lrange("names", 0, 2);
        for(int i=0;i<list_name.size();i++){
            System.out.println("list value:{}"+list_name.get(i));
        }

    }
}

二、Lettuce

Lettuce是一个RedisJava驱动包,初识她的时候是使用RedisTemplate的时候遇到点问题Debug到底层的一些源码,发现spring-data-redis的驱动包在某个版本以后替换为LettuceLettuce翻译为生菜,没错,就是吃的那种生菜,因此它的Logo长这样

Lettuce简介

Lettuce是一个高性能基于Java编写的Redis驱动框架,底层集成了Project Reactor提供自然的反应式编程,通讯框架集成了Netty使用了非阻塞IO5.x版本以后融合了JDK1.8的异步编程特性,在保证高性能的同时提供了十分丰富易用的API5.1版本的新特性以下:node

  • 支持Redis的新增命令ZPOPMIN, ZPOPMAX, BZPOPMIN, BZPOPMAX
  • 支持经过Brave模块跟踪Redis命令执行。
  • 支持Redis Streams
  • 支持异步的主从链接。
  • 支持异步链接池。
  • 新增命令最多执行一次模式(禁止自动重连)。
  • 全局命令超时设置(对异步和反应式命令也有效)。
  • ......等等

注意一点:Redis的版本至少须要2.6,固然越高越好,API的兼容性比较强大。

只须要引入单个依赖就能够开始使用Lettuce

<!-- https://mvnrepository.com/artifact/io.lettuce/lettuce-core -->
<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>6.2.3.RELEASE</version>
</dependency>

连接Redis

单机、哨兵、集群模式下链接Redis须要一个统一的标准去表示链接的细节信息,在Lettuce中这个统一的标准是RedisURI。案例如下:

package com.redis7.demo.Demo;

import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.SortArgs;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class LettuceDemp {
    public static void main(String[] args) {
        //使用构建器redisURI.builder
        RedisURI aDefault = RedisURI.Builder
                .redis("192.168.42.132")
                .withPort(6379)
                .withAuthentication("default", "123456")
                .build();

        //创建连接客户端
        RedisClient redisClient = RedisClient.create(aDefault);

        //创建客户端连接对象
        StatefulRedisConnection<String, String> connect = redisClient.connect();

        //操作命令api
        RedisCommands<String, String> sync = connect.sync();

        //获取所有的key
        List<String> list = sync.keys("*");

        for (String k : list) {
            System.out.println("keys:"+k);
        }

        //创建str
        sync.set("k12", "v12");
        //获取值
        String k12 = sync.get("k12");
        System.out.println("k12的值:"+k12);

        //创建list
        sync.lpush("myList", "100","200","300");

        //获取值
        List<String> myList = sync.lrange("myList", 0, -1);
        for (String s : myList) {
            System.out.println("list-------:"+s);
        }

        //创建set
        sync.sadd("mySet", "v1", "v2", "v3");

        Set<String> mySet = sync.smembers("mySet");
        for (String s : mySet) {
            System.out.println("set-------:"+s);
        }

        //创建hash
        HashMap<String, String> map = new HashMap<>();
        map.put("k20", "v20");
        map.put("k21", "v21");
        map.put("k22", "v22");

        //添加键为myHash 值为map
        sync.hmset("myHash", map);

        //查看值
        Map<String, String> myHash = sync.hgetall("myHash");
        for (String k : myHash.keySet()) {
            System.out.println("hash :"+myHash.get(k));
        }

        //创建zet
        sync.zadd("myZset",100.0,"s1",110.0,"s2",90.0,"s3");
        List<String> myZset = sync.zrange("myZset", 0, 10);

        for (String s : myZset) {
            System.out.println("zset-------:"+s);
        }

        //sort
        SortArgs sortArgs = new SortArgs();
        sortArgs.alpha();
        sortArgs.desc();

        List<String> myList1 = sync.sort("myList", sortArgs);
        for (String s : myList1) {
            System.out.println("sort ----------:"+s);
        }

        //关闭
        connect.close();
        redisClient.shutdown();
    }
}

三、RedisTemplate

RedisTemplate是Redis模块的中心类,它为与Redis的交互提供了一个高级抽象。RedisConnection提供低级方法,接收和返回二进制数组,RedisTemplate则负责序列化和连接管理,使用户不用处理这些细节。官方文档中,RedisTemplate定义为:performs automatic serialization/deserialization between the given objects and the underlying binary data in the Redis Store。

RedisTemplate一旦配置好,就是线程安全的,可供多个实例重用。

RedisTemplate的大多数操作都使用基于Java的序列化程序。这意味着它读写的任何对象都通过Java进行序列化和反序列化。你可以更改序列化机制(org.springframework.data.redis.serializer中提供了几种实现)。RedisCache和RedisTemplate默认的情况下使用JdkSerializationRedisSerializer。对于JSON格式的数据,可以使用Jackson2JsonRedisSerializer或者GenericJackson2JsonRedisSerialize。对于String密集型操作,可以考虑StringRedisTemplate。

启动一台redis主机,并且需要在防火墙开放其端口,否则将会连接出错,切记!!!

3.1.基础环境搭建

1).创建一个springboot项目:redis7_cloud

设置项目名称

2).在pom.xml中引入依赖

内容如下:我这里使用的springboot2.6.10,在使用2.7.9版本的时候有bug,所以建议版本保持一致

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.redis7</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <swagger.version>2.9.2</swagger.version>
    </properties>

    <dependencies>
        <!--springboot通用依赖模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--SpringBoot与Redis整合依赖-->
        <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>
        <!--swagger2-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>

        <!--通用基础配置-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3).添加配置内容

在application.properties中添加如下内容:

server.port=7777
spring.application.name=redis7_study

# ========================logging=====================
logging.level.root=info
logging.level.com.atguigu.redis7=info
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger- %msg%n 

logging.file.name=D:/mylogs2023/redis7_study.log
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger- %msg%n

# ========================swagger=====================
spring.swagger2.enabled=true
#在springboot2.6.X结合swagger2.9.X会提示documentationPluginsBootstrapper空指针异常,
#原因是在springboot2.6.X中将SpringMVC默认路径匹配策略从AntPathMatcher更改为PathPatternParser,
# 导致出错,解决办法是matching-strategy切换回之前ant_path_matcher
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

# ========================redis单机=====================
spring.redis.database=0
# 修改为自己真实IP
spring.redis.host=192.168.42.132
spring.redis.port=6379
spring.redis.password=123456
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0

4).创建config层

创建 config.SwaggerConfig 类,进行swagger的配置:

package com.redis7.demo.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Configuration
@EnableSwagger2 //开启swagger功能
public class SwaggerConfig{
    @Value("${spring.swagger2.enabled}")
    private Boolean enabled;

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(enabled)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.redis7.demo")) //你自己的package
                .paths(PathSelectors.any())
                .build();
    }
    public ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("springboot利用swagger2构建api接口文档 "+"\t"+ DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDateTime.now()))
                .description("springboot项目整合")
                .version("1.0")
                .termsOfServiceUrl("https://www.augus.com/")
                .build();
    }
}

5).创建service层

创建 service.OrderService 类代码如下:

package com.redis7.demo.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

@Service
@Slf4j
public class OrderService {
    public static final String ORDER_NUMBER = "order";

    @Resource
    private RedisTemplate redisTemplate;

    //创建订单
    public void CreateOrder(){
        //模拟订单id
        int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;

        //模拟订单编号
        String orderId = UUID.randomUUID().toString();

        redisTemplate.opsForValue().set(ORDER_NUMBER+keyId,"订单:"+orderId);

        log.info("=============>编号"+keyId+"的订单流水号:{}",orderId);
    }

    //根据id查询
    public String getOrderById(Integer id){
        return (String) redisTemplate.opsForValue().get(ORDER_NUMBER+id);
    }
}

6).创建controller层

创建 controller.OrderController 类代码如下:

package com.redis7.demo.controller;

import com.redis7.demo.service.OrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@Api(tags = "订单接口")
@RestController
@Slf4j
public class OrderController {
    @Resource
    private OrderService orderService;

    @ApiOperation("新增订单")
    @PostMapping(value = "/oreder/add")
    public void createOrder()
    {
        orderService.CreateOrder();
    }

    @ApiOperation("根据Id查询订单")
    @GetMapping(value = "/oreder/{id}")
    public String getOrderById(@PathVariable Integer id){
        return orderService.getOrderById(id);
    }
}

7).测试

启动项目,浏览器访问:http://localhost:7777/swagger-ui.html#/,如下出现swagger的的接口:

现在执行创建订单流水的接口

这时候查看结果:

3.2.序列化问题处理

出现上节的问题主要是因为 RedisTemplate,序列化对应的是defaultSerializer,默认采用的是JDK 序列化方式

1).方式一:使用 StringRedisTemplate的子类

在service层之前使用的是RedisTemplate,现在可以采用 StringRedisTemplate 子类,StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。如下:

所以如上所示,只需要将service层的代码修改如下(将RedisTemplate替换为StringRedisTemplate ):

package com.redis7.demo.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

@Service
@Slf4j
public class OrderService {
    public static final String ORDER_NUMBER = "order";

//    @Resource
//    private RedisTemplate redisTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    //创建订单
    public void CreateOrder(){
        //模拟订单id
        int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;

        //模拟订单编号
        String orderId = UUID.randomUUID().toString();

        stringRedisTemplate.opsForValue().set(ORDER_NUMBER+keyId,"订单:"+orderId);

        log.info("=============>编号"+keyId+"的订单流水号:{}",orderId);
    }

    //根据id查询
    public String getOrderById(Integer id){
        return stringRedisTemplate.opsForValue().get(ORDER_NUMBER+id);
    }
}

重新启动项目,然后再次通过swagger发出接口请求

查看创建的数据:

会发现key虽然不乱码了,但是在客户端查看key对应的值依然乱码,这个只是客户端显示的问题,只需要在登录的时候添加上 参数:--raw

redis-cli -a 123456 --raw

如下图所示,再次查看不在乱码:

 

 

2).方式二:修改 RedisTemplate的序列化策略

RedisTemplate,序列化对应的是defaultSerializer,默认采用的是JDK 序列化方式,所有只需要修改其序列表策略即可,

在config下创建 RedisConfig 配置类,代码如下,替换掉序列化方式即可

package com.redis7.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    /**
     * redis序列化的工具配置类,下面这个请一定开启配置
     * 127.0.0.1:6379> keys *
     * 1) "ord102"  序列化过
     * 2) "\xac\xed\x00\x05t\x00\aord102"   没有序列化过
     * this.redisTemplate.opsForValue(); //提供了操作string类型的所有方法
     * this.redisTemplate.opsForList(); // 提供了操作list类型的所有方法
     * this.redisTemplate.opsForSet(); //提供了操作set的所有方法
     * this.redisTemplate.opsForHash(); //提供了操作hash表的所有方法
     * this.redisTemplate.opsForZSet(); //提供了操作zset的所有方法
     * @param lettuceConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();

        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        //设置key序列化方式string
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

service层依然使用RedisTemplate,重启项目,然后执行swagger,如下:

查看生成的值

3.3.RedisTemplate和StringRedisTemplate的区别

  • 两者的关系是StringRedisTemplate继承RedisTemplate。RedisTemplate是一个泛型类,而StringRedisTemplate则不是。
  • 两者序列化策略不同,
  • StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。
  • RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。
  • 两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。
  • StringRedisTemplate只能对key=String,value=String的键值对进行操作,RedisTemplate可以对任何类型的key-value键值对操作。

四、RedisTemplate操作redis集群

这里准备6台集群环境,三主三从的形式,每一台redis机器的端口都必须允许防火墙访问,否则是无法进行连接的。

4.1.修改配置文件,设置集群的配置连接

修改后application.properties文件内容如下:

server.port=7777
spring.application.name=redis7_study

# ========================logging=====================
logging.level.root=info
logging.level.com.atguigu.redis7=info
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger- %msg%n 

logging.file.name=D:/mylogs2023/redis7_study.log
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger- %msg%n

# ========================swagger=====================
spring.swagger2.enabled=true
#在springboot2.6.X结合swagger2.9.X会提示documentationPluginsBootstrapper空指针异常,
#原因是在springboot2.6.X中将SpringMVC默认路径匹配策略从AntPathMatcher更改为PathPatternParser,
# 导致出错,解决办法是matching-strategy切换回之前ant_path_matcher
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

# ========================redis集群=====================
spring.redis.password=123456
# 获取失败 最大重定向次数
spring.redis.cluster.max-redirects=3
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
spring.redis.cluster.nodes=192.168.42.132:6381,192.168.42.132:6382,192.168.42.134:6383,192.168.42.134:6384,192.168.42.135:6385,192.168.42.135:6386

4.2.直接通过微服务访问redis集群

在swagger中执行创建订单流水的接口:

在控制台查看生成的数据信息

同时在swagger中利用查询接口查询214这个键,对应的值:

4.3.模拟某台redis主机的宕机故障

1.模拟master-6381机器意外宕机,手动执行shutdown关闭服务,

2.先对redis集群通过命令方式,手动验证各种读写命令,看看其从机6384是否上位成为主机master

3.通过swagger再次读写访问试试

Redis Cluster集群部署采用了3主3从拓扑结构,数据读写访问master节点, slave节点负责备份。当master宕机主从切换成功,redis手动操作吗,没有问题,但是通过swagger代码执行报错:

上述问题是由于SpringBoot客户端没有动态感知到RedisCluster最新集群变化信息而导致的

4.导致原因分析

SpringBoot 2.X 版本, Redis默认的连接池采用 Lettuce当Redis 集群节点发生变化后,Letture默认是不会刷新节点拓扑,

5.如何解决上述问题

  • 方案一:排除lettuce采用jedis(不推荐)

  • 方案二:重写连接工厂实例(极度不推荐)
@Bean
public DefaultClientResources lettuceClientResources() {
    return DefaultClientResources.create();
}
 
@Bean
public LettuceConnectionFactory lettuceConnectionFactory(RedisProperties redisProperties, ClientResources clientResources) {
 
    ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
            .enablePeriodicRefresh(Duration.ofSeconds(30)) //按照周期刷新拓扑
            .enableAllAdaptiveRefreshTriggers() //根据事件刷新拓扑
            .build();
 
    ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
            //redis命令超时时间,超时后才会使用新的拓扑信息重新建立连接
            .timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(10)))
            .topologyRefreshOptions(topologyRefreshOptions)
            .build();
 
    LettuceClientConfiguration clientConfiguration = LettuceClientConfiguration.builder()
            .clientResources(clientResources)
            .clientOptions(clusterClientOptions)
            .build();
 
    RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration(redisProperties.getCluster().getNodes());
    clusterConfig.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
    clusterConfig.setPassword(RedisPassword.of(redisProperties.getPassword()));
}
  • 方案:刷新节点集群拓扑动态感应(官网推荐)

 参考网址:https://github.com/lettuce-io/lettuce-core/wiki/Redis-Cluster,如下图,本质是就是设置每隔一段时间去刷新一下集群的拓扑:

6.如何解决上述问题

采用方案三:刷新节点集群拓扑动态感应,的方式解决,修改application.properties文件内容,在redis集群下面添加内容:

# ========================redis集群=====================
spring.redis.password=123456
# 获取失败 最大重定向次数
spring.redis.cluster.max-redirects=3
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
#支持集群拓扑动态感应刷新,自适应拓扑刷新是否使用所有可用的更新,默认false关闭
spring.redis.lettuce.cluster.refresh.adaptive=true
#定时刷新 单位是毫秒
spring.redis.lettuce.cluster.refresh.period=2000
spring.redis.cluster.nodes=192.168.42.132:6381,192.168.42.132:6382,192.168.42.134:6383,192.168.42.134:6384,192.168.42.135:6385,192.168.42.135:6386

7.测试

先启动6381,恢复到三主三从的效果

手动创建数据操作正常:

再次模拟6381宕机

再次在swagger中执行创建订单流水的接口,会发现执行的swagger转圈等了一下,然后控制台显示生成了数据

通过查询接口验证,也能查询出来数据:

posted @ 2023-03-14 15:31  酒剑仙*  阅读(410)  评论(0)    收藏  举报