Good developers know how things work. Great developers know why things work.
Latency and bandwidth
时延:从源发送数据包到接收数据包的目的地的时间
源和目的地之间的距离越远,传播所需的时间就越长。经过的中间路由器越多,每个数据包的处理和传输延迟就越高。最后,路径上的流量负载越高,我们的数据包在接收缓冲区内被延迟的可能性就越高。
带宽:逻辑或物理通信路径的最大吞吐量
可以使用 traceroute
或者 tracert
诊断网络包在在一个 ip 网络上每一跳的延迟。
为了提高应用程序的性能,我们需要在明确意识到可用带宽和光速的限制的情况下构建和优化我们的协议和网络代码:如减少往返,将数据移近客户端,并构建可以通过缓存、预取和各种类似技术隐藏延迟的应用程序。
TCP 协议(Transmission Control Protocol)提供在不可靠信道上运行可靠网络的抽象层。
RFC 1122—Communication Layers ( link layer, IP layer, and transport layer )
RFC 1123—Application and Support ( application and support protocols )
TCP 协议保证所有发送的字节将与收到的字节相同,并且它们将以相同的顺序到达对端,TCP 协议是为了准确传输而优化。
TCP 和 IP 协议的相互交织的历史
我们都熟悉 IPv4
和 IPv6
,但 为啥没有 IPv1
, IPv2
…? IPv4
中的 4
代表 1981 年 9 月发布的 TCP/IP
协议第 4 版。最初的 TCP/IP
提案将两个协议结合起来,正是 v4
草案将两者正式划分为单独的 RFC。因此, IPv4
中的 v4
是其与 TCP
关系的遗产:以前没有独立的 IPv1
、 IPv2
或 IPv3
协议。当工作组于 1994 年开始研究 “下一代互联网协议” (IPng)
时,需要一个新的版本号,但 v5
已经分配给另一个外部协议:“互联网流协议” (ST)
。这是 IPv6
中的 6
的由来。
三次握手
Three-way handshake
SYN
客户端挑选一个随机序列号 x
并发送一个 SYN
包,其中还可能包括额外的 TCP 标志和配置。
SYN ACK
服务端将 x
递增 1,挑选自己的随机序列号 y
,附加自己的标志和配置,并返回响应 SYN ACK
。
ACK
客户端将 x
和 y
都增加 1,并通过发送握手中的最后一个 ACK
包来完成握手。
TCP Fast Open: TCPFast Open (TFO)
是一种机制,允许在 SYN
包内进行数据传输。对 SYN
包内数据有效载荷的最大尺寸受拥塞控制和底层 IP 协议切片大小的限制,只能发送某些类型的 HTTP 请求。它通过使用 TFO cookie(一个TCP选项)来工作,这是一个存储在客户端的加密 cookie,在与服务器的初始连接时设置。如果成功,服务器甚至可以在收到三方握手的最后 ACK
包之前就开始向客户端发送数据,从而跳过一个往返延迟,降低了数据传输开始时的延时。
Fast Open Handshake
Fast Open Options
TFO 的 Cookie 要求和客户端的 IP 绑定以及时间戳(针对 NAT 网络的客户端需要),且有确切的过期时间,服务端能对随时对 Cookie 作废。
使用 TFO 的客户端在首次发送 SYN 包时,会出现重复发送 SYN 包。
此时会导致服务端收到重复的 SYN 包,这在严格的 TCP 规则里是不允许的会产生错误。故 TFO 放宽了 TCP 的规则定义,允许发送重复的 SYN 包。
TFO 主要在对建立连接延迟敏感的场景下使用更有效益。如果可以,应用程序应该尽可能考虑复用 TCP 连接来传输数据来获得更好的效益提升。
TCP 的拥塞避免与控制 ( Congestion Avoidance and Control )
TCP 拥塞控制 = 慢启动 ( slow start ) + 拥塞避免 ( congestion avoidance ) + 快速重传 ( fast retransmit ) + 快速恢复 ( fast recovery )
流控制 (Flow Control)
Receive window size ( rwnd ) advertisement
TCP 滑动窗口 ( TCP Window Scaling )
允许最大接收窗口大小最高到 1 GByte。TCP Window Scaling 在三向握手期间传达,并计算一个值,该值表示在未来ACK中左移16位窗口大小字段的位数。
能够使用如下命令查看和启用 TCP Window Scaling 配置:
sysctl net.ipv4.tcp_window_scaling
sysctl -w net.ipv4.tcp_window_scaling=1
慢启动 ( Slow-Start )
Congestion control and congestion avoidance
估计客户端和服务器之间可用容量的唯一方法是通过交换数据来测量它,而这正是慢速启动的设计目的。开始时,服务器为每个 TCP 连接初始化一个新的拥塞窗口 ( cwnd ) 变量,并将其初始值设置为一个保守的、系统指定的值 ( Linux 上为 intcwnd )( 目前的规范 intcwnd 为 10 个 Segments )。并发送数据包,每次收到 ACK 后,为 cwnd 增加一个 Segment 的发送量。从而达到指数增长。旨在迅速收敛到网络路径的可用带宽。
新 TCP 连接发送的数据量最高为 rwnd 和 cwnd 之间的最小值。
Maximum Segment Size (MSS) :它指的是在单个 TCP 片段中可发送的最大有效载荷量。它受 Maximum Transmission Unit ( MTU ) 大小的限制,即可以在网络上传输的IP数据包的最大尺寸。
Maximum Transmission Unit ( MTU ):它指可以在网络上传输的单个 IP 片段的最大有效载荷。
当MTU 被设置为标准的 1500 Byte 时,MSS 通常被设置为最大的 1460 Byte。
Congestion window ( cwnd ) size: 发送方的限制,即发送方在重新收到客户端的确认(ACK)之前,可以有飞行中的数据量。
每个TCP连接都必须经过慢速启动阶段,我们不能立即使用链路的全部容量。
Congestion window size growth
因为有慢启动的存在,故存在一定延迟才达到最大带宽。Time 为慢启动的时延。
$$ Time = {RTT \times \left[ \log_{2} \left( \frac{N}{inital\quad cwnd} \right) \right]} $$
$$ N = \frac{双方可接收窗口的最小值的字节数(单位Byte)}{单个TCPSegment的字节数(单位Byte)(1460Bytes)} $$
减少延迟的方法:
RTT
,拉近双方距离,使用边缘节点或者 CDN。inital cwnd
到 10 segments ( RFC—6928 )。对于许多 HTTP 连接来说,它们往往是短暂的和突发的,请求在达到最大窗口大小之前终止可能是正常的。许多网络应用的性能往往受到服务器和客户端之间往返时间的限制,这一因素可能无法有效改善。慢速启动限制了可用的带宽吞吐量,这时对小型传输的性能就产生了不利影响。
TCP 还实现了 slow-start restart ( SSR ) 机制。该机制在连接空闲一段时间后重置拥堵窗口。其原理很简单:当连接处于空闲状态时,网络条件可能发生了变化,为了避免拥堵,窗口被重置为一个 "安全" 的默认值。 SSR 会对可能会空闲一段时间的 TCP 连接的性能产生重大影响。例如,HTTP keepalive 连接。可以使用如下命令关闭:
sysctl net.ipv4.tcp_slow_start_after_idle
sysctl -w net.ipv4.tcp_slow_start_after_idle=0
复用 TCP 可以让后面发起的 HTTP 请求跳过 slow-start 的过程,达到更小的传输时延。
拥塞避免 ( congestion avoidance ) + 快速重传 ( fast retransmit ) + 快速恢复 ( fast recovery )
cwnd < ssthresh (slow start threshold window)
时,使用慢速启动算法;当 cwnd ≥ ssthresh
时,使用拥塞避免算法。TCP Reno (原始实现)
、TCP NewReno
、TCP CUBIC(Linux上默认)
或 Compound TCP(Windows上默认)
、TCP BBR (Google 实现)
TCP Reno 算法(慢启动 + 拥塞避免 + 快速重传 + AIMD):
TCP NewReno 算法 (慢启动 + 拥塞避免 + 快速重传 + PPR) :
TCP CUBIC 算法:CUBIC 针对高速长距离网络的 TCP 拥塞控制算法,其主要目标是通过实时估算网络空闲带宽和延迟,优化 TCP 窗口大小的增长速率,从而提高数据传输的吞吐量和性能。通过 CUBIC 算法,网络能够更好地适应并应对拥塞和网络流量变化,从而实现更高效的数据传输。
TCP BBR 算法:BBR 拥塞控制算法考虑网络传递数据的速度。对于一个给定的网络连接,它使用最近对网络传输率和往返时间的测量来建立一个明确的模型,其中包括该连接最近可用的最大带宽,以及其最近的最小往返延迟。然后,BBR使用这个模型来控制它发送数据的速度和它愿意在任何时候允许进入网络的最大数据量。
TCP 中内置的拥塞控制具有另一个重要的性能含义:最佳的发送和接收窗口大小必须根据往返时间和它们之间的目标数据速率而变化。
Transmission gaps due to low congestion window size, If either the sender or receiver are frequently forced to stop and wait for ACKs for previous packets, then this would create gaps in the data flow