写点什么

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

用户头像
三石
关注
发布于: 2021 年 06 月 21 日
活久见!低访问量竟然也能导致系统问题

前言

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


近期不断有同事反馈,发布验证时不时会遇到 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 里面对数据库的连接池配置都已经分别加上检活配置<check-valid-connection-sql>,仍然出现无效链接导致的异常,只能怀疑检活没起到应有作用。继续分析日志发现 mycat.backend.mysql.nio.MySQLConnection$StatusSync 基本每次异常都会出现它的身影,难道是 Mycat 连接池的检活不能清除这种无效链接?


问题诊断三板斧之二:推测溯源,复盘求证

要想弄清楚,就必须从源码入手了解 Mycat(针对目前使用的 1.6 release 版本)是如何管理连接池的。



如上图所示 Io.mycat.MycatServer,Mycat 启动以后连接池会对配置的数据库节点初始化一批链接,同时针对这些链接也会进行心跳检测。

对应的处理类:io.mycat.backend.mysql.nio.handler.ConnectionHeartBeatHandler

执行周期是 server.xml 里面配置的,单位 ms。

<property name="dataNodeHeartbeatPeriod">600000</property>

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 的数据库空闲链接失效时间导致的时间差,再加上有大量空闲链接的情况下,一定会出现一些失效链接清理不及时的情况,导致问题出现。


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

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

1.     调低心跳检测间隔。

2.     降低最低连接数配置。

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

总结

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

后记

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

发布于: 2021 年 06 月 21 日阅读数: 15
用户头像

三石

关注

大道至简,知易行难! 2018.05.06 加入

我思故我在! 一个苏宁老兵的点滴记录

评论

发布
暂无评论
活久见!低访问量竟然也能导致系统问题