网络软中断跑满单个核,你会如何调整 RPS 网卡多队列与亲和性

解读

  1. 场景定位:生产或压测环境出现 si 软中断飙高,且仅集中在 CPU0(或某一颗逻辑核),导致该核 100 % 占用,业务线程因拿不到 CPU 而 RT 抖动、吞吐下降。
  2. 根因判断:
    • 单队列网卡或虽有多队列但队列数 < CPU 核数,导致所有报文中断由一颗核处理;
    • 硬中断已均衡,但内核协议栈后续处理(RPS/RFS)未开启或未正确绑核,仍回落到 CPU0;
    • 业务线程与软中断落在同一核,互相抢占。
  3. 调优目标:让“中断+协议栈+业务”三层负载均摊到多核,且避免跨 NUMA 访存,最终使 si 单核跑满现象消失,系统吞吐线性提升,CPU 利用率各核均衡 < 80 %。

知识点

  1. 中断路径:
    硬中断(IRQ)(NIC 触发)→ 软中断(NET_RX)→ 协议栈 → socket → 业务线程。
  2. 网卡多队列(RSS,Receive Side Scaling):
    硬件依据 hash(IP、端口)把报文散列到多个 RX 队列,每队列绑定不同 CPU,硬中断即可分散。
  3. RPS(Receive Packet Steering):
    软件模拟 RSS,在单队列网卡或队列数不足时,由软中断阶段把报文再次散列到多个 CPU 的 backlog 队列处理。
  4. RFS(Receive Flow Steering):
    在 RPS 基础上,把同一五元组流定向到“运行该流应用线程”的 CPU,提高缓存命中率。
  5. 中断亲和性(irqbalance、smp_affinity):
    控制硬中断落在哪些核;关闭 irqbalance 可手动精准绑核。
  6. 调度亲和性(taskset、sched_setaffinity、cgroup cpuset):
    把业务线程与软中断处理核隔离,避免抢占。
  7. NUMA 亲和:
    网卡、内存、CPU 同属一个 node,减少跨 node 访存延迟。
  8. 观测工具:
    top/mpstat -P ALL 1 看 si 分布;cat /proc/interrupts 看 IRQ 分布;perf top -g 看软中断热点;ethtool -l/-x/-S 查看队列、hash、计数;cat /proc/softirqs 看 NET_RX 分布。

答案

  1. 确认硬件能力
    ethtool -l eth0 查看网卡最大队列数与当前队列数;若最大>1 且当前=1,先开启多队列:
    ethtool -L eth0 combined N (N 取 CPU 核数或≤最大队列数)。

  2. 硬中断绑核

    • 关闭自动均衡:systemctl stop irqbalance;systemctl disable irqbalance
    • 计算 NUMA node:cat /sys/class/net/eth0/device/numa_node
    • 把各队列 IRQ 平均绑定到同一 node 的不同核,避开 CPU0:
      echo 00000002 > /proc/irq/24/smp_affinity (示例把 IRQ24 绑到 CPU1,十六进制位掩码)。
  3. 启用并调优 RPS

    • 计算掩码:期望参与软中断的核掩码,如 8 核机器 CPU1-7 参与,掩码为 0xFE。
    • 设置队列级 RPS:echo FE > /sys/class/net/eth0/queues/rx-0/rps_cpus
      若已开多队列,对每个 rx-N 目录都写相同掩码,实现软中断继续分核。
    • 调大 rps_flow_cnt 与 rps_sock_flow_entries:
      echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
      echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
      保证五元组 hash 表不冲突。
  4. 启用 RFS(可选)
    内核版本≥2.6.35 默认编译了 RFS,只需保证 rps_sock_flow_entries 足够大即可;
    若业务流<队列数,可进一步把 rps_flow_cnt 按流数调小,节省内存。

  5. 隔离业务线程

    • taskset -c 4-7 java -jar app.jar 把应用绑到 NUMA node0 的后半部分核;
    • 或在 systemd service 里加 CPUAffinity=4-7
    • 保证软中断核(1-3)与应用核(4-7)不重叠,减少上下文切换。
  6. 验证

    • mpstat -P ALL 1 观察 si 列各核均匀且总占比下降;
    • cat /proc/softirqs | grep NET_RX 看计数在各核增量均衡;
    • 压测同样并发,TPS 提升、RT P99 下降、CPU0 不再跑满。
  7. 兜底策略
    若老旧内核或云主机无法改硬中断,可仅依赖 RPS+RFS,也能把软中断分散到多核,效果略逊于 RSS,但可解决单核跑满问题。

拓展思考

  1. 容器场景:
    Kubernetes 下网卡多队列仍受宿主机控制,但容器内看到的 CPU 集合由 cpuset 限制;
    需在宿主机层完成 IRQ/RPS 绑核,再通过 cpuManagerPolicy=static 把业务容器绑到专属核,避免与软中断核冲突。

  2. 发送方向优化:
    若瓶颈在 TX 软中断(如反向代理上行流量大),可开启 XPS(Transmit Packet Steering):
    echo 0xFE > /sys/class/net/eth0/queues/tx-0/xps_cpus,把发送负载也分散。

  3. 新内核技术:

    • aRFS(hardware-accelerated RFS): 网卡驱动与内核协同,把 RFS 提示直接写进硬件过滤器,实现零开销流定向;
    • XDP + AF_XDP:在用户态完成收包,绕过软中断,适合高频小包场景;
    • IRQ 合并与自适应中断调节:ethtool -C eth0 adaptive-rx on 减少中断风暴。
  4. 云厂商差异:
    阿里云增强型实例已默认开启最大队列数并自动绑核,但掩码可能仍包含 CPU0;
    腾讯云黑石裸金属允许自定义 IRQ 亲和性,需手动调优;
    AWS ENA 驱动推荐 ethtool -L combined 开到最大,并关闭 irqbalance,再配 RPS。

  5. 监控闭环:
    /proc/softirqs/proc/interrupts 指标接入 Prometheus + Grafana,设置“单核 NET_RX 增量>80 % 持续 1 min”告警,实现调优效果可观测、回归可发现。