背景

进入6月后,随着一个主要功能版本api的上线,服务端的QPS翻了一倍,平时服务器的CPU使用稳定在30%上下,高峰期则在60%上下,但是偶尔会有单台机器出现持续数分钟突然飙到90%以上,导致大量api响应缓慢超过客户端等待时间,触发其主动断开连接产生大量nginx499。

问题分析与解决

问题期间器资源情况

仔细查看问题期间的zabbix监控数据,发现90%的CPU占用中有10%上下是sys time, 5%上下是softirq time,两者相加可占到接近20%, interrupt和context switch数由之前的10k/s飙升至20k+/s。

定位kafka log发送代码

首先猜测就是某个新加功能实现有bug,可能造成长时间未执行完成,多个类似请求长期占用CPU而后又被切换为其他线程,反复在这些耗时请求中来回切换却没法完成任意一个请求,并造成后续请求在队列中排队等待,导致大量请求超时响应。

直接入手分析定位新增的某个复杂功能接口,确认不存在死循环的可能,怀疑是接口太耗性能长期占用CPU导致后续请求来不及处理最终连锁反应导致雪崩。

仔细分析其实现,发现总共会触发30+次kafka log发送,之前一直认为使用异步批量发送kafka log的情况下,多发几次log应该不会存在什么问题,不过这里一个请求触发30+次 kafka log发送确实有点太多了。

本着怀疑的精神决定实际验证一番kafka log发送耗时,结果发现每次kafka调用耗时居然在0.2-2ms之间波动,这相当于该复杂接口功能请求光是发送kafka消息就需要6-60ms时间,与之前认为应该很快的假设不符!

进一步分析原因,发现原来沿用的kafka producer初始化配置有大坑,其设置了batch_size=20之前一直理解为是每20条log触发一次实际发送,结果起始batch_size的单位是字节,其表示的是每满20字节触发一次实际发送==!所以实际效果是每次kafka log send都会触发实际发送。通过修改batch_size为64k并设置linger_ms为500ms,验证kafka log一边为批量发送模式后,再次测试kafka消息单次调用耗时变为了<0.1ms。

修改线上api服务kafka参数,并将复杂接口kafka send操作从30+次优化为10+次,reload服务后,意外发现内存占用居然还降了50%(2G=>1G),但是对于日常interrupt、context switch次数未观察到明显下降。

修改后连续几天未再出现CPU飙升偶发问题,但是坚持不到一周再次有机器出现类似问题,优化前平均1~2天一次,多的时候一天就有两三次,优化后出现频率降低为一周两三次,从这个角度来看优化具有一定效果。

尝试扩容解决

由于刚上了一个大版本功能,客户端确实新增了很多api调用,随着新版本覆盖用户数逐步升高,单机负载逐步升高,于是考虑再扩个容看能否解决问题,在某天下午快速扩容一台机器后,晚上又出现了该问题,该简单方案宣告失败。

定位Linux内存水位

在前两个方案尝试解决问题失败后,开始细究一下每5分钟打印一次的机器top快照,仔细硬瞅之下还真发现点端倪:查看了单台机器过去近10次CPU飙涨时段的指标,发现free内存一般在CPU飙涨前剩余不到200M,而CPU恢复正常后free内存一般都剩余>1G,这个看上去有点不同寻常。进一步按图索骥观察到kswapd_low_wmark_hit_quickly取值每天增长上千次,直觉上感觉是偏高的。

难道问题是free内存不足、回收引起的?然而zabbix监控上显示的可用内存一直都是>5G,理论上不应该存在不足才对,进一步探究了解到了free内存回收与内存水位的概念。

内存水位作用

Linux的设计思路是尽量多的使用空闲内存,除了保留一定量的真正空闲立马可用的内存作为free内存保证系统正常运转外,其他空闲内存会尽量用于系统缓存(buffer+cache),当free内存不足时则从buffer、cache中回收为free内存即可,而一般我们说linux的可用内存都是指available内存,其实际包括free+可回收的buffer+cache内存,这也是zabbix监控中可用内存使用的指标。

那实际应该保留多少free内存以及何时触发回收free内存呢?这里就需要引入linux的内存水位(watermark)概念了,具体可参考这篇文章--Linux内核调整watermark_scale_factor以缓解direct reclaim。简单来说就是linux设置了min/low/high三个内存水位,对应free内存在不同水位线的行为如下:

  • free > high,内存充足,什么都不用做
  • free内存由>high下降至<low,唤醒kswapd开始内存回收--其他进程依然可以正常申请内存
  • 若free内存一直下降至<min, 分配新内存的进程会直接触发自己同步内存回收操作--direct claim
  • kswapd终于回收free内存至>high,休眠100ms
    • 休眠100ms期间若free又下降至<low,则再次唤醒kswapd,并自增kswapd_low_wmark_hit_quickly值
    • 休眠100ms后若free变为<high,kswapd需继续回收内存至>high,而后继续休眠100ms,并自增kswapd_low_wmark_hit_quickly值
    • 休眠100ms后若free依然>high,kswapd将进入长期sleep等待下次被唤醒

内存水位计算与调整

而watermark的min/low/high三者的取值具体是由两个内核参数min_free_kbytes和watermark_scale_factor决定的,简单来说--参考vm内核参数之内存水位min_free_kbytes和保留内存lowmem_reserve_ratio:

watermark[WMARK_MIN] = (min_free_kbytes/4) * zone.pages/zone.allpages
watermark[WMARK_LOW] = 5/4watermark[WMARK_MIN]
watermark[WMARK_HIGH] = 3/2*watermark[WMARK_MIN]

min水位直接由min_free_kbytes决定(后面的zone.pages/zone.allpages表示不同内存区按占总物理内存的比例均分对应水位值),而后min/low/high之间的差值则=1/4low,所以在一台8G(7969M)的线上机器上min/low/high取值默认为:

Node 0, zone      DMA
per-node stats
nr_inactive_anon 21704
nr_active_anon 171130
nr_inactive_file 1490263
nr_active_file 153139
--
Node 0, zone DMA32
pages free 58451
min 6322
low 7902
high 9482
node_scanned 0
--
Node 0, zone Normal
pages free 13169
min 10540
low 13175
high 15810
node_scanned 0

主要的Normal区域的min/low/high差值也就105400.254KB=10M左右,如果线上有突增流量,很可能一下子就跑到low乃至min水位之下了。

内存水位调整效果

通过watermark_scale_factor参数将默认值10/10000调整为200/10000,内存水位取值变为:

Node 0, zone      DMA
per-node stats
nr_inactive_anon 21910
nr_active_anon 278859
nr_inactive_file 1366921
nr_active_file 150022
--
Node 0, zone DMA32
pages free 56340
min 6342
low 21660
high 36978
node_scanned 0
--
Node 0, zone Normal
pages free 35915
min 10520
low 35926
high 61332
node_scanned 0

Normal zone内存水位min/low/high差值变为:low-min=99MB,调整完后对单台机器逐步放量至近期峰值的150%流量测试,未再出现该问题,至今2周过去了,线上机器未再出现该问题。

另一个验证水位调整效果的数据是查看并自增kswapd_low_wmark_hit_quickly值变化值,在调整水位值之前,每天kswapd_low_wmark_hit_quickly新增在1000左右,调整后变为100次,降低了一个数量级。

转载请注明出处,原文地址: https://www.cnblogs.com/AcAc-t/p/linux_watermark_and_kafka_batch_send.html

参考

https://zhuanlan.zhihu.com/p/440688960

https://help.aliyun.com/document_detail/316788.html

https://unix.stackexchange.com/questions/211646/what-is-kswapd-low-wmark-hit-quickly-from-proc-vmstat

https://blog.csdn.net/u010039418/article/details/105281933

https://kafka-python.readthedocs.io/en/master/apidoc/KafkaProducer.html

https://doc.opensuse.org/documentation/leap/archive/42.3/tuning/html/book.sle.tuning/cha.tuning.memory.html

记一次Linux server偶发CPU飙升问题的跟进与解决的更多相关文章

  1. 记java应用linux服务单个CPU使用率100%分析

    之前在做项目的过程中,项目完成后在linux服务器上做性能测试,当服务跑起来的时候发现cpu使用率很奇怪,java应用把单个cpu跑满了,其他cpu利用率0%. 刚开始遇到这问题的时候我第一时间反应使 ...

  2. 【转】Java程序CPU飙升问题排查方法

    windows环境下cpu飙升问题 线上某台runtime机器(windows Server)cpu报警,这种情况初步就是代码里面死循环了,先把机器下线了保证不再有新的任务分配进来,然而cpu使用依然 ...

  3. 记一次CPU飙升BUG

    图文地址:https://mp.weixin.qq.com/s?__biz=Mzg3NjEzODQ4NQ==&mid=2247483690&idx=1&sn=7c926f400 ...

  4. 【原创】记一次MySQL大表高并发写入引发CPU飙升的排障过程

    目录 一.故障现象... 1 二.初步分析... 2 三.排障过程... 2 1.排查是否QPS或insert并发请求上升导致问题发生... 2 2.排查是否锁资源等待或block导致了insert变 ...

  5. ORACLE Install (10g r2) FOR Red Hat Enterprise Linux Server release 5.5 (64 bit) (转)

    OS Info----------# cat /etc/redhat-releaseRed Hat Enterprise Linux Server release 5.5 (Tikanga)# cat ...

  6. Linux server关闭自己主动

    公司linux server发生错误.mysql server没有理由关闭,我找不到理由.Version: '5.6.13-enterprise-commercial-advanced' socket ...

  7. linux下查看cpu物理个数和逻辑个数 - chw1989的专栏 - 博客频道 - CSDN.NET

    body { font-family: 微软雅黑,"Microsoft YaHei", Georgia,Helvetica,Arial,sans-serif,宋体, PMingLi ...

  8. linux下的CPU、内存、IO、网络的压力测试

    linux下的CPU.内存.IO.网络的压力测试  要远程测试其实很简单了,把结果放到一个微服务里直接在web里查看就可以了,或者同步到其他服务器上 一.对CPU进行简单测试: 1.通过bc命令计算特 ...

  9. [转帖]linux下的CPU、内存、IO、网络的压力测试

    linux下的CPU.内存.IO.网络的压力测试 https://www.cnblogs.com/zhuochong/p/10185881.html 一.对CPU进行简单测试: 1.通过bc命令计算特 ...

随机推荐

  1. 课堂练习——neo4j简单使用

    启动neo4j: neo4j.bat console 进入neo4j数据库的conf目录下,编辑neo4j.conf文件:将当前数据库设置为你要建立的数据库名称(数据库不能重名): dbms.acti ...

  2. Fail2ban 简介

    Fail2ban是一个基于日志的IP自动屏蔽工具.可以通过它来防止暴力破解攻击. Fail2ban通过扫描日志文件(例如/var/log/apache/error_log),并禁止恶意IP(太多的密码 ...

  3. 从零搭建Pytorch模型教程(四)编写训练过程--参数解析

    ​  前言 训练过程主要是指编写train.py文件,其中包括参数的解析.训练日志的配置.设置随机数种子.classdataset的初始化.网络的初始化.学习率的设置.损失函数的设置.优化方式的设置. ...

  4. v87.01 鸿蒙内核源码分析 (内核启动篇) | 从汇编到 main () | 百篇博客分析 OpenHarmony 源码

    本篇关键词:内核重定位.MMU.SVC栈.热启动.内核映射表 内核汇编相关篇为: v74.01 鸿蒙内核源码分析(编码方式) | 机器指令是如何编码的 v75.03 鸿蒙内核源码分析(汇编基础) | ...

  5. vue同时监听多个参数变化

    computed: { // 同时监听多个参数 toWatch() { const { params1, params2 } = this.observeObj; return { params1, ...

  6. Java 线程创建与常用方法

    进程与线程 进程 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存.在指令运行过程中还需要用到磁盘.网络等设备.进程就是用来加载指令.管理内存.管理 IO ...

  7. 如何用 UDP 实现可靠传输?

    作者:小林coding 计算机八股文刷题网站:https://xiaolincoding.com 大家好,我是小林. 我记得之前在群里看到,有位读者字节一面的时候被问到:「如何基于 UDP 协议实现可 ...

  8. MySQL - 分页问题

    MySQL中的分页 MySQL中通过LIMIT关键字可以实现分页,如:(其中0表示开始记录数,10表示返回数据的数量) SELETE * FROM table_name LIMIT 0, 10 MyB ...

  9. 【实操干货】做好这 16 项优化,你的 Linux 操作系统焕然一新

    大家好,这次跟大家谈谈又拍云的操作系统优化方案.往简单地说,我们使用的 Linux 操作系统主要都是基于 CentOS6/7 的精简和优化.往复杂地说,则是我们有两套系统,业务上使用的定制 Linux ...

  10. 使用FileSystemWatcher监听文件状态

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年7月2日. 一.FileSystemWatcher类型介绍 在.NET中使用 FileSystemWatcher 类型可以进行监视指定 ...