活久见,Mycat低访问量竟然也能导致系统问题!
前言
如果对系统来说高并发是原罪,低访问量也可能是引起问题的根源!
近期不断有同事反馈,发布验证时不时会遇到mycat链接异常的问题,但是相关功能多点击几次以后也能正常。都是上线运行几年了的老功能,起初认为是mycat的连接数不够用导致的,没有在意。俗话说“欠债迟早要还”,果不其然前几天一个很重要的功能因为同样的问题,导致需要紧急操作的业务受阻!虽然及时搞定了阻塞的业务,终究是系统隐患,是时候亮出三板斧查清问题根源了!
## 问题诊断三板斧之一:分析日志,合理推测
从日志排查发现,功能异常时都报了一个java.io.IOException, 原因是Connection reset by peer。简单分析下,通常导致链接异常的根源无外乎两种:一个是Sql执行时间超长导致链接中断,再一种是使用了无效的链接。根据排查错误日志和相关场景的复现,没有发现超时执行的sql,而且异常信息的日志也是相关执行功能后就几乎同时产生了,基本排除了第一种情况的可能性。那么什么情况下会导致使用了无效链接?更确切的说是什么原因导致数据库链接被中断了?这里就会牵涉到三个主体Jboss,Mycat,Mysql
Jboss维护了一个连接池,负责获取和管理Mycat的链接。同时Mycat也维护了一个连接池,主要是获取和管理Mysql数据库的链接。为了降低空闲链接对系统资源的占用浪费, Mycat和Mysql通常默认都加了对链接的空闲超时配置。Mysql的thread_pool_idle_timeout默认的值是60s,Mycat的idleTimeout的默认配置是1800000ms(1800s),理论上空闲链接只要超过上述时间间隔就会从服务端主动中断,而不会通知前端连接池的,使用了这种中断的链接就会出现上述异常。
但是为了尽可能避免使用这种无效链接而造成的业务影响,通常配置数据源连接池的时候都会加上检活,定期对连接池的链接进行检测,失效链接会被关闭。难道是没有加检活配置?分别核实了Jboss上对Mycat的数据源连接池配置,以及Mycat的server.xml里面对数据库的连接池配置都已经分别加上检活配置,仍然出现无效链接导致的异常,只能怀疑检活没起到应有作用。继续分析日志发现mycat.backend.mysql.nio.MySQLConnection$StatusSync 基本每次异常都会出现它的身影,难道是Mycat连接池的检活不能清除这种无效链接?
## 问题诊断三板斧之二:推测溯源,复盘求证
要想弄清楚,就必须从源码入手了解Mycat(针对目前使用的Mycat的1.6 release版本)是如何管理连接池的。
如上图所示Io.mycat.MycatServer,Mycat启动以后连接池会对配置的数据库节点初始化一批链接,同时针对这些链接也会进行心跳检测,对应的处理类ConnectionHeartBeatHandler
执行频率是server.xml里面配置的600000--单位是ms
HeartBeat的核心功能主要有三个:心跳检测,关闭超时的空闲链接,空闲链接数少于最小值时生成链接(详见类io.mycat.backend.datasource.PhysicalDatasource)
针对问题,需要重点关注下心跳检测
上图是心跳检测的前置检查io.mycat.backend.datasource.PhysicalDatasource
有个参数需要注意int maxConsInOneCheck = 5,每次进行心跳检测的最大值。
取出链接池的所有链接,依次循环处理确认超时(最新连接时间lastTime小于当前时间后推两个心跳时间)链接直接关闭,对可能有效的链接(最新连接时间lastTime小于当前时间往后推一个心跳间隔时间)进行心跳检测,并且限制一次最大处理链接数5个,超过则重新返回线程池,等待下次心跳检测(600s后)处理。
需要注意的是绿框的con.setBorrowed(true)该方法最终会重置当前链接的lastTime
连接池基于ConcurrentLinkedQueue进行链接的管理,功能正常使用的链接和检活使用的链接都是取自该队列的,队列的FIFO特性可以保证有限次的心跳检测可以覆盖所有的连接池链接,但是600s的心跳检测时间间隔*n(n>=1)的检测次数和60s的数据库空闲链接失效时间导致的时间差,再加上有大量空闲链接的情况下,一定会出现一些失效链接清理不及时的情况,导致问题出现。
## 问题诊断三板斧之三:对症下药,观测监控
问题根源已经定位,只要解决了心跳检测时间和数据库空闲链接失效时间的时间差问题,就可以避免异常的出现,对应措施如下:
- 调低心跳检测间隔。
- 降低最低连接数配置。 参数的配置都需要契合系统的实际使用和业务情况,这里不再赘述。每个系统都有自己的特性,过大过小都会对系统造成一定的影响,建议通过压测和实际业务的定期监控来定一个适合系统的值。
## 总结
复盘发现,问题的诱因居然是对Mycat的连接池使用率过低。项目初期对Mycat使用比较多,随着一些功能的迁移,使用率也逐渐降低,过低的使用率和前期配置的较大的连接数,加上不合理的超时配置,以及Mycat的心跳机制导致问题的出现。
## 后记
排查期间有个插曲,数据库的空闲链接失效时间开始误以为是connection_timeout,这个值配置的是10h,反推心跳检测的逻辑,按目前配置的连接数,即使再翻几倍的量,也无论如何不会出现链接超时的情况,但错误日志又明确摆在那里!无奈都打算放弃了,偶现灵光,重新按idle关键字搜索Mysql全局配置,thread_pool_idle_timeout=60s,激动之余,立马查资料核实其作用,确认这才是正主,一切都解释的通了!柳暗花明的感觉真好!
转载自:https://juejin.cn/post/6984018058989797384