--- tags: [计算机网络, 带宽, 延迟, RTT, 吞吐量, BDP, Jitter, Goodput] create time: 2026-05-17 22:40 --- # 带宽、延迟、RTT、吞吐量 ## 概述 理解网络性能的核心指标,是优化应用的前提。带宽决定"管道有多粗",延迟决定"跑一程要多久",RTT 是往返一次的时间,而吞吐量是实际能搬多少数据。它们之间的关系经常影响系统架构设计。 ## 核心定义 ### 带宽(Bandwidth) 单位时间内能传输的**最大比特数**,通常用 bps(bits per second)表示: ``` 1 Gbps = 10⁹ bits/s = 125 MB/s(注意:bit vs Byte) ``` | 链路类型 | 理论带宽 | 实际可用 | |----------|---------|---------| | 千兆以太网 (GbE) | 1 Gbps | ~940 Mbps(协议开销) | | Wi-Fi 6 (802.11ax) | 9.6 Gbps | ~1.2–2 Gbps(理论峰值) | | SATA III | 6 Gbps | 600 MB/s 实际 | | PCIe 4.0 x16 | 32 Gbps | ~3.9 GB/s | > [!tip] bit vs Byte:记住除 8 > 运营商说的 "100M 宽带" 是 **100 Mbps**。换算成下载速度约 `100 ÷ 8 ≈ 12.5 MB/s`。 ### 延迟(Latency) 一个数据包从发送到被接收所经历的**总时间**,由四部分组成: $$\text{Latency} = \text{传播延迟} + \text{传输延迟} + \text{排队延迟} + \text{处理延迟}$$ ```mermaid flowchart LR A["传播延迟
Propagation"] --> B["传输延迟
Transmission"] B --> C["排队延迟
Queuing"] C --> D["处理延迟
Processing"] style A fill:#DDA0DD,color:#000 style B fill:#87CEEB,color:#000 style C fill:#FFD700,color:#000 style D fill:#98FB98,color:#000 ``` | 延迟类型 | 公式 | 取决于 | 典型值 | |---------|------|--------|--------| | **传播延迟** | $\frac{\text{Distance}}{v}$ | 物理距离、介质 | 光纤中 $v \approx 2 \times 10^8$ m/s(真空中光速的 2/3) | | **传输延迟** | $\frac{\text{Packet Size}}{\text{Bandwidth}}$ | 数据包大小、链路带宽 | 1500B 以太网帧在 1Gbps 链路上 ≈ 12 μs | | **排队延迟** | 不确定 | 路由器拥塞程度 | 空闲时 ≈ 0,拥塞时可达数百 ms | | **处理延迟** | 不确定 | 路由器/交换机性能 | 通常 < 1 ms | > [!question] 这四类延迟,哪个最"不可控"? > 排队延迟。传播延迟由物理距离决定(选不了机房位置就改不了),传输延迟由包大小和带宽决定,处理延迟通常很小。但排队延迟随网络拥塞程度剧烈波动——这也是为什么它直接关联**抖动(Jitter)** 和 **丢包**。 | 场景 | 典型单向延迟(主要贡献:传播延迟) | |------|---------| | 同机房内 | 0.1–1 ms | | 同城(同一城市数据中心) | 1–5 ms | | 跨省(国内) | 20–40 ms | | 跨洋(中美) | 80–150 ms | | 卫星轨道(LEO) | 20–40 ms | > [!tip] TCP + TLS 的额外延迟 > 建立一个 HTTPS 连接还需要 **TCP 三次握手(1 RTT)+ TLS 1.3 握手(1 RTT)**,在数据发出前就已经"花掉"了 2 个 RTT。这也是 HTTP/2、连接复用和 TLS Session Resumption 存在的重要原因。 ### RTT(Round-Trip Time) 从发送方发出数据包到收到确认应答的总时间。严格来说: $$\text{RTT} = 2 \times (\text{传播延迟} + \text{传输延迟} + \text{排队延迟} + \text{处理延迟})$$ 实际测量中,`ping` 工具测得的 RTT 主要反映传播延迟(往返),而 TCP 的 RTT 估算(用于计算 RTO)还需包含协议栈处理时间。 ```mermaid flowchart LR A["t=0ms: 发送 SYN"] -->|"传播+路由器处理"| B["t=5ms: 到达服务端"] B -->|"处理+准备响应"| C["t=5.1ms: 服务端回发 SYN-ACK"] C -->|"传播+路由器处理"| D["t=10.2ms: 客户端收到 → RTT = 10.2ms"] style A fill:#DDA0DD,color:#000 style D fill:#98FB98,color:#000 ``` ### 抖动(Jitter) 连续数据包之间延迟的**变化量**。即使平均延迟很低,抖动大也会严重影响体验: $$\text{Jitter} = |\text{RTT}_n - \text{RTT}_{n-1}|$$ | 应用类型 | 对抖动的敏感度 | 说明 | |---------|--------------|------| | 语音通话 / 视频会议 | 🔴 极高 | 抖动 > 30ms 就会出现卡顿、断续 | | 在线游戏 | 🔴 高 | 操作响应不稳定,玩家体验崩塌 | | 文件下载 | 🟢 无所谓 | 只关心总吞吐量,不在乎每个包的到达节奏 | | HTTP API 调用 | 🟡 中等 | 长尾延迟影响 P99 响应时间 | > [!question] 如何对抗抖动? > 答案是**接收端缓冲(Jitter Buffer)**。音视频应用会在接收端先攒一小段数据再播放,用少量额外延迟换取平滑输出。WebRTC 的 jitter buffer 通常 20–200ms。 ### 吞吐量(Throughput) 单位时间内**实际成功传输的数据量**,受限于最窄环节: $$\text{Throughput} = \min(\text{带宽}, \text{拥塞窗口限制}, \text{应用处理能力})$$ 实际吞吐量永远 ≤ 带宽。常见瓶颈: ```mermaid flowchart LR A["磁盘IO
可能 < 网卡"] --> B["TCP收发缓冲区"] B --> C["网络带宽"] C --> D["对端带宽"] D --> E["对端CPU/应用"] F["中间路由拥塞"] -.-> C G["丢包重传"] -.-> C style C fill:#FFD700,color:#000 style F fill:#FF6B6B,color:#fff style G fill:#FF6B6B,color:#fff ``` > [!question] 为什么我的千兆网卡只跑到 100MB/s? > 因为千兆以太网理论上限是 `1Gbps ÷ 8 = 125MB/s`,去掉以太网帧头、IP 头、TCP 头等协议开销后,纯 Payload 大约 **94 MB/s**。如果还用了 HTTPS/TLS,CPU 加解密还会进一步限制吞吐量。 > [!info] Goodput vs Throughput:别搞混 > **Throughput**(吞吐量)是单位时间内传输的**总数据量**(含协议头)。**Goodput**(有效吞吐量)是应用层实际收到的**有效数据量**,要扣除所有协议头、重传包、确认包等开销。 > $$\text{Goodput} < \text{Throughput} \leq \text{Bandwidth}$$ > 用 `iperf3` 测出来的通常是 throughput;你的文件下载速度反映的更接近 goodput。 ## 带宽时延积(BDP) **BDP(Bandwidth-Delay Product)** 定义了链路上"在途"数据的最大量——这是维持满带宽所需的发送缓冲大小: $$\text{BDP} = \text{Bandwidth} \times \text{RTT}$$ ### 实际意义 假设带宽 1 Gbps,RTT = 100 ms: $$\text{BDP} = 10^9 \text{ bps} \times 0.1s = 10^8 \text{ bits} = 12.5 \text{ MB}$$ 这意味着: - TCP 发送窗口至少要有 **12.5 MB**,才能填满这条管道 - 如果窗口太小,TCP 会频繁停下来等 ACK,吞吐量上不去 - 这就是 Linux 的 `net.core.rmem_max` / `wmem_max` 要调大的原因 ```mermaid flowchart TD Pipe["管道: 1Gbps, RTT=100ms
BDP = 12.5MB"] Small["发送窗口 1MB ❌
只能填 8% 管道 → 吞吐量 ≈ 80Mbps"] Big["发送窗口 25MB ✅
填满管道 → 吞吐量 ≈ 1Gbps"] Pipe --> Small Pipe --> Big style Small fill:#FF6B6B,color:#fff style Big fill:#98FB98,color:#000 ``` ### 各场景 BDP 速查 | 链路 | 带宽 | RTT | BDP | |------|------|-----|-----| | 局域网 | 1 Gbps | 0.5 ms | 62.5 KB | | 同机房 | 10 Gbps | 1 ms | 1.25 MB | | 同省 | 1 Gbps | 20 ms | 2.5 MB | | 跨洋 | 100 Mbps | 150 ms | 1.875 MB | ## 延迟 vs 带宽的典型对比 | 场景 | 带宽瓶颈还是延迟瓶颈? | 说明 | |------|---------------------|------| | 大文件传输 | 带宽 | 传输时间长,RTT 占比小 | | 小请求/高频交互(RPC) | 延迟 | 每个请求 2~3 RTT,带宽几乎不占 | | 视频流 | 带宽 | 持续大量数据传输 | | DNS 查询 | 延迟 | 单次查询仅几十字节 | | Web 首屏加载 | 两者兼有 | TCP+TLS握手(延迟)+ HTML/CSS/JS 下载(带宽) | ## Go 中的实践示例 ### 测量 RTT(TCP 连接延迟) ```go // 测量一次 TCP 连接的 RTT —— 最直接的延迟指标 func measureRTT(addr string) (time.Duration, error) { start := time.Now() conn, err := net.DialTimeout("tcp", addr, 5*time.Second) if err != nil { return 0, err } conn.Close() return time.Since(start), nil // TCP 握手完成 ≈ 1 RTT } ``` ### 按 BDP 调大 TCP 缓冲区 ```go // 高带宽长延迟链路(如跨洋 100Mbps, RTT 150ms)的 BDP = 1.875MB // 系统默认的 TCP 窗口(128KB~256KB)远远不够 transport := &http.Transport{ DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { d := net.Dialer{} conn, err := d.DialContext(ctx, network, addr) if err != nil { return nil, err } tcpConn := conn.(*net.TCPConn) // 根据 BDP 设置读写缓冲区(2MB,略大于 BDP) tcpConn.SetReadBuffer(2 << 20) tcpConn.SetWriteBuffer(2 << 20) return tcpConn, nil }, } ``` > [!tip] 为什么不直接用系统默认窗口? > Linux 默认 `tcp_rmem` 的最大值通常是 6MB,但单连接的初始窗口只有 ~128KB。对于高 BDP 链路,窗口增长(慢启动)到填满管道需要好几个 RTT,前几秒吞吐量会远低于带宽上限。应用层手动设大缓冲区 + 启用窗口缩放(`tcp_window_scaling`)可以加速这个过程。 ## 关联笔记 - [[hhs/NETWORK/01-基础概念/01-OSI与TCP-IP模型对比]] — 网络分层中各层的角色 - [[hhs/NETWORK/09-性能优化/01-TCP内核调优与连接复用]] — BDP 对应内核参数 rmem/wmem