6.19号下午 线上系统出现了一次实时链路数据 不通畅的问题, 业务方反应更新的增量数据没有流入到HA3搜索集群
登录机器后检查日志后发现,在周六晚上到周天下午,cr_search_merge(机器人schema统一)表增量数据猛增,初步估计瞬间压在DRC-reader上的数据达百万级别的量
DRC-reader 应用会不间断的报 :
和 下面的错误:
这2个错误都是之前未曾出现过的错误。
尝试重启应用后,隔几分钟后出现相同的错.
线上 DRC-reader 的原理如下图:
原理: 程序从DRC 读取数据后, 经过不同步骤的数据处理后,通过多个管道不停的往后传输,最终达到输出器输出在不同的持久化介质上。输出器在把数据输出到介质时,会双写时间戳到ZK,应用重启时,从该时间戳的点位开始读,这样可以解决在应用重启时或机器崩溃时内存中的数据丢失的问题。
第一个错定位到日志打印处,分析代码后发现:当最后一个数据处理队列(上图中的 LBQueue writeRecords 队列)的容量达到限定的容量后,会抛出这个异常,并中断程序.
于是尝试把最后一个队列的限制长度调大,从1W调到100W , 重启应用第一个错不再出现。
但写ZK的错依然在报,再次调整配置参数:
(1) 把ZK的写时间戳间隔加大,
(2)把ZK的启动时间戳改写到1个小时前.
这个错也不再报了, 但输出器输出到持久化介质的速度非常慢,在经过几个小时的消化后,系统恢复正常,数据正常流转。
由于出问题时正值团队在外outing,只能通过调系统参数来规避这些问题。
周二回到公司后,尝试来重现当时的场景。 于是找了一台测试机器,把DRC的时间戳定位到一天前,把系统参数调回去,另外添加了些JVM DEBUG参数,把GC日志打印出来,保存系统崩溃时的内存快照。运行系统,果然问题复现了。
top 指令结果如下:
load升高,CPU飙升,系统卡死,同时应用日志报如下的错:
这个错很明显了,有GC问题,于是, 执行下面的命令,把内存dump下来分析
jmap -J-d64 -dump:format=b,file=dump.bin 23218
用 jvisualvm分析发现:
发现虽然最后的管道限制了长度为10000,但中间的管道消息数量堆积达到了25W,这些数据堆积在内存里,使得JVM频繁GC,占据了大量的CPU时间。
现在的架构使得,前面的DRC入口处并不知道后面管道的容量情况,无法对系统进行保护,不断的放入数据到系统,内存瞬间暴增,发生了GC问题。
数据流入系统的速度 比 系统输出数据的速度快,系统本身并未做限流措施,是本次GC问题的根本原因。
我做了个简单的统计(单机):
流入的速度 大概高峰期间每秒可达: 3500/QPS
流出的速度: 最后一根管道输出器: 单线程的情况下,250QPS左右.
开到三个线程并发输出: 400QPS左右
这中间的差异会导致消息堆积,堆积在内存到一定量后必然发生GC问题,GC问题会进一步导致输出QPS降低,产生恶性循环。
解决方案:
在系统内部 和 外部进行限流。
内部: 在读取DRC数据入口处,通过接口拿到中间管道的队列长度,进行限流(目前限制1W)。
外部: 依赖外部的系统承受能力,和相关同学沟通,限流500左右.
做完这些工作后,再次运行系统,观察系统指标:
系统运行比较平稳了,上述的错误均未再出现。
后续会进一步提升输出器的并发能力,把这个提升上去后,可调高限流参数参数,使得整个系统的吞吐数据的能力提高