lettuce连接池很香,撸撸它的源代码(上)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: lettuce连接池很香,撸撸它的源代码

springboot中lettuce配置

lettuce初始化

使用netty创建连接

管理连接

actuator健康检查获取连接

释放不掉的连接

共享连接

总结

微信图片_20221212111846.jpg

Lettuce是一个高性能的redis客户端,底层基于netty框架来管理连接,天然是非阻塞和线程安全的。比起jedis需要为每个实例创建物理连接来保证线程安全,lettuce确实很优秀。本文主要介绍springboot使用lettuce整合redis客户端。说明一下,本文的源代码是使用springboot2.1.6,对应lettuce版本是5.1.7.RELEASE。


springboot中lettuce配置


springboot中配置lettuce是非常容易的,代码如下:

pom.xml文件

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.0</version>
</dependency>

application.properties配置

spring.redis.database=0
spring.redis.host=192.168.59.138
spring.redis.password=
spring.redis.port=6379
spring.redis.timeout=5000
#最大连接数
spring.redis.lettuce.pool.max-active=50
#最大阻塞等待时间
spring.redis.lettuce.pool.max-wait=5000
#连接池中最大空闲连接
spring.redis.lettuce.pool.max-idle=50
#连接池中最小空闲连接
spring.redis.lettuce.pool.min-idle=5
#eviction线程调度时间间隔
spring.redis.lettuce.pool.time-between-eviction-runs=1

redis配置类RedisConfig.java

@Configuration
public class RedisConfig {
    @Bean
    RedisTemplate redisTemplate(LettuceConnectionFactory factory){
        factory.setShareNativeConnection(false);
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }
}

上面3步就能完成springboot使用lettuce连接池整合redis的配置,之后我们就可以在业务类中注入RedisTemplate来使用了。


lettuce初始化


我们看一下整个初始化流程相关类的UML类图

微信图片_20221212112010.png

LettuceConnectionConfiguration类是lettuce初始化的起始类,这个类是spring的管理的配置类,它初始化了lettuce连接工厂类,见如下代码

@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public LettuceConnectionFactory redisConnectionFactory(ClientResources clientResources)
    throws UnknownHostException {
  LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(clientResources,
      this.properties.getLettuce().getPool());
  return createLettuceConnectionFactory(clientConfig);
}

初始化的过程会判断是单点模式/集群模式/哨兵模式,来初始化连接工厂,本文以单点模式为例来讲解

private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
  if (getSentinelConfig() != null) {
    return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration);
  }
  if (getClusterConfiguration() != null) {
    return new LettuceConnectionFactory(getClusterConfiguration(), clientConfiguration);
  }
  return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
}

获取到工厂类以后,lettuce会用如下2个Provider来获取和释放连接,分别管理普通模式和交互模式的连接。本示例采用单机的redis模式,所以初始化后的Provider是StandaloneConnectionProvider。

private @Nullable LettuceConnectionProvider connectionProvider;
private @Nullable LettuceConnectionProvider reactiveConnectionProvider;
public void afterPropertiesSet() {
    this.client = createClient();
    this.connectionProvider = createConnectionProvider(client, LettuceConnection.CODEC);
    this.reactiveConnectionProvider = createConnectionProvider(client, LettuceReactiveRedisConnection.CODEC);
        //省略部分代码
  }

注意:上面创建的provider类型是LettucePoolingConnectionProvider,它是StandaloneConnectionProvider的装饰器类,每次获取和释放连接,工厂类都会通过LettucePoolingConnectionProvider类调用LettucePoolingConnectionProvider的获取和释放操作


使用netty创建连接


lettuce的连接是靠netty来管理的,这或许是它性能优秀的重要原因。我们看一下通过netty来创建连接的代码,看一下StandaloneConnectionProvider的下面方法:

public <T extends StatefulConnection<?, ?>> T getConnection(Class<T> connectionType) {
    //省略部分代码
    if (StatefulConnection.class.isAssignableFrom(connectionType)) {
      return connectionType.cast(readFrom.map(it -> this.masterReplicaConnection(redisURISupplier.get(), it))
          .orElseGet(() -> client.connect(codec)));
    }
    throw new UnsupportedOperationException("Connection type " + connectionType + " not supported!");
  }

上面的client.connect(codec)是创建连接的代码,一直跟踪这个方法

private void initializeChannelAsync0(ConnectionBuilder connectionBuilder, CompletableFuture<Channel> channelReadyFuture,
            SocketAddress redisAddress) {
        logger.debug("Connecting to Redis at {}", redisAddress);
        Bootstrap redisBootstrap = connectionBuilder.bootstrap();
        RedisChannelInitializer initializer = connectionBuilder.build();
        redisBootstrap.handler(initializer);
        clientResources.nettyCustomizer().afterBootstrapInitialized(redisBootstrap);
        CompletableFuture<Boolean> initFuture = initializer.channelInitialized();
        ChannelFuture connectFuture = redisBootstrap.connect(redisAddress);
    //省略部分代码    
    }

管理连接


执行请求命令的时候首先要获取连接,流程图如下

微信图片_20221212112238.png

关键代码

LettucePoolingConnectionProvider中getConnection

public <T extends StatefulConnection<?, ?>> T getConnection(Class<T> connectionType) {
  GenericObjectPool<StatefulConnection<?, ?>> pool = pools.computeIfAbsent(connectionType, poolType -> {
    return ConnectionPoolSupport.createGenericObjectPool(() -> connectionProvider.getConnection(connectionType),
        poolConfig, false);
  });
  try {
    StatefulConnection<?, ?> connection = pool.borrowObject();
    poolRef.put(connection, pool);
    return connectionType.cast(connection);
  } catch (Exception e) {
    throw new PoolException("Could not get a resource from the pool", e);
  }
}


相关文章
|
NoSQL 网络协议 数据库
为什么 Lettuce 会带来更长的故障时间
本文详述了阿里云数据库 Tair/Redis 将使用长连接客户端在非预期故障宕机切换场景下的恢复时间从最初的 900s 降到 120s 再到 30s的优化过程,涉及产品优化,开源产品问题修复等诸多方面。
68275 11
为什么 Lettuce 会带来更长的故障时间
|
NoSQL IDE 网络协议
Redis高级客户端Lettuce详解(下)
Lettuce是一个Redis的Java驱动包,初识她的时候是使用RedisTemplate的时候遇到点问题Debug到底层的一些源码,发现spring-data-redis的驱动包在某个版本之后替换为Lettuce。既然能被Spring生态所认可,Lettuce想必有过人之处,于是笔者花时间阅读她的官方文档,整理测试示例,写下这篇文章。
5552 0
|
NoSQL 前端开发 Java
Lettuce的特性和内部实现问题之Lettuce基于Netty框架实现的问题如何解决
Lettuce的特性和内部实现问题之Lettuce基于Netty框架实现的问题如何解决
221 0
|
NoSQL 网络协议 Java
Redis客户端Lettuce深度分析介绍(上)
Spring Boot自2.0版本开始默认使用Lettuce作为Redis的客户端(注1)。Lettuce客户端基于Netty的NIO框架实现,对于大多数的Redis操作,只需要维持单一的连接即可高效支持业务端的并发请求 —— 这点与Jedis的连接池模式有很大不同。同时,Lettuce支持的特性更加全面,且其性能表现并不逊于,甚至优于Jedis。本文通过分析Lettuce的特性和内部实现(基于6.0版本),及其与Jedis的对照比较,对这两种客户端,以及Redis服务端进行深度探讨。
102955 8
|
7月前
|
NoSQL Java API
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Spring Boot 集成 Redis
本文介绍了在Spring Boot中集成Redis的方法,包括依赖导入、Redis配置及常用API的使用。通过导入`spring-boot-starter-data-redis`依赖和配置`application.yml`文件,可轻松实现Redis集成。文中详细讲解了StringRedisTemplate的使用,适用于字符串操作,并结合FastJSON将实体类转换为JSON存储。还展示了Redis的string、hash和list类型的操作示例。最后总结了Redis在缓存和高并发场景中的应用价值,并提供课程源代码下载链接。
1753 0
|
算法 NoSQL 安全
Lettuce的特性和内部实现问题之Lettuce的多连接模式与连接池模式有何不同
Lettuce的特性和内部实现问题之Lettuce的多连接模式与连接池模式有何不同
324 2
|
NoSQL Java 数据库连接
springboot整合Redis中连接池jedis与lettuce的对比和实现
springboot整合Redis中连接池jedis与lettuce的对比和实现
2243 0
|
缓存 数据可视化 安全
Lettuce的特性和内部实现问题之Lettuce在连接池模式下的整体性能表现偏低的问题如何解决
Lettuce的特性和内部实现问题之Lettuce在连接池模式下的整体性能表现偏低的问题如何解决
361 0
|
NoSQL 网络协议 安全
Lettuce的特性和内部实现问题之Lettuce天然地使用管道模式与Redis交互的问题如何解决
Lettuce的特性和内部实现问题之Lettuce天然地使用管道模式与Redis交互的问题如何解决
168 0
|
监控 NoSQL 网络安全
Redis 6和7:探索新版本中的新特性
Redis 6和7:探索新版本中的新特性