spring-data-redis 连接泄漏,我 TM 人傻了

本文讲述了作者在使用 spring-data-redis + lettuce 组合时遇到的连接泄漏问题,分析了 lettuce 客户端和 spring-data-redis API 的工作原理,特别是涉及 Pipeline、事务和连接池的情况。作者指出,对于简单的 Redis 命令,不使用连接池可能是更好的选择,而大量使用事务或 Pipeline 应考虑使用连接池。文中提供了防止连接泄漏的建议,如避免使用 `execute(SessionCallback)` 与 `executeWithStickyConnection` 的混合使用,并封装事务相关命令。
摘要由CSDN通过智能技术生成

最近线上又出事儿了,新上线了一个微服务系统,上线之后就开始报各种发往这个系统的请求超时,这是咋回事呢

image

还是经典的通过 JFR 去定位(可以参考我的其他系列文章,经常用到 JFR),对于历史某些请求响应慢,我一般按照如下流程去看:

  1. 是否有 STW(Stop-the-world,参考我的另一篇文章:JVM相关 - SafePoint 与 Stop The World 全解):
  2. 是否有 GC 导致的长时间 STW
  3. 是否有其他原因导致进程所有线程进入 safepoint 导致 STW
  4. 是否 IO 花了太长时间,例如调用其他微服务,访问各种存储(硬盘,数据库,缓存等等)
  5. 是否在某些锁上面阻塞太长时间?
  6. 是否 CPU 占用过高,哪些线程导致的?

通过 JFR 发现是很多 HTTP 线程在一个锁上面阻塞了,这个锁是从 Redis 连接池获取连接的锁。我们的项目使用的 spring-data-redis,底层客户端使用 lettuce。为何会阻塞在这里呢?经过分析,我发现 spring-data-redis 存在连接泄漏的问题

image

我们先来简单介绍下 Lettuce,简单来说 Lettuce 就是使用 Project Reactor + Netty 实现的 Redis 非阻塞响应式客户端。spring-data-redis 是针对 Redis 操作的统一封装。我们项目使用的是 spring-data-redis + Lettuce 的组合。

为了和大家尽量说明白问题的原因,这里先将 spring-data-redis + lettuce API 结构简单介绍下。

首先 lettuce 官方,是不推荐使用连接池的,但是官方没有说,这是什么情况下的决定。这里先放上结论:

  • 如果你的项目中,使用的 spring-data-redis + lettuce,并且使用的都是 Redis 简单命令,没有使用 Redis 事务,Pipeline 等等,那么不使用连接池,是最好的(并且你没有关闭 Lettuce 连接共享,这个默认是开启的)。
  • 如果你的项目中,大量使用了 Redis 事务,那么最好还是使用连接池
  • 其实更准确地说,如果你使用了大量会触发 execute(SessionCallback) 的命令,最好使用连接池,如果你使用的都是 execute(RedisCallback) 的命令,就不太有必要使用连接池了。如果大量使用 Pipeline,最好还是使用连接池。

接下来介绍下 spring-data-redis 的 API 原理。在我们的项目中,主要使用 spring-data-redis 的两个核心 API,即同步的 RedisTemplate 和异步的 ReactiveRedisTemplate。我们这里主要以同步的 RedisTemplate 为例子,说明原理。ReactiveRedisTemplate 其实就是做了异步封装,Lettuce 本身就是异步客户端,所以 ReactiveRedisTemplate 其实实现更简单。

RedisTemplate 的一切 Redis 操作,最终都会被封装成两种操作对象,一是 RedisCallback<T>

public interface RedisCallback<T> {
	@Nullable
	T doInRedis(RedisConnection connection) throws DataAccessException;
}
复制代码

是一个 Functional Interface,入参是 RedisConnection,可以通过使用 RedisConnection 操作 Redis。可以是若干个 Redis 操作的集合。大部分 RedisTemplate 的简单 Redis 操作都是通过这个实现的。例如 Get 请求的源码实现就是:

//在 RedisCallback 的基础上增加统一反序列化的操作
abstract class ValueDeserializingRedisCallback implements RedisCallback<V> {
	private Object key;

	public ValueDeserializingRedisCallback(Object key) {
		this.key 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值