更新时间:2023-09-15 GMT+08:00

Lettuce

介绍使用同一VPC内弹性云服务器ECS上的Lettuce连接Redis实例的方法。更多的客户端的使用方法请参考Redis客户端

在springboot类型的项目中,spring-data-redis中已提供了对jedislettuce的集成适配。另外,在springboot1.x中默认集成的是jedis,springboot2.x中改为了lettuce,因此在springboot2.x及更高版本中想集成使用lettuce,无需手动引入lettuce依赖包。

前提条件

  • 已成功申请Redis实例,且状态为“运行中”。
  • 查看并获取待连接Redis实例的IP地址/域名和端口。

    具体步骤请参见查看实例信息

  • 已创建弹性云服务器,创建弹性云服务器的方法,请参见创建弹性云服务器
  • 如果弹性云服务器为Linux系统,该弹性云服务器必须已经安装java编译环境。

Pom配置

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

基于application.properties配置

  • 单机、主备、读写分离、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=2000
  • Cluster集群实例配置
    # redis cluster节点信息
    spring.redis.cluster.nodes=<ip:port>,<ip:port>,<ip:port>
    # redis cluster 最大重定向次数
    spring.redis.cluster.max-redirects=3
    # redis cluster 节点密码
    spring.redis.password=<password>
    # redis cluster 超时配置
    spring.redis.timeout=2000
    # 开启自适应拓扑刷新
    spring.redis.lettuce.cluster.refresh.adaptive=true
    # 开启每10S定时刷新拓扑结构
    spring.redis.lettuce.cluster.refresh.period=10S

基于Bean方式配置

  • 单机、主备、读写分离、Proxy集群实例配置
    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:2000}")
        private Integer redisReadTimeout = 2000;
    
        @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().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))
                    .clientOptions(clientOptions)
                    .build();
            return clientConfiguration;
        }
    }
  • 单机、主备、读写分离、Proxy集群实例池化配置
    引入池化组件
    <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:2000}")
        private Integer redisReadTimeout = 2000;
    
        @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;
    
        @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().connectTimeout(Duration.ofMillis(redisConnectTimeout)).build();
    
            ClientOptions clientOptions = ClientOptions.builder()
                    .autoReconnect(true)
                    .pingBeforeActivateConnection(true)
                    .cancelCommandsOnReconnectFailure(false)
                    .disconnectedBehavior(ClientOptions.DisconnectedBehavior.ACCEPT_COMMANDS)
                    .socketOptions(socketOptions)
                    .build();
    
            LettucePoolingClientConfiguration poolingClientConfiguration = LettucePoolingClientConfiguration.builder()
                    .poolConfig(redisPoolConfig())
                    .commandTimeout(Duration.ofMillis(redisReadTimeout))
                    .clientOptions(clientOptions)
                    .build();
            return poolingClientConfiguration;
        }
    
        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集群实例配置
    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:2000}")
        private Integer redisReadTimeout = 2000;
    
        @Value("${redis.cluster.topology.refresh.period.millis:10000}")
        private Integer redisClusterTopologyRefreshPeriodMillis = 10000;
    
        @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().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))
                    .clientOptions(clientOptions)
                    .build();
            return clientConfiguration;
        }
    }
  • Cluster实例池化配置
    引入池化组件
    <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:2000}")
        private Integer redisReadTimeout = 2000;
    
        @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;
    
        @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().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)
                    .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

-

连接密码

表3 RedisClusterConfiguration参数

参数

说明

clusterNodes

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

maxRedirects

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

password

连接密码

表4 LettuceClientConfiguration参数

参数

类型

默认值

说明

timeout

Duration

60s

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

clientOptions

ClientOptions

-

配置项

表5 LettucePoolingClientConfiguration参数

参数

类型

默认值

说明

timeout

Duration

60s

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

clientOptions

ClientOptions

-

配置项

poolConfig

GenericObjectPoolConfig

-

连接池配置

表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

1800000

连接空闲多久后逐出,(空闲时间>该值 && 空闲连接>最大空闲数)时直接逐出

minEvictableIdleTimeMillis

60000

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

timeBetweenEvictionRunsMillis

60000

空闲连接逐出的检测周期,单位:毫秒

DCS实例配置建议

  • 连接池化

    因lettuce底层采用基于netty的NIO模式,和redis server进行通信,不同于jedis的BIO模式。底层采用长连接 + 队列的组合模式,借助TCP顺序发、顺序收的特性,来实现同时处理多请求发送和多响应接收,单条连接可支撑的QPS在3K~5K不等,线上系统建议不要超过3K。lettuce本身不支持池化,且在springboot中默认不开启池化,如需开启池化,需通过手动引入commons-pool2组件,并关闭LettuceConnectionFactory.shareNativeConnection(共享连接)来实现池化。

    因每条lettuce连接默认需要配置两个线程池-I/O thread pools、computation thread pool,用于支撑IO事件读取和异步event处理,如配置成连接池形式使用,每个连接都将会创建两个线程池,对内存资源的占用偏高。鉴于lettuce的底层模型实现,及单连接突出的处理能力,不建议通过池化的方式使用lettuce。

  • 拓扑刷新

    在连接cluster类型实例中,lettuce会在初始化时,向配置的节点列表随机发送cluster nodes来获取集群slot的分布信息。如后续cluster扩/缩容、主备切换等,会导致集群拓扑结构发生变化,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组件替换