mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1197 字
3 分钟
为什么 TCP 协议有性能问题
2023-04-18

TCP(Transmission Control Protocol)是互联网最核心的协议之一,提供了可靠的、有序的数据传输服务。然而,TCP 也有著名的性能问题:队头阻塞(Head-of-Line Blocking)、连接创建开销、拥塞控制对吞吐量的限制等。

这些”问题”并非设计缺陷,而是可靠性优先带来的代价。理解 TCP 的性能问题,是设计高性能网络系统的基础。

一、TCP 的核心设计目标#

1.1 可靠传输的代价#

TCP 的设计目标优先级:

flowchart LR subgraph TCP 设计优先级 R[可靠性] --> O[有序性] O --> F[流量控制] F --> C[拥塞控制] end P[性能] --> C style R fill:#f96 style O fill:#f96 style F fill:#ff9 style C fill:#ff9 style P fill:#9f9

可靠性是 TCP 的首要目标,性能是在可靠性前提下的优化。

1.2 TCP 的四大问题#

问题根源影响
队头阻塞按序交付机制丢包阻塞所有流
连接创建开销三次握手 + 慢启动高延迟
拥塞控制保守的发送策略吞吐量受限
流量控制滑动窗口机制发送速率受限

二、队头阻塞(Head-of-Line Blocking)#

2.1 什么是队头阻塞?#

TCP 要求数据按序交付。如果某个数据包丢失,即使后续数据已到达,也必须等待丢失的数据重传:

sequenceDiagram participant S as 发送方 participant R as 接收方 S->>R: Packet 1 S->>R: Packet 2 S->>R: Packet 3 (丢失) S->>R: Packet 4 S->>R: Packet 5 Note over R: 必须等待 Packet 3 重传<br/>才能交付 Packet 4, 5

2.2 HTTP/2 的队头阻塞#

HTTP/1.1 的队头阻塞在应用层,而 HTTP/2 的队头阻塞在 TCP 层

flowchart LR subgraph HTTP/1.1 H1[流 1] --> H1Q[请求队列] H2[流 2] --> H1Q end subgraph HTTP/2 (多路复用) M[TCP] --> S1[流 1] M --> S2[流 2] M --> S3[流 3] end Note over M: TCP 丢包 → 所有流都被阻塞

问题:即使 HTTP/2 在应用层实现了多路复用,TCP 的按序交付特性仍然会导致所有流被一个丢包阻塞。

2.3 QUIC 如何解决#

QUIC(Quick UDP Internet Connections)将多路复用下沉到传输层

flowchart LR subgraph TCP + HTTP/2 T[TCP] --> H1[流 1] T --> H2[流 2] T --> H3[流 3] Note over T: 一个丢包阻塞所有流 end subgraph QUIC Q[UDP] --> Q1[流 1] Q --> Q2[流 2] Q --> Q3[流 3] Note over Q: 丢包只阻塞对应流 end

QUIC 在用户空间实现,避免了内核空间 TCP 的队头阻塞问题。

三、连接创建开销#

3.1 TCP 三次握手的延迟#

sequenceDiagram participant C as 客户端 participant S as 服务端 C->>S: SYN Note over C: 等待 1 RTT S->>C: SYN + ACK Note over C: 等待 1 RTT C->>S: ACK Note over C,S: 总共 1 RTT 才能开始传输数据
延迟类型固定开销
TCP 三次握手1 RTT
TLS 1.22 RTT
TLS 1.31 RTT(0-RTT 可选)
HTTP/1.1 请求1 RTT
总计(TLS 1.2 + HTTP)4 RTT

3.2 慢启动:甜蜜的烦恼#

即使连接建立后,TCP 的拥塞控制也会限制发送速率:

flowchart LR subgraph 慢启动过程 SS1[拥塞窗口=1 MSS] --> SS2[1 RTT 后<br/>窗口=2 MSS] SS2 --> SS3[2 RTT 后<br/>窗口=4 MSS] SS3 --> SS4[3 RTT 后<br/>窗口=8 MSS] SS4 --> SS5[直到遇到丢包<br/>或达到带宽上限] end

慢启动(Slow Start) 是 TCP 避免网络拥塞的机制,但会导致新连接在开始时无法充分利用带宽。

3.3 连接复用的方案#

方案说明解决的问题
HTTP Keep-Alive多个请求复用 TCP 连接避免重复三次握手
TLS Session Resumption复用 TLS 会话减少 TLS 握手 RTT
0-RTT首个请求就携带数据完全消除握手延迟

四、拥塞控制机制#

4.1 TCP 拥塞控制四剑客#

flowchart LR subgraph 拥塞控制算法 SS[慢启动] --> CA[拥塞避免] CA --> FR[快速恢复] FR --> SS end
阶段窗口增长策略目的
慢启动指数增长快速探测可用带宽
拥塞避免线性增长稳定运行在带宽附近
快速恢复窗口减半 + 线性增长快速恢复传输

4.2 丢包检测的局限性#

TCP 依赖丢包作为拥塞信号:

flowchart TB A[发送数据] --> B{收到 ACK?} B -->|是| C[窗口增长] B -->|否| D{超时?} D -->|是| E[拥塞!<br/>窗口重置为 1] D -->|否| F[快速重传<br/>窗口减半] C --> A E --> A F --> A

问题

  • 丢包不一定是拥塞(也可能是丢线)
  • 丢包后才调整,已经造成延迟
  • 无线网络中丢包率较高,TCP 性能差

4.3 BBR vs CUBIC#

算法策略适用场景
CUBIC(Linux 默认)基于丢包检测有线网络,低丢包率
BBR(Google)基于带宽和延迟测量高带宽高延迟网络

五、流量控制#

5.1 滑动窗口机制#

TCP 使用滑动窗口进行流量控制:

sequenceDiagram participant S as 发送方 participant R as 接收方 R->>S: 窗口大小 = 65535 S->>R: 发送 65535 字节 Note over R: 接收缓冲区已满 R->>S: 窗口大小 = 0(窗口关闭) S->>S: 停止发送,等待窗口更新 R->>S: 窗口大小 = 1000(窗口打开) S->>R: 继续发送

5.2 糊涂窗口综合征#

如果接收方每次只打开很小的窗口,发送方会发送大量小数据包,效率极低:

flowchart LR S[发送方] -->|大量小数据包| R[接收方] R -->|小窗口更新| S

解决方案:接收方等待窗口达到一定大小才更新,发送方使用 Nagle 算法合并小数据包。

六、高性能网络的设计原则#

6.1 减少连接数#

策略方法
连接池复用 HTTP 连接
HTTP/2多路复用,单连接
HTTP/3QUIC,0-RTT

6.2 减少数据传输量#

# 数据压缩
Content-Encoding: gzip, br, zstd
# 请求压缩
Accept-Encoding: gzip, br
# 响应压缩
Transfer-Encoding: chunked

6.3 选择合适的协议#

场景推荐协议
Web 页面HTTP/2 或 HTTP/3
实时通信WebSocket, QUIC
文件传输TCP 直接传输,或 UDP(如 QUIC)
流媒体UDP(如 RTMP, QUIC)

七、总结#

TCP 的性能问题是可靠性设计的必然代价:

问题根源解决方案
队头阻塞按序交付QUIC, HTTP/3
连接创建开销三次握手TLS 1.3, 0-RTT
慢启动拥塞控制TCP Fast Open, 更快加速
拥塞控制保守策略BBR, 高带宽网络优化

理解 TCP 的局限性,才能更好地设计高性能网络系统。QUIC/HTTP/3 的出现,正是为了解决 TCP 的这些问题。

参考资料#

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

为什么 TCP 协议有性能问题
https://blog.souloss.com/posts/why-the-design/why-tcp-has-performance-issues/
作者
Souloss
发布于
2023-04-18
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时