最近使用jmeter压测了一下网站首页,发现流量上来之后,过了一会,整个网站就不能打开了,白页。然后就登录sentry日志平台查看异常,发现有几千个jedis相关的异常
异常信息:获取不到redis链接
Spring Boot版本:2.0.8.RELEASE
jedis: 2.9.0
commons-pool2: 2.2
[http-nio-8017-exec-515] - get异常
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:286)
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
jstack
然后就尝试使用jstack抓一下JVM线程堆栈,看看JVM在忙什么。ssh远程登录到服务器,然后使用jps -l来查看java进程,定位到进程pid
[root@xxxbt02 current]# jstack 15782 > ec-04.txt
通过观察jstack发现有很多业务线程接近200个在超时等待redis连接。顺便抓一下内存dump文件。
jmap
[root@xxxbt02 current]# jmap -dump:format=b,file=ec-jmap-01.hprof 15782
Dumping heap to /opt/xxx/xxx-order-api/current/ec-jmap-01.hprof ...
Heap dump file created
[root@xxxbt02 current]#
结论
通过分析jstack堆栈发现,大量线程在尝试获取redis连接,因为配置了最大等待时间2s, 在2s后获取不到连接,导致抛出异常RedisConnectionFailureException。然后线程任务结束。这就证明在2s内已始终获取不到redis连接。由于程序没有兜底方案,所以整个JVM中的服务不可用。应该是redis连接池中始终无可用连接。那应该就是默认的8个连接全部被占用了,没有被释放。最后通过Jedis GitHub源码库也验证了这个问题。原文地址:JedisPool exhausted in Jedis 2.10.0,根据异常表现,线程堆栈及相关源码,确实验证了这一点:连接池中的8个连接被耗尽了,没有返还到池中。所以后来的线程任务一直拿不到redis连接。接着将jedis版本升级到2.10.2,再次测试发现问题修复了。后来也督促项目组对相关代码做了优化,减少redis访问次数,增加兜底方案,增加guava本地缓存,同时将jedis换成lettuce。
参考资料
- 记一次 Redis 连接池泄漏问题排查
- JedisPool资源池优化
- 使用redisTemplate高并发下连接池满的问题
- redis连接未释放,导致redis连接池满,从而应用服务不可用的问题定位和解决
- 由Redis客户端连接数大小说开去
- 彻底解决 Jedis 连接池 获取不到连接,连接放回连接池错误的问题
- Jedis连接池满相关解决方案
- seaweedfs文件存储服务器搭建
网友评论