性能之巅第6章:网络性能分析

📑 目录

网络是分布式系统的"血液循环系统"。它不像CPU和内存那样在单机内闭环——网络问题往往涉及多个节点、多个路由、多种协议。诊断网络性能,需要的是"端到端"的系统性思维。


第6章 网络性能分析:追踪每一毫秒的去向

6.1 故事:跨机房迁移后的"神秘延迟"

2024年5月,某电商平台将核心服务从A机房迁移到B机房。迁移完成后,压测显示:同机房调用延迟5ms,跨机房调用延迟120ms。

"物理距离增加了30公里,延迟多了115ms?光速30公里只需要0.1ms,这说不通。"网络工程师小陈皱眉。

他开始逐层排查。

第一步:ping测试

ping -c 100 api-server-b
# rtt min/avg/max/mdev = 1.234/1.456/2.890/0.345 ms

网络层延迟只有1.5ms,不是网络距离问题。

第二步:TCP握手时间

tcpdump -i eth0 host api-server-b and port 8080 -w capture.pcap
# 用wireshark分析:
# SYN → SYN+ACK = 1.5ms(正常)
# 但 SYN+ACK → ACK = 45ms(异常!)

客户端收到SYN+ACK后,花了45ms才发ACK。为什么?

第三步:深挖客户端

ss -ti '( dport = :8080 )'
# tcp   ESTAB  0  0  10.0.1.2:54321  10.0.2.3:8080
#      ts sack cubic wscale:7,7 rto:201 rtt:1.456/0.345 mss:1448 cwnd:10 ...

注意到 cwnd:10——拥塞窗口只有10个MSS(约14KB)。而服务端一次响应就80KB,需要6个RTT才能传完。

第四步:定位根因

B机房的LB(负载均衡器)配置了一个"安全"的TCP初始拥塞窗口限制:10。A机房的是系统默认值(Linux 4.x之后是10,但之前是2-4)。等等,两边都是10?

继续查,发现B机房的防火墙对TCP窗口缩放(window scaling)选项进行了修改,导致实际可用的窗口比协商的更小。

根因:防火墙修改TCP选项 → 实际窗口小于协商窗口 → 大量小数据包传输 → 延迟飙升。

修复:调整防火墙策略,保留TCP窗口缩放选项。跨机房延迟降到6ms。


6.2 TCP/IP协议栈

数据包旅程

flowchart TD
    A["应用程序"] -->|"send()"| B["Socket层"]
    B --> C["TCP层"]
    C --> D["IP层"]
    D --> E["网卡驱动"]
    E --> F["Ring Buffer"]
    F --> G["网卡硬件"]
    G --> H["网络"]

    C -->|"连接管理"| I["三次握手"]
    C -->|"可靠性"| J["ACK/重传"]
    C -->|"流量控制"| K["滑动窗口"]
    C -->|"拥塞控制"| L["CUBIC/BBR"]

    style C fill:#e8f5e9
    style H fill:#ffebee

TCP三次握手与性能

sequenceDiagram
    participant C as 客户端
    participant S as 服务端
    
    C->>S: SYN
    Note over C,S: 1 RTT
    S-->>C: SYN+ACK
    Note over C,S: 1 RTT
    C->>S: ACK + DATA
    Note over C,S: 数据传输开始
    
    Note right of C: TCP Fast Open (TFO)
允许在SYN包中携带数据
减少1 RTT

RTT(Round Trip Time)是网络性能的核心。 优化TCP性能,本质上就是减少RTT或提高每RTT的传输量。


6.3 网络性能指标

flowchart TD
    A["网络性能"] --> B["延迟 Latency"]
    A --> C["带宽 Bandwidth"]
    A --> D["吞吐量 Throughput"]
    A --> E["丢包率 Loss"]
    A --> F["抖动 Jitter"]

    B --> B1["RTT"]
    B --> B2["TTFB"]

    C --> C1["理论峰值"]
    C --> C2["实际可用"]

    D --> D1["受限于"]
    D1 <--> C2
    D --> D2["受限于"]
    D2 <--> B
    D --> D3["受限于"]
    D3 <--> E

    style B fill:#e3f2fd
    style E fill:#ffebee

关键公式

最大吞吐量 = 窗口大小 / RTT

例如:
- 窗口 = 64KB
- RTT = 100ms
- 最大吞吐量 = 64KB / 0.1s = 640KB/s = 5.12Mbps

要跑满1Gbps,需要:
窗口 = 1Gbps × 100ms = 12.5MB

这就是带宽延迟积(BDP)


6.4 关键工具链

ss / netstat:连接状态

# 查看TCP连接状态统计
ss -s
# Total: 1234
# TCP:   2345 (estab 1234, closed 567, orphaned 89, timewait 456)

# 查看具体连接详情(包含TCP内部参数)
ss -ti '( dport = :443 or sport = :443 )'
# ts sack cubic wscale:7,7 rto:201 rtt:15.234/2.345 mss:1448 cwnd:123 ssthresh:456
#                                          ^^^ RTT
#                                                          ^^^ 拥塞窗口
#                                                                         ^^^ 慢启动阈值

tcpdump / tshark:抓包分析

# 抓特定端口
sudo tcpdump -i eth0 port 8080 -w capture.pcap

# 实时分析
sudo tcpdump -i eth0 port 8080 -nn -tttt
# 14:32:01.123456 IP 10.0.1.2.54321 > 10.0.2.3.8080: Flags [S], seq 1234567890
# 14:32:01.125012 IP 10.0.2.3.8080 > 10.0.1.2.54321: Flags [S.], seq 9876543210, ack 1234567891
# 14:32:01.567890 IP 10.0.1.2.54321 > 10.0.2.3.8080: Flags [.], ack 1, win 640

# 注意第三行ACK到数据的时间差 = 442ms(异常!)

iperf3 / qperf:吞吐量测试

# 服务端
iperf3 -s

# 客户端
iperf3 -c server_ip -t 30 -P 4 -w 1M
# -t 30: 测试30秒
# -P 4: 4个并行流
# -w 1M: 窗口大小1MB

# 输出:
# [ ID] Interval       Transfer     Bitrate
# [  5] 0.00-30.00 sec  8.92 GBytes  2.55 Gbits/sec

mtr:路由追踪+延迟

mtr --report --report-cycles 100 api.example.com
# 显示每一跳的丢包率和延迟,比traceroute更实用

6.5 TCP拥塞控制算法

算法演进

timeline
    title TCP拥塞控制演进
    section 1988
        Reno : 慢启动 + 拥塞避免
             : 丢包即拥塞
    section 1996
        CUBIC : 三次函数窗口增长
              : Linux默认
              : 高带宽友好
    section 2016
        BBR : 基于带宽和RTT
              : 不依赖丢包
              : Google出品
              : 需要内核4.9+

BBR vs CUBIC 对比

# 查看当前算法
sysctl net.ipv4.tcp_congestion_control
# net.ipv4.tcp_congestion_control = cubic

# 切换到BBR(需要内核4.9+且启用拥塞控制模块)
echo 'net.ipv4.tcp_congestion_control=bbr' >> /etc/sysctl.conf
sysctl -p

# 验证
sysctl net.ipv4.tcp_congestion_control
# net.ipv4.tcp_congestion_control = bbr
场景CUBICBBR
浅缓存网络更好
深缓存网络填充过满,bufferbloat精准控制,低延迟
高丢包率吞吐量暴跌维持高吞吐
长肥管道窗口增长慢快速探测带宽

建议:Linux内核4.9+的Server,建议切换到BBR。


6.6 网络优化清单

# ===== TCP核心参数调优 =====

# 启用SYN Cookie,防SYN Flood
echo 1 > /proc/sys/net/ipv4/tcp_syncookies

# 开启TCP Fast Open(客户端+服务端)
echo 3 > /proc/sys/net/ipv4/tcp_fastopen

# 增大TCP最大缓冲区
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"

# 自动调优窗口
sysctl -w net.ipv4.tcp_window_scaling=1
sysctl -w net.ipv4.tcp_moderate_rcvbuf=1

# 增大本地端口范围
sysctl -w net.ipv4.ip_local_port_range="1024 65535"

# 缩短TIME_WAIT时间(高并发场景)
sysctl -w net.ipv4.tcp_tw_reuse=1
# 注意:tcp_tw_recycle在4.12+已移除,不要设置

# 开启BBR(内核4.9+)
echo bbr > /sys/module/tcp_bbr/parameters/tcp_congestion_control

6.7 本章总结

mindmap
  root(("网络\n性能分析"))
    TCP核心
      三次握手
      滑动窗口
      拥塞控制
    关键指标
      RTT
      吞吐量
      丢包率
    工具链
      ss/netstat
      tcpdump/wireshark
      iperf3
      mtr
    优化方向
      减少RTT
      增大窗口
      切换BBR
      TCP Fast Open

核心要点

  1. RTT是网络性能的基石——优化前先测准RTT
  2. 吞吐量 = 窗口 / RTT——窗口不够大,千兆带宽跑不满
  3. BBR在高丢包、高延迟场景远超CUBIC——内核4.9+建议切换
  4. tcpdump能精确定位延迟发生在哪个阶段——SYN?ACK?数据传输?
  5. ss -ti 是查看TCP内部状态的最快方式——cwnd、rtt、ssthresh一目了然

"网络问题最难的不是定位,而是证明’不是网络问题’。"