在CentOS服务器上,当网卡流量超过单核处理能力时,软中断(softirq)会集中在一个CPU核心上打满,导致丢包、延迟飙升。解决这个问题的核心手段就是通过sysctl参数调优网卡队列,再配合RPS(Receive Packet Steering)和RFS(Receive Flow Steering)把网络包分发到多个CPU核心上处理。具体做法是:先用ethtool把网卡的多队列打开,设置队列数等于CPU核心数,然后通过sysctl开启rps和rfs,最后把每个网卡队列的中断亲和性绑定到不同CPU上。这套组合拳打下来,单网卡吞吐量可以轻松翻倍,PPS处理能力提升数倍。

一、先搞清楚软中断为什么会成为瓶颈

Linux内核处理网络包的流程是这样的:网卡收到数据包后触发硬件中断,内核在中断处理程序里把数据放到网卡的接收队列里,然后触发软中断(NET_RX_SOFTIRQ)来真正处理这些数据包。问题在于,默认情况下,一个网卡只有一个接收队列,所有数据包的软中断都在同一个CPU核心上处理。当流量达到几十万PPS或者几Gbps带宽时,单核根本扛不住,CPU使用率直接100%,数据包来不及处理就被丢弃了。

二、查看当前网卡中断和软中断分布情况

动手调优之前,先看现状。用下面几条命令快速诊断:

# 查看每个CPU核心的软中断分布
cat /proc/softirqs | grep NET_RX

# 查看网卡中断绑定到了哪个CPU
cat /proc/interrupts | grep eth

# 查看网卡支持几个队列
ethtool -l eth0

# 查看当前网卡队列数和中断数
ethtool -c eth0

如果你看到NET_RX那一行的数字大部分集中在cpu0或者某一个核心上,而且ethtool -l显示队列数只有1,那就是典型的单队列瓶颈。如果你用的是多队列网卡(比如Intel的ixgbe、i40e、ixgbevf等),那恭喜你,硬件基础已经具备了,只需要做软件配置。

三、用ethtool开启网卡多队列

这是第一步,也是最关键的一步。没有多队列,后面的RPS和RFS都是空谈。假设你的网卡是eth0,CPU有8个核心,操作如下:

# 查看网卡支持的最大队列数
ethtool -l eth0

# 设置队列数为8(等于CPU核心数)
ethtool -L eth0 combined 8

# 确认设置成功
ethtool -c eth0

注意,combined参数表示收发共用队列数。有些网卡支持分开设置收发队列,用tx和rx参数分别指定。设置完之后,你会发现/proc/interrupts里eth0的中断变成了eth0-0、eth0-1、eth0-2……每个队列对应一个中断。这一步只是把硬件队列打开了,但默认情况下这些中断可能还是集中在一个CPU上,所以还需要下一步。

四、配置中断亲和性(IRQ Affinity)

中断亲和性的作用是把每个网卡队列的硬件中断绑定到指定的CPU核心上。CentOS 7/8系统里,中断亲和性文件在/proc/irq/目录下。先找到每个队列对应的中断号:

# 查看eth0各队列的中断号
cat /proc/interrupts | grep eth0

假设输出显示eth0-0对应中断号120,eth0-1对应121,以此类推。然后把每个中断绑定到对应的CPU:

# 把中断120绑定到CPU0
echo 1 > /proc/irq/120/smp_affinity

# 把中断121绑定到CPU1
echo 2 > /proc/irq/121/smp_affinity

# 把中断122绑定到CPU2
echo 4 > /proc/irq/122/smp_affinity

# 以此类推,值是2的幂次方,CPU0=1, CPU1=2, CPU2=4, CPU3=8...

如果是8核以上的系统,smp_affinity支持十六进制掩码。比如要绑定到CPU0-7,值就是ff。也可以直接用irqbalance服务自动分配,但在高负载场景下,手动绑定更可控,避免irqbalance频繁调整导致抖动。

五、开启RPS(Receive Packet Steering)

RPS是软件层面的包分发机制,它不需要网卡硬件支持多队列也能工作。它的原理是在软中断处理阶段,根据数据包的hash值把包分发到不同的CPU核心上处理。对于不支持多队列的老网卡,RPS是救命稻草;对于支持多队列的新网卡,RPS可以作为补充和兜底。

RPS的配置通过每个网卡接收队列的sysfs文件来完成。假设eth0有8个队列,每个队列对应一个rps_cpus文件:

# 查看有哪些RPS配置文件
ls /sys/class/net/eth0/queues/rx-*/rps_cpus

# 给每个队列设置RPS,绑定到所有CPU(用ff表示8核全开)
for i in /sys/class/net/eth0/queues/rx-*/rps_cpus; do
  echo ff > $i
done

# 或者只绑定到CPU0-3
for i in /sys/class/net/eth0/queues/rx-*/rps_cpus; do
  echo 0f > $i
done

RPS还有一个更高级的兄弟叫RFS(Receive Flow Steering),它比RPS更智能。RPS是按包分发,同一个连接的包可能被分到不同CPU,导致缓存失效。RFS则是按流(flow)分发,同一个TCP/UDP连接的包始终由同一个CPU处理,大幅提升缓存命中率。

六、开启RFS(Receive Flow Steering)

RFS需要内核版本2.6.35以上,CentOS 7的3.10内核完全支持。开启RFS需要先设置rps_sock_flow_entries,这个参数控制RFS维护的流表大小,值越大支持的并发连接数越多:

# 设置RFS流表大小为32768
echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_sock_flow_entries

# 对所有队列都设置
for i in /sys/class/net/eth0/queues/rx-*/rps_sock_flow_entries; do
  echo 32768 > $i
done

RFS的核心逻辑是:当一个CPU处理某个流的数据包时,如果发现这个流的处理负载超过了阈值,内核会自动把这个流迁移到负载较低的CPU上。这个阈值通过rps_flow_cnt参数控制,默认值一般就够用,高负载场景可以调大:

# 设置流迁移阈值(默认是2,表示处理2个包后判断是否迁移)
for i in /sys/class/net/eth0/queues/rx-*/rps_flow_cnt; do
  echo 10 > $i
done

七、sysctl参数层面的全局调优

除了针对单个网卡的配置,还有一些全局sysctl参数对网络软中断性能有影响。编辑/etc/sysctl.conf,加入以下配置:

# 增大网络设备接收队列长度,防止高负载时丢包
net.core.netdev_max_backlog = 30000

# 增大默认接收缓冲区
net.core.rmem_default = 262144
net.core.rmem_max = 16777216

# 增大默认发送缓冲区
net.core.wmem_default = 262144
net.core.wmem_max = 16777216

# 增大TCP接收缓冲区
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# 开启TCP窗口缩放
net.ipv4.tcp_window_scaling = 1

# 增大半开连接队列
net.ipv4.tcp_max_syn_backlog = 65536

# 减少TIME_WAIT socket重用等待时间
net.ipv4.tcp_tw_reuse = 1

# 增大somaxconn
net.core.somaxconn = 65535

# 开启RPS(全局开关,某些内核需要)
net.core.rps_sock_flow_entries = 32768

配置完之后执行sysctl -p生效。这些参数不是孤立的,它们配合网卡队列、RPS、RFS一起工作,形成完整的性能优化链条。

八、实战验证效果

调优完成后,用压测工具验证。推荐用netperf、iperf3或者pktgen来打流量,同时监控软中断分布:

# 实时监控软中断
watch -n 1 cat /proc/softirqs | grep NET_RX

# 监控各CPU使用率
mpstat -P ALL 1

# 监控网卡丢包
ifconfig eth0 | grep -i drop
ethtool -S eth0 | grep -i drop

如果看到NET_RX均匀分布在多个CPU上,每个核心的softirq占比在合理范围内(比如8核系统每个核心10%-15%),丢包数为零或者极低,那就说明调优成功了。如果还是有瓶颈,检查一下是不是队列数设置超过了网卡硬件支持的上限,或者CPU本身的处理能力已经到顶了,那就需要考虑网卡绑定(bonding)或者升级硬件。

九、常见踩坑点和注意事项

第一,不是所有网卡都支持多队列。用ethtool -l先确认,如果显示只有1个队列,那只能靠RPS来做软件分发,效果会打折扣。第二,RPS和RFS的rps_cpus掩码值要和实际CPU核心数对应,设错了等于没设。第三,虚拟机环境下,vCPU和物理CPU的映射关系会影响亲和性效果,最好在宿主机层面做CPU pinning。第四,别把队列数设得比CPU核心数还多,多余的队列没有CPU处理,反而增加调度开销。第五,调优是个迭代过程,先开多队列,再配RPS,最后加RFS,一步步来,每步都验证,别一次性全改了出问题不知道是哪里的原因。

十、总结

CentOS网卡软中断优化的完整链路是:ethtool开多队列 → 中断亲和性绑定CPU → RPS软件分发 → RFS按流智能分发 → sysctl全局参数兜底。这套方案不需要额外花钱买硬件,纯软件调优就能把单网卡性能榨干。对于高并发Web服务器、数据库中间件、负载均衡节点这些网络密集型场景,这是必须掌握的基本功。记住核心原则:让每个CPU都有事干,别让一个核累死其他核闲着。