发布时间:2026-09-20 12:30 更新时间:2026-09-20 12:30 阅读量:0
很多站长遇到过高流量时的怪现象:CPU 总体占用并不高,但网卡收包速率上不去,top 里 si(软中断)这一项却一直飘红,或者只有一个核被 ksoftirqd 吃满。这类问题往往不是带宽不够,而是收包路径上只有一颗 CPU 在干活。本文从原理讲到命令,帮你理清 RSS、RPS 和 irqbalance 三者的关系,知道什么场景该调、什么场景保持默认更稳。
现代服务器网卡普遍支持多队列(multi-queue)。一块物理网卡在系统里会呈现为 eth0 以及若干 eth0-0、eth0-1 这样的队列接口,每个队列对应一组独立的收发描述符。当数据包到达时,网卡根据包头的四元组做哈希,把同一个连接的数据尽量分到同一个队列,这个过程叫 RSS(Receive Side Scaling)。哈希结果由网卡硬件决定,好处是同一连接不乱序,代价是队列之间负载不一定均匀。
每个队列会向某个 CPU 发送中断,通知内核来取包。如果某块网卡的所有队列中断都落在 CPU0,那就等于回到了单核收包。所以多队列要真正生效,需要两件事同时成立:队列数量足够,且中断被分散到不同 CPU 上。
如果网卡硬件或虚拟化环境不支持多队列(不少云主机、老网卡就是单队列),内核还提供了软件层的 RPS(Receive Packet Steering)。RPS 在软中断阶段根据哈希把包重新分发给其他 CPU 的 backlog 队列,相当于软件模拟 RSS。配套的 RFS 则尝试把包交给运行该 socket 的 CPU,减少跨核缓存失效。RPS 会额外消耗 CPU 做分发,所以它是硬件能力不足时的补救手段,不是无代价的优化。
排查前先确认网卡和驱动支持多少队列。下面的命令都是只读查询,不会改动线上配置,具体输出字段以你的内核与驱动版本为准。
# 查看网卡队列与中断信息,不同驱动输出字段略有差异
ethtool -l eth0
查看网卡各队列的中断号
ls /sys/class/net/eth0/queues/
统计每个中断号在各 CPU 上的触发次数,观察是否集中在某一核
cat /proc/interrupts | grep -i eth0
查看某队列的 RPS 配置(为空表示未启用)
cat /sys/class/net/eth0/queues/rx-0/rps_cpus
查看 RFS 流表大小
cat /proc/sys/net/core/rps_sock_flow_entries
看 /proc/interrupts 时重点关注两件事:一是网卡占用了几个中断号,二是每行后面的数字是否明显偏向某一列(某一颗 CPU)。如果所有中断都堆在 CPU0,同时 si 很高,基本可以确认是中断集中导致。
需要临时验证 RPS 效果时,可以这样写掩码(十六进制,每个 bit 对应一颗 CPU,f 表示 CPU0~3):
# 让 rx-0 队列的软中断在 CPU0-3 上分担
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
适当设置 RFS 流表条目,0 表示关闭
sysctl -w net.core.rps_sock_flow_entries=32768
该队列对应的 RFS 流表大小
sysctl -w net.core.rps_sock_flow_entries=32768
每个 rx 队列可用的流表条目写在队列目录下
echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
这些 /sys 下的写入重启即失效,想持久化需写进 systemd 服务、udev 规则或网卡调优脚本,不同发行版做法不一,以官方文档为准。注意 RPS 掩码不要跨 NUMA 节点随意扩散,否则包在节点间来回搬运反而更慢。
irqbalance 是多数发行版默认开启的中断均衡服务,它会周期性把中断迁移到相对空闲的 CPU 上。对通用业务来说,默认开启通常够用,不建议上来就关。但在下面这些场景,手动绑定往往更稳定:
一是网卡支持 RSS 且队列数与业务核数匹配,希望中断固定落在特定物理核上,避免迁移带来的缓存抖动;二是做了 CPU 隔离(isolcpus、cgroup cpuset)把业务进程钉在指定核,需要把网卡中断也钉到对应的核;三是延迟敏感型服务,抖动比吞吐更重要。
手动绑定用 /proc/irq/<中断号>/smp_affinity_list 或 smp_affinity(十六进制掩码)。下面的例子把中断 128 绑到 CPU2:
# 查看该中断当前绑定的 CPU
cat /proc/irq/128/smp_affinity_list
绑定到 CPU2(列表形式,支持 2 或 2-3 这样的范围)
echo 2 > /proc/irq/128/smp_affinity_list
如使用十六进制掩码,CPU2 对应 4
echo 4 > /proc/irq/128/smp_affinity
停用 irqbalance 前先确认它是否在运行
systemctl status irqbalance
如果你决定手动绑定,建议先 systemctl stop irqbalance 并 disable,否则它会把你的设置改回去。做这一步前务必留好回滚方式:记录原始 smp_affinity_list 值,或准备好重新启用 irqbalance 的命令。生产环境建议先在测试机验证,再灰度上线。
取舍上可以记一个简单原则:能靠硬件 RSS 解决就别用 RPS,能靠默认 irqbalance 稳住就别手动绑。只有当监控明确显示中断集中、si 偏高、单核软中断饱和,且业务对延迟或吞吐有进一步要求时,才值得进入手动调优。调整后继续用 /proc/interrupts 和 mpstat -P ALL 1 观察分布是否改善,而不是凭感觉认为“绑了就更快”。
收包性能问题先看队列数和中断分布,再判断是硬件多队列没生效、队列数不够,还是中断全挤在一颗核上。RSS 是硬件分流,RPS 是软件补救,irqbalance 负责动态均衡,三者不是互相替代而是配合关系。建议你先用 ethtool -l、/proc/interrupts 和 mpstat 建立一份基线数据,确认瓶颈确实在收包路径,再考虑关闭 irqbalance 做手动绑定。任何改动都保留回滚脚本,并关注业务指标而非单一系统指标。
| 📑 | 📅 |
|---|---|
| Linux时间与时区排查:date、timedatectl与容器时区一致性 | 2026-09-20 |
| Docker 容器日志写满磁盘:json-file 限制与 max-size 配置 | 2026-09-20 |
| Nginx 与后端长连接调优:keepalive 与 upstream 复用 | 2026-09-20 |
| Docker 容器内 CPU 被限流排查:cfs_quota、cpuset 与 top 显示异常 | 2026-09-19 |
| Nginx gzip 与 Brotli 压缩配置实战:静态资源体积优化 | 2026-09-19 |
| Nginx 上传大文件报 413 与超时:参数与缓冲区排查 | 2026-09-20 |
| Docker容器缺命令:精简镜像补装与临时容器调试 | 2026-09-20 |
| Nginx location 匹配优先级实战:=、^~、~ 命中顺序验证 | 2026-09-21 |
| Docker 容器健康检查实战:HEALTHCHECK 与 unhealthy 自动重启 | 2026-09-21 |
| MySQL索引失效排查:EXPLAIN执行计划与隐式转换定位 | 2026-09-21 |