从一次线上卡顿说起
上周五晚上,公司直播平台突然报警,大量用户反馈视频卡顿、加载慢。运维一通排查,带宽没满,服务器负载也正常,最后定位到是内核协议栈处理能力成了瓶颈。这种场景其实挺常见——系统明明资源还有余量,但网络就是跑不满,问题往往就出在协议栈这层。
协议栈为啥会拖后腿?
数据从网卡进来,要经过中断处理、软中断(softirq)、协议解析、缓冲区拷贝、再到应用层,每一步都可能成为性能杀手。比如默认配置下,一个CPU处理所有网络中断,一旦并发上来,软中断占满CPU,应用线程根本抢不到资源。
再比如,TCP接收窗口太小,或者缓冲区设置不合理,即使带宽充足,传输速率也会被自我限制。这就像高速修好了,但收费站只开了一条通道,车再多也走不快。
几个立竿见影的调优点
调整 RSS(Receive Side Scaling),让多核分担网络中断。现代网卡支持多队列,配合 irqbalance 或手动绑定中断到不同 CPU,能显著降低单核压力。查看中断分布:
cat /proc/interrupts | grep eth0
如果发现大部分中断集中在 CPU0,就得动手了。通过修改 /proc/irq/xxx/smp_affinity,把队列分散开。
另一个重点是增大 socket 缓冲区。应用层 recv() 调用频率跟不上数据到达速度时,丢包就会发生。调整系统级参数:
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
这些值单位是字节,最后一位代表最大可动态分配的内存。别怕设大,现代服务器内存足够支撑。
绕过协议栈?有时候真得这么做
对极致性能有要求的场景,比如高频交易或实时音视频转发,可以考虑旁路协议栈。DPDK、XDP 这类技术让应用直接收发包,跳过内核协议处理流程。
比如用 XDP 在驱动层做简单过滤或转发,处理速度能达到千万pps级别。写个简单的 XDP 程序:
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
SEC("xdp") int pass_func(struct xdp_md *ctx) {
return XDP_PASS;
}
虽然学习成本高点,但在关键路径上值得投入。
应用层也不能躺平
别以为调好系统参数就万事大吉。应用如果用阻塞式 read/write,哪怕底层再快,照样拖后腿。改用 epoll + 非阻塞 I/O 是基本操作。连接数上来后,每个 fd 的开销都得精打细算。
还有批量处理。别来一个包就进一次系统调用,攒一波一起读,效率更高。Nginx 的 accept 多次循环、Linux 的 TCP_DEFER_ACCEPT 选项,都是这个思路。
监控比优化更重要
上线前用 netperf、iperf3 测吞吐,用 sar -n DEV 看网卡状态,用 cat /proc/net/softnet_stat 分析 softirq 丢包。线上跑着的话,zabbix 或 Prometheus 抓住 drops、overruns 这些指标,异常波动立马告警。
有一次我们发现 throughput 上不去,查到最后是 TSO(TCP Segmentation Offload)和某个虚拟化环境不兼容,关掉反而更快。所以经验不能照搬,得看实际数据说话。