现在的位置: 首页 > 综合 > 正文

高性能浏览器网络之网络部分 — Building Blocks of TCP

2014年08月26日 ⁄ 综合 ⁄ 共 11675字 ⁄ 字号 评论关闭

Building Blocks of TCP -- 题目怎么翻译呢? TCP构建块? 深入浅出TCP?认识TCP?TCP的构建?

---------------------------------------------------------------------------------------

第二章 Building Blocks of TCP


互联网的核心是两个协议,IP和TCP。IP(Internet Protocol),提供主机到主机的路由和寻址, TCP(Transmission Control Protocol),提供运行在不可靠通道上的可靠网络抽象。TCP/IP通常指Internet Protocol Suite,它由Vint Cerf 和Bob
Kahn在他们的论文“A Protocol for Packet Network Intercommunication”中第一次提出。

最初的提议(RFC 675)被修正了很多次,第四版本的TCP/IP规格于1981年发布,被分为两个独立的RFCs:
  • RFC 791 -- Internet Protocol
  • RFC 793 -- Transmission Control Protocol
自那以后,出现了很多改善建议,并被应用与TCP,但核心操作一直没有多大变化。TCP迅速取代了之前的协议,现在已被大部分流行应用所采用:World Wide Web,email, file transfers, and 很多其它应用。
TCP提供了有效的运行于不可靠通道上的可靠网络抽象。对应用程序隐藏了大部分网络通信的复杂性:重传丢失数据、按序发送、拥塞控制和避免、数据完整性等等。当你使用TCP流时,TCP保证你发送什么对方就收到什么,而且保证数据按序到达。正因如此,TCP被优化用于准确发送,而不是及时快速发送。结果,当要在浏览器中优化网络性能时,这给我们带来了一些挑战。

HTTP标准并没有指定非得用TCP进行传输。如果你愿意,你也可以用UDP或其它任何传输协议传输HTTP,但实际上,由于TCP提供了许多易用的,强大的功能,所以,今天的互联网上所有的HTTP通信都是基于TCP的。

所以,理解一些TCP的核心机制是构建最优网络体验的必备知识。没准在你的应用程序中你不会直接同TCP sockets打交道,但你在应用层的设计决策可能会影响到TCP和底层网络的性能。


三次握手

所有的TCP连接开始于三次握手(图2-1)。在客户端与服务器能够交换任何应用数据之前,它们必须就初始包系列号,以及双方的若干连接参数达成协议。出于安全考虑,双方的初始系列号都是随机产生。

SYN
        客户端挑选一个随机系列号x并发送一个SYN报文,其中可能包含额外的TCP标志和选项
SYN ACK
        服务器给x加1,挑选自己的随机系列号y,添加它自己的标志集和选项,然后发送响应
ACK
        客户端递增x和y,并发送握手过程的最后ACK报文以完成握手
       

一旦三次握手完成,应用数据就可以开始在客户端与服务器之间流动了。客户端可以在ACK报文之后立即发送一个数据包,服务器必须等到收到ACK之后才能发送数据。这个启动过程适用于每一个TCP连接,而且对所有使用TCP的网络应用的性能有重要影响:在能够发送应用数据之前,每一个新连接都会有一个完整的往返延迟。

例如,假设客户端位于纽约,服务器位于伦敦,我们通过光纤链路启动一个新的TCP连接,那么三次握手将花费56毫秒(表 1-1):单方向上传播数据包需要28毫秒,之后它还必须返回到纽约。注意这里不考虑连接的带宽。延迟主要由两个城市之间的距离决定。

由三次握手强加的延迟使得创建新的TCP连接变得很“昂贵”,这也是为何重用连接对运行在TCP之上的任何应用程序都是一个关键优化项的很大原因。

TCP快速打开
TCP握手阶段已经被确认为总网页浏览时延的一个重要来源,很多程度上是由于,对于很多网页,它的几十到上百个子资源分布在不同的主机上,需要大量很短的TCP流来获取这些资源。
TCP Fast Open是一个旨在减少新的TCP连接强加的延迟惩罚的机制。基于Google所做的流量分析和网络模拟,研究人员已经证明,TFO,它允许SYN报文携带数据,能够HTTP事务网络延迟降低15%,整个网页平均加载时间减少10%,在某些高延迟的场景甚至达到40%。

在linux 3.7+ 内核中,客户端和服务器都可以获得TFO支持。花虽如此,TFO并不能解决所有问题。它可以帮助消除三次握手的往返惩罚,但它只能用于特定场景:SYN报文能够携带的数据有效载荷大小是有限制的,只能发送特定类型的HTTP请求,因为它需要一个加密的cookie,所以只能用于重复的连接。

拥塞避免和控制

早在1984年,John Nagle记录了一个被称为“拥塞奔溃”的情况(condition,这个怎么翻合适呢??),它会对节点之间任何非对称带宽网络造成影响:

拥塞控制是复杂网络中一个公认的问题。我们发现,当这两个协议,国防部的Internet Protocol(IP),一个纯粹的数据包协议,和传输控制协议(TCP),一个传输层协议,一起使用时,由于传输层和数据报层的交互,会引起不寻常的拥塞问题。尤其是,IP网关很容易发生“拥塞奔溃”现象,特别是在它连接到一个带宽差别很大的网络时。
一旦往返时间超过了主机的最大传输间隔,主机就会开始发送越来越多的相同数据包的副本到网络中。现在网络就麻烦了。最终交换节点的可用缓冲将被填满,后面的包只能丢掉。被发送的数据包的往返时间现在达到了最大值。主机不断的重发数据包,最终,每一个包都有多个副本到达目标主机。这就是拥塞奔溃。
这种情况是稳定的。一旦达到饱和点,如果挑选丢弃包的算法是公平的,网络将继续在退化状态下运行。
                                                                                                                                             -- John Nagle RFC896
该报告认为拥塞奔溃还没有成为ARPANET的问题,是因为它的大部分节点带宽相同,而且骨干网有大量富余带宽。然而,不久之后,这些断言就不正确了。1986年,随着大量(5000+)、各种各样的节点加入网络,一系列的拥塞奔溃横扫网络 -- 在有的情况下,网络带宽甚至下降了1000倍,网络变得没法再使用。
为了解决这一问题,在TCP中实现多种机制,以控制两个方向上的数据发送速率:流控制,拥塞控制,拥塞避免。

ARPANET是现代互联网的先驱,是世界上第一个包交换网络。该项目正式启动于1969年,1983年,TCP/IP取代了早期的NCP,成为主要的通信协议。后来的事,便是总所周知的历史了。

流控制

流控制是一种防止发送方发送过多数据导致接收方处理不过来的机制 -- 接收端可能在忙、负载严重、或者不愿意再分配更多缓冲空间。为了解决这个问题,TCP连接的每一端都通告(图2-2)它自己的接收窗口(rwnd),它用于沟通用于接收数据的缓冲空间的大小。
当连接第一次建立时,每一端都使用它们的系统默认设置来初始化rwnd的值。浏览网页时,大多数的数据流是从服务器到客户端,所以客户端的窗口是可能的瓶颈。然而,在上传大量数据,比如图片或视频到服务器时,服务器接收窗口可能成为限制因素。
任何一端由于某种原因跟不上了, 它都可以向发送端通告一个更小的接收窗口。如果接收窗口变为0,就表示不应该再发送数据了。这一流程贯穿每一个TCP连接的整个生命周期:每一个ACK携带有每一端的最新的rwnd值,发送端可以据此动态调整发送速率,以协调发送者和接收者的速率。



窗口伸缩(Window Scaling RFC 1323)
最初的TCP规范为通告接收窗口大小分配了16位,这就给能通告的接收窗口的最大值做了一个硬性限制。结果表明,为了获得最佳性能,这个硬上限设置往往是不够的,尤其是在有高带宽时延乘积的网络中。
为了解决这个问题,起草了RFC1323以提供“TCP Window Scaling”选项。它允许我们把接收窗口的最大值从65535字节提升到1G字节!窗口扩大选项是在三次握手时进行沟通的,它的值表示在后面的ACKs中,16位的窗口大小域应该左移的位数。
如今,基本上所有主流平台都是默认支持窗口扩大选项的。然而,中间节点,路由器,防火墙可能重填甚至删除该选项。如果你与服务器,或客户端的连接,不能完全利用可用带宽,那么检查一下你的窗口大小的互动总是一个良好的开端。在Linux平台,能够通过如下命令,检查和设置窗口伸缩选项:
  • $> sysctl net.ipv4.tcp_window_scaling
  • $> sysctl -w net.ipv4.tcp_window_scaling=1

慢启动

尽管在TCP中有了流控制,网络拥塞奔溃仍然在19世界中后期成为了一个真正的问题。问题是流控制防止了发送端淹没接收端,但是没有机制来防止这两端淹没底层网络。发送端和接收端在一开始的时候都不知道网络的可用带宽,因此需要一种机制来估计可用带宽并调整它们的速度以适应网络不断变化的情况。
为了说明这种调整适应是有益的,想象你在家观看一个来自远程主机的大视频,为了提供最好的质量体验,它占满了你的下行链路。然后,你家中的另一个用户打开一个新的连接以下载一些软件更新。突然,视频流可用的下行带宽变少了,视频服务器必须调整它的数据发送速率 -- 否则,如果它继续保持相同的速率的话,数据只会简单的堆积在某些中间网关而且大量数据包将会被丢弃,最终导致低效的网络使用。
1988年, Van Jacobson 和 Michael J.Karels记录了几个解决这些问题的算法:慢启动,拥塞避免,快速重传,以及快速恢复。这四种算法很快就成为了TCP规范的强制部分。普遍认为,正是这些TCP的更新使得Internet没有在80年代和90年代处随着网络流量呈指数增长的情况下崩溃。
想要理解慢启动,最好看看它的实际应用。那么,让我们再次回到我们的客户端,它位于纽约,想要从伦敦的服务器上获取一个文件。首先,执行三次握手,这期间双方在ACKs包中通告它们各自的rwnd大小(图 2-2)。一旦最终的ACK包发送到线路上,我们就可以开始交换应用数据了。
估计客户端与服务器之间可用带宽的唯一办法是通过交换数据来测量,而这正是慢启动被设计要做的。启动时,服务器为每个TCP连接初始化一个新的拥塞窗口(cwnd)变量,并初始化为一个保守,由系统指定的值(在Linux上是initcwnd)。


拥塞窗口大小(cwnd)
        再收到客户端的确认(ACK)前,发送端能发送到网络的数据大小限制。

cwnd变量并不会在发送者和接收者之间通告或交换 -- 在这个例子中,它将是一个由伦敦的服务器维护的私有变量。此外,介绍一个新的规则:在客户端与服务器之间的网络上(没有确认)的最大数据量是rwnd和cwnd变量的最小值。目前为止,一切顺利,但是问题来了,客户端与服务器怎么选择一个最优的拥塞窗口值呢?毕竟,即便是在相同的两个网络节点之间,网络状况也总是在变,这我们在之前的例子中已经看到过了,如果我们能够通过算法而不必手工调整每个连接的窗口大小那就太棒了。
解决方法是慢启动并随着包的确认而增加窗口大小:慢启动!原来,cwnd的启动值被设置为1个网络段;1999年4月,RFC 2581把这个值更新为最大到4个报文段,最近,2013年4月,RFC 6928再一次被增加到了10个报文段。
一个新的TCP连接的最大飞行(in flight)数据量是rwnd 和 cwnd这两个值的最小值;因此服务器能够发送多达4个网络段给客户端,然后它必须停下来等待确认。随后,每确认一个包,根据慢启动算法,服务器可以使cwnd值增加1 -- 确认了一个包,可以发送两个新的包。TCP连接的这个阶段通常被称为“指数增长”算法(图 2-3),因为客户端和服务器都试图快速收敛于它们之间网络路径的可用带宽。

       

慢启动是我们在开发浏览器时需要牢记的一个重要因素,为什么呢?HTTP和许多其它应用协议运行在TCP之上,而且无论带宽怎么样,每一个TCP连接都必须经历慢启动阶段 -- 我们不能立即完全利用带宽。
我们以一个小的拥塞窗口开始,并在每一个往返时间后使其加倍。这样,达到指定目标吞吐量的时间就是RTT和初始拥塞窗口大小的函数:
Equation 2-1. Time to reach the cwnd size of size N

                                             

下面举例说明慢启动的影响:假定
  • 客户端和服务端接收窗口大小65,535字节(64KB)
  • 初始拥塞窗口大小:4报文段 (RFC 2581)
  • RTT: 56ms (伦敦到纽约)
在这个例子以及后面的例子中,我们将使用旧的4个网络段的值作为拥塞窗口的初始值,因为大部分服务器仍然使用这个值。后面的例子应该能够很好的激励你更新你的服务器!
尽管接收窗口的值是64KB,但新的TCP连接的吞吐量由拥塞窗口大小做了初始限制。事实上,为了达到64KB限制,我们将需要把拥塞窗口增加到45个报文段,这将花费224毫秒。

                                             

为了达到64KB的吞吐量,需要四个往返时间(图 2-4),总计几百毫秒的延迟。事实上客户端与服务器可能完全能够以Mbps+的速度进行传输 -- 这就是慢启动!



为了减少增长拥塞窗口所花费的时间,我们可以减少客户端与服务器之间的RTT -- eg., 把服务器移到地理上靠近客户端的地方。或者增加初始拥塞窗口值到新的RFC 6928规定的10个报文段。
慢启动对于大数据量的流下载不是什么大问题,因为客户端和服务器在几百毫秒后将达到它们的最大窗口值,并在之后以接近最大值的速度进行传输 -- 慢启动的成本在这种长时间、大数据的传输中就显得微不足道。
然而,很多的HTTP连接,通常是突发性的,而且很短,在请求终止前达到最大窗口值的情况很少见。所以,很多web应用的性能通常受客户端与服务器之间的RTT所限:慢启动限制了可用的带宽吞吐量,这对小传输的性能有不利影响。


慢启动重启
除了控制新连接的传输率,TCP还实现了慢启动重启(SSR)机制,一个连接空闲一段时间后,该机制就会重设拥塞窗口。基本原理很简单:在网络空闲期间,网络状况可能已经变化了,为了避免拥塞,窗口被重设为一个安全的默认值。
SSR对空闲了一段时间的长TCP连接的性能有重要影响,这也没什么奇怪的 -- eg., HTTP 长连接。所有,推荐在服务器端关闭SSR。在Linux平台,可用通过如下命令检查和关闭SSR:
  • $> sysctl net.ipv4.tcp_slow_start_after_idle
  • $> sysctl -w net.ipv4.tcp_slow_start_after_idle=0
为了说明TCP三次握手和慢启动阶段对简单HTTP传输的影响,让我们假定我们位于纽约的客户端要通过TCP连接请求位于伦敦的服务器上的一个20KB的文件(图 2-5),连接参数如下:
  • RTT:56ms
  • 带宽:5 Mbps
  • 接收窗口大小: 65,535字节
  • 初始拥塞窗口:4 segments(4x1460字节 = 5.7KB)
  • 服务器处理时间:40ms
  • 没有包丢失,一包一确认,GET请求位于单个报文段中

0 ms       客户端通过SYN包开始握手
28 ms     服务器响应SYN-ACK并指定它的rwnd大小
56 ms     客户端确认SYN-ACK, 指定它的rwnd大小, 并立即发送了HTTP请求
84 ms     服务器收到HTTP请求
124 ms   服务器生成完20KB的响应并发送4个报文段,之后暂停等待ACK
152 ms   客户端收到4个报文段并对每一个进行确认
180 ms   服务端收到ACK后增加它的cwnd,并发送8个报文段
208 ms   客户端收到8个报文段,并发送确认
236 ms   服务端收到ACK后增加它的cwnd,并发送剩余报文段
264 ms   客户端收到剩余报文段,并发送确认

作为一个练习,把cwnd值设为10个报文段替换原来的4,然后在跑一遍 图 2-5。你应该会看大一个完整的RTT延迟消失了 -- 性能提升了22%!

花费了264ms来在56ms的往返时间的客户端与服务器之间传输一个20KB的文件!作为一个对比,现在,让我们假设客户端能够重用同样的TCP连接(图 2-6)并再一次发送了同样的请求。


0 ms      客户端发送HTTP请求
28 ms    服务器收到HTTP请求
68 ms    服务器生成完20KB的响应,但是cwnd的值已经比发送文件需要的15个报文段大了;因此它一次性发送了所有的报文段
96 ms    客户端收到了所有的15个报文段,然后发送确认
同样的连接,同样的请求,但是没有三次握手的代价和慢启动阶段的惩罚,现在只需要96ms,也就是性能提升了275%!

在这两个例子中,客户端和服务器都能够访问上行5Mbps的带宽,这个事实在TCP启动阶段没什么影响。三次握手延迟和拥塞窗口值才是限制因素。
事实上,如果我们增加RTT,这两个例子的性能差距也将扩大。作为一个练习,你可以用一个不同的值来试试。一旦你对TCP拥塞控制有了直观的认识,你可能就会自觉的使用诸多优化方法,比如:长连接,pipelining, 复用。


增加TCP初始拥塞窗口
在服务器端增加初始拥塞窗口的值为新的RFC 6928规定的10个报文段,是最简单的提高所有用户和运行于TCP之上的应用程序性能的方法之一。好消息是,许多操作系统已经更新了它们最近的内核以使用这个更大的值 -- 检查相应的文档和release notes。
对于Linux,IW10是所有2.6.39之后内核的新的默认值。不过,不要就此满足:升级到3.2+, 还可以获得其它重要更新的益处;

拥塞避免

TCP是专门设计使用包丢失作为帮助控制它的性能的反馈机制,认识到这一点是很重要的。换句话说,不应该说如果包丢失怎么怎么样,而应该说包丢失的时候怎么怎么样。慢启动以一个保守的窗口值来初始化连接,并且每一次往返加倍飞行数据的总量,直到超过接收者流控制窗口、系统配置的拥塞阀值(ssthresh)窗口,或者发生包丢失,然后,拥塞避免算法开始接手(图
2-3)。
在拥塞避免机制中隐含了一个假设,包丢失表示网络发生了拥塞:沿途的某个地方,一条链路或一个路由器发生了拥塞,不得不丢弃包,因此我们不得不调整我们的窗口以避免引起更多的包丢失和淹没网络。
拥塞窗口被重设时,拥塞避免指定它自己的算法用于计算如何增长窗口大小,以最大限度减少进一步的包丢失。在某个点,可能发生另一个包丢失,这个过程将再一次重复。如果你曾经对TCP连接做过吞吐量追踪,并且在其中发现锯齿模式,那现在你就知道为什么会是这样了:拥塞控制和避免算法根据包丢失情况调整拥塞窗口大小。
最后,值得注意的是,改进拥塞控制和避免在学术研究和商业产品上都是一个活跃的领域:为了适应不同的网络类型,不通过的数据传输类型等等。如今,依赖于运行的系统,你可能正在运行众多变种中的一个:TCP Tahoe and Reno(最初的实现),TCP Vegas, TCP New Reno, TCP BIC, TCP CUBIC(Linux默认),Compound
TCP(windows默认),等等还有很多。不过,忽略它们的各具特色,拥塞控制和避免对核心性能的影响都是一样的。

带宽延迟乘积(Bandwith-Delay Product)

TCP内建的拥塞控制和拥塞避免机制对性能有另一个重要影响:发送方和接收方的接收窗口优化必须基于它们之间的RTT,目标速率的优化也如此。
为了理解为什么这样,首先,回想一下,发送者与接收者之间,未确认的,飞行数据的最大量是由接收窗口和拥塞窗口的最小值定义的:当前的接收窗口大小由每一个ACK通告,拥塞窗口大小,由发送者基于拥塞控制和避免算法动态调整。
如何发送者或接受者发送的数据超过了未确认数据的最大值,在继续之前,它必须挺下来等待另一端对某些数据包的确认。要等待多久呢?由双方之间的RTT决定。

带宽延迟乘积
       数据链路容量与端到端延迟的乘积。结果就是在任意时刻处于飞行状态的未被确认的数据量的最大值。

如果有一方频繁的被强制停下来等待之前数据包的ACKs,就会产生数据量间隔(图 2-7),这将限制连接所能达到的吞吐量。为了解决这个问题,窗口大小应该被设置的足够大,以便两方都能够在对一个更早的包的确认达到之前持续不断的发送数据 -- 没有间隔,达到最大的吞吐量。因此,最优的窗口大小依赖于RTT!选择一个低的窗口尺寸,将会限制你的连接吞吐量,无论两端的可用或通告带宽有多大。




那么流控制窗口(rwnd)和拥塞窗口(cwnd)的值应该设置为多大呢?实际计算是很简单的。首先,让我们假设cwnd和rwnd的最小值是16KB,RTT是100ms:


                                  

无论发送者和接受者之间的可用带宽是多少,该TCP连接的数据速率将不会超过1.31 Mbps!为了获得更高的吞吐量,我们需要提高最小窗口值或者降低RTT。
类似的,如果我们知道RTT和两端之间的可用带宽,我们也能够计算出最优的窗口大小。在这个场景中,让我们假设RTT保持不变(100ms),但发送者有10Mbps的可用带宽,接收方位于高吞吐量100Mbps+的链路上。假定它们之间没有网络拥塞,我们的目标是充分利用10Mbps的链路:

                                     



窗口大小至少是122.1KB才能充分利用10Mbps的链路。记得如果没有窗口扩大选项,TCP的最大接收窗口值是64KB -- 查阅“Window
Scaling (RFC 1323)”
 -- 是时候检查你的客户端和服务端设置了!
好消息是,窗口大小的协商和调整是由网络堆栈自动管理的。坏消息是有时候这也是TCP性能的限制因素。如果你很奇怪为何你的连接只是用了可用带宽的一部分来传输数据,而且你知道客户端和服务端都能应该传输得更快的,那么这可能就是由于小窗口尺寸引起的了:饱和的一方通告了一个低的接收窗口,糟糕的网络环境和太多的包丢失重置了拥塞窗口,或者你的连接被限速了。


高速局域网的带宽延迟乘积
BDP是RTT和目标速率的函数。因此,RTT不但是高传播延迟网络的常见瓶颈,而且也可能成为本地局域网的瓶颈!
为了在1msRTT的网络上获得1Gbit/s的速率,我们同样也需要拥塞窗口至少为122KB。计算方式如前所述。我们只是在目标速率后面添加几个0,同时从RTT后面移走几个0。

队头阻塞 (Head-of-line blocking)

TCP提供了运行在不可靠通道上的可靠网络抽象,它包括了基本的包错误检测与纠错,按序发送,重传丢失包,以及流控制,拥塞控制,和拥塞避免,这些机制都是为了使网络能够最有效的运行。这些功能,集合在一起,使TCP成为大部分应用首先的传输协议。
然而,尽管TCP广受欢迎,但在有的场景下它不是唯一的,也不是必须的最好选择。特别是,其中一些功能,比如有序和可靠的数据发送,不总是必须的,而且可能引入不必要的延迟和对性能的负面影响。
为什么会这样呢?记得每一个被发送的TCP包都有一个唯一的系列号,而且数据必须被按序传输到接收端(图 2-8)。如果一个包在路由的过程中丢失了,那么所有后续的包必须被存放在接收端的TCP缓冲中,知道丢失的包被重传并到达接收端。因为这个工作是在TCP层完成的,所以我们的应用程序看不到TCP重传或包缓冲队列,而且必须有了完整系列后才能访问数据。当从socket读取数据时,我们很容易观察到延迟。这种效应被称为对头阻塞。
对头阻塞导致的延迟使我们的应用程序避免了包重组处理,也使我们的应用程序的代码更简单。不过,鱼和熊掌不可兼得,这是以引入不可预期的包到达延迟为代价的,通常称之为jitter,它会个应用程序性能代理负面影响。




此外,有的应用程序甚至不需要可靠传输或有序传输:如果每一个包都是一个单独的消息,那么按序发送就不是一定需要的,而且如果每一条消息都覆盖所有之前的消息,那可靠传输就完全可以移除了。不幸的是,TCP不提供这种配置 -- 所有的包被系列化并按序发送。
能够处理包乱序或者包丢失的应用程序,可能更适合使用另一个传输层协议,比如UDP。

TCP的优化

TCP是一种自适应协议,设计目标是所有网络用户能够公平使用网络,并且能够做到最有校的利用当前网络。因此,优化TCP的最好方法是,弄明白TCP是如何感知当前网络状况并基于下面层次的类型和上层的需要调整它的行为:无线网络可能需要不同的拥塞算法,为了提供最好的用户体验,有的应用需要定制服务器质量(QoS)。
各种应用需求紧密相关,每一个TCP算法都有诸多可调选项,这使得TCP调整和优化成为了一个永无止境的学术和商业研究领域。在本章中,我们只是蜻蜓点水式的了解下影响TCP性能的诸多因素。额外的机制,比如选择性确认(SACK),延迟确认,快速重传,还有很多其它机制,使得要理解,分析并调整每一个TCP会话都变得很复杂(或有趣,看你怎么看了)。
但话说回来,虽然每一个算法的特定细节和反馈机制会不断进化,但核心原则会保持不变:
  • TCP三次握手引入一个完整的RTT延迟
  • 每一个新的TCP连接都要经历慢启动
  • 所有的TCP连接通过TCP流控制和拥塞控制来调节吞吐量
  • TCP吞吐量由当前拥塞窗口大小决定
结果,在现代高速网络中,一个TCP连接能够达到的传输速率往往受发送者和接收者之间的RTT所限。此外,带宽在不断增长,但延迟受光速所限,没有更多改善空间。在大多数情况下,TCP的瓶颈往往是延迟,而不是带宽(图 2-5)。

调整服务器配置

有几十个可调的TCP参数,在你为每一个缓冲和超时参数指定特定的值之前,首先把你的主机升级到最新版本是个明智的做法。TCP最佳实践和影响它的性能的现行算法不断的改进与完善,这些改进的大多数只在最新的内核中可用。简而言之,为了保证发送这和接收者TCP栈之间的最优交互,你应该保持更新你的服务器。

升级你的服务器版本,表面上,这看起来像个微不足道的建议。然而,实际中,这可能阻力重重:很多现行的服务器都针对特定内核版本做了优化,而且系统管理员也懒得去升级。
公平而言,每一次升级都有风险,但为了获得最好的性能,这可能也是你能做的最好的投资。
有了最新的内核之后,好的做法是配置你的服务器以使用如下的最佳实践:
增加TCP初始拥塞窗口
        一个大的初始拥塞窗口允许TCP在第一次往返时传输更多数据,而且会显著加快窗口增长 -- 这是对突发性短连接的一个特别关键的优化。
慢启动重启
        关闭空闲之后的慢启动能够提高TCP长连接的性能。
窗口伸缩
        打开窗口伸缩选项能够增加接收窗口的最大值,以使高延迟连接获得更好的性能。
TCP快速打开
       在特定场景下,允许在初始SYN包中发送应用数据。TFO是一个新的优化方案,它需要客户端与服务器共同支持;检查看你的应用程序是否可以利用。

通过这些设置,再加上最新的内核,就可以达到最好的性能 -- 更低的延迟和更高的吞吐量。
根据你的应用程序需要,你可能还需要调整服务器的其它TCP配置,做进一步的优化,以达到更好的连接率,更好的内存使用,或者其它类似的目标。这些配置与平台、应用、硬件相关 -- 需要的时候查阅你的平台文档。

对于Linux用户,ss是一个有用的强大工具,用于检查已打开sockets的各种统计信息。在命令行,运行ss --option --extended --memory --processes --info,可以查看当前连接和它们各自的配置。

调整应用程序行为

通过调整TCP的性能,应用程序和服务器的单个TCP连接能够获得最好的吞吐量和最小的延迟。然而,应用程序如何使用每一个新的,或已创建的TCP连接对性能也有重要影响,甚至更大。
  • 不发送数据才是最快的;尽量少发数据
  • 我们不能使数据传输的更快,但我们可以使数据更近
  • TCP连接重用是提升性能的关键
消除不必要的数据传输,毫无疑问是最好的优化 -- 例如,消除不必要的资源,或通过采用合适的压缩算法来保证传输的数据量最小。接着,通过分布在全球各地的服务器,把数据放在离客户端更近的地方 -- 例如,采用CDN -- 将有助于降低网络延迟并显著提升TCP性能。最后,尽可能的利用已存在的连接,以最大限度降低慢启动和其它拥塞机制的开销。

性能清单(checklist)

无论你的应用是什么,优化TCP性能都能给每一个与你的服务器的连接带来很多好处。下面是一个简短的性能优化清单:
  • 升级服务器内核到最新的版本
  • 保证cwnd被设置为10
  • 关闭空闲后慢启动
  • 保证窗口伸缩选项是打开的
  • 消除冗余数据传输
  • 对传输数据进行压缩
  • 把服务器部署在离用户近的地方以减少往返时间
  • 尽可能重用TCP连接


原文:http://chimera.labs.oreilly.com/books/1230000000545/ch02.html






抱歉!评论已关闭.