本文共 1049 字,大约阅读时间需要 3 分钟。
内网沙盒环境中,我们遇到了API持续卡死的问题,所有API无响应。刚开始测试人员反映响应慢,重启应用后恢复正常,但后来问题频发,越来越多同事抱怨,怀疑代码存在问题。经过排查,发现本地IDE环境没有异常,数据库、Redis均正常,无特殊错误日志,初步怀疑是沙盒环境机器问题。
SSH上服务器执行top命令,发现机器状态正常,但仍有疑虑。打算查看JVM堆栈信息,先查看问题应用较耗资源的线程,执行top -H -p 12798,找到前3个相对比较耗资源的线程。接着使用jstack查看堆内存,分析线程状态,发现大量线程处于锁定状态,但未发现业务相关代码,暂时无头绪。
为了保护现场,dump了问题进程的堆内存,进入调试模式重启测试环境应用,等待问题再次出现后远程调试问题机器。第二天问题再现,通知运维将Nginx转发取消问题应用,自己远程调试Tomcat。
随意找一个接口,断点在入口处,发现API等待服务响应,未进入断点。尝试在AOP之前打断点,发现进入断点,F8步进后发现执行Redis命令时卡主。继续跟踪,发现问题出在Jedis的连接获取过程。
进一步分析发现,pool.getResource()后线程开始wait状态。查看pool配置,发现未配置MaxWaitMillis,尝试配置后问题依旧。继续追踪,发现线程处于锁状态,使用Arthas诊断工具,发现大量http-nio线程处于waiting状态。
详细分析发现,问题是由于Redis连接获取问题,原因是pool配置不合理,未设置MaxWaitMillis。配置后重启服务,等待一天,问题再次复现。检查Tomcat accesslog,发现大量API请求出现500错误。
追踪500错误,发现问题出在stringRedisTemplate的使用方式上。stringRedisTemplate.getConnectionFactory().getConnection()获取连接后未正确释放,导致Redis连接池中连接未归还。正确使用RedisCallback执行时,操作完成后使用RedisConnectionUtils.releaseConnection()释放连接。
总结:使用stringRedisTemplate执行特定命令时,应采用RedisCallback方式,并正确释放连接。避免直接使用getConnection(),以免影响连接池状态。合理配置Redis pool,避免长时间等待,确保连接及时释放,避免阻塞和资源浪费。
转载地址:http://aktfk.baihongyu.com/