腾讯云和阿里云tcp三次握手的区别原创
前言
近日同事遇到一个诡异的问题,帮忙进行了排查,好家伙不查不知道,一查让我知道了,腾讯云和阿里云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 -s 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 -e | grep -v -E '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 -s 1472 -M 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