在高并发网络服务器上,经常能看到CPU0的软中断占用长期超过80%,而其他核心却相对空闲,此时即便业务线程没有明显瓶颈,网络吞吐也上不去。这个现象的背后,往往是网卡接收队列只对应了少数几个中断号,所有收包软中断被固定在单核上。启用网卡多队列和RSS可以让硬件先把数据流按哈希分散到不同队列,但硬件队列数量有限,无法覆盖全部核心。RPS和RFS就是在这个基础上继续做软件层的分摊与亲和优化。本文会把配置步骤和验证方法按顺序拆开,帮助运维人员把软中断负载真正摊平。

理解三者的分工很重要:RSS决定数据包进入哪个硬件队列,RPS决定由哪些CPU处理该队列的backlog,RFS决定数据包最终是否落在等待它的应用所在CPU上。三层配合之后,单核软中断打满的问题才会有明显改善。
RSS与多队列的基本原理
RSS全称是接收方缩放,它利用网卡硬件对数据包的源IP、目的IP、源端口、目的端口和协议做哈希计算,然后把哈希结果映射到不同的接收队列中。这样一来,同一个数据流的所有包会固定进入同一个队列,而不同数据流则可能被分散到不同队列。队列会对应独立的中断号,每个中断号可以绑定到不同的CPU核心,从而实现接收中断的初步负载均衡。
网卡支持的队列数量由硬件能力和驱动程序共同决定,常见万兆网卡可能是8个或16个队列,部分旗舰型号可以到32个甚至更多。但服务器核心数往往超过队列数,比如一台48核的机器上,就算把16个队列全部用满,仍然会有大量核心没有被硬件中断覆盖。此外,RSS哈希只按流分发,如果某条数据流特别大,它所在队列的负载还是会明显高于其他队列。此时单纯依赖RSS就不够用了,需要借助RPS和RFS继续做软件层的细化分发。
RSS配置方法与队列数调整
查看网卡当前队列数和支持范围,可以执行ethtool -l eth0。输出中会显示当前接收队列、发送队列以及硬件支持的最大队列数。调整队列数一般使用ethtool -L eth0 combined 8,这个命令把收发队列统一设为8个;如果网卡支持收发分离,也可以单独设置rx和tx。需要注意的是,部分驱动要求网卡先关闭再修改队列数,否则会报错,生产环境操作前要确认业务影响。
调整队列数之后,还要检查哈希配置是否合理。使用ethtool -n eth0 rx-flow-hash tcp4可以查看TCP流量的哈希字段,通常默认已经包含源目IP和端口,一般不需要修改。如果发现特定业务下流量分布很不均匀,可以尝试ethtool -X eth0 equal 8把各队列权重设为等分。修改哈希权重对现有连接不会生效,只影响新连接的分布。队列数变化后,中断号也会重新分配,需要接着调整中断亲和性绑定,否则中断可能又全部落到CPU0上。
RPS的作用与配置
RPS是软件层面的接收包分发机制,它在每个硬件接收队列的软中断处理路径上,根据数据包的哈希值,把包进一步投递到其他CPU的backlog中处理。配置RPS需要向每个队列对应的文件中写入CPU位图掩码。例如8个CPU的系统里,想让队列0使用CPU0到CPU3,就写入十六进制值f;要让队列1使用CPU4到CPU7,写入f0。路径是/sys/class/net/eth0/queues/rx-0/rps_cpus,队列号从0开始。
写入掩码后立即生效,不需要重启网卡。掩码的计算可以用二进制位来表示,第n位为1表示允许第n个CPU处理该队列。实际操作中建议把不同队列的掩码错开,让所有核心都有机会参与软中断处理,同时避免多个队列共享同一组CPU导致竞争。还要考虑NUMA拓扑,尽量把每个队列的RPS掩码限定在同一个NUMA节点内的CPU,否则跨节点内存访问会带来额外延迟。RPS对没有多队列支持的旧网卡也有效,但最好在有多队列的网卡上与RSS配合使用。
RFS的作用与配置
RFS在RPS的基础上进一步优化,它不仅按流分散,还会记录应用进程正在哪个CPU上等待接收数据,然后优先把包投递到那个CPU的backlog。这样应用处理数据时不需要跨核读取缓存内容,能显著减少cache miss,对短连接和请求响应型业务效果明显。RFS依赖两个参数,一个是全局的流表大小,位于/proc/sys/net/core/rps_sock_flow_entries,另一个是每个队列的流表条目数,位于/sys/class/net/eth0/queues/rx-0/rps_flow_cnt。
一般建议把全局流表大小设置为系统可能出现的最大并发连接数,比如32768或65536。每个队列的rps_flow_cnt可以平均分配该值,例如16个队列时每个队列写2048。需要注意,RFS只有在对应队列已经配置了RPS的情况下才会生效,如果rps_cpus为0,rps_flow_cnt设置了也不起作用。设置完成后,可以通过查看/proc/net/softnet_stat和各CPU的si占用变化来判断优化效果。
中断亲和性设置与irqbalance取舍
每个硬件队列都会注册一个独立的中断号,可以在/proc/interrupts中查看具体编号。为了让中断分散,需要把中断号绑定到不同CPU,命令是echo 0 > /proc/irq/中断号/smp_affinity_list,或者使用位图方式echo 1 > /proc/irq/中断号/smp_affinity。通常做法是把队列0的中断绑定到CPU0,队列1绑定到CPU1,以此类推。如果同时启用了RPS,中断CPU不需要太多,因为软件会把收包处理继续分到其他核心。
irqbalance服务会自动调整中断亲和性,它可能打乱手动绑定的结果。在做精细网络调优时,更推荐关闭irqbalance,通过脚本或系统服务在开机后统一设置中断绑定。如果必须保留irqbalance,应配置策略文件明确哪些中断禁止自动移动,否则在高负载时irqbalance反而可能把多个网卡中断挤到同一核心,引发新的热点。绑定中断时也要注意该中断属于哪个NUMA节点,跨节点绑定会让数据包处理引入远程内存访问。
验证方法与常用检查项
配置完成后,首先用mpstat -P ALL 1观察各核心的%si列。理想情况下所有参与网络处理的核心软中断占用应大致相近,而不是集中在某个CPU上。如果仍然存在单核偏高,首先检查队列数和对应中断是否已经分散,再检查每个队列的rps_cpus是否写入了正确的掩码。使用cat /proc/net/softnet_stat可以看到每个CPU的接收统计,如果某一行中的dropped数值持续增加,说明该CPU的backlog队列存在溢出,需要扩大RPS覆盖范围或增加backlog长度。
进一步检查网卡各队列的包数量分布,可以执行ethtool -S eth0并关注rx_queue开头的统计项。如果某些队列明显偏多,可能是RSS哈希分布不均或业务流量特征导致,可以尝试调整哈希字段或权重。对于TCP业务,还可以结合netstat -s查看接收错误和丢包计数,确认优化是否真正降低了系统层面的压力。
| 优化层 | 机制 | 配置位置 | 主要作用 |
|---|---|---|---|
| RSS | 硬件哈希分流 | ethtool -L / -X | 将流分散到多个硬件队列 |
| RPS | 软件 backlog 分发 | /sys/class/net/eth0/queues/rx-0/rps_cpus | 将队列接收处理分散到更多CPU |
| RFS | 应用CPU亲和投递 | rps_sock_flow_entries 与 rps_flow_cnt | 减少跨核缓存失效,提升连接局部性 |
常见误区与调优建议
很多人认为队列数越多越好,但在核心数不多或流量不大的场景下,过多队列会带来额外的中断开销和上下文切换,反而降低性能。配置队列数时应结合实际CPU规模和业务PPS,通常队列数不超过需要参与网络处理的核心数量比较合适。另一个常见误区是只配置RPS而不配置RFS,导致包虽然被分散处理,但应用所在的CPU并不接收对应的包,仍然存在不必要的跨核缓存操作。
还需要注意,不要把所有队列的rps_cpus都写成全F,即让所有CPU都处理每一个队列。这样会造成大量软中断处理上的竞争和缓存抖动。每个队列分配4到8个核心,且各队列掩码可以有一定重叠,是比较稳妥的做法。最后,改完配置后一定要用压测或真实流量验证,因为不同驱动、内核版本和网卡型号对RSS、RPS、RFS的支持程度会有差异,部分参数可能需要内核启动选项配合才能完全生效。
网卡多队列RSS配置RPS与RFS软中断优化修改时间:2026-09-22 23:30:09