一、问题
代码中使用了redis的发布订阅功能,因为jedis实例不是线程安全的,所以用了jedispool来获取jedis实例,但是因为对jedispool不太了解,所以使用的时候出了一点问题。就是当jedispool资源耗尽之后,代码会进入等待,但是不报错,也不会自动断开,经过查询发现如果没有自己设置阻塞的时间,这个阻塞是永久的。就特么坑。所以记录下jedispool的一些介绍
二、JiedisPool介绍
为什么选择JedisPool
- Jedis实例不是线程安全的,所以不可以多个线程共用一个Jedis实例,但是创建太多的实现也不好因为这意味着会建立很多sokcet连接。
- JedisPool是一个线程安全的网络连接池。可以用JedisPool创建一些可靠Jedis实例,可以从池中获取Jedis实例,使用完后再把Jedis实例还回JedisPool。这种方式可以避免创建大量socket连接并且会实现高效的性能.
JedisPool的使用
JedisPool#getResource()
方法从连接池中获取一个Jedis实例- 使用Jedis实例进行正常的数据操作
- 资源释放,Jedis实例使用完成之后,在放回到JedisPool中(我的问题就出在这里,使用完了之后没有归还实例,导致线程池耗尽,进入永久阻塞)
资源释放
- 关于如何将使用完后的Jedis实例还回连接池,网上看到的大部分文章都是建议用
JedisPool#returnResource
方法,这些文章大多是3,4年前的文章。 - 现在已经更新到了3.3.0的版本了,在jedis2.8.0中已经将
JedisPool#returnResource
方法废弃了,并明确说明这个方法的功能由Jedis.close()
方法代替。
void redis.clients.jedis.JedisPool.returnResource(Jedis resource)
@Override
@Deprecated
不推荐. starting from Jedis 3.0 this method will not be exposed. Resource cleanup should be done using @see redis.clients.jedis.Jedis.close()
本文作者注:从Jedis 3.0开始这个方法将不再暴露给外部(public),资源清除应该调用用Jedis.close()方法
覆盖:Pool 中的 returnResource(...)
参数:resource
- 如果进一步查看
Jedis#close()
方法的源码,会发现,close()方法最终依然是调用JedisPool#returnResource
方法(本文作者注:2.8.0版本是这样,查看了3.3.0版本,已经不在调用这个方法了),可以看到,当使用JedisPool时,close方法并没有真的执行client.close方法,只是将它还给JedisPool连接池,以供下次使用。
问题解决
根据上面的说明,对于最开始的问题解决就是在使用完jedis实例之后关闭jedis实例,果然最后问题解决了。
public void startPub(String msg) {
Jedis publishJedis = jedisPool.getResource();
publishJedis.publish(channel, msg);
if (publishJedis != null) {
publishJedis.close();
}
}
参考: