Linux 网络性能优化:从应用到内核

第一章 应用层优化:能不发就不发,能少发就少发

1.1 减少不必要的网络 IO

网络 IO 是系统中最昂贵的操作之一。即使是本机 Loopback 通信,开销也远超想象:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              网络 IO 的真实成本                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  单次网络请求的开销分解:                                   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  1. 用户态 → 内核态 上下文切换                       │   │
│  │  2. 内核协议栈处理(TCP/IP 各层头部封装)              │   │
│  │  3. 软中断处理(ksoftirqd 介入)                     │   │
│  │  4. 进程唤醒(睡眠 → 可运行状态切换)                 │   │
│  │  5. 数据返回时再走一遍上述流程                        │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  典型案例:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  某项目 Java 服务调用本机 C/C++ SDK                  │   │
│  │  原方案:TCP/UDP 网络调用                             │   │
│  │  优化后:直接函数调用                                 │   │
│  │  结果:CPU 核数削减 20%                              │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  结论:单机内部模块通信,优先使用:                         │
│  - 共享内存                                                │
│  - 本地函数调用                                            │
│  - 进程间通信(IPC)                                       │
│  避免不必要的网络 Socket 通信                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2 合并网络请求:减少 RTT

往返时间(RTT)是网络延迟的主要来源。减少请求次数是提升性能的关键:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              请求合并的威力                                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  场景:批量获取 Redis 中的 60 个 key                      │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  错误做法:循环 60 次调用                            │   │
│  │                                                     │   │
│  │  for (int i = 0; i < 60; i++) {                    │   │
│  │      redis->get(key[i]);  // 每次一次网络往返       │   │
│  │  }                                                  │   │
│  │                                                     │   │
│  │  总耗时 = 60 × RTT + 处理时间                        │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  正确做法:批量命令或 Pipeline                        │   │
│  │                                                     │   │
│  │  redis->hmget(keys);  // 一次网络往返               │   │
│  │  或                                                  │   │
│  │  redis.pipeline()                                  │   │
│  │      .get(key[0])                                   │   │
│  │      .get(key[1])                                   │   │
│  │      ...                                            │   │
│  │      .execute()                                     │   │
│  │                                                     │   │
│  │  总耗时 = 1 × RTT + 处理时间                        │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  性能提升:60 倍的 RTT 节省!                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.3 就近部署:物理距离决定延迟

复制代码
┌─────────────────────────────────────────────────────────────┐
│              延迟与物理距离的关系                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  光速延迟参考:                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  路由节点            典型延迟                        │   │
│  │  ────────────────────────────────────────────────   │   │
│  │  同机房(<1km)      0.5-1 ms                       │   │
│  │  同城(100km)        2-5 ms                        │   │
│  │  同区域(1000km)     10-20 ms                      │   │
│  │  跨区域(5000km)     50-100 ms                     │   │
│  │  跨国/洲际            100-300 ms                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  优化策略:                                                 │
│  - 客户端与服务端尽量部署在同一机房                        │
│  - 核心服务优先考虑同机架部署                              │
│  - CDN 加速:静态资源分发到用户就近节点                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.4 内网调用避免使用外网域名

复制代码
┌─────────────────────────────────────────────────────────────┐
│              内网调用外网域名的代价                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  错误场景:服务 A(内网)调用服务 B(内网),却用公网域名  │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │      服务 A                                          │   │
│  │         │                                            │   │
│  │         │ www.xxx.com (公网域名)                     │   │
│  │         ▼                                            │   │
│  │      DNS 解析 ───────────────────────────────→       │   │
│  │                                               │       │   │
│  │      NAT 网关(公网出口)                          │       │   │
│  │         │                                            │       │   │
│  │         │ Hairpin NAT(NAT 回流)                   │       │   │
│  │         ▼                                            │       │
│  │      服务 B                                          │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  增加的开销:                                               │
│  1. DNS 解析延迟(通常 5-50ms)                           │
│  2. NAT 网关路由跳数                                        │
│  3. Hairpin NAT 处理(额外 CPU 消耗)                       │
│                                                             │
│  正确做法:                                                 │
│  - 使用内网 IP 直连(如 10.0.1.100:8080)                  │
│  - 使用内网私有域名(如 svc-b.internal.company.com)        │
│  - 服务注册中心(Consul / etcd / Nacos)服务发现            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第二章 发送过程优化:减少拷贝与延迟

2.1 传统方式的四次拷贝

在没有优化的情况下,发送一个文件需要经过复杂的拷贝过程:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              传统 read + write 方式                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  步骤 1        步骤 2        步骤 3        步骤 4        │
│  ┌────┐      ┌────┐      ┌────┐      ┌────┐              │
│  │硬盘│ ───→ │内核│ ───→ │用户│ ───→ │Socket│ ──→ 网卡    │
│  └────┘ DMA  └────┘ CPU  └────┘ CPU  └────┘ DMA         │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  全程 4 次上下文切换:                               │   │
│  │  1. read() 调用:用户态 → 内核态                     │   │
│  │  2. read() 返回:内核态 → 用户态                     │   │
│  │  3. write() 调用:用户态 → 内核态                     │   │
│  │  4. write() 返回:内核态 → 用户态                     │   │
│  │                                                     │   │
│  │  全程 2 次 CPU 拷贝(不必要):                      │   │
│  │  - 内核缓冲区 → 用户缓冲区                           │   │
│  │  - 用户缓冲区 → Socket 缓冲区                        │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.2 mmap + write:减少一次拷贝

复制代码
┌─────────────────────────────────────────────────────────────┐
│              mmap + write 优化                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  mmap() 系统调用:                                   │   │
│  │  - 将内核缓冲区映射到用户虚拟地址空间                │   │
│  │  - 用户态和内核态共享同一块物理内存                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  优化后的流程:                                      │   │
│  │                                                     │   │
│  │  硬盘 ──DMA──→ 内核缓冲区(用户已映射)             │   │
│  │                  │                                   │   │
│  │                  │ 共享内存(无需拷贝)               │   │
│  │                  ▼                                   │   │
│  │              Socket 缓冲区 ──DMA──→ 网卡           │   │
│  │                                                     │   │
│  │  CPU 拷贝次数:1 次(从内核到 Socket)               │   │
│  │  上下文切换:2 次(write 调用 + 返回)                │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.3 sendfile:真正的零拷贝

复制代码
┌─────────────────────────────────────────────────────────────┐
│              sendfile 零拷贝技术                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  sendfile() 的革命性设计:                           │   │
│  │                                                     │   │
│  │  - 数据完全不经过用户态                              │   │
│  │  - 只在两个文件描述符之间传输                        │   │
│  │  - 配合网卡 SG-DMA,可实现真正零拷贝                │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  优化后的流程:                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │      硬盘 ──DMA──→ 内核缓冲区                       │   │
│  │                      │                              │   │
│  │                      │ SG-DMA 直接传输              │   │
│  │                      ▼                              │   │
│  │                  网卡(直接读取)                    │   │
│  │                                                     │   │
│  │  CPU 拷贝:0 次                                     │   │
│  │  上下文切换:2 次(sendfile 调用 + 返回)            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  适用场景:                                         │   │
│  │  - 文件服务器(Nginx 静态文件服务)                  │   │
│  │  - CDN 服务                                         │   │
│  │  - 日志收集系统                                      │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.4 TSO/GSO:推迟分片,减少 CPU 消耗

复制代码
┌─────────────────────────────────────────────────────────────┐
│              分片卸载技术                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  问题背景:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  MTU 通常为 1500 字节                                │   │
│  │  应用层发送 10KB 数据,需要协议栈分片成 7 个小包     │   │
│  │  每个包都要:                                        │   │
│  │  - 计算校验和                                        │   │
│  │  - 添加 TCP/IP 头部                                  │   │
│  │  - 计算分片偏移                                      │   │
│  │  CPU 开销巨大                                        │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  GSO(Generic Segmentation Offload):               │   │
│  │                                                     │   │
│  │  - 软件层面优化                                      │   │
│  │  - 将分片推迟到协议栈最后一刻                        │   │
│  │  - 减少中间处理开销                                  │   │
│  │  - 内核 2.6+ 版本支持                                │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  TSO(TCP Segmentation Offload):                   │   │
│  │                                                     │   │
│  │  - 硬件层面最强优化                                  │   │
│  │  - 大数据包直接扔给网卡                              │   │
│  │  - 网卡芯片完成分片、校验和计算                      │   │
│  │  - CPU 完全不参与分片                                │   │
│  │  - 需要网卡硬件支持                                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  查看和配置:                                               │
│  $ ethtool -k eth0  # 查看当前状态                         │
│  $ ethtool -K eth0 tso on  # 开启 TSO                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.5 XPS:多队列绑定的艺术

复制代码
┌─────────────────────────────────────────────────────────────┐
│              XPS 发送数据包转向                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  问题:多队列网卡如果没有正确绑定,会导致负载不均          │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  无 XPS 优化:                                       │   │
│  │                                                     │   │
│  │       ┌─────────────────┐                          │   │
│  │       │     CPU 0      │ ← 所有发送都来这里       │   │
│  │       └─────────────────┘                          │   │
│  │              │                                        │   │
│  │       ┌──────┴──────┐                              │   │
│  │       ▼              ▼                              │   │
│  │  ┌────────┐    ┌────────┐                         │   │
│  │  │ 队列 0  │    │ 队列 1  │  ← 竞争同一把锁       │   │
│  │  └────────┘    └────────┘                         │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  XPS 优化后:                                       │   │
│  │                                                     │   │
│  │  CPU 0 ─────────────────→ 队列 0                   │   │
│  │  CPU 1 ─────────────────→ 队列 1                   │   │
│  │  CPU 2 ─────────────────→ 队列 2                   │   │
│  │  ...                                               │   │
│  │                                                     │   │
│  │  利用 CPU 缓存局部性,减少锁竞争                    │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  配置示例:                                                 │
│  # 设置 CPU 1,3 绑定到队列 1                                │
│  echo 10 > /sys/class/net/eth0/queues/tx-1/xps_cpus       │
│  # (10 的二进制 1010 表示 CPU 1 和 CPU 3)                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第三章 接收过程优化:让网卡和内核配合得更好

3.1 RingBuffer 大小调整

复制代码
┌─────────────────────────────────────────────────────────────┐
│              RingBuffer 溢出与丢包                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  工作原理:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │    网卡 ──DMA──→ [ RingBuffer ] ──→ 内核处理      │   │
│  │                        │                            │   │
│  │                        │ 循环使用                   │   │
│  │                        └──────────────┘            │   │
│  │                                                     │   │
│  │  如果内核处理速度 < 网卡接收速度                    │   │
│  │  → RingBuffer 满                                   │   │
│  │  → 新数据包被丢弃(丢包)                          │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  诊断方法:                                                 │
│  $ ifconfig eth0                                           │
│  rx_fifo_errors: 1234    # FIFO 溢出丢包                   │
│  rx_dropped: 567         # 其他原因丢包                     │
│                                                             │
│  或使用:                                                   │
│  $ ethtool -S eth0 | grep -E "rx_dropped|rx_fifo"         │
│                                                             │
│  优化命令:                                                 │
│  # 查看当前 RingBuffer 大小                                 │
│  $ ethtool -g eth0                                        │
│                                                             │
│  # 调整接收 RingBuffer 大小                                 │
│  $ ethtool -G eth0 rx 4096 tx 4096                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.2 RSS 多队列并行处理

复制代码
┌─────────────────────────────────────────────────────────────┐
│              RSS 接收侧缩放                                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  问题:单队列网卡的瓶颈                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │         网卡(单队列)                               │   │
│  │              │                                      │   │
│  │              │ 所有包都走这里                        │   │
│  │              ▼                                      │   │
│  │        ┌──────────┐                                 │   │
│  │        │   CPU 0  │ ← 软中断都在这一个核处理       │   │
│  │        └──────────┘                                 │   │
│  │                                                     │   │
│  │  其他 CPU 核心闲置                                  │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  解决方案:RSS 多队列                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │         网卡(多队列)                               │   │
│  │              │                                      │   │
│  │    ┌─────────┼─────────┐                          │   │
│  │    ▼         ▼         ▼                           │   │
│  │  ┌────┐   ┌────┐   ┌────┐                        │   │
│  │  │队列│   │队列│   │队列│                        │   │
│  │  └──┬─┘   └──┬─┘   └──┬─┘                        │   │
│  │     ▼        ▼        ▼                            │   │
│  │  ┌────┐   ┌────┐   ┌────┐                        │   │
│  │  │CPU0│   │CPU1│   │CPU2│ ← 每个 CPU 处理独立队列 │   │
│  │  └────┘   └────┘   └────┘                        │   │
│  │                                                     │   │
│  │  并行处理,吞吐量线性提升                            │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  配置命令:                                                 │
│  $ ethtool -l eth0          # 查看队列数                   │
│  $ ethtool -L eth0 combined 4  # 设置为 4 个合并队列      │
│                                                             │
│  绑定中断到指定 CPU:                                       │
│  # 查看中断亲和性                                           │
│  $ cat /proc/interrupts | grep eth0                      │
│                                                             │
│  # 设置中断亲和性(将 eth0-TxRx-0 绑定到 CPU 0)            │
│  $ echo 1 > /proc/irq/XXX/smp_affinity                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.3 硬中断合并

复制代码
┌─────────────────────────────────────────────────────────────┐
│              中断合并机制                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  问题:每个包都触发中断,开销巨大                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │  包1 ──IRQ──→ CPU 处理 ──返回──                     │   │
│  │  包2 ──IRQ──→ CPU 处理 ──返回──                     │   │
│  │  包3 ──IRQ──→ CPU 处理 ──返回──                     │   │
│  │  ...                                                │   │
│  │                                                     │   │
│  │  每个中断:保存上下文 → 处理 → 恢复上下文 ≈ 5-10μs  │   │
│  │  1Gbps 线路 = 约 8 万个包/秒 = 大量 CPU 浪费        │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  解决方案:中断合并                                  │   │
│  │                                                     │   │
│  │  积累多个包后一次性触发中断:                        │   │
│  │  ┌────┬────┬────┬────┐                             │   │
│  │  │包1 │包2 │包3 │包4 │ ──→ 一次性 IRQ            │   │
│  │  └────┴────┴────┴────┘                             │   │
│  │                                                     │   │
│  │  Adaptive RX/TX:                                    │   │
│  │  - 流量大时:延迟合并,降低 CPU 消耗                │   │
│  │  - 流量小时:立即响应,保证低延迟                   │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  查看和配置:                                               │
│  $ ethtool -c eth0           # 查看中断合并参数            │
│  $ ethtool -C eth0 rx-frames 32  # 积累 32 个包触发中断   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.4 NAPI 软中断调优

复制代码
┌─────────────────────────────────────────────────────────────┐
│              软中断 budget 调优                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  问题:软中断处理太慢导致系统假死                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │  ksoftirqd 处理 NET_RX_SOFTIRQ:                   │   │
│  │                                                     │   │
│  │  net_rx_action() {                                 │   │
│  │      while (budget > 0) {                         │   │
│  │          process_packets();  // 处理包             │   │
│  │          budget--;                                 │   │
│  │      }                                             │   │
│  │  }                                                │   │
│  │                                                     │   │
│  │  如果包太多 → ksoftirqd 占用 CPU 100% → 其他进程卡 │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  调优参数:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  net.core.netdev_budget                             │   │
│  │  - 含义:每次软中断最多处理的包数                    │   │
│  │  - 默认值:300                                     │   │
│  │  - 调大:每次处理更多包,减少软中断频率             │   │
│  │  - 调小:减少单次处理时间,更快响应其他任务          │   │
│  │                                                     │   │
│  │  net.core.netdev_budget_usecs                      │   │
│  │  - 含义:每次软中断允许运行的时间(微秒)           │   │
│  │  - 默认值:0(不使用)                             │   │
│  │  - 设置后:基于时间而非包数限制                     │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  查看和配置:                                               │
│  $ sysctl net.core.netdev_budget                          │
│  $ sysctl -w net.core.netdev_budget=600                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.5 LRO/GRO 接收端合并

复制代码
┌─────────────────────────────────────────────────────────────┐
│              GRO 通用接收卸载                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  发送端有 Nagle 算法合并小包,接收端也有类似机制:         │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  LRO(Large Receive Offload):                      │   │
│  │  - 在网卡硬件层面合并                               │   │
│  │  - 合并后的大包一次性传给内核                       │   │
│  │  - 节省协议栈处理次数                              │   │
│  │  - 但只适合相同流的包                               │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  GRO(Generic Receive Offload):                    │   │
│  │                                                     │   │
│  │  - 在内核软件层面实现                              │   │
│  │  - 兼容性更好,支持更多协议                        │   │
│  │  - Linux 默认开启                                  │   │
│  │  - 可合并 TCP/UDP 等多种协议                       │   │
│  │                                                     │   │
│  │  工作原理:                                        │   │
│  │  ┌────────────────────────────────────────────┐    │   │
│  │  │  多个小包 → GRO 合并 → 一个大包 → 协议栈   │    │   │
│  │  └────────────────────────────────────────────┘    │   │
│  │                                                     │   │
│  │  效果:协议栈处理包数量大幅减少                    │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  查看和配置:                                               │
│  $ ethtool -k eth0 | grep gro                             │
│  gro_flush_timeout: 500                                   │
│  gro: on                                                 │
│                                                             │
│  $ ethtool -K eth0 gro on    # 开启 GRO                  │
│  $ ethtool -K eth0 gro off   # 关闭 GRO                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第四章 内核与进程协作优化

4.1 阻塞 IO vs IO 多路复用

复制代码
┌─────────────────────────────────────────────────────────────┐
│              阻塞模式的代价                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  传统 recvfrom 的问题:                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │  进程调用 recvfrom()                               │   │
│  │       │                                              │   │
│  │       ├── 数据没到?→ 进程睡眠(让出 CPU)          │   │
│  │       │                                              │   │
│  │       │  数据到达 → 唤醒进程                        │   │
│  │       │                                              │   │
│  │       └── 从内核拷贝数据到用户空间                  │   │
│  │                                                     │   │
│  │  每次睡眠-唤醒切换:约 3-5 微秒                    │   │
│  │  管理 1 万连接 = 大量无效切换                       │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  epoll 的革命:事件驱动                             │   │
│  │                                                     │   │
│  │  ┌─────────────────────────────────────────────┐   │   │
│  │  │  单线程管理成千上万个连接:                   │   │   │
│  │  │                                             │   │   │
│  │  │  epoll_wait() ──→ 返回就绪事件列表           │   │   │
│  │  │       │                                      │   │   │
│  │  │       │ 只处理真正有数据的连接                │   │   │
│  │  │       ▼                                      │   │   │
│  │  │  处理事件                                     │   │   │
│  │  │                                             │   │   │
│  │  │  无数据时:线程休眠,不消耗 CPU              │   │   │
│  │  └─────────────────────────────────────────────┘   │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.2 Kernel-Bypass:终极优化

复制代码
┌─────────────────────────────────────────────────────────────┐
│              内核旁路技术                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  传统内核协议栈的瓶颈:                              │   │
│  │                                                     │   │
│  │  应用 ──系统调用──→ 内核协议栈 ──驱动──→ 网卡      │   │
│  │              ↑                          ↑           │   │
│  │           上下文切换              数据拷贝           │   │
│  │                                                     │   │
│  │  DPDK / Solarflare OpenOnload 的思路:             │   │
│  │  "既然内核慢,那就绕过它!"                        │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Kernel-Bypass 架构:                               │   │
│  │                                                     │   │
│  │  ┌─────────────────────────────────────────────┐   │   │
│  │  │                                             │   │   │
│  │  │      应用 ←──共享内存──→ 网卡(轮询)        │   │   │
│  │  │                                             │   │   │
│  │  │      完全绕过内核协议栈                      │   │   │
│  │  │      无系统调用                              │   │   │
│  │  │      无上下文切换                            │   │   │
│  │  │      微秒级甚至纳秒级延迟                    │   │   │
│  │  │                                             │   │   │
│  │  └─────────────────────────────────────────────┘   │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  适用场景:                                                 │
│  - 金融高频交易系统                                         │
│  - 负载均衡器(如 DPDK 版 VPP)                            │
│  - 电信级网关                                               │
│  - 超低延迟数据中心                                        │
│                                                             │
│  代价:                                                     │
│  - 需要专用网卡支持                                        │
│  - 应用需要重新编写                                        │
│  - 失去了内核的流量控制、安全机制                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第五章 TCP 握手与连接管理优化

5.1 端口范围与快速重用

复制代码
┌─────────────────────────────────────────────────────────────┐
│              端口耗尽问题                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  问题背景:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │  客户端发起连接需要占用一个本地端口                  │   │
│  │                                                     │   │
│  │  默认端口范围:约 28000-38000 ≈ 6.5 万个            │   │
│  │                                                     │   │
│  │  高并发爬虫/压测时轻易耗尽                          │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  优化方案:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  1. 扩大端口范围:                                   │   │
│  │                                                     │   │
│  │  $ sysctl -w net.ipv4.ip_local_port_range="1024 65535"  │   │
│  │                                                     │   │
│  │  2. 快速复用 TIME_WAIT 端口:                       │   │
│  │                                                     │   │
│  │  $ sysctl -w net.ipv4.tcp_tw_reuse=1              │   │
│  │  $ sysctl -w net.ipv4.tcp_timestamps=1           │   │
│  │                                                     │   │
│  │  效果:TIME_WAIT 状态的端口可被新连接快速复用        │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.2 连接队列溢出

复制代码
┌─────────────────────────────────────────────────────────────┐
│              连接队列与 backlog                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  TCP 连接建立流程:                                  │   │
│  │                                                     │   │
│  │  客户端 ──SYN──→ 服务器(进入半连接队列)           │   │
│  │              │                                      │   │
│  │              │  服务器响应 SYN+ACK                  │   │
│  │              │                                      │   │
│  │         ──ACK──→ 客户端                            │   │
│  │              │                                      │   │
│  │              ▼                                      │   │
│  │         进入全连接队列(accept queue)             │   │
│  │              │                                      │   │
│  │              ▼                                      │   │
│  │         应用调用 accept() 取走                      │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  溢出场景:                                          │   │
│  │                                                     │   │
│  │  如果应用处理太慢,accept() 不及时调用:             │   │
│  │  - 全连接队列满                                     │   │
│  │  - 新连接无法进入                                   │   │
│  │  - 客户端 SYN 超时重传                              │   │
│  │  - 连接建立失败                                     │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  调优参数:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │  内核限制:net.core.somaxconn                        │   │
│  │  应用限制:listen(fd, backlog) 参数                 │   │
│  │                                                     │   │
│  │  实际队列长度 = min(somaxconn, backlog)            │   │
│  │                                                     │   │
│  │  调优建议:                                         │   │
│  │  $ sysctl -w net.core.somaxconn=65535              │   │
│  │                                                     │   │
│  │  Nginx 配置:                                       │   │
│  │  listen 80 backlog 65535;                          │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.3 减少握手重试

复制代码
┌─────────────────────────────────────────────────────────────┐
│              SYN 重试次数调优                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  TCP 三次握手失败时的重试机制:                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │  SYN ──→ 无响应 ──→ 重试(1秒后)                   │   │
│  │           │                                         │   │
│  │           │ 无响应 ──→ 重试(2秒后)               │   │
│  │           │                                         │   │
│  │           │     无响应 ──→ 重试(4秒后)           │   │
│  │           │           ...                           │   │
│  │           │                                         │   │
│  │           │ 总耗时可达数十秒!                      │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  优化策略:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │  $ sysctl -w net.ipv4.tcp_syn_retries=3           │   │
│  │                                                     │   │
│  │  参数含义:SYN 最大重试次数                          │   │
│  │                                                     │   │
│  │  推荐值:                                            │   │
│  │  - 互联网服务:2-3(总超时约 3-6 秒)               │   │
│  │  - 内网服务:1(总超时约 1.5 秒)                   │   │
│  │  - 高可用要求:保持默认                              │   │
│  │                                                     │   │
│  │  原则:3 秒连不上,通常重试也没用,快速失败更好      │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.4 TIME_WAIT 状态管理

复制代码
┌─────────────────────────────────────────────────────────────┐
│              TIME_WAIT 状态优化                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  问题背景:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │  TCP 断开连接时,主动关闭方进入 TIME_WAIT            │   │
│  │                                                     │   │
│  │  持续时间 = 2MSL(通常约 60 秒)                    │   │
│  │                                                     │   │
│  │  短连接频繁时:                                     │   │
│  │  - 大量连接处于 TIME_WAIT                          │   │
│  │  - 消耗端口资源                                    │   │
│  │  - 消耗内存                                        │   │
│  │  - 可能导致端口耗尽                                │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  解决方案:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │  1. 长连接(最佳方案):                             │   │
│  │     - 复用连接,避免频繁断开                         │   │
│  │     - HTTP Keep-Alive                              │   │
│  │     - 连接池                                        │   │
│  │                                                     │   │
│  │  2. 调整参数:                                      │   │
│  │                                                     │   │
│  │  # 限制 TIME_WAIT 最大数量                          │   │
│  │  $ sysctl -w net.ipv4.tcp_max_tw_buckets=262144   │   │
│  │                                                     │   │
│  │  # 允许复用 TIME_WAIT 端口                         │   │
│  │  $ sysctl -w net.ipv4.tcp_tw_reuse=1             │   │
│  │                                                     │   │
│  │  3. 调整 MSL:                                     │   │
│  │  $ sysctl -w net.ipv4.tcp_fin_timeout=30        │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.5 TCP Fast Open (TFO)

复制代码
┌─────────────────────────────────────────────────────────────┐
│              TCP 快速打开                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  传统 TCP 三次握手:                                        │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │  客户端 ──── SYN ────→ 服务器                       │   │
│  │              │                                      │   │
│  │              │      (等待 1 RTT)                   │   │
│  │              │                                      │   │
│  │         ←── SYN+ACK ──                              │   │
│  │              │                                      │   │
│  │              │      (等待 1 RTT)                   │   │
│  │              │                                      │   │
│  │  ────── ACK ─────→                                 │   │
│  │              │                                      │   │
│  │              │                                      │   │
│  │              ▼                                      │   │
│  │         数据传输                                    │   │
│  │                                                     │   │
│  │  总耗时:2 RTT + 数据传输                           │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  TFO 第一次握手就带数据:                           │   │
│  │                                                     │   │
│  │  客户端 ── SYN + 数据 ─→ 服务器(直接使用数据)     │   │
│  │              │                                      │   │
│  │              │      (节省 1 RTT)                   │   │
│  │              │                                      │   │
│  │         ←── SYN+ACK + 响应 ──                       │   │
│  │                                                     │   │
│  │  总耗时:1 RTT + 数据传输                           │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  开启 TFO:                                                 │
│  $ sysctl -w net.ipv4.tcp_fastopen=3                      │
│  # 3 = 客户端 + 服务端都开启                              │
│                                                             │
│  前提条件:                                                 │
│  - 客户端首次连接时仍需 2 RTT 建立"Cookie"                │
│  - 之后访问同一服务器可直接使用 TFO                        │
│  - 适用于频繁访问同一服务器的场景(如网页加载)            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第六章 优化参数速查表

复制代码
┌─────────────────────────────────────────────────────────────┐
│              网络优化参数速查表                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  分类           参数                    推荐值      │   │
│  │  ──────────────────────────────────────────────── │   │
│  │  端口范围       ip_local_port_range     1024 65535 │   │
│  │  TIME_WAIT      tcp_max_tw_buckets       262144    │   │
│  │  TIME_WAIT      tcp_tw_reuse            1          │   │
│  │  FIN超时        tcp_fin_timeout          30        │   │
│  │  连接队列       somaxconn               65535     │   │
│  │  SYN重试        tcp_syn_retries         3         │   │
│  │  软中断预算     netdev_budget           600        │   │
│  │  文件描述符     nofile                  1000000+  │   │
│  │  内存           rmem_max/wmem_max       调整到较大 │   │
│  │  TFO           tcp_fastopen             3          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第七章 优化方向总结

复制代码
┌─────────────────────────────────────────────────────────────┐
│              性能优化全景图                                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                  应用层优化                         │   │
│  │  ┌─────────────────────────────────────────────┐   │   │
│  │  │  • 减少不必要的网络 IO                        │   │   │
│  │  │  • 合并请求(Pipeline/MGET)                  │   │   │
│  │  │  • 就近部署(同一机房)                        │   │   │
│  │  │  • 内网使用内网域名                            │   │   │
│  │  │  • epoll 替代阻塞 IO                          │   │   │
│  │  │  • 使用成熟网络框架(Netty/Swoole)           │   │   │
│  │  └─────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                  发送过程优化                       │   │
│  │  ┌─────────────────────────────────────────────┐   │   │
│  │  │  • sendfile 零拷贝                          │   │   │
│  │  │  • TSO/GSO 分片卸载                          │   │   │
│  │  │  • XPS 多队列绑定                            │   │   │
│  │  │  • Nagle 算法配置                            │   │   │
│  │  └─────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                  接收过程优化                       │   │
│  │  ┌─────────────────────────────────────────────┐   │   │
│  │  │  • RingBuffer 大小调整                       │   │   │
│  │  │  • RSS 多队列与 CPU 绑定                     │   │   │
│  │  │  • 中断合并(Adaptive RX)                    │   │   │
│  │  │  • NAPI budget 调优                          │   │   │
│  │  │  • GRO/LRO 合并                             │   │   │
│  │  └─────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                  连接管理优化                       │   │
│  │  ┌─────────────────────────────────────────────┐   │   │
│  │  │  • 扩大端口范围                              │   │   │
│  │  │  • TIME_WAIT 复用                           │   │   │
│  │  │  • somaxconn 调大                           │   │   │
│  │  │  • TCP Fast Open                            │   │   │
│  │  │  • 减少 SYN 重试次数                        │   │   │
│  │  └─────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                  终极优化                          │   │
│  │  ┌─────────────────────────────────────────────┐   │   │
│  │  │  • DPDK / Kernel-Bypass                      │   │   │
│  │  │  (超低延迟场景专用)                         │   │   │
│  │  └─────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

0voice · GitHub

相关推荐
goyeer1 小时前
【ITIL】指导原则
linux·运维·服务器·数字化·itil
落叶_Jim1 小时前
Let‘s Encrypt证书有效期缩短至90天后,如何实现自动续期
网络协议·https·ssl
顶点多余1 小时前
自定义协议、序列化、反序列化实现
java·linux·开发语言·c++·tcp/ip
Bruce_kaizy2 小时前
c++ linux环境编程——从应用层到linux内核深入了解文件io的调用机制(爆肝)
linux·c++·c·嵌入式linux·文件io
浪客灿心2 小时前
Linux网络IP协议
linux·网络·tcp/ip
yuanpan2 小时前
Python + psutil 实战:开发一个简易系统监控工具
linux·运维·python
苍煜2 小时前
Docker Compose 多容器编排实战(系列第五篇:开发环境一键部署)
运维·docker·容器
故事还在继续吗2 小时前
高性能网络
服务器·网络·c/c++
认真的薛薛2 小时前
阿里云: A记录 & CNAME
服务器·前端·阿里云