调试网络质量,我们一般关注两个因素:延迟和吞吐量(带宽)。延迟比较容易验证,通过Ping或者mtr[1]就可以看到。本文分享一种调试吞吐量的方法。重视吞吐量的场景通常是所谓的长胖管道(LongFatNetworks,LFN,rfc7323[2])。例如,下载大文件。吞吐量没有达到网络的上限,可能主要受三个方面的影响:发送端瓶颈接收端瓶颈中间网络层瓶颈发送端瓶颈通常buffer是不够大,因为发送过程是的,应用调用syscall,把要发送的数据放到buffer里,然后系统负责发送出去。如果缓冲区已满,应用程序将阻塞(如果使用块API)直到缓冲区可用,然后再继续写入、生产者和消费者模式。通常在发送端排查瓶颈更容易,甚至可以使用应用程序日志查看它们何时被阻止。大多数情况是情况2和3,很难排查。当发送端的应用程序已经将内容写入系统的缓冲区,但系统没有快速发送出去时,就会出现这种情况。为了优化传输效率(注意这里的传输效率不是单个TCP连接的传输效率,而是整个网络的效率),TCP会:保护接收端,发送的数据不会超过接收端的缓冲区大小(流量控制)。向接收端发送数据与上述过程类似。内核负责收集数据包并先放入缓冲区,然后上层应用程序处理缓冲区中的内容。如果接收端的缓冲区太小,很容易出现瓶颈,即应用程序来不及处理就被填满了。那么如果继续发送数据,缓冲区就存不下了,接收端只能丢弃。保护网络,发送的数据不会压垮网络(CongestionControl,拥塞控制),如果中间网络出现瓶颈,又长又胖的管道吞吐量将不尽如人意;为了保护接收端,在双方建立连接时会协商接收端的缓冲区大小(receiverwindowsize,rwnd),在后续的发送中,接收端也会报告其剩余以及每个ack返回数据包中接受的窗口大小。这样发送方在发送的时候就会保证自己不会发送超过接收方缓冲区大小的数据。(意思是发送方需要负责,接收方没有ack的总数,不会超过接收方的buffer。)对于网络保护,原则是维护一个窗口,叫做Congestionwindow,拥塞window,cwnd,这个窗口是当前网络的限制,发送端不会发送超过这个窗口的容量(noack的总数不会超过cwnd)。如何找到这个cwnd的值?这是关键。默认算法是cubic,也可以使用其他算法,比如谷歌的BBR[3]。主要逻辑是慢启动(Slowstart),发送数据进行测试,如果能被接收方正确接收到ack,说明当前网络可以容纳这个吞吐量,设置cwndx2,然后继续测试。直到出现如下情况:发送的数据包没有收到ACKcwnd已经等于rwnd第二点很好理解,说明网络吞吐量不是瓶颈,瓶颈是接收端的缓冲区不够大。cwnd不能超过rwnd,否则接收方会过载。对于第1点,本质上,发送方使用数据包丢失来检测网络状况。如果没有丢包,说明一切正常。如果发生丢包,则意味着网络无法处理发送速度。这时候发送端会直接发送减半的cwnd。但是,真正导致第1点的情况不一定是网络吞吐量瓶颈,可能是以下几种情况:网络已经达到瓶颈,网络质量问题,丢包,中间网络设备延误了数据包的投递,导致sender未能在预期的时间收到ACK2和3原因会导致cwnd下降,网络吞吐量无法得到充分利用。以上是基本原理,下面介绍如何定位这个问题。rwnd查看方式的窗口大小直接在TCP头中,抓取该字段即可查看。但真正的窗口大小需要乘以因子,这是在TCP握手节点[4]通过TCPOptions协商的。所以,如果分析一个TCP连接的窗口大小,一定要在握手阶段抓包,否则就不知道协商的因素是多少。cwnd的查看方法拥塞控制是发送方通过算法得到的一个动态变量。它会尝试调整它,但它不会反映在协议的传输数据中。所以要看到这个,你必须在发送机上观看。在Linux中,可以使用ss-i选项打印出TCP连接的参数。这里显示的单位是TCPMSS。[5]即实际大小为1460bytes*10Wireshark分析Wireshark提供了一个非常有用的统计功能,可以让你一目了然的看到当前瓶颈发生在什么地方。但是我第一次打开这张图的时候不知道怎么看。还好我的同事[6]可以,他教我的,我记录在这里,也教给大家。首先,打开方法如下:然后你会看到下图。首先需要明确的是,tcptrace的图显示的是单向传输数据,因为tcp是双工协议,两边都可以发送数据。其中,最上面写着你当前正在查看的图像数据是从10.0.0.1发送到192.168.0.1,然后按右下角的按钮可以切换查看方向。X轴代表时间,这个很容易理解。然后理解Y轴代表的SequenceNumber就是TCP包中的SequenceNumber,这个很重要。图中所有数据均以SequenceNumber为准。所以,如果你看到上图,说明你是倒着读的,因为数据的SequenceNumber没有增加,说明几乎没有数据发送,需要点击SwitchDirection。没错,我们可以看到我们传输的SequenceNumber是随着时间的推移而增加的。这里有3行,含义如下:另外,还有另外两行:需要永远记住的是Y轴是SequenceNumber,红线表示SACK线表示这一段SequenceNumberIhavereceived,然后配合黄线表示已经ACKed的SequenceNumber,那么发送方就知道数据包在中间的空隙丢失了,红线的垂直空白和黄线是尚未确认的数据包。因此,需要重传。蓝线表示再次重传。学会看这些图后,我们可以识别出几种常见的模式:很多红色的SACK都丢失了,说明接收端在重复:中间有个包我没收到,后面有个包中间那个我没收到。吞吐量受接收窗口大小的限制。从这个图中可以看出,黄线(接收端收到ACK)一上升,蓝线就上升(发送端开始发送),直到绿线(窗口大小)被填满。说明网络不是瓶颈,可以增加接收端的缓冲区大小。吞吐量受网络质量的限制。从这张图可以看出接收端的窗口大小距离瓶颈还差得很远,还有很多空闲时间。放大,可以看到中间有很多丢包和重传,每次只发送一点点数据,这说明cwnd很可能太小,受拥塞控制算法限制。
