性能文章>腾讯云和阿里云tcp三次握手的区别>

腾讯云和阿里云tcp三次握手的区别原创

1年前
366428

前言

近日同事遇到一个诡异的问题,帮忙进行了排查,好家伙不查不知道,一查让我知道了,腾讯云和阿里云TCP三次握手居然还有差异,没有想到云厂商这种Iass级别的服务,还有不同的标准~

问题现象

•客户是半托管客户,我们部署服务请求阿里云的nginx,nginx作为LB,反向代理了N个java服务,Java服务入库.请求链路如下: 客户物理机Java->阿里云 nginx->阿里云Java•从客户物理机请求阿里云的nginx的时候,出现了包太大,读取超时,包小,则无问题。从 物理机到-腾讯云的nginx->Java服务 包大和包小都没有问题。

半托管Java错误日志如下:

nginx日志出现408

•阿里云nginx也有其他客户在用,没有出现408错误,就这个客户出现•根据日志,复原请求内容,在物理机上直接curl 阿里云的机器也是同样的问题,同样的请求,curl腾讯云的服务器没有问题,同样请求不在客户的物理机,在本地请求阿里云的机器没有问题。•据运维说,这台物理机坏过,客户那去维修后,重新部署就出现这个问题,维修前没有这个问题•物理机直接把请求送给阿里云的Java服务,也是同样的问题 物理机--》阿里云Java服务•询问客户,维修后就装了个centos系统,配置了网络,其他什么都没有动•询问运维,只部署服务,配置和以前一样

nginx 408

通常产生408错误有两个地方的配置会导致:client_body_timeout和client_header_timeout。

这两个超时时间默认都是60s。

client_body_timeout:定义读取客户端请求正文的超时。超时是指相邻两次读操作之间的最大时间间隔,而不是整个请求正文完成传输的最大时间。如果客户端在这段时间内没有传输任何数据,nginx将返回408 (Request Time-out)错误到客户端。

client_header_timeout:定义读取客户端请求头部的超时。如果客户端在这段时间内没有传送完整的头部到nginx, nginx将返回错误408 (Request Time-out)到客户端。

网上都说是要配置这2个参数,但是我们同样的请求内容,在腾讯云的nginx上是没有问题的,腾讯云nginx和阿里云的nginx版本和配置是完全能一致的。所以不会是nginx参数配置的问题,而且直接送阿里云的Java服务,也是读取超时,所以基本上可以排除是nginx的问题

我们可以看到408都是读取超时,那么请求到底送没送到nginx,这个才是关键点。

遇事不决,我们抓个包看看

抓包

•本次抓包 物理机--》阿里云nginx

物理机抓包

tcpdump -i em1 host ali-nginx-ip -w btg.pcap

ali-nginx抓包

tcpdump -i eh1 host btg.ip -w ali-nginx.pcap

使用wireshark分析抓包文件

从图中可以看出三次握手是成功了的,在发送http报文的时候,出现了以下错误。

TCP dup ack XXX#X是nginx给物理机返回的,就是重复应答#前的表示报文到哪个序号丢失,#后面的是表示第几次丢失

TCP Out_of_Order的原因分析:一般来说是网络拥塞,导致顺序包抵达时间不同,延时太长,或者包丢失,需要重新组合数据单元,因为他们可能是由不同的路径到达你的电脑上面。

TCP Retran**ission原因分析:很明显是上面的超时引发的数据重传

nginx上的抓包内容如下

nginx上抓包就更简单了,上面物理机发送的http报文,在nginx上居然没有捕获到,难道是偶然的?然后又重新试了几次,在nginx上都没有捕获到。到这里,就能确定了,物理机到阿里云的网络链路有问题,出现丢包了。

检查网络链路

•长ping阿里云的nginx

ping aliyun-nginx-ip

没有发现丢包,网络很稳定

联想到只有包特别大才会出问题,所以在ping上加了参数

ping -4000 aliyun-nginx-ip

发现到了4000的时候,ping就丢包了,推测是数据太大,客户那边做了白名单限制,联系客户的网工,进行加白后,测试ping是正常了。

这样就以为搞定了?那就错了。客户加白后,ping是没有问题了,但是http请求一样,上面的问题还是一样,加不加白,对这个问题没有什么影响。

网卡丢包

重新理下思路,只有这个客户出现问题,那这个客户本身网络问题,嫌疑比较大。询问客户网工,网络架构是怎么搭建的,结果一问三不知。

只有大包才会出现问题,那么很可能和分包有关系,马上查看客户网卡信息,发现MTU的值是1500,没有改过,而且是生产环境,总不能直接改MTU的值吧,但是另外一个线索引起了注意。

监控下网卡信息

watch netstat --interfaces

是的,没有看错,dropped数据一直在增加。

RX==receive,接收,从开启到现在接收封包的情况,是下行流量。

TX==Tran**it,发送,从开启到现在发送封包的情况,是上行流量。

讲道理,我们的问题也应该是TX,但是他RX一直在丢包。

查看网卡的ring buffer

ethtool -g em1
Ring parameters for em1:
Pre-set maximums:
RX:             2040
RX Mini:        0
RX Jumbo:       8160
TX:             255
Current hardware settings:
RX:             2040
RX Mini:        0
RX Jumbo:       0
TX:             255

已经设置最大了,所以不是ring buffer的问题。

我们通过linux源码[1]可以知道,Linux支持的协议都在这里定义了,如果协议不被linux支持,会直接drop,导致上面网卡的drop数一直在增加。

通过上面可以linux源代码可以看到,我们的tcp/ip肯定是受支持的,所以drop这个和我们这个问题没什么关系。

但是我还是很好奇,是什么协议导致了Linux内核不支持,我们可以通过以下方式判断。

tcpdump -i em1 -| grep --'ARP|IP|802.1Q|802.1ADP'

打印出包的ether type,然后过滤掉操作系统支持的包,剩下的就是丢掉的包

listening on em1, link-type EN10MB (Ethernet), capture size 262144 bytes
17:31:55.517403 0c:38:3e:3f:aa:cf (oui Unknown) > 01:80:c2:00:00:0e (oui Unknown), ethertype LLDP (0x88cc), length 207: LLDP, length 193: X3SG
17:31:58.242948 0c:38:3e:3f:b1:31 (oui Unknown) > 01:80:c2:00:00:0e (oui Unknown), ethertype LLDP (0x88cc), length 207: LLDP, length 193: X3SG
17:31:58.318690 0c:38:3e:3f:b1:2f (oui Unknown) > 01:80:c2:00:00:0e (oui Unknown), ethertype LLDP (0x88cc), length 207: LLDP, length 193: X3SG
^C141 packets captured399 packets received by filter

我们在linux代码搜一下LLDP协议,发现并没有。在ethertypes[2]发现他的定义是数据链路层协议。所以结果很明显了。

网卡丢包问题和我们前文提到的问题无关。

修改MTU

到这里,我们的怀疑点就只有MTU了,通过ping设置不允许分包来手工探测MTU值 物理机上

ping -1472 -do aliyun-nginx-ip

1500-8(icmp头部)-20(ip头)=1472 ping不通,自己电脑上在来试试,是ok 的,那么结论很明显了,就是客户机器上中间链路上有小于1500的mtu设备。

继续减少字节数,临界点在1465是可以通的,超过1465就不行 那么最佳的mtu的值,等于=1465+8+20=1493

接下来就是改设备的MTU值进行测试了

ifconfig em1 mtu 1493

ps:这么修改mtu值,只要重启机器,就会失效,要研究修改mtu的值

vim  /etc/sysconfig/network-scripts/ifcfg-em1

增加如下内容

MTU="1493"

重启服务

service network restart

改完进行curl测试,发现请求阿里云的nginx是没有问题了。

到这里问题就已经解决了,但是有个疑问为什么没有改mtu值前,腾讯的可以,阿里的不行?

腾讯和阿里 tcp三次握手的区别

带着疑问,在腾讯的云主机和物理机上抓了个包,来和阿里云的进行对比。以下抓包文件,是以MTU为1500的为例

发往腾讯云的SYN包当中,MSS值为1460=(1500-20(tcp头部字节)-20(ip头部字节))1460其实是一个默认的规范 而腾讯回的SYN,ACK包当中,MSS值为1424,比常规的1460,少16个字节,当然这16个字节是减了个啥,我也不知道。

回到上面,我们说中间链路有设备MTU的值不是1500,最大的为1493. 1493-20-20=1453,1453才应该是物理机的MSS值,而常规的1460比1453大,会导致丢包,而按照1424发包,则是没有问题的。

所有的一切都能解释的通了。

腾讯云的机器MTU值不是默认的1500吗?

不,他就是默认的1500,那1424的逻辑肯定不是我们腾讯机器加的,为了确认这个猜想,我们在腾讯云上进行了抓包,由于机器是NAT机器,腾讯云并没有绑定公网ip,我们抓的包为nginx代理阿里的java服务的三次握手

可以看到,nginx去和java建立连接的时候使用的是1460,也就是说,1424这个逻辑在进入机器网卡前发生的。而阿里的java服务,回的是1460,说明在出去的时候,腾讯也把mss改为1424了。

到这里,基本上解决了我们的所有疑问。

后续

我们在和客户的网工,沟通后发现1493是他们那边设置的,而他没有改过,以前就是用的这个。而我们的运维说也没改过。那就奇怪了~

沟通后,客户的网工设置mtu值为1560,说是最大的值,我其实想让他改1500的,但是不听我的。

References

[1] linux源码: https://elixir.bootlin.com/linux/v3.10/source/include/uapi/linux/if\_ether.h
[2] ethertypes: https://github.com/openbsd/src/blob/master/sys/net/ethertypes.h#L305

点赞收藏
少放盐
请先登录,查看2条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

为什么要做代码Review?

为什么要做代码Review?

日常Bug排查-集群逐步失去响应

日常Bug排查-集群逐步失去响应

浅析AbstractQueuedSynchronizer

浅析AbstractQueuedSynchronizer

8
2