You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

26 KiB

09 | 长肥管道:为何文件传输速度这么慢?

你好,我是胜辉。

在上节课里我们通过一个MTU引发问题的案例学习了MTU相关的知识点了解了MTU、MSS、TCP分段、IP分片这些概念之间的区别和联系。我们也学习了用iptables修改MSS来达到规避MTU问题的目的是不是觉得网络虽然学起来不简单但学会了好像也挺好玩的

那这个感觉就对了,学习本来就是一件有意思的事情,也是很有收获感的事情。学得更多,收益更多,反过来就能推动自己继续学得更多,形成良性循环。

当然,上节课我们主要说的是如果传输失败会怎么样,而从这节课开始,我们会讨论讨论传输起来以后的效率问题,比如传输速度。

传输速度是网络性能中非常重要的一部分内容,因为它直接影响了我们享受到的网络服务的品质。比如现在移动端网络越来越快,所以无论传输大文件还是实时的视频通话,都得到了很大的发展。

数据中心更是如此。服务器的网卡早就从多年前的千兆网卡1Gbps升级到万兆网卡10Gbps。而负载均衡、防火墙等网络设备更是从10Gbps升级到40Gbps乃至100Gbps。

在这个大背景下,照理说在数据中心之间传个文件早已不在话下。但是我们偏偏遇到了这样一个奇葩的问题:数据中心间传输文件的速度居然只有400多KB/s,是不是很奇怪?你有没有遇到过类似的传输速度方面的问题,你又是如何通过网络分析,找到根因的呢?

所以这节课我们就来研究分析下这个文件传输速度的案例一起来深入探讨下如何提升TCP传输速度的话题。

这样,当你日后也遇到传输速度方面的问题时,就可以运用这节课学到的知识,去真正解决问题。甚至,即使看起来没有速度问题,你也能主动发现提高网络性能的机会,带来额外的惊喜,同时也把你的网络维护的能力提升到一个更高的层次上来。

案例背景

这是2017年我处理的eBay内部的案例。当时我们有一个需求是把一个Firmware安装文件从美国数据中心拷贝到欧洲的一个POP点这个点在荷兰的阿姆斯特丹。文件不算大95MB。其实这是一项常规任务因为我们每年都会做一到两次的Firmware升级经常在美国几个数据中心之间拷贝安装文件一般这样一个100MB左右的文件约二十多秒就能完成。

这一次我们需要把全球POP节点的设备也进行升级。对我们来说确实也是第一次做从北美到欧洲这么长距离的文件传输结果发现传输速度慢了一个数量级。我们的POP点有很多个按顺序做升级的话每次文件传输都增加3分钟那么20个POP就增加60分钟显著拖累了整个的升级速度。

当然地理上看从美国加州到荷兰阿姆斯特丹确实极为遥远距离数千公里坐飞机也要14个多小时

图片

然后我们也来看一下美国不同的数据中心之间拷贝的速度:

再看一下从美国拷贝到荷兰的速度:

当你看到两种场景下传输同一个文件的速度有这么大的差别,第一感觉是什么呢?你觉得会是什么因素导致了速度变这么慢?会不会想到带宽?

确实当时我们也才开始维护欧洲POP点对美国和欧洲之间的底层传输网络情况并不熟悉所以也怀疑**会不会是带宽本身就不大呢?**我们找了负责骨干网的同事了解到这个带宽有10Gbps远超这个传输速度。

然后你会想到什么呢对了我补充一下这个传输是scp拷贝所以是基于TCP的。你自然明白TCP的传输速度也受丢包的影响。如果丢包严重传输速度肯定跑不起来。我们觉得这个也是有可能的就去抓包检查。结果发现丢包率并不高不高的丢包率却导致如此低的速度这个可能性很小。

更为重要的是,既然我们要排查传输速度的问题那么首先要看的是不是网络I/O的状况呢

补充:抓包示例文件已经传输至Gitee建议用Wireshark打开示例文件结合文稿学习效果更好。

在Wireshark的众多统计工具中有两个工具就是I/O分析而生的一个是 I/O Graph,另一个是 TCP Stream Graph。我们先看I/O Graph。

I/O Graph

这个工具在Statistics下拉菜单中

图片

点击I/O Graph后Wireshark会弹出一个趋势图其X轴是时间Y轴是性能指标

图片

注意这时的图是不准确的所以我们需要做一些调整。既然我们关注传输速度所以就选择All Bytes这个指标项也是默认选中的那项作为Y轴然后修改它的计量单位。很可能你的Wireshark默认选中的是AVG(Y Field),而这并不是我们要关注的字节数。我们可以双击AVG(Y Field)进入编辑模式,把它改为Bytes

图片

很棒这时我们就能清晰地看到Wireshark帮助我们计算出来的分时的速度趋势柱状图了差不多速度在480KB/s上下

图片

你可能也注意到了在X时间轴上看一开始几秒速度比较低第7秒才达到400KB/s以上。为什么一开始速度这么低呢其实这正是TCP慢启动的一个缩影初始阶段,速度是特别低的,但是会很快爬高

整体来看,这个传输速度还是很“稳定”的。对,一直很“稳定”得低。

TCP Stream Graphs

如果说I/O Graph展示的速度值很容易理解那么TCP Stream Graphs展示的信息就需要一点TCP的知识来辅助理解了。

还是到Statics下拉菜单选择TCP Stream Graphs在子菜单中选择Time sequence (Stevens)。

补充Stevens这个名字你应该是熟悉的他就是鼎鼎大名的TCP/IP三卷和Unix网络编程两卷等名著的作者Richard Stevens。他把网络协议的圣火带到人间却又早早离开了人间。这个工具以他的名字命名也代表了大家对他的深深的怀念。

图片

然后就能看到时间为X轴、TCP序列号为Y轴的图了。你应该知道序列号其实就等于字节数那么显然这里这条线的斜率也就是传输速率了。

图片

我们可以自己计算这个斜率(速率) 是多少。比如可以计算10秒和40秒两处的序列号的差距再除以(40-10)秒就是速率了。10秒处的序列号是280009240秒处是16480292那么速率就是(16480292-2800092)/(40-10)=456KB/s。

图片

图片

你可能发现了,**两个Graph算出来的速度怎么有点差异**一个是480KB/s左右一个是456KB/s相差有5%左右。

其实这是正常的。因为I/O Graph统计的Bytes是二层帧的大小而TCP Stream Graphs关注的是四层TCP段的大小。后者比前者少了二层到四层的头部。严格来说TCP Stream Graphs的斜率只是TCP payload的速率而I/O Graph展示的才是我们一般谈论的传输速度。当然在定性的讨论中这点差异是可以忽略的。

理解与传输相关的知识点

其实上面用了两种方式查证速度也无非是印证了我们在scp命令输出里看到的速度值本身是准确的。但目前为止我们还没有找到造成这个速度的原因。这时候我们稍微调整一下问题描述假如带宽足够网络也稳定那又是什么决定了TCP传输的速度

回想一个小学时就学过的公式:速度=距离/时间

看上去极为简单的公式,似乎在这里没什么帮助。不过奥妙的地方在于:什么是距离,什么又是时间?搞清楚了这两个问题的答案,就搞清楚了这次传输分析的精髓。不过我这里先卖个关子,先继续把接下来的知识点讲清楚,到后面你也许自己就能悟到答案了。

假设你在开车开出去100公里耗时1小时。这是因为你眼观四路、耳听八方在确保安全的前提下用比较快的速度行驶。如果你蒙上眼睛那是1米都不敢开的是不是

对于网络传输来说,报文发送出去后,发送端本身就“失去视力”了,也就是看不到报文在错综复杂的网络中行走时,到底遇到了什么样的“路况”。那它是继续发送更多数据好呢,还是先等对方确认这部分数据,然后再发送下一份数据好呢?如果确认了,那下一份数据又该发多少呢?一系列问题,需要我们回答。

不过还好睿智的TCP的设计者们早就考虑到了这些问题。这里呢有几个相关知识点你需要了解且听我细细道来。

首先,一个报文被视为成功发送,基于以下的路径:

(开始)发送端>>数据报文>>接收端>> ACK报文>>发送端(结束)

这样一来一回的时间,就是报文被成功传送的耗时。你可能会有疑问:数据报文到了接收端不就是已经完成传输了吗?其实,这个时候发送端还不知道接收端是否已经完成接收了,因为还没收到确认。

操作系统的角度来看“完成”的标志是这部分数据可以从发送缓存中删除从缓存中删除这部分数据的前提就是收到ACK报文。即“确认多少我就清除多少”。像下图这样

来回的时间,在英文里叫 Round Trip TimeRTT即往返时间也叫时延。这是你需要知道的第一个名词。

你有没有发现报文大部分时候就像飞机在空中航行跟两端的哪一端都碰不着除了起飞和降落。在“空中”的时间就取决于RTT。RTT越长报文在“空中”的时间就越长。这个时候这些报文就有了一个新的身份叫做“在途数据”它的大小叫做在途字节数。英文是 Bytes in flight。这是你需要知道的第二个名词。

事实上Wireshark也很周到地帮我们想到了这一点在TCP详情页的SEQ/ACK analysis部分就有 Bytes in flight 信息。比如下面这个:

图片

带宽类似道路的车道数你应该见过节假日里高速公路上停满私家车的图片。可见车道越多高速路越长能容纳的车就越多。在网络世界里带宽很大、RTT很长的网络被冠以一个特定的名词叫做长肥网络,英文是 Long Fat Network。在长肥网络中的TCP连接叫做长肥管道,英文是 Long Fat Pipeline。这是你需要知道的第三个名词。

道路上最多可以容纳多少辆车呢显然是车道数×道路长度。那么类似地带宽跟往返时间RTT相乘就是在空中飞行的报文的最大数量带宽时延积。在英文里叫 Bandwidth Delay Product缩写是BDPDelay就是RTT。不用我说你也明白带宽时延积是你需要知道的第四个名词。

其实,以上这些名词都是从英文翻译过来的,所以读起来感觉不太像本土词汇,特别是“长肥管道”。你别把“长肥”记成“肥肠”就行了。

我们用图对比一下,就能明显看出,所谓的长肥管道,就是带宽时延积更大的网络,蓝色部分表示的就是带宽时延积:

你可能还会有疑问在途数据不是应该RTT的一半即单程时间再乘以带宽吗从飞机的比喻来看也许是的。但是你要考虑下图这种情况。如果回程时间只是用来传输ACK没有被用来传输实际的数据效率就打了对折了。就像图的右侧表示的只有一半的时间在真正传输数据另外一半时间没有在发送数据

事实上发送端并不知道什么时候报文到了接收端它唯一知道的是什么时候自己收到了ACK报文所以它会把这些时间全部用来发送报文。我把上图中原先不传数据的时间段2和4也改为发送数据于是形成了下面这张图

当然,实际情况是远比示意图复杂的。比如,在途字节数一定等于带宽×RTT要知道接收端也不傻也是可以源源不断地对数据进行确认的这样也就形成了更加复杂的关系发送端在不断发出在途数据接收端也在不断回复ACK。这时候的在途数据可以用下面的公式来获得

inflight_data = (latest_sequence_sent + latest_len_sent) - max_ack_received

最后还有个重要的概念是“窗口”。有人开玩笑说讨论TCP握手的时候大家侃侃而谈一旦说到窗口就都沉默了气氛尴尬。继续往下学习这一部分你会成为那个打破沉默的人。

其实下次别人跟你切磋“窗口技术”的时候可以先下手为强“你说的是到底哪个窗口”因为事实上TCP有3个窗口接收窗口、拥塞窗口还有发送窗口。

  • 接收窗口它代表的是接收端当前最多能接收的字节数。通过TCP报文头部的Window字段通信双方能互相了解到对方的接收窗口。
  • 拥塞窗口:发送端根据实际传输的拥塞情况计算出来的可发送字节数,但不公开在报文中。各自暗地里各维护各的,互相不知道,也不需要知道。
  • 发送窗口:对方的接收窗口和自身的拥塞窗口两者中,值较小者。实际发送的在途字节数不会大于这个值。

接收窗口是明的,在抓包文件里就能看到;拥塞窗口和发送窗口是暗的,抓包文件里没有。我们看这张图就明白了:

解题

好了,终于把传输相关的关键知识点介绍完了,是不是感觉信息量有点大,甚至还没完全理解?别急,下面我们把这些知识点应用到这个案例中,这些看似干涩的知识点就会逐步丰润起来。

时延

我们首先收集时延信息。用美国数据中心的发送端去Ping阿姆斯特丹接收端的IP我们发现时延在134ms 左右。用Ping的原因是它的ICMP报文比较轻量不会引起双方很多的额外处理时间所以适合用来获取相对纯粹的网络往返时间。

在TCP通信中因为协议栈本身也需要做拆解包、缓冲、Socket处理等工作所以TCP层面的RTT会比IP层面的RTT略长一点。好在Wireshark也提供了RTT信息。我们选取一个报文在TCP详情的SEQ/ACK analysis部分就有iRTT信息。这里是141ms比Ping探测到的134ms多了一点这是因为TCP协议栈本身处理也有一些延迟

图片

iRTT是intial RTT的缩写Wireshark就是从TCP握手阶段的报文里计算出这个值的。对于客户端来说就是发出SYN和收到SYN+ACK的间隔。对于服务端就是发出SYN+ACK和收到ACK的间隔。

带宽时延积

接下来我们算算带宽时延积是多少。时延是134ms带宽是10Gbps那么带宽时延积就是0.134×10Gb。转换为Byte需要再除以8得到约168MB。这个数值的意思是假设这条链路完全被这次的文件传输连接所占满那么最多可以有168MB的在途数据。

我们这个Firmware文件也才95MB如果按这个速度那一个来回就传完了也就是只需要134ms当然TCP有慢启动机制不可能一开始就把一百多MB这么大的数字作为初始拥塞窗口所以也不会真的一个来回就传完了。

到这一步,我们已经明白,带宽时延积并不是限制速度的因素。那么一定是别的东西,在暗地里约束着这次传输。这个东西是什么呢?我们快接近真相了。

发送窗口

在Wireshark里查看接收窗口的数值也是很直观的。任意一个TCP报文的头部都有Window字段长度为2个字节所以最大值为2的16次方即64KB。在第3讲我们讨论TCP握手相关案例时提到过Window Scale。它是在RFC1323中引进的使得Window值最大能达到2的30次方即1GB。

我们看一下当时阿姆斯特丹POP点作为接收端它宣告给美国数据中心的TCP接收窗口值是多少。为了方便查看你可以在Wireshark里这样做

  • 添加Calculated window size为自定义列。操作方法是任选一个TCP报文在TCP详情那里找到[Calculated window size:xxxx]右单击在弹出菜单中点击Apply as Column。然后主窗口里就多了这样一列Calculated window size。
  • 在过滤器输入框输入tcp.srcport eq 22这样就把POP点监听在22端口发出的报文过滤出来了更加方便我们查看。

我得到的界面是这样的:

图片

左上角是过滤器右侧第二列就是刚刚新添加的Calculated window size。显然能从图中看出传输起始阶段有不少重传。不过没关系让我们集中注意力到接收窗口值上。

由图可见这个阶段的接收窗口是64KB上下浮动值偏小。照理来说传输稳定阶段接收窗口会大很多也就应该会比64KB大不少吧我们往下翻滚动到整体报文的中间位置看看此时接收窗口值是多少

图片

有点意外不仅没有上升反而偶尔还略微下降了在54~64KB之间浮动。这是怎么回事这跟速度慢是否有关系

前面提到过,发送端实际的发送窗口是拥塞窗口和对方接收窗口这两者中的较小者。那究竟是多少呢?我们可以这么找到它:

  • 选中传输中的一个报文在TCP详情的[SEQ/ACK analysis]部分,找到[Bytes in flight]。

图片

  • 然后右单击选中Apply as column。

主界面里有Bytes in flight这一列后你就可以排序下图中显示这次传输的客户端在途字节数最大是65700接近64KB这就是发送窗口。

图片

64KB很小啊这就相当于在一个长肥网络中存在着一个“瘦”很多的管道完全没有把带宽利用起来。我觉得可以叫做“长瘦管道”。

这个长瘦管道的传输速度是多少呢?在这个案例里,发送窗口=接收窗口那么在网络上跑的数据量最多就是64KB。那传输这64KB的耗时是多久呢这不就是往返时间RTT吗现在你是否终于回想起前面我卖关子的那个小学公式呢

速度 = 距离/时间

所以这次的传输速度的上限就是window/RTT = 64KB/134ms = 478KB/s还记得前面用TCP Stream Graphs(Stevens)斜线图得到的速率吗那个是456KB/s。可见实际值只比上限值略低说明虽然窗口很小但传输过程本身还是比较充分地利用了这个有限的窗口。

整个案件得以真相大白:限制速度的最大因素,既不是带宽,也不是丢包,而是窗口,确切地说就是接收端POP点的接收窗口。因为这些接收端的设备比较特殊沿用了老旧的配置导致TCP接收窗口过小。

窗口小是因为Window Scale没被启用吗我们检查发现Window Scale其实启用了你可以回头看一下窗口值的截图有几次的值是65728明确超过了65535。另外在握手包里也可以看到Window Scale被启用的信息

图片

红框中的 TCP Option-Windows scale: 6 就是表示窗口系数Scale Factor为6即2的6次方得64。也就是说真正的接收窗口值是Window字段的值乘以64。

**那为什么启用了Window Scale还是被限制在64KB附近呢**我们发现这还是受限于这台设备本身的特点。在某些情况下这里的Window Scale虽然启用了但无法充分工作导致实际上这台设备的接收窗口一直被压制在64KB附近。

想象一下发送端每次“喂到”网络上的数据只有64KB这64KB还需要经过134ms后才能到达接收端。然后就这样周而复始直到全部的95MB发送完成。明明是很宽敞的网络但可惜对端的收发室太小了你只好把大货车停车库里改开电瓶车一小车一小车地送货你急也没用。这速度哪里还起得来

我们对它的配置做了修改,使得接收窗口大幅增加后,速度马上提上去很多倍,这才彻底解决了问题。

最后的疑问

不过你是否留意到了,难道当时美国本土数据中心里面的设备就没有这个问题吗?如果也是用了这个老旧的配置,难道就没有传输慢的问题吗?

我给你的答案是:其实也是老旧的配置,其实速度也是慢的。但是为什么这个问题就没有被暴露呢?这又是跟前面的知识点有关系了。你能想到是哪个因素掩盖了这个问题吗?

这次的答案不是窗口,而是往返时间。在美国本土的多个数据中心之间RTT大约在10ms多一点也就是只有美国到荷兰的134ms的十分之一。联系公式“速度=距离/时间”,分子(接收窗口)不变,分母(时延)变为原来的十分之一,那么速度会变成原来的十倍。十倍的速度就没有显得那么慢了,所以并没有引起我们的注意。

我用一个图表示一下这种RTT的不同引起的传输上的区别

你看,传输速度的问题,可以说就是窗口和往返时间这两个大玩家在起作用。你只要抓住这两个主要矛盾,就能解决大部分传输速度的问题了。其他因素也可能有它的作用,但一般不是核心矛盾。我们要学会抓重点,这对于工作,乃至对于人生,都是很有意义的。

小结

这节课我用了一个典型的文件传输的案例帮你回顾了跟TCP传输有关的方方面面的知识点包括

  • 时延:也叫往返时间,是通信两端之间的一来一回的时间之和。
  • 在途报文:发送端已经发出但还未被确认的报文,时延越长,发送窗口越大,在途报文可能越多,这两者是正比关系。
  • 带宽时延积:带宽和时延的乘积,表示这个网络能承载的最多的在途数据量。
  • 发送窗口:发送窗口是拥塞窗口和对方接收窗口两者中的较小值。

基于以上的知识,我们得以推导出最终的核心公式:速度上限=发送窗口/往返时间。用英文可以表示为velocity = window/RTT。

以后你在处理TCP传输速度问题的时候一样可以应用上面这些知识先获取时延再定位发送窗口最后用这个公式去得到速度的上限值。

除了这些TCP的知识点我也提到了使用Wireshark的一些技巧包括

  • 查看I/O Graph的方法,这个可以直观地展示数据传输的速度。
  • 查看TCP Stream Graphs的方法这个能看到TCP序列号随着时间的变化趋势同时也可以经过简单计算推导出TCP载荷的传输速度因为没有计入各种报文头部这个值比#1的速度值要略低一些
  • 查看TCP Window Scale是否启用以及启用的话Scale Factor的值的查找方法。

总而言之你在自己处理类似的传输速度、延迟等问题的时候一方面可以充分利用我分享的协议方面的知识获得坚实的理论支撑一方面也可以使用这节课提到的这些Wireshark分析技巧获得数据上的支持。当你把理论和数据这两条“腿”都锻炼健壮后你一定能在网络排查的世界里更加坚定有力地前行。

思考题

你有没有在工作中遇到过TCP传输速度相关的问题呢通过这节课的学习你已经掌握了传输速度相关的不少知识你准备怎么运用这些知识来解决这个传输问题呢

欢迎在留言区分享出你的答案和思考过程,也欢迎你把今天的内容分享给更多的朋友,我们一起成长。

附录

抓包示例文件:https://gitee.com/steelvictor/network-analysis/tree/master/09