某一日正在孜孜不倦的研究代码,忽然测试童鞋说系统服务挂了,完全不可用。
程序大量抛出如下异常:
对于程序员来说,系统宕机就是军令,更可况是难得一见的连接池泄露问题。因为在企业级的Java项目中,一般采用连接池技术和Spring的事务管理技术来处理数据访问需求,而这两项技术都是久经考验的成熟可靠技术,出问题的几率很小。
其实从异常堆栈来看,明确指出连接池中的连接全部处于激活使用的状态
按照我个人经验来说,连接耗尽可能有两种原因:
1. 系统负载很高,总连接数确实不够用,因此资源耗尽。
2. 数据库连接出现泄漏,无法正常回收,资源只出不进,导致资源耗尽。
第一种可能性很好验证,调整连接池配置参数`maxActive=500`,观察系统是否健康运行。但调整之后验证失败,500个连接依然迅速耗尽,那基本上确认是连接泄露问题。
首先要定位可能导致连接泄露的代码位置,查看系统运行日志,果然从中找出蛛丝马迹(注:资源使用的异常处理机制非常重要不可或缺,否则大海捞针全靠猜)。
日志中发现ERROR信息:
这个ERROR连续出现N次之后,系统抛出CannotGetJdbcConnectionException,猜测很有可能问题就出现在这里。
`No value for key`的告警信息初始体现在Spring框架TransactionSynchronizationManager的unbindResource方法中,源码如下:
从源码中可以看出,Spring在线程中unbind resource时,发现待处理的key不存在,抛出异常,终止了资源释放处理过程。
Spring的资源释放流程简图如下:
了解了这个过程之后,仔细分析异常日志中的代码行数,发现其中有一行编程式的事务处理代码:
具体为什么写这一行,在这里就不再赘述了。但是因为这个编程式的事务处理逻辑不当,错误的unbind dataSource,导致后续Spring的资源处理过程中(上图第2步)处理失败,无法继续回收数据库连接,连接只能获取不能回收,因此资源池迅速耗尽。
问题基本上已经明确解决,但因此衍生的思考是Spring的事务处理机制原理。
每一次线上问题的处理,都需要深刻理解背后的技术原理,多体验多分析多积累!
领取专属 10元无门槛券
私享最新 技术干货