TCP数据重传时间细节探秘及数据中心优化

在数据中心网络内,机器之间数据传输的往返时间(rtt)一般在10ms以内,为此调内部服务的超时时间一般会设置成50ms、200ms、500ms等,如果在传输过程中出现丢包,这样的服务超时时间,tcp层有机会发现并重传一次数据么?如果设置成200ms以内,答案是没有机会,原因是linux系统下第一次重传时间等于传输的往返时间上至少加上200ms的预测偏差值,即如果rtt值是7ms,第一次重传超时时间至少是207ms,这样如果对某个接口的超时时间设置成200ms以内, 即便是rtt时间很小,仍然无法容忍一次丢包,因为在tcp发现丢包之前,该接口已经超时了。

本文针对linux系统tcp数据包第一次重传时间的计算进行探究,结果会让人大吃一惊。提出的优化方法,理论上能够降低内部服务调用时延和出错量。

tcp发送数据包后,会设置一个定时器,到期后如果还没有收到对方的回复(ack),就会重传数据包。从发出数据包到第一次重传之间的间隔时间称为retransmission timeout(RTO),rto由数据包的往返时间(rtt)加上rtt的预测偏差(波动值)计算出来。

即 rto = srtt + rttvar,其中srtt是rtt的平滑值,而rttvar是波动值,代表可能的预测偏差。

接下来我们做一个试验。

先ping一下www.weibo.com,看一下数据包的往返时间,如下:

再看一下tcp对到www.weibo.com的rtt相关数据,下面的命令是针对centos7(如果是以下的版本,运行的命令是ip route list tab cache)如下:

由上面看出,平滑后的rtt值约为7ms,rttvar约为7ms,那按理说rto值应该是14ms左右,也就是等14ms后,如果没有收到对方的响应,就会重传数据。实际的情况会是这样么?

在一个命令窗口里,运行下面的命令:

同时再开一个命令行窗口里,运行下面的命令:

从上面的结果可以看出,实际的rto值是207ms,相当于rtt值加上200ms,为什么呢?

下面从内核tcp源代码中分析原因。

设置超时时间的函数是tcp_set_rto,在net/ipv4/tcp_input.c中,如下:

可以看出,重传的定时值isck_rto实际上是调用 __tcp_set_rto,接着看它的源码,这个在文件include/tcp/net/tcp.h中,如下:

为了避免浮点数运算,rtt乘以8保存在socket数据结构中,从代码可以确认:

而计算和影响srtt和rttvar的函数是tcp_rtt_estimator,在文件net/ipv4/tcp_input.c中,代码如下:

从上面的代码可以看出,srtt  = 7/8 old srtt + 1/8 new rtt,这个跟RFC一致,没有啥可以说的。

获得第一个往返时间数据时(一般是建立连接完成时,对于客户端就是发出sync请求,收到服务端的回应时,而对于服务器端就是发出syc+ack后,收到客户端的ack时)的计算分析如下:

再看tcp_rto_min的代码,在文件include/net/tcp.h中:

结合起来看,如果第一个数据包往返时间在100ms以内,rtt预测初始的偏差值就固定为200ms,当数据包往返时间超过100ms,rtt预测偏差的初始值是2倍的rtt值,也就是说rttvar最小值是200ms。

接着分析计算和影响srtt和rttvar的函数是tcp_rtt_estimator的代码:

也就是说,rtt预测偏差值rttvar会跟着实际的rtt预测偏差值变化,如果波动变大,则跟着变大,反之,如果波动变小,也会跟着变小。但因为每个发送周期内,偏差的最大值会重置为tcp_rto_min,所以,rtt预测偏差值rttvar不会小于200ms。

那这200ms的限制,有啥简单的方法调整么?继续看tcp_rto_min的代码,前面也贴过,如下:

从上面的代码可以看出,如果对应的目标的路由表项中设置了rto_min值,则以设置的值为准。这可以通过netlink机制来修改,具体可以通过ip route命令,增加rto_min选项来完成。

分析完源代码,接着试验一下。

运行下面的命令修改成20ms:

看以下修改后的结果:

清除以下路由表的缓存,这样可以立即查看效果:

再测试访问weibo.com:

在另外的终端中确认一下结果:

可以看出,本次的rtt值是2ms,rto为22ms,即已经生效。

欢迎一起讨论,拍砖也可以。呵呵。

2 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部