最近项目在做性能压测,框架使用的是 spring boot 2.1.2 + jedis 2.9.1,80个并发持续压测4-5分钟服务就假死,所有的请求就pending,查看服务日志没有任何异常,查看其它没有使用redis的接口都能正常请求。
查看了一下redis的连接配置,都是正常够用的
再使用jstack看一下堆栈信息

发现很多WAITING的线程,再往下看都是redis的getResource方法导致的等待。
查看redis的源码Jedis.java
@Override
public void close() {
if (dataSource != null) {//1
if (client.isBroken()) {
this.dataSource.returnBrokenResource(this);
} else {
this.dataSource.
returnResource(this); // 2
}
this.dataSource = null; // 3
} else {
super.close();
}
}
分析一下这个代码,client.isBroken()这里默认值是false,连接释放的时候先释放resource 然后再将dataSource置为null,那如果是并发的情况下话,那有可能再下一个线程进来的时候dataSource已经就是null了,在执行第2步的时候dataSource刚好被置为null,那这个时候就无法释放连接了。这个时候我们再看下获取连接的方法。JedisSentinelPool.java
@Override
public Jedis getResource() {
while (true) {
Jedis jedis = super.getResource();
jedis.setDataSource(this); // <-- This line
// get a reference because it can change concurrently
final HostAndPort master = currentHostMaster;
final HostAndPort connection = new HostAndPort(jedis.getClient().getHost(), jedis.getClient()
.getPort());
if (master.equals(connection)) {
// connected to the correct master
return jedis;
} else {
returnBrokenResource(jedis);
}
}
}
这里jedis.setDataSource(this);设置的则为null。
我查看了一下jedis的github,在issue上有找到有人曾经提出过这样的问题,https://github.com/redis/jedis/issues/1920 ,给出的解决方案是升级jedis的jar包到2.10.2版本以上,换成高版本的以后问题果然就解决了。
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.10.2version>
dependency>
这里要注意一下的是jedis的版本跟spring-data-redis的版本是有一个对应关系的。
spring-data-redis 2.1.x 对应的jedis版本是2.x.x 版本
spring-data-redis 2.2.x 对应的jedis 版本就是3.x版本了
还是看那两个类,看看新版本做了什么改动
public void close() {
if (this.dataSource != null) {
Pool<Jedis> pool = this.dataSource;
this.dataSource = null;
if (this.client.isBroken()) {
pool.returnBrokenResource(this);
} else {
pool.returnResource(this);
}
} else {
super.close();
}
}
这里很明显的处理方式就是讲dataSource复制给pool,然后用pool去释放资源,这个时候设置dataSource与这个就没有关系了,就不存在释放资源释放不了的情况了。