admin管理员组

文章数量:1664814

互联网的核心是两个协议,IP 和 TCP。IP 或 Internet 协议提供主机到主机的路由和寻址,而 TCP 或传输控制协议提供在不可靠通道上运行的可靠网络的抽象。TCP/IP 通常也称为 Internet 协议套件,由 Vint Cerf 和 Bob Kahn 在其 1974 年题为“分组网络互通协议”的论文中首次提出。

最初的提案(RFC 675)经过多次修订,1981 年 TCP/IP 的 v4 规范不是作为一个,而是作为两个独立的 RFC 发布:

RFC 791 — 互联网协议
RFC 793 — 传输控制协议

从那时起,对 TCP 提出并做出了许多改进,但核心操作没有显着改变。TCP 迅速取代了以前的协议,现在是许多最流行的应用程序的首选协议:万维网、电子邮件、文件传输等。

TCP 提供了对运行在不可靠通道上的可靠网络的有效抽象,向我们的应用程序隐藏了大部分网络通信的复杂性:丢失数据的重传、按顺序传递、拥塞控制和避免、数据完整性等等。当您使用 TCP 流时,您可以保证发送的所有字节都与接收的字节相同,并且它们将以相同的顺序到达客户端。因此,TCP 针对准确交付进行了优化,而不是及时交付。事实证明,在优化浏览器中的 Web 性能时,这也带来了一些挑战。

HTTP 标准不要求 TCP 作为唯一的传输协议。如果我们愿意,我们可以通过数据报套接字(用户数据报协议或 UDP)或我们选择的任何其他传输协议来传递 HTTP,但实际上,今天 Internet 上的所有 HTTP 流量都是通过 TCP 传递的,因为有许多很棒的和方便的它提供开箱即用的功能。

因此,了解 TCP 的一些核心机制对于构建优化的 Web 体验是必不可少的知识。您可能不会直接在应用程序中使用 TCP 套接字,但您在应用程序层所做的设计选择将决定 TCP 的性能以及交付应用程序的底层网络。

§TCP 和 IP 协议的交织历史
我们都熟悉 IPv4 和 IPv6,但 IPv{1,2,3,5} 发生了什么?IPv4 中的 4 代表 TCP/IP 协议的第 4 版,发布于 1981 年 9 月。最初的 TCP/IP 提案耦合了这两种协议,正是 v4 草案正式将这两种协议拆分为单独的 RFC。因此,IPv4 中的 v4 继承了它与 TCP 的关系:没有独立的 IPv1、IPv2 或 IPv3 协议。

当工作组在 1994 年开始研究“下一代互联网协议”(IPng)时,需要一个新的版本号,但 v5 已经分配给另一个实验性协议:互联网流协议(ST)。事实证明,ST 从未起飞,这就是为什么很少有人听说过它。因此 IPv6 中的 6。

§三向握手
所有 TCP 连接都以三次握手开始(图 2-1)。在客户端或服务器可以交换任何应用程序数据之前,它们必须就开始数据包序列号以及来自双方的许多其他连接特定变量达成一致。出于安全原因,序列号是从双方随机挑选的。

同步
客户端选择一个随机序列号x并发送一个 SYN 数据包,其中可能还包括额外的 TCP 标志和选项。

同步确认
服务器递增x1,选择自己的随机序列号y,附加自己的一组标志和选项,然后发送响应。

确认
客户端将两者都加x一并y通过在握手中分派最后一个 ACK​​ 数据包来完成握手。

图 2-1。 三向握手

一旦三次握手完成,应用程序数据就可以开始在客户端和服务器之间流动。客户端可以在 ACK 包之后立即发送数据包,服务器必须等待 ACK 才能调度任何数据。此启动过程适用于每个 TCP 连接,并对所有使用 TCP 的网络应用程序的性能产生重要影响:每个新连接在传输任何应用程序数据之前都会有一个完整的往返延迟。

例如,如果我们的客户端在纽约,服务器在伦敦,并且我们正在通过光纤链路启动一个新的 TCP 连接,那么三次握手至少需要 56 毫秒(表 1-1):向一个方向传播数据包需要 28 毫秒,之后它必须返回纽约。请注意,连接的带宽在这里不起作用。相反,延迟由客户端和服务器之间的延迟控制,而延迟又由纽约和伦敦之间的传播时间控制。

三次握手造成的延迟使得创建新 TCP 连接的成本很高,这也是为什么连接重用对于任何通过 TCP 运行的应用程序来说都是关键优化的重要原因之一。

§TCP 快速打开
加载网页通常需要从数十个不同的主机获取数百个资源。反过来,这可能需要浏览器建立数十个新的 TCP 连接,每个连接都必须产生 TCP 握手的开销。不用说,这可能是网页浏览延迟的重要来源,尤其是在较慢的移动网络上。

TCP 快速打开 (TFO) 是一种机制,旨在通过允许在 SYN 数据包内传输数据来消除对新 TCP 连接施加的延迟损失。但是,它也有其自身的限制:SYN 数据包中数据负载的最大大小有限制,只能发送某些类型的 HTTP 请求,并且由于需要加密cookie。有关 TFO 的功能和限制的详细讨论,请查看最新的 IETF 草案“TCP Fast Open”。

启用 TFO 需要客户端、服务器的明确支持以及应用程序的选择加入。为获得最佳效果,请在服务器上使用 Linux 内核 v4.1+、兼容的客户端(例如 Linux 或 iOS9+/OSX 10.11+),并在您的应用程序中启用适当的套接字标志。

根据 Google 进行的流量分析和网络仿真,研究人员表明,TFO 可以将 HTTP 事务网络延迟降低 15%,将整个页面加载时间平均降低 10% 以上,在某些情况下,最高可降低 40%。延迟场景!

§拥塞避免和控制
1984 年初,John Nagle 记录了一种称为“拥塞崩溃”的情况,这可能会影响任何网络节点之间带宽容量不对称的网络:

拥塞控制是复杂网络中公认的问题。我们发现,国防部的 Internet 协议 (IP),一种纯数据报协议,和传输控制协议 (TCP),一种传输层协议,当一起使用时,会出现由传输和数据报之间的交互引起的异常拥塞问题层。特别是,IP 网关容易受到我们称之为“拥塞崩溃”的现象的影响,尤其是当此类网关连接带宽差异很大的网络时……

如果往返时间超过任何主机的最大重传间隔,该主机将开始将越来越多的相同数据报副本引入网络。网络现在陷入了严重的困境。最终,交换节点中的所有可用缓冲区都将被填满,并且必须丢弃数据包。传送的数据包的往返时间现在已达到最大值。主机多次发送每个数据包,最终每个数据包的一些副本到达其目的地。这就是拥堵崩溃。

这种情况是稳定的。一旦达到饱和点,如果选择要丢弃的数据包的算法是公平的,则网络将继续在降级状态下运行。

约翰纳格尔,RFC 896

该报告得出结论,拥塞崩溃尚未成为 ARPANET 的问题,因为大多数节点具有统一的带宽,并且骨干网有大量过剩的容量。然而,这些断言都没有长期成立。1986 年,随着网络上节点数量(5,000+)和种类的增加,一系列拥塞崩溃事件席卷了整个网络——在某些情况下,容量下降了 1,000 倍,网络变得无法使用。

为了解决这些问题,在 TCP 中实现了多种机制来控制双向发送数据的速率:流量控制、拥塞控制和拥塞避免。

高级研究计划署网络 (ARPANET) 是现代互联网和世界上第一个可操作的分组交换网络的前身。该项目于 1969 年正式启动,1983 年 TCP/IP 协议取代了早期的 NCP(网络控制程序)成为主要的通信协议。其余的,正如他们所说,是历史。

§流量控制
流量控制是一种机制,可防止发送方用它可能无法处理的数据压倒接收方——接收方可能很忙、负载过重,或者可能只愿意分配固定数量的缓冲区空间。为了解决这个问题,TCP 连接的每一端都通告(图 2-2)它自己的接收窗口(rwnd),它传达可用缓冲区空间的大小来保存传入数据。

首次建立连接时,双方都使用系统默认设置启动其 rwnd 值。典型的网页会将大部分数据从服务器流式传输到客户端,从而使客户端的窗口成为可能的瓶颈。但是,如果客户端将大量数据流式传输到服务器,例如在图像或视频上传的情况下,则服务器接收窗口可能成为限制因素。

如果出于某种原因,其中一方无法跟上,那么它可以向发送者通告一个较小的窗口。如果窗口达到零,则将其视为不应发送更多数据的信号,直到缓冲区中的现有数据已被应用层清除。此工作流程在每个 TCP 连接的整个生命周期中都持续存在:每个 ACK​​ 数据包都为每一方携带最新的 rwnd 值,从而允许双方根据发送方和接收方的容量和处理速度动态调整数据流率。


图 2-2。接收窗口(rwnd)大小广告

§窗口缩放 (RFC 1323)
最初的 TCP 规范分配了 16 位用于通告接收窗​​口大小,这为发送方和接收方可以通告的最大值(2 16或 65,535 字节)设置了一个硬上限。事实证明,这个上限通常不足以获得最佳性能,尤其是在表现出高带宽延迟积的网络中;有关这方面 的更多信息,请参阅Bandwidth-Delay Product。

为了解决这个问题,起草了 RFC 1323 以提供“TCP 窗口缩放”选项,它允许我们将最大接收窗口大小从 65,535 字节提高到 1 GB!窗口缩放选项在三次握手期间进行通信,并带有一个值,该值表示在未来的 ACK 中左移 16 位窗口大小字段的位数。

今天,所有主要平台都默认启用 TCP 窗口缩放。然而,中间节点、路由器和防火墙可以重写甚至完全去除这个选项。如果您与服务器或客户端的连接无法充分利用可用带宽,那么检查窗口大小的交互始终是一个不错的起点。在 Linux 平台上,可以通过以下命令检查和启用窗口缩放设置:

$> sysctl net.ipv4.tcp_window_scaling

$> sysctl -w net.ipv4.tcp_window_scaling=1

§慢启动
尽管 TCP 中存在流量控制,但网络拥塞崩溃在 1980 年代中后期成为一个真正的问题。问题是流量控制阻止了发送方压倒接收方,但是没有机制可以防止任何一方压倒底层网络:发送方和接收方都不知道新连接开始时的可用带宽,因此需要一种估计它的机制,并使它们的速度适应网络内不断变化的条件。

为了说明这种适应是有益的一个示例,假设您在家并从远程服务器流式传输大型视频,该服务器设法使您的下行链路饱和以提供最高质量的体验。然后您家庭网络上的另一个用户打开一个新连接以下载一些软件更新。突然之间,视频流的可用下行带宽少了很多,视频服务器必须调整它的数据速率——否则,如果它继续以相同的速率,数据将简单地堆积在某个中间网关上,然后数据包将被丢弃,导致网络使用效率低下。

1988 年,Van Jacobson 和 Michael J. Karels 记录了几种算法来解决这些问题:慢启动、拥塞避免、快速重传和快速恢复。所有这四个都很快成为 TCP 规范的强制性部分。事实上,人们普遍认为,正是这些 TCP 更新阻止了 80 年代和 90 年代初的互联网崩溃,因为流量继续以指数速度增长。

要了解慢启动,最好在实际中看到它。因此,让我们再次回到位于纽约的客户,该客户试图从伦敦的服务器中检索文件。首先,执行三次握手,在此期间双方在 ACK 数据包中通告各自的接收窗口 (rwnd) 大小(图 2-2)。一旦最终的 ACK 数据包上线,我们就可以开始交换应用程序数据了。

估计客户端和服务器之间可用容量的唯一方法是通过交换数据来衡量它,而这正是慢启动设计的目的。首先,服务器为每个 TCP 连接初始化一个新的拥塞窗口 (cwnd) 变量,并将其初始值设置为保守的系统指定值(Linux 上的 initcwnd)。

拥塞窗口大小 (cwnd)
发送方对发送方在接收到来自客户端的确认 (ACK) 之前可以在飞行中拥有的数据量的限制。

cwnd 变量不会在发送者和接收者之间公布或交换——在这种情况下,它将是由伦敦服务器维护的私有变量。此外,引入了一个新规则:客户端和服务器之间传输(未确认)的最大数据量是 rwnd 和 cwnd 变量中的最小值。到目前为止一切都很好,但是服务器和客户端如何确定其拥塞窗口大小的最佳值?毕竟,网络状况一直在变化,即使在相同的两个网络节点之间也是如此,正如我们在前面的示例中看到的那样,如果我们可以使用该算法而无需手动调整每个连接的窗口大小,那就太好了。

解决方案是缓慢启动并在确认数据包时增大窗口大小:慢启动!原来cwnd起始值设置为1网段;RFC 2581 于 1999 年 4 月将此值更新为 4 段;最近,RFC 6928 在 2013 年 4 月将该值再次增加到 10 个段。

新 TCP 连接的最大传输数据量是 rwnd 和 cwnd 值中的最小值;因此,现代服务器最多可以向客户端发送十个网段,此时它必须停止并等待确认。然后,对于每个收到的 ACK,慢启动算法表明服务器可以将其 cwnd 窗口大小增加一个段——对于每个 ACK​​ 的数据包,可以发送两个新的数据包。TCP 连接的这一阶段通常被称为“指数增长”算法(图 2-3),因为客户端和服务器正试图快速收敛到它们之间的网络路径上的可用带宽。


图 2-3。拥塞控制和拥塞避免

那么,当我们为浏览器构建应用程序时,为什么慢启动是一个需要牢记的重要因素呢?嗯,HTTP 和许多其他应用程序协议运行在 TCP 上,无论可用带宽如何,每个 TCP 连接都必须经过慢启动阶段——我们不能立即使用链路的全部容量!

相反,我们从一个小的拥塞窗口开始,并在每次往返时将其加倍——即指数增长。因此,达到特定吞吐量目标所需的时间是客户端和服务器之间的往返时间和初始拥塞窗口大小 的函数(达到大小为 N 的 cwnd 大小的时间)。

达到大小为 N 的 cwnd 大小的时间

对于慢启动影响的动手示例,让我们假设以下场景:

客户端和服务器接收窗口:65,535 字节 (64 KB)
初始拥塞窗口:10 段 (RFC 6928)
往返时间:56 毫秒(伦敦到纽约)

尽管接收窗口大小为 64 KB,但新 TCP 连接的吞吐量最初受到拥塞窗口大小的限制。事实上,要达到 64 KB 的接收窗口限制,我们首先需要将拥塞窗口大小增加到 45 个段,这需要 168 毫秒:

这是客户端和服务器之间达到 64 KB 吞吐量的三个往返(图 2-4 )!当建立新连接时,客户端和服务器可能能够以 Mbps+ 数据速率传输这一事实没有任何影响——这就是慢启动。

上面的示例使用十个网段的新 (RFC 6928) 值作为初始拥塞窗口。作为一个练习,对旧的四个段的大小重复相同的计算——你会看到这将为上述结果增加额外的 56 毫秒往返!

图 2-4。拥塞窗口大小增长

为了减少增加拥塞窗口所需的时间,我们可以减少客户端和服务器之间的往返时间——例如,将服务器在地理位置上移近客户端。或者我们可以将初始拥塞窗口大小增加到 10 个段的新 RFC 6928 值。

对于大型流式下载来说,慢启动不是什么大问题,因为客户端和服务器将在几百毫秒后达到它们的最大窗口大小并继续以接近最大速度进行传输——慢启动的成本阶段在较大转移的生命周期内摊销。

但是,对于许多 HTTP 连接,这些连接通常很短且具有突发性,在达到最大窗口大小之前完成数据传输的情况并不少见。因此,许多 Web 应用程序的性能通常受到服务器和客户端之间往返时间的限制:慢启动限制了可用带宽吞吐量,这对小型传输的性能产生了不利影响。

§慢启动重启
除了调节新连接的传输速率外,TCP 还实现了慢启动重启(SSR)机制,该机制在连接空闲一定时间后重置其拥塞窗口。理由很简单:网络条件可能在连接空闲时发生了变化,为了避免拥塞,窗口被重置为“安全”默认值。

毫不奇怪,SSR 会对长期空闲的 TCP 连接的性能产生重大影响,这些连接可能会因用户不活动而空闲一段时间。因此,通常建议在服务器上禁用 SSR,以帮助提高长期 HTTP 连接的性能。在 Linux 平台上,可以通过以下命令检查和禁用 SSR 设置:

$> sysctl net.ipv4.tcp_slow_start_after_idle
$> sysctl -w net.ipv4.tcp_slow_start_after_idle=0

为了说明三次握手和慢启动阶段对简单 HTTP 传输的影响,假设我们在纽约的客户端通过新的 TCP 连接从伦敦的服务器请求一个 64 KB 的文件(图 2-5 ),并且有以下连接参数:

往返时间:56 毫秒
客户端和服务器带宽:5 Mbps
客户端和服务器接收窗口:65,535 字节
初始拥塞窗口:10 个段 ( )
服务器生成响应的处理时间:40 ms
无丢包,每包 ACK,GET 请求适合单个段


图 2-5。通过新的 TCP 连接获取文件

0 ms
客户端使用 SYN 数据包开始 TCP 握手。

28 ms
服务器回复 SYN-ACK 并指定其 rwnd 大小。

56 ms
客户端确认 SYN-ACK,指定其 rwnd 大小,并立即发送 HTTP GET 请求。

84 ms
服务器收到 HTTP 请求。

124 ms
服务器完成生成 64 KB 响应并在暂停 ACK 之前发送 10 个 TCP 段(初始 cwnd 大小为 10)。

152 ms
客户端收到 10 个 TCP 段,每个段都收到 ACK。

180 ms
服务器为每个 ACK​​ 增加其 cwnd 并发送 20 个 TCP 段。

208 ms
客户端接收到 20 个 TCP 段和每个 ACK​​。

236 ms
服务器为每个 ACK​​ 增加其 cwnd 并发送剩余的 15 个 TCP 段。

264 ms
客户端收到 15 个 TCP 段,每个段都有 ACK。

在新的 TCP 连接上传输一个 64 KB 的文件需要 264 毫秒,客户端和服务器之间的往返时间为 56 毫秒!相比之下,我们现在假设客户端能够重用相同的 TCP 连接(图 2-6)并再次发出相同的请求。

图 2-6。通过现有 TCP 连接获取文件

0 ms
客户端发送 HTTP 请求。

28 ms
服务器收到 HTTP 请求。

68 ms
服务器完成生成 64 KB 响应,但 cwnd 值已经大于发送文件所需的 45 段;因此它在一次突发中调度所有段。

96 ms
客户端收到所有 45 个段,每个段都有 ACK。

在相同的连接上发出相同的请求,但没有三次握手的成本和慢启动阶段的损失,现在需要 96 毫秒,这意味着性能提高了 275%!

在这两种情况下,服务器和客户端都可以访问 5 Mbps 的上行带宽这一事实在 TCP 连接的启动阶段没有影响。相反,延迟和拥塞窗口大小是限制因素。

事实上,如果我们增加往返时间,通过现有连接发送的第一个和第二个请求之间的性能差距只会扩大;作为练习,尝试使用几个不同的值。一旦你对 TCP 拥塞控制的机制有了一种直觉,数十种优化(如保活、流水线和多路复用)将不需要进一步的动力。

§增加 TCP 的初始拥塞窗口
将服务器上的初始 cwnd 大小增加到 10 段 (IW10) 的新 RFC 6928 值是提高所有用户和所有通过 TCP 运行的应用程序性能的最简单方法之一。好消息是,许多操作系统已经更新了最新的内核以使用增加的价值——查看相应的文档和发行说明。

对于 Linux,IW10 是 2.6.39 以上所有内核的新默认值。但是,不要止步于此:升级到 3.2+ 以获得其他重要更新的好处;请参阅TCP 的按比例降低速率。

§拥塞避免
重要的是要认识到 TCP 是专门为使用丢包作为反馈机制来帮助调节其性能而设计的。换句话说,这不是是否会发生丢包的问题,​​而是何时会发生丢包的问题。慢启动使用保守窗口初始化连接,并且对于每次往返,传输中的数据量加倍,直到超过接收器的流量控制窗口、系统配置的拥塞阈值 (ssthresh) 窗口或直到数据包丢失,此时拥塞避免算法(图 2-3)接管。

拥塞避免的隐含假设是丢包表明网络拥塞:在路径上的某个地方我们遇到了拥塞的链路或路由器,它被迫丢弃数据包,因此我们需要调整我们的窗口以避免诱导更多数据包丢失,以避免使网络不堪重负。

一旦拥塞窗口被重置,拥塞避免会指定自己的算法来增加窗口以最小化进一步的损失。在某一时刻,将发生另一个丢包事件,并且该过程将重复一次。如果您曾经查看过 TCP 连接的吞吐量跟踪并观察到其中的锯齿模式,那么现在您知道为什么会这样了:它是拥塞控制和避免算法调整拥塞窗口大小以解决数据包丢失的问题。网络。

最后,值得注意的是,改进拥塞控制和避免是学术研究和商业产品的一个活跃领域:有针对不同网络类型、不同类型数据传输等的适应。今天,根据您的平台,您可能会运行许多变体之一:TCP Tahoe 和 Reno(原始实现)、TCP Vegas、TCP New Reno、TCP BIC、TCP CUBIC(Linux 上的默认值)或 Compound TCP(默认值Windows)等等。然而,不管是什么风格,拥塞控制和避免的核心性能影响都适用。

§TCP 的比例降速
确定从丢包中恢复的最佳方法是一项非常重要的练习:如果您过于激进,那么间歇性丢包将对整个连接的吞吐量产生重大影响,如果您没有足够快地调整,那么您将诱导更多丢包!

最初,TCP 使用乘减加加 (AIMD) 算法:当发生丢包时,将拥塞窗口大小减半,然后每次往返缓慢增加固定量的窗口。然而,在许多情况下,AIMD 过于保守,因此开发了新的算法。

Proportional Rate Reduction (PRR) 是 RFC 6937 规定的一种新算法,其目标是提高数据包丢失时的恢复速度。好多少?根据在开发新算法的谷歌进行的测量,它可以将丢包连接的平均延迟降低 3-10%。

PRR 现在是 Linux 3.2+ 内核中的默认拥塞避免算法——另一个升级服务器的好理由!

§带宽延迟积
TCP 中内置的拥塞控制和拥塞避免机制具有另一个重要的性能含义:最佳发送方和接收方窗口大小必须根据往返时间和它们之间的目标数据速率而变化。

要理解为什么会这样,首先回想发送方和接收方之间未确认的传输中数据的最大数量定义为接收 (rwnd) 和拥塞 (cwnd) 窗口大小的最小值:当前接收窗口是在每个 ACK​​ 中进行通信,并且拥塞窗口由发送方根据拥塞控制和避免算法动态调整。

如果发送方或接收方中的任何一个超过了未确认数据的最大数量,则它必须停止并等待另一端确认某些数据包才能继续。还要等多久?这是由两者之间的往返时间决定的!

带宽延迟积 (BDP)
数据链路容量及其端到端延迟的乘积。结果是可以在任何时间点传输的最大数量的未确认数据。

如果发送方或接收方经常被迫停止并等待先前数据包的 ACK,那么这将在数据流中产生间隙(图 2-7),从而限制连接的最大吞吐量。为了解决这个问题,窗口大小应该足够大,这样任何一方都可以继续发送数据,直到从客户端返回一个更早的数据包的 ACK ——没有间隙,最大吞吐量。因此,最佳窗口大小取决于往返时间!选择一个较小的窗口大小,您将限制您的连接吞吐量,而不管对等点之间的可用带宽或广告带宽如何。

图 2-7。由于低拥塞窗口大小导致的传输间隙

那么流量控制(rwnd)和拥塞控制(cwnd)窗口值需要多大呢?实际计算很简单。首先,让我们假设 cwnd 和 rwnd 窗口大小的最小值为 16 KB,往返时间为 100 ms:

无论发送方和接收方之间的可用带宽如何,此 TCP 连接都不会超过 1.31 Mbps 的数据速率!为了实现更高的吞吐量,我们需要提高窗口大小或降低往返时间。

同样,如果我们知道往返时间和两端的可用带宽,我们就可以计算出最佳窗口大小。在这种情况下,我们假设往返时间保持不变(100 毫秒),但发送方有 10 Mbps 的可用带宽,而接收方位于 100 Mbps+ 的高吞吐量链路上。假设它们之间没有网络拥塞,我们的目标是使客户端可用的 10 Mbps 链路饱和:

窗口大小至少需要为 122.1 KB 才能使 10 Mbps 链路饱和。回想一下,TCP 中的最大接收窗口大小为 64 KB,除非存在窗口缩放 — 请参阅窗口缩放 (RFC 1323):请仔细检查您的客户端和服务器设置!

好消息是窗口大小的协商和调整由网络堆栈自动管理,并应相应调整。坏消息是有时它仍然是 TCP 性能的限制因素。如果您曾经想知道为什么您的连接以可用带宽的一小部分传输,即使您知道客户端和服务器都能够提供更高的速率,那么这很可能是由于窗口尺寸小:饱和的对等广告低接收窗口、恶劣的网络天气和高数据包丢失重置拥塞窗口,或显式流量整形可能已应用于限制连接的吞吐量。

§高速 LAN 中的带宽延迟积
BDP 是往返时间和目标数据速率的函数。因此,虽然往返时间是高传播延迟情况下的常见瓶颈,但它也可能是本地 LAN 的瓶颈!

为了以 1 ms 的往返时间实现 1 Gbit/s,我们还需要至少 122 KB 的拥塞窗口。计算和我们之前看到的完全一样;我们只是在目标数据速率上添加了几个零,并从往返延迟中删除了相同数量的零。

§线头阻塞
TCP 提供了在不可靠通道上运行的可靠网络的抽象,其中包括基本的数据包错误检查和纠正、按顺序传送、丢失数据包的重传,以及旨在运行网络的流量控制、拥塞控制和拥塞避免在效率最高的地方。结合起来,这些特性使 TCP 成为大多数应用程序的首选传输方式。

然而,虽然 TCP 是一种流行的选择,但它并不是所有场合的唯一选择,也不一定是最佳选择。具体来说,某些功能,例如有序和可靠的数据包传送,并不总是必要的,并且可能会引入不必要的延迟和负面的性能影响。

要理解为什么会这样,回想一下,每个 TCP 数据包在连接到网络时都带有一个唯一的序列号,并且数据必须按顺序传递给接收器(图 2-8)。如果其中一个数据包在发送到接收方的途中丢失,则所有后续数据包都必须保存在接收方的 TCP 缓冲区中,直到丢失的数据包被重新传输并到达接收方。因为这项工作是在 TCP 层内完成的,所以我们的应用程序无法看到 TCP 重传或排队的数据包缓冲区,并且必须等待完整序列才能访问数据。相反,它只是在尝试从套接字读取数据时看到了传递延迟。这种效应称为 TCP 线头 (HOL) 阻塞。

线头阻塞造成的延迟使我们的应用程序可以避免处理数据包重新排序和重组,这使我们的应用程序代码更加简单。然而,这样做的代价是在数据包到达时间中引入不可预测的延迟变化,通常称为抖动,这会对应用程序的性能产生负面影响。
![图 2-8。 TCP 线头阻塞](https://img-blog.csdnimg/dd1af8c3410346ad8183506a295e838a.
图 2-8。TCP 线头阻塞

此外,一些应用程序甚至可能不需要可靠传递或按顺序传递:如果每个数据包都是独立的消息,则完全不需要按顺序传递,并且如果每条消息都覆盖所有先前的消息,那么对可靠传递的要求可以被完全删除。不幸的是,TCP 不提供这样的配置——所有数据包都按顺序排列和传递。

可以处理无序交付或数据包丢失以及对延迟或抖动敏感的应用程序可能更好地使用备用传输,例如 UDP。

§丢包没问题
事实上,丢包对于从 TCP 获得最佳性能是必要的!丢弃的数据包充当反馈机制,允许接收方和发送方调整其发送速率以避免网络过载,并最大限度地减少延迟;请参阅本地路由器中的 Bufferbloat。此外,一些应用程序可以容忍丢包而不会产生不利影响:音频、视频和游戏状态更新是应用程序数据的常见示例,它们不需要可靠或按顺序交付——顺便说一句,这也是 WebRTC 使用 UDP 作为其基础的原因运输。

如果数据包丢失,则音频编解码器可以简单地在音频中插入一个小中断并继续处理传入的数据包。如果间隙很小,用户甚至可能不会注意到,并且等待丢失的数据包会冒着在音频输出中引入可变暂停的风险,这会给用户带来更糟糕的体验。

类似地,如果我们正在传递游戏状态更新,那么等待描述其状态的数据包 time T-1,当我们已经拥有 time 数据包时T,通常根本没有必要——理想情况下,我们会收到每一个更新,但为了避免游戏延迟,我们可以接受间歇性丢失以降低延迟。

§针对 TCP 进行优化
TCP 是一种自适应协议,旨在对所有网络对等方公平,并最有效地利用底层网络。因此,优化 TCP 的最佳方法是调整 TCP 如何感知当前网络状况并根据其上下层的类型和要求调整其行为:无线网络可能需要不同的拥塞算法,而某些应用程序可能需要自定义服务质量 (QoS) 语义以提供最佳体验。

不同应用需求的密切相互作用,以及每个 TCP 算法中的许多旋钮,使 TCP 调整和优化成为学术和商业研究的无穷无尽的领域。在本章中,我们只触及了控制 TCP 性能的许多因素的皮毛。其他机制,例如选择性确认 (SACK)、延迟确认和快速重传等,使每个 TCP 会话更复杂(或更有趣,取决于您的观点),以便理解、分析和调整。

话虽如此,虽然每个算法和反馈机制的具体细节会不断发展,但核心原理及其含义保持不变:

TCP 三向握手引入了完整的往返延迟。
TCP 慢启动适用于每个新连接。
TCP 流量和拥塞控制调节所有连接的吞吐量。
TCP 吞吐量由当前拥塞窗口大小调节。

因此,TCP 连接在现代高速网络中传输数据的速率通常受到接收方和发送方之间往返时间的限制。此外,虽然带宽继续增加,但延迟受到光速的限制,并且已经在其最大值的一个小的恒定因子内。在大多数情况下,延迟而不是带宽是 TCP 的瓶颈——例如,见图 2-5。

§调整服务器配置
作为起点,在为 TCP 中的每个缓冲区和超时变量调整任何特定值之前,您最好将主机升级到最新的系统版本。控制其性能的 TCP 最佳实践和底层算法不断发展,其中大部分更改仅在最新内核中可用。简而言之,让您的服务器保持最新,以确保发送方和接收方的 TCP 堆栈之间的最佳交互。

从表面上看,升级服务器内核版本似乎是微不足道的建议。然而,在实践中,它经常会遇到很大的阻力:许多现有的服务器都针对特定的内核版本进行了调整,系统管理员不愿意执行升级。

公平地说,每次升级都会带来风险,但为了获得最佳的 TCP 性能,它也可能是您可以做出的最佳投资。

使用最新内核后,最好确保您的服务器配置为使用以下最佳实践:

增加 TCP 的初始拥塞窗口
较大的起始拥塞窗口允许 TCP 在第一次往返中传输更多数据,并显着加速窗口增长。

慢启动重启
在空闲后禁用慢启动将提高以周期性突发传输数据的长期 TCP 连接的性能。

窗口缩放 (RFC 1323)
启用窗口缩放会增加最大接收窗口大小并允许高延迟连接以实现更好的吞吐量。

TCP 快速打开
在某些情况下,允许在初始 SYN 数据包中发送应用程序数据。TFO是新的优化,需要客户端和服务端都支持;调查您的应用程序是否可以使用它。

上述设置和最新内核的组合将为单个 TCP 连接实现最佳性能——更低的延迟和更高的吞吐量。

根据您的应用程序,您可能还需要调整服务器上的其他 TCP 设置,以针对高连接率、内存消耗或类似标准进行优化。请查阅您的平台文档并通读由 HTTP 工作组维护的 “TCP Tuning for HTTP”文档以获取更多建议。

对于 Linux 用户,ss它是一个有用的电动工具,可以检查打开的套接字的各种统计信息。从命令行运行 ss --options --extended --memory --processes --info以查看当前对等点及其各自的连接设置。

§调整应用程序行为
调整 TCP 的性能允许服务器和客户端为单个连接提供最佳吞吐量和延迟。但是,应用程序如何使用每个新的或已建立的 TCP 连接可能会产生更大的影响:

没有比特比不发送的比特快;发送更少的位。

我们不能让位移动得更快,但我们可以将位移动得更近。

TCP 连接重用对于提高性能至关重要。

当然,消除不必要的数据传输是唯一的最佳优化——例如,消除不必要的资源或通过应用适当的压缩算法确保传输最少的比特数。之后,通过在世界各地地理分布服务器(例如,使用 CDN)将位定位在更靠近客户端的位置,将有助于减少网络往返的延迟并显着提高 TCP 性能。最后,在可能的情况下,应该重用现有的 TCP 连接,以尽量减少慢启动和其他拥塞机制造成的开销。

§绩效清单
无论应用程序类型如何,优化 TCP 性能都会为与服务器的每个新连接带来高额收益。列入议程的简短清单:

将服务器内核升级到最新版本。
确保 cwnd 大小设置为 10。
确保启用窗口缩放。
空闲后禁用慢启动。
调查启用 TCP 快速打开。
消除冗余数据传输。
压缩传输的数据。
将服务器放置在离用户更近的位置以减少往返时间。
尽可能重用 TCP 连接。
调查“HTTP TCP Tuning”建议。

本文标签: 组成部分TCP