likes
comments
collection
share

活久见,Mycat低访问量竟然也能导致系统问题!

作者站长头像
站长
· 阅读数 3

前言

如果对系统来说高并发是原罪,低访问量也可能是引起问题的根源!

近期不断有同事反馈,发布验证时不时会遇到mycat链接异常的问题,但是相关功能多点击几次以后也能正常。都是上线运行几年了的老功能,起初认为是mycat的连接数不够用导致的,没有在意。俗话说“欠债迟早要还”,果不其然前几天一个很重要的功能因为同样的问题,导致需要紧急操作的业务受阻!虽然及时搞定了阻塞的业务,终究是系统隐患,是时候亮出三板斧查清问题根源了!

## 问题诊断三板斧之一:分析日志,合理推测

从日志排查发现,功能异常时都报了一个java.io.IOException, 原因是Connection reset by peer。简单分析下,通常导致链接异常的根源无外乎两种:一个是Sql执行时间超长导致链接中断,再一种是使用了无效的链接。根据排查错误日志和相关场景的复现,没有发现超时执行的sql,而且异常信息的日志也是相关执行功能后就几乎同时产生了,基本排除了第一种情况的可能性。那么什么情况下会导致使用了无效链接?更确切的说是什么原因导致数据库链接被中断了?这里就会牵涉到三个主体Jboss,Mycat,Mysql

活久见,Mycat低访问量竟然也能导致系统问题! 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版本)是如何管理连接池的。

活久见,Mycat低访问量竟然也能导致系统问题! 如上图所示Io.mycat.MycatServer,Mycat启动以后连接池会对配置的数据库节点初始化一批链接,同时针对这些链接也会进行心跳检测,对应的处理类ConnectionHeartBeatHandler 执行频率是server.xml里面配置的600000--单位是ms HeartBeat的核心功能主要有三个:心跳检测,关闭超时的空闲链接,空闲链接数少于最小值时生成链接(详见类io.mycat.backend.datasource.PhysicalDatasource)

活久见,Mycat低访问量竟然也能导致系统问题! 针对问题,需要重点关注下心跳检测

活久见,Mycat低访问量竟然也能导致系统问题! 上图是心跳检测的前置检查io.mycat.backend.datasource.PhysicalDatasource 有个参数需要注意int maxConsInOneCheck = 5,每次进行心跳检测的最大值。

活久见,Mycat低访问量竟然也能导致系统问题! 取出链接池的所有链接,依次循环处理确认超时(最新连接时间lastTime小于当前时间后推两个心跳时间)链接直接关闭,对可能有效的链接(最新连接时间lastTime小于当前时间往后推一个心跳间隔时间)进行心跳检测,并且限制一次最大处理链接数5个,超过则重新返回线程池,等待下次心跳检测(600s后)处理。 需要注意的是绿框的con.setBorrowed(true)该方法最终会重置当前链接的lastTime

活久见,Mycat低访问量竟然也能导致系统问题! 连接池基于ConcurrentLinkedQueue进行链接的管理,功能正常使用的链接和检活使用的链接都是取自该队列的,队列的FIFO特性可以保证有限次的心跳检测可以覆盖所有的连接池链接,但是600s的心跳检测时间间隔*n(n>=1)的检测次数和60s的数据库空闲链接失效时间导致的时间差,再加上有大量空闲链接的情况下,一定会出现一些失效链接清理不及时的情况,导致问题出现。

## 问题诊断三板斧之三:对症下药,观测监控

问题根源已经定位,只要解决了心跳检测时间和数据库空闲链接失效时间的时间差问题,就可以避免异常的出现,对应措施如下:

  1. 调低心跳检测间隔。
  2. 降低最低连接数配置。 参数的配置都需要契合系统的实际使用和业务情况,这里不再赘述。每个系统都有自己的特性,过大过小都会对系统造成一定的影响,建议通过压测和实际业务的定期监控来定一个适合系统的值。

## 总结

复盘发现,问题的诱因居然是对Mycat的连接池使用率过低。项目初期对Mycat使用比较多,随着一些功能的迁移,使用率也逐渐降低,过低的使用率和前期配置的较大的连接数,加上不合理的超时配置,以及Mycat的心跳机制导致问题的出现。

## 后记

排查期间有个插曲,数据库的空闲链接失效时间开始误以为是connection_timeout,这个值配置的是10h,反推心跳检测的逻辑,按目前配置的连接数,即使再翻几倍的量,也无论如何不会出现链接超时的情况,但错误日志又明确摆在那里!无奈都打算放弃了,偶现灵光,重新按idle关键字搜索Mysql全局配置,thread_pool_idle_timeout=60s,激动之余,立马查资料核实其作用,确认这才是正主,一切都解释的通了!柳暗花明的感觉真好!