Linux 服务提流量前,先看内存回收和 Socket 水位
Linux 服务提流量前,先看内存回收和 Socket 水位
服务提流量后,如果 CPU sys 占比、内存回收和 Socket 队列同时变化,应用日志里未必有直接异常。此时要把请求入口与内核水位放在一条时间线上观察,而不是先猜某个函数变慢。
这类现象的深层原因,往往在于缺乏在 Linux 内核层与内存管理机制上的背压控制(Backpressure)与容量预估。当高并发流量突破了 Socket 缓冲区极限,触发了内核的直接内存回收(Direct Reclaim)或 cgroups 内存配额限制时,整个操作系统容易产生连锁性的延迟抖动。
理解 Linux 内核的内存治理路径,并在流量洪峰到来之前补齐物理隔离防线,是保障高并发系统稳定的底层基本功。
流量高发时,内核内存管理的核心瓶颈
构建高并发防护体系,首先需要厘清内核在面对内存压力时的三道水位线及其背后的处理逻辑。
1. Page Cache 脏页积压与 Direct Reclaim 卡顿
在高吞吐文件 IO 或日志密集写入场景中,应用程序调用 write() 通常仅将数据写入内核的 Page Cache(页缓存),随后依赖 kswapd 后台线程异步刷盘。
脏页达到 vm.dirty_background_ratio 或对应字节阈值后,内核的回写机制会开始后台回写;具体由回写线程而非 kswapd 负责。若写入持续快于设备回写能力并达到 vm.dirty_ratio 等限制,写路径可能被节流并参与回写。实际默认值和行为受内核版本、配置及 dirty_*_bytes 设置影响,应以目标主机的 /proc/sys/vm/ 为准。
2. Socket 缓冲区 (tcp_wmem / tcp_rmem) 膨胀
每个 TCP 连接在内核中都对应着发送与接收缓冲区。默认配置下,内核为了追求吞吐量,允许 Socket 自动扩展缓冲区。
当并发连接数突破 5 万级别时,TCP Socket 占用的 Slab 内存(如 sk_buff 结构体)及缓冲区将消耗大量的系统物理内存。一旦物理内存耗尽触发直接内存回收(direct reclaim),内核分配内存的路径将发生较长时间延迟,产生分配瓶颈。
3. cgroups v2 的 memory.high 与 memory.max 级联防御机制
在容器化(Docker/Kubernetes)环境中,如果仅配置了 memory.max(限制最大内存),当容器内存使用触及该临界点时,内核会直接触发 OOM Killer 杀死容器中的进程。
更为稳妥的防线应当使用 cgroups v2 引入的 memory.high 参数。当容器内存突破 memory.high 但未达到 memory.max 时,内核不会直接杀掉进程,而是插入减速逻辑(Throttling),让发起内存分配的线程进行短时间休眠,向应用层施加背压,为后台 kswapd 争取回收内存的时机。
深入内核:内存水位线与动态背压模型
在 Linux 伙伴系统(Buddy System)中,物理内存按 zone 分层管理,每个 zone 维护着三条关键的水位线:WMARK_MIN、WMARK_LOW 与 WMARK_HIGH。
物理内存 Zone 水位示意图:
======================================= (100% 内存容量)
▲
│ 系统内存充沛,无回收动作
=======┴=============================== WMARK_HIGH
▲
│ kswapd 后台线程被唤醒,开始异步回收 Page Cache
=======┴=============================== WMARK_LOW
▲
│ 到达紧急水位,触发 Direct Reclaim,阻塞分配线程
=======┴=============================== WMARK_MIN
▲
│ 仅保留给内核中断处理等特权分配 (vm.min_free_kbytes)
=======┴=============================== (0 内存容量)
当可用内存降至 WMARK_LOW 附近时,kswapd 会参与后台回收。匿名页是否换出、文件页是否需要回写,取决于回收策略和后备存储状态。若分配路径进入 direct reclaim,业务线程可能被阻塞;实际延迟应结合 PSI、回收事件和目标负载测量,不能预设为固定量级。
排障与诊断命令工具链
线上出现内存卡顿或 P99 延迟抖动时,工程师需要借助以下实战工具提取确切指标:
1. vmstat 1 监控内存回收行为
重点观察 si(Swap in)、so(Swap out)以及 r(运行队列)、b(不可中断休眠队列)。若 b 队列数值持续大于 0 且 CPU sys 态偏高,表明大量线程可能卡死在内核 IO 或直接内存回收上。
2. sar -n DEV,TCP 1 检查网络 Socket 积压
观察网络接口的 rxpck/s 与 pck-jumps,结合 /proc/net/sockstat 查看处于 TCP: inuse 与 alloc 状态的 Socket 数量及其消耗内存。
3. bpftrace 实时跟踪内核分配延迟
利用 eBPF 技术可以穿透用户态,实时测量内核函数 mm_page_alloc 或 direct reclaim 的精准耗时分布。
# 诊断内核直接内存回收耗时的 bpftrace 简短脚本
sudo bpftrace -e '
kprobe:mm_direct_reclaim {
@start[tid] = nsecs;
}
kretprobe:mm_direct_reclaim /@start[tid]/ {
@latency_us = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}'
内核参数与背压控制脚本示例
以下 Bash 脚本汇总了在高并发网关及高吞吐服务部署前,针对内核内存管理、TCP 缓冲区与 Dirty Page 的参数配置方案。
#!/usr/bin/env bash
set -euo pipefail
echo "[INFO] Applying production Linux kernel memory and backpressure defense configuration..."
# 1. 调整 Dirty Page 刷盘阈值,规避高并发写日志拉垮系统 IO
# 当脏页达到 5% 时立即启动后台异步刷盘
sysctl -w vm.dirty_background_ratio=5
# 当脏页达到 15% 时强行限制写入线程,规避脏页堆积到 20% 引发的阻塞
sysctl -w vm.dirty_ratio=15
# 2. 保留足够的内核保留内存 (min_free_kbytes)
# 防止高并发网络中断处理时无法分配 sk_buff,对于 64G 内存机器设为约 2GB
sysctl -w vm.min_free_kbytes=2097152
# 3. 避免过度使用 Swap 导致 IO 暴涨
sysctl -w vm.swappiness=10
# 4. 精细化控制 TCP Socket 缓冲区,防止单连接占用过多内存
# format: min default max (单位: 字节)
# 将单连接最大读写缓冲区限制在 4MB 以内,平衡吞吐量与高并发下的内存消耗
sysctl -w net.ipv4.tcp_rmem="4096 87380 4194304"
sysctl -w net.ipv4.tcp_wmem="4096 65536 4194304"
# 5. 开启 TCP TIME_WAIT 重用
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_max_tw_buckets=262144
echo "[OK] Linux kernel performance & backpressure guards successfully applied."
针对背压与容量估算的避坑指南
在大流量上线前做架构梳理时,应当关注以下三个隐蔽的工程细节。
第一,Socket 内存容量的精确估算。不能用简单的 系统内存 / 单连接业务 Payload 预估并发能力。每个 TCP 连接的内核开销包含 sk_buff 结构体开销 + tcp_rmem 实际分配 + tcp_wmem 实际分配。在 10 万并发长连接场景下,仅内核网络协议栈就需要预留足够的物理 RAM 空间。
第二,按需设置 memory.high 与 memory.max。memory.high 可作为回收和节流的预警水位,memory.max 是硬上限;两者的间距应根据工作集、突发内存和延迟预算压测决定。Kubernetes 是否直接暴露对应 cgroup v2 参数,也取决于运行时和节点配置。
第三,应用层日志刷盘接入 RingBuffer 丢弃机制。内核 Dirty Page 调节能缓解磁盘 IO 冲击。在应用层,日志写入应当配合有限容量的异步 RingBuffer。当 Buffer 积压达到阈值时,主动选择丢弃非关键级别的日志,避免阻塞内核 write() 系统调用。
背压要在内核水位触顶之前生效,日志丢弃也只能针对明确的低优先级记录。容量与阈值来自当前机器和负载测试,不把某组数字当通用答案。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)