文章目录
记一次Kafka不合理使用导致CPU过载的排查思路及解决办法
背景简述
背景
项目提测一天,测试杨主任火急火燎的找到研发说,部署了程序之后,Liunx服务器CPU一直处于100%的过载状态,进而导致整个平台出现卡顿,且整个接口调用链路出现宕机。情况紧急,几号测试人员干巴巴的等着研发解决此问题才能继续功能测试。
立马登录服务器看了CPU情况,当时脑子里也是一头雾水。但是立即整理了相关思绪,随即开始了艰难坎坷的问题排查解决过程。
服务器环境
硬件环境
[root@HikvisionOS ~]# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 20
On-line CPU(s) list: 0-19
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 20
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 94
Model name: Intel Core Processor (Skylake)
Stepping: 3
CPU MHz: 2194.916
BogoMIPS: 4389.83
Virtualization: VT-x
Hypervisor vendor: KVM
Virtualization type: full
L1d cache: 32K
L1i cache: 32K
L2 cache: 4096K
NUMA node0 CPU(s): 0-19
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpelm constant_tsc rep_good nopl eagerfpu pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsavedrand hypervisor lahf_lm abm 3dnowprefetch tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rdx smap xsaveopt arat
从服务器信息中基本可以得到以下几个重要信息:
- 这是一台通过KVM技术虚出来的虚拟机;
- CPU频率约为2G赫兹;
- 逻辑CPU线程数量为20个(虚拟CPU的处理能力相较于物理机会菜鸡很多);
软件环境
服务器上安装了OIM2.2产品,其中包括基础环境组件、通用服务组件、共性行业组件及行业业务组件,主要的行业业务组件为osurveillance
,opunishment
,oflowinspect
。
排查思路
问题现象
平台搭建完,开始发送模拟数据一段时间之后,就会出现如下两个问题:
1、CPU爆仓,一直处于99%的过载状态;
2、查看osurveillance组件日志,一直打印Kafka提交offsets失败,死循环重复消费数据;
问题分析
根据top查看CPU使用情况可以锁定导致CPU高的几个组件为osurveillance和opunishment,而这两个组件的共性问题都是存在大量的Kafka数据消费者,且代码实现逻辑和配置文件基本一致(同一批开发人员开发)。
因此大胆猜测问题是出在不合理的Kafka配置导致数据消费异常。
阶段一
由于更换服务器来排除是否是服务器本身问题比较耗费人力物力,所以还是从组件代码以及参数配置进行排查分析,真的迫不得已的情况下再考虑更换服务器环境。
首先停止osurveillance和opunishment组件一段时间之后,服务器CPU即可恢复至百分之三四十的状态,验证了一来是的猜测,基本可以锁定问题由几个业务组件导致;
修改Kafka组标识groupId之后,重启组件,不发送模拟数据,CPU也不会出现暴涨的状态,由此可知是数据消费导致的问题。在此过程中发现,offsets提交失败的数据会一直重复提交,几乎就是一个死循环;
根据错误日志,一开始会以为是每次拉取的数据过多导致消费来不及,所以修改相应Kafka配置参数(延长拉取间隔时间max.poll.interval.ms,及减少每次拉取数量max.poll.records),进行观察,但是发现问题依旧复现;
阶段二
在检查修改Kafka参数的过程中,发现配置并发消费线程数concurrency为10,直觉不妙,拿出纸笔加减乘除了一番:
程序段 | 消费者数量 | 线程数(消费者数*concurrency) |
---|---|---|
osurveillance-basic | 5 | 50 |
osurveillance-warn | 1 | 10 |
osurveillance-visual | 0 | 0 |
opunishment-basic | 3 | 30 |
opunishment-lawv1 | 0 | 0 |
opunishment-cover | 0 | 0 |
合计:90 |
一算吓一跳,假如消费者火力全开(都重复消费了,很明显已经火力全开),单单Kafka即会开启90个消费线程,而CPU逻辑线程才20个,参照性能彪悍的CPU内核,基本也只能承受23倍逻辑线程(4060)数量的应用线程,更何况这是虚拟CPU内核,可以锁定CPU爆仓问题即在此。
阶段三
接下来及排查下应用一直处于重复消费数据问题。根据实际的代码实现(使用了一种延时消费管道的思路,即应用消费能力不足停止数据接收,消费能力过剩数据消费者休眠,在应用层主动控制消费者的开关以及消费速率),整理数据消费流程如下:
整理数据消费流程之后,可以确定死循环消费数据是由于红线标注的流程环节导致,核心问题就是在应用消费能力不足时候调用了Kafka的stop方法手动关闭监听容器。
我猜想当时研发人员开发数据消费管道模型时候直接参考了ActiveMQ的消费方式,但是忽略了Kafka中有offsets偏移量的存在,且应用设置为自动提交偏移量,这就导致存在一直无法提交偏移量的可能及风险性。
问题小结
经过问题分析排查,基本已经锁定了问题的原因为:过大的Kafka并发消费数设置和代码中不合时宜的手动stop Kafka消费监听容器。而且这两个问题具备关联性,因为Kafka消费能力不足打开了所有的消费线程并且关闭了消费监听,导致CPU开始暴涨,服务器出现卡顿情况,由此引发应用消费变得缓慢,进而又导致了Kafka消费数据不及时及重复消费。这就产生了一个闭环的问题链,一般情况下无法通过时间或者重启组件修复问题。
解决方案
那么解决方案也就显而易见了。
根据实际硬件CPU逻辑线程数配置适宜的Kafka并发消费线程数,防止CPU爆仓:
在代码逻辑中慎重使用手动停止Kafka的消费监听容器,防止未提交偏移量和重复消费;
知识小结
- 查看服务器基本信息指令:lscpu;
- 查看当前服务器CPU使用情况:top -c,想要查看各个CPU内核使用状况,继续按1;
- 虚拟机的CPU为虚拟化构建,n个虚拟核心,大约最多只能够支持3*n个线程的java并发;
- 当服务器CPU达到100%的时候,执行shell执行都会卡的你“动弹不得”,这时候有一个应急措施:重启服务器,shutdown -r now;
- Kafka配置并发消费线程数concurrency会应用到每个消费组,即总并发线程数为:group消费组*concurrency;
- Kafka设置为自动提交offset偏移量之后,手动关闭消费容器会导致拉取时间间隔内的数据无法提交偏移量;
过程坎坷,结果喜人
created by 袁志鹏 on 四月 1,2021
last edited by 袁志鹏 on 五月 6,2021