JedisException:Could not get a resource from the pool
一、问题描述
最近一个老项目线上出现一个问题,操作 redis 的地方全都报错
org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:281)
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:464)
at org.springframework.data.redis.cache.DefaultRedisCacheWriter.execute(DefaultRedisCacheWriter.java:238)
at org.springframework.data.redis.cache.DefaultRedisCacheWriter.get(DefaultRedisCacheWriter.java:109)
at org.springframework.data.redis.cache.RedisCache.lookup(RedisCache.java:82)
at org.springframework.cache.support.AbstractValueAdaptingCache.get(AbstractValueAdaptingCache.java:58)
at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:73)
at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:554)
at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:519)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:401)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)…
Caused by: redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:51)
at redis.clients.jedis.JedisSentinelPool.getResource(JedisSentinelPool.java:210)
at redis.clients.jedis.JedisSentinelPool.getResource(JedisSentinelPool.java:17)
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:271)
… 89 common frames omitted
Caused by: java.util.NoSuchElementException: Pool exhausted
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:456)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:365)
at redis.clients.util.Pool.getResource(Pool.java:49)
… 92 common frames omitted
二、问题排查
看报错,感觉是连接池资源耗尽了,看了下配置,最大连接数配的128,然后有四台服务器,不觉得是配少的原因,所以怀疑是连接池资源使用后未释放,但是看了下代码,都是直接用 redisTemplate
操作的,不存在自己操作资源,因为是老项目,使用的版本都比较低,于是怀疑是不是 Jedis 的bug,这边使用的是 Jedis 的 2.9.1 版本,在网上搜了之后,发现确实存在bug,参考:https://github.com/redis/jedis/issues/1920
意思就是:
1、当线程A在 Jedis
类中操作到 returnResource
,还没执行到 this.dataSource = null;
2、线程B在 JedisSentinelPool
类里执行到 jedis.setDataSource(this);
3、这时候线程A执行了 this.dataSource = null;
4、这时候线程B里的 dataSource
就是null了,当线程B执行到 Jedis
类里的 close
方法时,就永远不会走到 returnResource
了
这就导致在高并发的时候,有可能存在线程资源没有正常的回收,长时间运行下来,线程池中可用的资源会越来越少,最终导致没有资源可用,我们这个老项目服务确实有大半年没有重启了,符合这个场景
Jedis在 2.10.2 版本对 Jedis
类的 close
方法做了修改,解决了这个问题
三、解决问题
升级 Jedis 的版本到2.10.2