更新时间:2026-05-20 GMT+08:00
分享

通过Lettuce连接GeminiDB Redis实例

本章节主要介绍使用Lettuce客户端连接GeminiDB Redis实例。

在Spring Boot项目中,Spring-Data-Redis已提供了对Jedis、Lettuce的集成适配。此外,Spring Boot 1.x默认集成Jedis,而从Spring Boot 2.x开始,默认切换为Lettuce。 因此,若您使用的是Spring Boot 2.x或更高版本,想要使用Lettuce,无需手动添加其依赖包,系统已为您默认集成。

使用须知

  • 请确保Spring Boot版本不低于2.3.12.RELEASE,Lettuce版本不低于6.3.0.RELEASE,Netty版本不低于4.1.100.Final。
  • Lettuce 可用于连接 GeminiDB Redis,但线上生产应用不建议优先选择 Lettuce。若业务无强依赖,建议优先使用 Jedis 或 Redisson;若必须使用 Lettuce,请按本文配置建议设置连接池、超时、拓扑刷新和读模式,并在上线前完成故障切换和高并发压测验证。

前提条件

  • 已成功创建GeminiDB Redis实例,且实例状态正常。如需了解如何创建GeminiDB Redis实例,请参见购买实例
  • 已创建弹性云服务器,如需了解如何创建弹性云服务器,请参见《弹性云服务器》中“购买弹性云服务器”章节。
  • 弹性云服务器上已安装JDK、Maven等编译工具。
  • 创建的弹性云服务器与GeminiDB Redis实例在区域、可用区、VPC及安全组配置上保持一致。

配置建议

  • 连接地址配置

    针对主备版、Proxy集群版或Cluster集群版,请统一填写实例的“负载均衡地址”以确保连接时的高可用, 您可以单击实例名称,进入“基本信息”页面,在网络信息区域获取“负载均衡地址”。

  • 连接池化

    Lettuce底层采用基于Netty的NIO模式与Redis Server通信,与Jedis使用的BIO模式不同。其底层利用长连接与队列相结合的方式,借助TCP的有序发送与接收特性,实现多请求的并发发送与多响应的并发接收。单条连接在正常情况下可以支撑3K~5K的QPS(每秒请求数),在生产系统中建议不要超过3K。

    需要注意的是,Lettuce本身不支持连接池化,并且在Spring Boot中默认没有启用连接池化。如需启用池化功能,需要手动引入commons-pool2组件,并关闭 LettuceConnectionFactory.shareNativeConnection(共享连接),从而实现池化支持。

    然而,由于每条Lettuce连接默认需要配置两个线程池(-I/O thread pools、computation thread pool),用于支持I/O事件的读取和异步事件的处理。如果配置为连接池方式进行使用,每个连接都将会创建两个线程池,对内存资源的占用偏高。结合Lettuce的底层模型实现方式以及单连接的高处理能力,我们不建议通过池化的方式来使用Lettuce。

  • 拓扑刷新

    在连接Cluster集群版的GeminiDB Redis实例时,Lettuce在初始化阶段会随机向配置的节点列表发送CLUSTER NODES命令,以获取集群中slot的分布信息。然而,后续集群发生扩缩容、主备切换等操作,会导致拓扑结构发生变化,Lettuce默认是不会自动感知这些变化的。为了使Lettuce主动感知并更新集群拓扑信息,需要手动开启相关配置,如下所示:

    • 基于application.properties配置
      # 开启自适应拓扑刷新
      spring.redis.lettuce.cluster.refresh.adaptive=true
      # 开启每10s定时刷新拓扑结构
      spring.redis.lettuce.cluster.refresh.period=10S
    • 基于API配置
      ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
          .enableAllAdaptiveRefreshTriggers()
          .enablePeriodicRefresh(Duration.ofMillis(redisClusterTopologyRefreshPeriodMillis))
          .build();
      
      ClusterClientOptions clientOptions = ClusterClientOptions.builder()
              ...
              ...
              .topologyRefreshOptions(topologyRefreshOptions)
              .build();
  • 爆炸半径

    Lettuce底层采用的是“单个长连接 + 请求队列”组合模式,一旦网络出现抖动、闪断,或者连接失效,将会影响所有的请求。特别是在连接失效的情况下,系统会尝试TCP重传,直至重传超时关闭连接。只有在连接重建完成后请求才能恢复。在重传期间,请求队列将不断堆积各种请求,进而导致上层业务大批量超时。在一些操作系统的内核中,若重传超时配置过长,甚至会使得业务系统长时间处于不可用状态。因此,出于稳定性和可用性的考虑,不建议使用Lettuce组件,推荐使用Jedis或Redisson等组件来替代

操作步骤

步骤一:Pom配置

<!-- 引入spring-data-redis组件,默认已集成Lettuce依赖SDK -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
   <groupId>io.lettuce</groupId>
   <artifactId>lettuce-core</artifactId>
   <version>6.3.0.RELEASE</version>
</dependency>

<dependency>
   <groupId>io.netty</groupId>
   <artifactId>netty-transport-native-epoll</artifactId>
   <version>4.1.100.Final</version>
   <classifier>linux-x86_64</classifier>
</dependency>

步骤二:application.properties文件或者Bean方式配置。

可以选择使用application.properties文件进行配置,也可以通过定义Bean的方式来实现配置。

主备版、Proxy集群版实例配置

#redis host
spring.redis.host=<host>
#redis 端口号
spring.redis.port=<port>
#redis 数据库下标
spring.redis.database=0
#redis 密码
spring.redis.password=<password>
#redis 读写超时
spring.redis.timeout=5000

Cluster集群版实例配置

# redis cluster节点信息
spring.redis.cluster.nodes=<ip:port>,<ip:port>,<ip:port>
# redis cluster 最大重定向次数
spring.redis.cluster.max-redirects=10
# redis cluster 节点密码
spring.redis.password=<password>
# redis cluster 超时配置
spring.redis.timeout=5000
# 开启自适应拓扑刷新
spring.redis.lettuce.cluster.refresh.adaptive=true
# 开启每10S定时刷新拓扑结构
spring.redis.lettuce.cluster.refresh.period=10S

主备版、Proxy集群版

  1. 实例配置
    import java.time.Duration;
    
    import org.springframework.beans.factory.annotation.Value;
    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.RedisStandaloneConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    
    import io.lettuce.core.ClientOptions;
    import io.lettuce.core.SocketOptions;
    
    /**
    * Lettuce 非池化配置,与 application.properties 配置方式二选一
    */
    @Configuration
    public class RedisConfiguration {
    
        @Value("${redis.host}")
        private String redisHost;
    
        @Value("${redis.port:6379}")
        private Integer redisPort = 6379;
    
        @Value("${redis.database:0}")
        private Integer redisDatabase = 0;
    
        @Value("${redis.password:}")
        private String redisPassword;
    
        @Value("${redis.connect.timeout:2000}")
        private Integer redisConnectTimeout = 2000;
    
        @Value("${redis.read.timeout:5000}")
        private Integer redisReadTimeout = 5000;
        /**
         *  TCP_KEEPALIVE 配置参数:
         *  两次 keepalive 间的时间间隔 = TCP_KEEPALIVE_TIME = 30
         *  连接空闲多久开始 keepalive = TCP_KEEPALIVE_TIME/3 = 10
         *  keepalive 几次之后断开连接 = TCP_KEEPALIVE_COUNT = 3
         */
        private static final int TCP_KEEPALIVE_TIME = 30;
    
        /**
         * TCP_USER_TIMEOUT 连接空闲限制时间,解决Lettuce长时间超时问题。
         * refer: https://github.com/lettuce-io/lettuce-core/issues/2082
         */
        private static final int TCP_USER_TIMEOUT = 30;
    
        @Bean
        public RedisConnectionFactory redisConnectionFactory(LettuceClientConfiguration clientConfiguration) {
    
            RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration();
            standaloneConfiguration.setHostName(redisHost);
            standaloneConfiguration.setPort(redisPort);
            standaloneConfiguration.setDatabase(redisDatabase);
            standaloneConfiguration.setPassword(redisPassword);
    
            LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(standaloneConfiguration, clientConfiguration);
            connectionFactory.setDatabase(redisDatabase);
            return connectionFactory;
        }
    
        @Bean
        public LettuceClientConfiguration clientConfiguration() {
    
    
            SocketOptions socketOptions = SocketOptions.builder()
                .keepAlive(SocketOptions.KeepAliveOptions.builder()
                    // 两次 keepalive 间的时间间隔
                    .idle(Duration.ofSeconds(TCP_KEEPALIVE_TIME))
                    // 连接空闲多久开始 keepalive
                    .interval(Duration.ofSeconds(TCP_KEEPALIVE_TIME/3))
                    // keepalive 几次之后断开连接
                    .count(3)
                    // 是否开启保活连接
                    .enable()
                    .build())
                .tcpUserTimeout(SocketOptions.TcpUserTimeoutOptions.builder()
                    // 解决服务端rst导致的长时间超时问题
                    .tcpUserTimeout(Duration.ofSeconds(TCP_USER_TIMEOUT))
                    .enable()
                    .build())
                // tcp 连接超时设置
                .connectTimeout(Duration.ofMillis(redisConnectTimeout))
                .build();
    
            ClientOptions clientOptions = ClientOptions.builder()
                    .autoReconnect(true)
                    .pingBeforeActivateConnection(true)
                    .cancelCommandsOnReconnectFailure(false)
                    .disconnectedBehavior(ClientOptions.DisconnectedBehavior.ACCEPT_COMMANDS)
                    .socketOptions(socketOptions)
                    .build();
    
    
            LettuceClientConfiguration clientConfiguration = LettuceClientConfiguration.builder()
                    .commandTimeout(Duration.ofMillis(redisReadTimeout))
                    // Proxy集群实例无需设置readFrom
                    .readFrom(ReadFrom.MASTER)
                    .clientOptions(clientOptions)
                    .build();
    
            return clientConfiguration;
        }
    }
  2. 池化配置
    • 引入池化组件
      <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-pool2</artifactId>
          <version>2.11.1</version>
      </dependency>
    • 代码配置
      import java.time.Duration;
      
      import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
      import org.springframework.beans.factory.annotation.Value;
      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.RedisStandaloneConfiguration;
      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 io.lettuce.core.ClientOptions;
      import io.lettuce.core.SocketOptions;
      
      /**
      * Lettuce 池化配置
      */
      @Configuration
      public class RedisPoolConfiguration {
          @Value("${redis.host}")
          private String redisHost;
      
          @Value("${redis.port:6379}")
          private Integer redisPort = 6379;
      
          @Value("${redis.database:0}")
          private Integer redisDatabase = 0;
      
          @Value("${redis.password:}")
          private String redisPassword;
      
          @Value("${redis.connect.timeout:2000}")
          private Integer redisConnectTimeout = 2000;
      
          @Value("${redis.read.timeout:5000}")
          private Integer redisReadTimeout = 5000;
      
          @Value("${redis.pool.minSize:50}")
          private Integer redisPoolMinSize = 50;
      
          @Value("${redis.pool.maxSize:200}")
          private Integer redisPoolMaxSize = 200;
      
          @Value("${redis.pool.maxWaitMillis:2000}")
          private Integer redisPoolMaxWaitMillis = 2000;
      
          @Value("${redis.pool.softMinEvictableIdleTimeMillis:1800000}")
          private Integer redisPoolSoftMinEvictableIdleTimeMillis = 30 * 60 * 1000;
      
          @Value("${redis.pool.timeBetweenEvictionRunsMillis:60000}")
          private Integer redisPoolBetweenEvictionRunsMillis = 60 * 1000;
          /**
           *  TCP_KEEPALIVE 配置参数:
           *  两次 keepalive 间的时间间隔 = TCP_KEEPALIVE_TIME = 30
           *  连接空闲多久开始 keepalive = TCP_KEEPALIVE_TIME/3 = 10
           *  keepalive 几次之后断开连接 = TCP_KEEPALIVE_COUNT = 3
           */
          private static final int TCP_KEEPALIVE_TIME = 30;
      
          /**
           * TCP_USER_TIMEOUT 连接空闲限制时间,解决Lettuce长时间超时问题。
           * refer: https://github.com/lettuce-io/lettuce-core/issues/2082
           */
          private static final int TCP_USER_TIMEOUT = 30;
      
          @Bean
          public RedisConnectionFactory redisConnectionFactory(LettuceClientConfiguration clientConfiguration) {
      
              RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration();
              standaloneConfiguration.setHostName(redisHost);
              standaloneConfiguration.setPort(redisPort);
              standaloneConfiguration.setDatabase(redisDatabase);
              standaloneConfiguration.setPassword(redisPassword);
      
              LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(standaloneConfiguration, clientConfiguration);
              connectionFactory.setDatabase(redisDatabase);
              //关闭共享链接,才能池化生效
              connectionFactory.setShareNativeConnection(false);
              return connectionFactory;
          }
      
          @Bean
          public LettuceClientConfiguration clientConfiguration() {
      
      
              SocketOptions socketOptions = SocketOptions.builder()
                  .keepAlive(SocketOptions.KeepAliveOptions.builder()
                      // 两次 keepalive 间的时间间隔
                      .idle(Duration.ofSeconds(TCP_KEEPALIVE_TIME))
                      // 连接空闲多久开始 keepalive
                      .interval(Duration.ofSeconds(TCP_KEEPALIVE_TIME/3))
                      // keepalive 几次之后断开连接
                      .count(3)
                      // 是否开启保活连接
                      .enable()
                      .build())
                  .tcpUserTimeout(SocketOptions.TcpUserTimeoutOptions.builder()
                      // 解决服务端rst导致的长时间超时问题
                      .tcpUserTimeout(Duration.ofSeconds(TCP_USER_TIMEOUT))
                      .enable()
                      .build())
                  // tcp 连接超时设置
                  .connectTimeout(Duration.ofMillis(redisConnectTimeout))
                  .build();
      
              ClientOptions clientOptions = ClientOptions.builder()
                      .autoReconnect(true)
                      .pingBeforeActivateConnection(true)
                      .cancelCommandsOnReconnectFailure(false)
                      .disconnectedBehavior(ClientOptions.DisconnectedBehavior.ACCEPT_COMMANDS)
                      .socketOptions(socketOptions)
                      .build();
      
      
              LettucePoolingClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder()
                      .poolConfig(poolConfig())
                      .commandTimeout(Duration.ofMillis(redisReadTimeout))
                      .clientOptions(clientOptions)
                      // Proxy集群实例无需设置readFrom
                      .readFrom(ReadFrom.MASTER)
                      .build();
              return clientConfiguration;
          }
      
          private GenericObjectPoolConfig redisPoolConfig() {
              GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
              //连接池的最小连接数
              poolConfig.setMinIdle(redisPoolMinSize);
              //连接池的最大空闲连接数
              poolConfig.setMaxIdle(redisPoolMaxSize);
              //连接池的最大连接数
              poolConfig.setMaxTotal(redisPoolMaxSize);
              //连接池耗尽后是否需要等待,默认true表示等待。当值为true时,setMaxWait才会生效
              poolConfig.setBlockWhenExhausted(true);
              //连接池耗尽后获取连接的最大等待时间,默认-1表示一直等待
              poolConfig.setMaxWait(Duration.ofMillis(redisPoolMaxWaitMillis));
              //创建连接时校验有效性(ping),默认false
              poolConfig.setTestOnCreate(false);
              //获取连接时校验有效性(ping),默认false,业务量大时建议设置为false减少开销
              poolConfig.setTestOnBorrow(true);
              //归还连接时校验有效性(ping),默认false,业务量大时建议设置为false减少开销
              poolConfig.setTestOnReturn(false);
              //是否开启空闲连接检测,如为false,则不剔除空闲连接
              poolConfig.setTestWhileIdle(true);
              //连接空闲多久后逐出,当空闲时间>该值,并且空闲连接数>最小空闲连接数时直接逐出
              poolConfig.setSoftMinEvictableIdleTime(Duration.ofMillis(redisPoolSoftMinEvictableIdleTimeMillis));
              //关闭根据MinEvictableIdleTimeMillis判断逐出
              poolConfig.setMinEvictableIdleTime(Duration.ofMillis(-1));
              //空闲连接逐出的检测周期,默认为60s
              poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(redisPoolBetweenEvictionRunsMillis));
              return poolConfig;
          }
      }

Cluster集群版

  1. 实例配置
    import java.time.Duration;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisClusterConfiguration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.RedisNode;
    import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    
    import io.lettuce.core.ClientOptions;
    import io.lettuce.core.SocketOptions;
    import io.lettuce.core.cluster.ClusterClientOptions;
    import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
    
    /**
    * Lettuce Cluster 非池化配置,与 application.properties 配置方式二选一
    */
    @Configuration
    public class RedisConfiguration {
    
        @Value("${redis.cluster.nodes}")
        private String redisClusterNodes;
    
        @Value("${redis.cluster.maxDirects:3}")
        private Integer redisClusterMaxDirects;
    
        @Value("${redis.password:}")
        private String redisPassword;
    
        @Value("${redis.connect.timeout:2000}")
        private Integer redisConnectTimeout = 2000;
    
        @Value("${redis.read.timeout:5000}")
        private Integer redisReadTimeout = 5000;
    
        @Value("${redis.cluster.topology.refresh.period.millis:10000}")
        private Integer redisClusterTopologyRefreshPeriodMillis = 10000;
        /**
         *  TCP_KEEPALIVE 配置参数:
         *  两次 keepalive 间的时间间隔 = TCP_KEEPALIVE_TIME = 30
         *  连接空闲多久开始 keepalive = TCP_KEEPALIVE_TIME/3 = 10
         *  keepalive 几次之后断开连接 = TCP_KEEPALIVE_COUNT = 3
         */
        private static final int TCP_KEEPALIVE_TIME = 30;
    
        /**
         * TCP_USER_TIMEOUT 连接空闲限制时间,解决Lettuce长时间超时问题。
         * refer: https://github.com/lettuce-io/lettuce-core/issues/2082
         */
        private static final int TCP_USER_TIMEOUT = 30;
    
        @Bean
        public RedisConnectionFactory redisConnectionFactory(LettuceClientConfiguration clientConfiguration) {
    
            RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
    
            List<RedisNode> clusterNodes = new ArrayList<>();
            for (String clusterNodeStr : redisClusterNodes.split(",")) {
                String[] nodeInfo = clusterNodeStr.split(":");
                clusterNodes.add(new RedisNode(nodeInfo[0], Integer.valueOf(nodeInfo[1])));
            }
            clusterConfiguration.setClusterNodes(clusterNodes);
    
            clusterConfiguration.setPassword(redisPassword);
            clusterConfiguration.setMaxRedirects(redisClusterMaxDirects);
    
            LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(clusterConfiguration, clientConfiguration);
            return connectionFactory;
        }
    
        @Bean
        public LettuceClientConfiguration clientConfiguration() {
            SocketOptions socketOptions = SocketOptions.builder()
                .keepAlive(SocketOptions.KeepAliveOptions.builder()
                    // 两次 keepalive 间的时间间隔
                    .idle(Duration.ofSeconds(TCP_KEEPALIVE_TIME))
                    // 连接空闲多久开始 keepalive
                    .interval(Duration.ofSeconds(TCP_KEEPALIVE_TIME/3))
                    // keepalive 几次之后断开连接
                    .count(3)
                    // 是否开启保活连接
                    .enable()
                    .build())
                .tcpUserTimeout(SocketOptions.TcpUserTimeoutOptions.builder()
                    // 解决服务端rst导致的长时间超时问题
                    .tcpUserTimeout(Duration.ofSeconds(TCP_USER_TIMEOUT))
                    .enable()
                    .build())
                // tcp 连接超时设置
                .connectTimeout(Duration.ofMillis(redisConnectTimeout))
                .build();
    
            ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
                    .enableAllAdaptiveRefreshTriggers()
                    .enablePeriodicRefresh(Duration.ofMillis(redisClusterTopologyRefreshPeriodMillis))
                    .build();
    
            ClusterClientOptions clientOptions = ClusterClientOptions.builder()
                    .autoReconnect(true)
                    .pingBeforeActivateConnection(true)
                    .cancelCommandsOnReconnectFailure(false)
                    .disconnectedBehavior(ClientOptions.DisconnectedBehavior.ACCEPT_COMMANDS)
                    .socketOptions(socketOptions)
                    .topologyRefreshOptions(topologyRefreshOptions)
                    .build();
    
    
            LettuceClientConfiguration clientConfiguration = LettuceClientConfiguration.builder()
                    .commandTimeout(Duration.ofMillis(redisReadTimeout))
                    .readFrom(ReadFrom.MASTER)
                    .clientOptions(clientOptions)
                    .build();
            return clientConfiguration;
        }
    }
  2. 池化配置
    • 引入池化组件
      <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-pool2</artifactId>
          <version>2.11.1</version>
      </dependency>
    • 代码配置
      import java.time.Duration;
      import java.util.ArrayList;
      import java.util.List;
      
      import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.data.redis.connection.RedisClusterConfiguration;
      import org.springframework.data.redis.connection.RedisConnectionFactory;
      import org.springframework.data.redis.connection.RedisNode;
      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 io.lettuce.core.ClientOptions;
      import io.lettuce.core.SocketOptions;
      import io.lettuce.core.cluster.ClusterClientOptions;
      import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
      
      /**
      * Lettuce 池化配置
      */
      @Configuration
      public class RedisPoolConfiguration {
      
          @Value("${redis.cluster.nodes}")
          private String redisClusterNodes;
      
          @Value("${redis.cluster.maxDirects:3}")
          private Integer redisClusterMaxDirects;
      
          @Value("${redis.password:}")
          private String redisPassword;
      
          @Value("${redis.connect.timeout:2000}")
          private Integer redisConnectTimeout = 2000;
      
          @Value("${redis.read.timeout:5000}")
          private Integer redisReadTimeout = 5000;
      
          @Value("${redis.cluster.topology.refresh.period.millis:10000}")
          private Integer redisClusterTopologyRefreshPeriodMillis = 10000;
      
          @Value("${redis.pool.minSize:50}")
          private Integer redisPoolMinSize = 50;
      
          @Value("${redis.pool.maxSize:200}")
          private Integer redisPoolMaxSize = 200;
      
          @Value("${redis.pool.maxWaitMillis:2000}")
          private Integer redisPoolMaxWaitMillis = 2000;
      
          @Value("${redis.pool.softMinEvictableIdleTimeMillis:1800000}")
          private Integer redisPoolSoftMinEvictableIdleTimeMillis = 30 * 60 * 1000;
      
          @Value("${redis.pool.timeBetweenEvictionRunsMillis:60000}")
          private Integer redisPoolBetweenEvictionRunsMillis = 60 * 1000;
          /**
           *  TCP_KEEPALIVE 配置参数:
           *  两次 keepalive 间的时间间隔 = TCP_KEEPALIVE_TIME = 30
           *  连接空闲多久开始 keepalive = TCP_KEEPALIVE_TIME/3 = 10
           *  keepalive 几次之后断开连接 = TCP_KEEPALIVE_COUNT = 3
           */
          private static final int TCP_KEEPALIVE_TIME = 30;
      
          /**
           * TCP_USER_TIMEOUT 连接空闲限制时间,解决Lettuce长时间超时问题。
           * refer: https://github.com/lettuce-io/lettuce-core/issues/2082
           */
          private static final int TCP_USER_TIMEOUT = 30;
      
          @Bean
          public RedisConnectionFactory redisConnectionFactory(LettuceClientConfiguration clientConfiguration) {
      
              RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
      
              List<RedisNode> clusterNodes = new ArrayList<>();
              for (String clusterNodeStr : redisClusterNodes.split(",")) {
                  String[] nodeInfo = clusterNodeStr.split(":");
                  clusterNodes.add(new RedisNode(nodeInfo[0], Integer.valueOf(nodeInfo[1])));
              }
              clusterConfiguration.setClusterNodes(clusterNodes);
      
              clusterConfiguration.setPassword(redisPassword);
              clusterConfiguration.setMaxRedirects(redisClusterMaxDirects);
      
              LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(clusterConfiguration, clientConfiguration);
              //一定要关闭共享连接,否则连接池将不会生效
              connectionFactory.setShareNativeConnection(false);
              return connectionFactory;
          }
      
          @Bean
          public LettuceClientConfiguration clientConfiguration() {
              SocketOptions socketOptions = SocketOptions.builder()
                  .keepAlive(SocketOptions.KeepAliveOptions.builder()
                      // 两次 keepalive 间的时间间隔
                      .idle(Duration.ofSeconds(TCP_KEEPALIVE_TIME))
                      // 连接空闲多久开始 keepalive
                      .interval(Duration.ofSeconds(TCP_KEEPALIVE_TIME/3))
                      // keepalive 几次之后断开连接
                      .count(3)
                      // 是否开启保活连接
                      .enable()
                      .build())
                  .tcpUserTimeout(SocketOptions.TcpUserTimeoutOptions.builder()
                      // 解决服务端rst导致的长时间超时问题
                      .tcpUserTimeout(Duration.ofSeconds(TCP_USER_TIMEOUT))
                      .enable()
                      .build())
                  // tcp 连接超时设置
                  .connectTimeout(Duration.ofMillis(redisConnectTimeout))
                  .build();
      
              ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
                      .enableAllAdaptiveRefreshTriggers()
                      .enablePeriodicRefresh(Duration.ofMillis(redisClusterTopologyRefreshPeriodMillis))
                      .build();
      
              ClusterClientOptions clientOptions = ClusterClientOptions.builder()
                      .autoReconnect(true)
                      .pingBeforeActivateConnection(true)
                      .cancelCommandsOnReconnectFailure(false)
                      .disconnectedBehavior(ClientOptions.DisconnectedBehavior.ACCEPT_COMMANDS)
                      .socketOptions(socketOptions)
                      .topologyRefreshOptions(topologyRefreshOptions)
                      .build();
      
      
              LettucePoolingClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder()
                      .poolConfig(poolConfig())
                      .commandTimeout(Duration.ofMillis(redisReadTimeout))
                      .clientOptions(clientOptions)
                      .readFrom(ReadFrom.MASTER)
                      .build();
              return clientConfiguration;
          }
      
          private GenericObjectPoolConfig poolConfig() {
              GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
              //连接池的最小连接数
              poolConfig.setMinIdle(redisPoolMinSize);
              //连接池的最大空闲连接数
              poolConfig.setMaxIdle(redisPoolMaxSize);
              //连接池的最大连接数
              poolConfig.setMaxTotal(redisPoolMaxSize);
              //连接池耗尽后是否需要等待, 默认true表示等待. 当值为true时, setMaxWait才会生效
              poolConfig.setBlockWhenExhausted(true);
              //连接池耗尽后获取连接的最大等待时间, 默认-1表示一直等待
              poolConfig.setMaxWait(Duration.ofMillis(redisPoolMaxWaitMillis));
              //创建连接时校验有效性(ping),默认false
              poolConfig.setTestOnCreate(false);
              //获取连接时校验有效性(ping), 默认false, 业务量大时建议设置为false减少开销
              poolConfig.setTestOnBorrow(true);
              //归还连接时校验有效性(ping),默认false, 业务量大时建议设置为false减少开销
              poolConfig.setTestOnReturn(false);
              //是否开启空闲连接检测,如为false,则不剔除空闲连接
              poolConfig.setTestWhileIdle(true);
              //禁止最小空闲时间关闭连接
              poolConfig.setMinEvictableIdleTime(Duration.ofMillis(-1));
              //连接空闲多久后逐出,当空闲时间>该值,并且空闲连接数>最小空闲连接数时直接逐出,不再根据MinEvictableIdleTimeMillis判断(默认逐出策略)
              poolConfig.setSoftMinEvictableIdleTime(Duration.ofMillis(redisPoolSoftMinEvictableIdleTimeMillis));
              //空闲连接逐出的检测周期,默认为60s
              poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(redisPoolBetweenEvictionRunsMillis));
      
              return poolConfig;
          }
      
      }

参数解释

表1 LettuceConnectionFactory参数

参数

类型

默认值

说明

configuration

RedisConfiguration

-

redis连接配置,常用两个子类:

  • RedisStandaloneConfiguration
  • RedisClusterConfiguration

clientConfiguration

LettuceClientConfiguration

-

客户端配置参数,常用子类:

LettucePoolingClientConfiguration(用于池化)

shareNativeConnection

boolean

true

是否采用共享连接,默认true,采用连接池时必须设置为false

表2 RedisStandaloneConfiguration参数

参数

默认值

说明

hostName

localhost

连接Redis实例的IP地址/域名, 请填写实例的负载均衡地址,以确保可用性。

port

6379

连接端口号。

database

0

数据库下标。

password

-

连接Redis实例的密码。如果实例已开启免密访问,无需输入实例的访问密码

表3 RedisClusterConfiguration参数

参数

说明

clusterNodes

cluster节点连接信息,需节点IP、Port。

maxRedirects

cluster访问最大重定向次数,建议值:10

password

连接Redis实例的密码。如果实例已开启免密访问,无需输入实例的访问密码

表4 LettuceClientConfiguration参数

参数

类型

默认值

说明

timeout

Duration

60s

命令超时时间配置,建议值:5s

clientOptions

ClientOptions

-

配置项。

readFrom

readFrom

MASTER

读取模式,建议值:MASTER,其余配置在发生故障切换场景下,均存在访问失败风险。

表5 LettucePoolingClientConfiguration参数

参数

类型

默认值

说明

timeout

Duration

60s

命令超时时间配置,建议值:5s

clientOptions

ClientOptions

-

配置项。

poolConfig

GenericObjectPoolConfig

-

连接池配置。

readFrom

readFrom

MASTER

读取模式,建议值:MASTER,其余配置在发生故障切换场景下,均存在访问失败风险。

表6 ClientOptions参数

参数

类型

默认值

说明

autoReconnect

boolean

true

连接断开后,是否自动发起重连,建议值:true

pingBeforeActivateConnection

boolean

true

连接创建后,是否通过ping/pong校验连接可用性,建议值:true

cancelCommandsOnReconnectFailure

boolean

true

连接重连失败时,是否取消队列中的命令,建议值:false

disconnectedBehavior

DisconnectedBehavior

DisconnectedBehavior.DEFAULT

连接断开时的行为,建议值:ACCEPT_COMMANDS

  • DEFAULT:当autoReconnect为true时,允许命令进入队列等待,当autoReconnect为false时,禁止命令进入队列等待。
  • ACCEPT_COMMANDS:允许命令进入队列等待。
  • REJECT_COMMANDS:禁止命令进入队列等待。

socketOptions

SocketOptions

-

网络配置项。

表7 SocketOptions参数

参数

默认值

说明

connectTimeout

10s

连接超时时间配置,建议值:2s

表8 GenericObjectPoolConfig参数

参数

默认值

说明

minIdle

-

连接池的最小连接数。

maxIdle

-

连接池的最大空闲连接数。

maxTotal

-

连接池的最大连接数。

blockWhenExhausted

true

连接池耗尽后是否需要等待,默认true表示等待。当值为true时,设置maxWaitMillis才会生效。

maxWaitMillis

-1

连接池耗尽后获取连接的最大等待时间,默认-1表示一直等待。

testOnCreate

false

创建连接时校验有效性(ping),默认false。

testOnBorrow

false

获取连接时校验有效性(ping),默认false,业务量大时建议设置为false减少开销。

testOnReturn

false

归还连接时校验有效性(ping),默认false,业务量大时建议设置为false减少开销。

testWhileIdle

false

是否开启空闲连接检测,如为false,则不剔除空闲连接,建议值:true

softMinEvictableIdleTimeMillis

-1

连接空闲多久后逐出,(空闲时间>该值 && 空闲连接数>最小空闲连接数)时直接逐出,建议值:1800000,单位:毫秒。

minEvictableIdleTimeMillis

1800000

根据minEvictableIdleTimeMillis判断逐出,建议值:-1,关闭该策略,改用softMinEvictableIdleTimeMillis策略。

timeBetweenEvictionRunsMillis

-1

空闲连接逐出的检测周期,建议值:60000,单位:毫秒。

相关文档