前言
近日同事遇到一個(gè)詭異的問(wèn)題,幫忙進(jìn)行了排查姓建,好家伙不查不知道缓溅,一查讓我知道了蛇损,騰訊云和阿里云TCP三次握手居然還有差異,沒(méi)有想到云廠商這種Iass級(jí)別的服務(wù),還有不同的標(biāo)準(zhǔn)~
問(wèn)題現(xiàn)象
?客戶是半托管客戶淤齐,我們部署服務(wù)請(qǐng)求阿里云的nginx股囊,nginx作為L(zhǎng)B,反向代理了N個(gè)java服務(wù)更啄,Java服務(wù)入庫(kù).請(qǐng)求鏈路如下: 客戶物理機(jī)Java->阿里云 nginx->阿里云Java?從客戶物理機(jī)請(qǐng)求阿里云的nginx的時(shí)候稚疹,出現(xiàn)了包太大,讀取超時(shí)祭务,包小内狗,則無(wú)問(wèn)題。從 物理機(jī)到-騰訊云的nginx->Java服務(wù) 包大和包小都沒(méi)有問(wèn)題义锥。
nginx日志出現(xiàn)408
?阿里云nginx也有其他客戶在用柳沙,沒(méi)有出現(xiàn)408錯(cuò)誤,就這個(gè)客戶出現(xiàn)?根據(jù)日志拌倍,復(fù)原請(qǐng)求內(nèi)容赂鲤,在物理機(jī)上直接curl 阿里云的機(jī)器也是同樣的問(wèn)題,同樣的請(qǐng)求柱恤,curl騰訊云的服務(wù)器沒(méi)有問(wèn)題数初,同樣請(qǐng)求不在客戶的物理機(jī),在本地請(qǐng)求阿里云的機(jī)器沒(méi)有問(wèn)題梗顺。?據(jù)運(yùn)維說(shuō)泡孩,這臺(tái)物理機(jī)壞過(guò),客戶那去維修后荚守,重新部署就出現(xiàn)這個(gè)問(wèn)題珍德,維修前沒(méi)有這個(gè)問(wèn)題?物理機(jī)直接把請(qǐng)求送給阿里云的Java服務(wù),也是同樣的問(wèn)題 物理機(jī)--》阿里云Java服務(wù)?詢問(wèn)客戶矗漾,維修后就裝了個(gè)centos系統(tǒng)锈候,配置了網(wǎng)絡(luò),其他什么都沒(méi)有動(dòng)?詢問(wèn)運(yùn)維敞贡,只部署服務(wù)泵琳,配置和以前一樣
nginx 408
通常產(chǎn)生408錯(cuò)誤有兩個(gè)地方的配置會(huì)導(dǎo)致:client_body_timeout和client_header_timeout。
這兩個(gè)超時(shí)時(shí)間默認(rèn)都是60s誊役。
client_body_timeout:定義讀取客戶端請(qǐng)求正文的超時(shí)获列。超時(shí)是指相鄰兩次讀操作之間的最大時(shí)間間隔,而不是整個(gè)請(qǐng)求正文完成傳輸?shù)淖畲髸r(shí)間蛔垢。如果客戶端在這段時(shí)間內(nèi)沒(méi)有傳輸任何數(shù)據(jù)击孩,nginx將返回408 (Request Time-out)錯(cuò)誤到客戶端。
client_header_timeout:定義讀取客戶端請(qǐng)求頭部的超時(shí)鹏漆。如果客戶端在這段時(shí)間內(nèi)沒(méi)有傳送完整的頭部到nginx巩梢, nginx將返回錯(cuò)誤408 (Request Time-out)到客戶端创泄。
網(wǎng)上都說(shuō)是要配置這2個(gè)參數(shù),但是我們同樣的請(qǐng)求內(nèi)容括蝠,在騰訊云的nginx上是沒(méi)有問(wèn)題的鞠抑,騰訊云nginx和阿里云的nginx版本和配置是完全能一致的。所以不會(huì)是nginx參數(shù)配置的問(wèn)題忌警,而且直接送阿里云的Java服務(wù)搁拙,也是讀取超時(shí),所以基本上可以排除是nginx的問(wèn)題
我們可以看到408都是讀取超時(shí)法绵,那么請(qǐng)求到底送沒(méi)送到nginx箕速,這個(gè)才是關(guān)鍵點(diǎn)。
遇事不決朋譬,我們抓個(gè)包看看
抓包
?本次抓包 物理機(jī)--》阿里云nginx
物理機(jī)抓包
tcpdump -i em1 host ali-nginx-ip -w btg.pcap
ali-nginx抓包
tcpdump -i eh1 host btg.ip -w ali-nginx.pcap
使用wireshark分析抓包文件
從圖中可以看出三次握手是成功了的弧满,在發(fā)送http報(bào)文的時(shí)候,出現(xiàn)了以下錯(cuò)誤此熬。
?
TCP dup ack XXX#X是nginx給物理機(jī)返回的,就是重復(fù)應(yīng)答#前的表示報(bào)文到哪個(gè)序號(hào)丟失滑进,#后面的是表示第幾次丟失
?
TCP Out_of_Order的原因分析:一般來(lái)說(shuō)是網(wǎng)絡(luò)擁塞犀忱,導(dǎo)致順序包抵達(dá)時(shí)間不同,延時(shí)太長(zhǎng)扶关,或者包丟失阴汇,需要重新組合數(shù)據(jù)單元,因?yàn)樗麄兛赡苁怯刹煌穆窂降竭_(dá)你的電腦上面节槐。
?
TCP Retransmission原因分析:很明顯是上面的超時(shí)引發(fā)的數(shù)據(jù)重傳
?
nginx上的抓包內(nèi)容如下
nginx上抓包就更簡(jiǎn)單了搀庶,上面物理機(jī)發(fā)送的http報(bào)文,在nginx上居然沒(méi)有捕獲到铜异,難道是偶然的哥倔?然后又重新試了幾次,在nginx上都沒(méi)有捕獲到揍庄。到這里咆蒿,就能確定了,物理機(jī)到阿里云的網(wǎng)絡(luò)鏈路有問(wèn)題蚂子,出現(xiàn)丟包了沃测。
檢查網(wǎng)絡(luò)鏈路
?長(zhǎng)ping阿里云的nginx
ping aliyun-nginx-ip
沒(méi)有發(fā)現(xiàn)丟包,網(wǎng)絡(luò)很穩(wěn)定
聯(lián)想到只有包特別大才會(huì)出問(wèn)題食茎,所以在ping上加了參數(shù)
ping -s 4000 aliyun-nginx-ip
發(fā)現(xiàn)到了4000的時(shí)候蒂破,ping就丟包了,推測(cè)是數(shù)據(jù)太大别渔,客戶那邊做了白名單限制附迷,聯(lián)系客戶的網(wǎng)工惧互,進(jìn)行加白后,測(cè)試ping是正常了挟秤。
這樣就以為搞定了壹哺?那就錯(cuò)了∷腋眨客戶加白后管宵,ping是沒(méi)有問(wèn)題了,但是http請(qǐng)求一樣攀甚,上面的問(wèn)題還是一樣箩朴,加不加白,對(duì)這個(gè)問(wèn)題沒(méi)有什么影響秋度。
網(wǎng)卡丟包
重新理下思路炸庞,只有這個(gè)客戶出現(xiàn)問(wèn)題,那這個(gè)客戶本身網(wǎng)絡(luò)問(wèn)題荚斯,嫌疑比較大埠居。詢問(wèn)客戶網(wǎng)工,網(wǎng)絡(luò)架構(gòu)是怎么搭建的事期,結(jié)果一問(wèn)三不知滥壕。
只有大包才會(huì)出現(xiàn)問(wèn)題,那么很可能和分包有關(guān)系兽泣,馬上查看客戶網(wǎng)卡信息绎橘,發(fā)現(xiàn)MTU的值是1500,沒(méi)有改過(guò)唠倦,而且是生產(chǎn)環(huán)境称鳞,總不能直接改MTU的值吧,但是另外一個(gè)線索引起了注意稠鼻。
watch netstat --interfaces
是的冈止,沒(méi)有看錯(cuò),dropped數(shù)據(jù)一直在增加候齿。
RX==receive靶瘸,接收,從開(kāi)啟到現(xiàn)在接收封包的情況毛肋,是下行流量怨咪。
TX==Transmit,發(fā)送润匙,從開(kāi)啟到現(xiàn)在發(fā)送封包的情況诗眨,是上行流量。
講道理孕讳,我們的問(wèn)題也應(yīng)該是TX匠楚,但是他RX一直在丟包巍膘。
查看網(wǎng)卡的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
已經(jīng)設(shè)置最大了,所以不是ring buffer的問(wèn)題芋簿。
我們通過(guò)linux源碼[1]可以知道峡懈,Linux支持的協(xié)議都在這里定義了,如果協(xié)議不被linux支持与斤,會(huì)直接drop肪康,導(dǎo)致上面網(wǎng)卡的drop數(shù)一直在增加。
通過(guò)上面可以linux源代碼可以看到撩穿,我們的tcp/ip肯定是受支持的磷支,所以drop這個(gè)和我們這個(gè)問(wèn)題沒(méi)什么關(guān)系。
但是我還是很好奇食寡,是什么協(xié)議導(dǎo)致了Linux內(nèi)核不支持雾狈,我們可以通過(guò)以下方式判斷。
tcpdump -i em1 -e | grep -v -E 'ARP|IP|802.1Q|802.1ADP'
打印出包的ether type抵皱,然后過(guò)濾掉操作系統(tǒng)支持的包善榛,剩下的就是丟掉的包
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
我們?cè)趌inux代碼搜一下LLDP協(xié)議,發(fā)現(xiàn)并沒(méi)有呻畸。在ethertypes[2]發(fā)現(xiàn)他的定義是數(shù)據(jù)鏈路層協(xié)議锭弊。所以結(jié)果很明顯了。
網(wǎng)卡丟包問(wèn)題和我們前文提到的問(wèn)題無(wú)關(guān)擂错。
修改MTU
到這里,我們的懷疑點(diǎn)就只有MTU了樱蛤,通過(guò)ping設(shè)置不允許分包來(lái)手工探測(cè)MTU值 物理機(jī)上
ping -s 1472 -M do aliyun-nginx-ip
1500-8(icmp頭部)-20(ip頭)=1472 ping不通钮呀,自己電腦上在來(lái)試試,是ok 的昨凡,那么結(jié)論很明顯了爽醋,就是客戶機(jī)器上中間鏈路上有小于1500的mtu設(shè)備。
繼續(xù)減少字節(jié)數(shù)便脊,臨界點(diǎn)在1465是可以通的蚂四,超過(guò)1465就不行 那么最佳的mtu的值,等于=1465+8+20=1493
接下來(lái)就是改設(shè)備的MTU值進(jìn)行測(cè)試了
ifconfig em1 mtu 1493
ps:這么修改mtu值哪痰,只要重啟機(jī)器遂赠,就會(huì)失效,要研究修改mtu的值
vim /etc/sysconfig/network-scripts/ifcfg-em1
增加如下內(nèi)容
MTU="1493"
重啟服務(wù)
service network restart
改完進(jìn)行curl測(cè)試晌杰,發(fā)現(xiàn)請(qǐng)求阿里云的nginx是沒(méi)有問(wèn)題了跷睦。
到這里問(wèn)題就已經(jīng)解決了,但是有個(gè)疑問(wèn)為什么沒(méi)有改mtu值前肋演,騰訊的可以抑诸,阿里的不行烂琴?
騰訊和阿里 tcp三次握手的區(qū)別
帶著疑問(wèn),在騰訊的云主機(jī)和物理機(jī)上抓了個(gè)包蜕乡,來(lái)和阿里云的進(jìn)行對(duì)比奸绷。以下抓包文件,是以MTU為1500的為例
回到上面层玲,我們說(shuō)中間鏈路有設(shè)備MTU的值不是1500号醉,最大的為1493. 1493-20-20=1453,1453才應(yīng)該是物理機(jī)的MSS值,而常規(guī)的1460比1453大称簿,會(huì)導(dǎo)致丟包扣癣,而按照1424發(fā)包,則是沒(méi)有問(wèn)題的憨降。
所有的一切都能解釋的通了父虑。
騰訊云的機(jī)器MTU值不是默認(rèn)的1500嗎?
不授药,他就是默認(rèn)的1500士嚎,那1424的邏輯肯定不是我們騰訊機(jī)器加的,為了確認(rèn)這個(gè)猜想悔叽,我們?cè)隍v訊云上進(jìn)行了抓包莱衩,由于機(jī)器是NAT機(jī)器,騰訊云并沒(méi)有綁定公網(wǎng)ip娇澎,我們抓的包為nginx代理阿里的java服務(wù)的三次握手
到這里笨蚁,基本上解決了我們的所有疑問(wèn)。
后續(xù)
我們?cè)诤涂蛻舻木W(wǎng)工趟庄,溝通后發(fā)現(xiàn)1493是他們那邊設(shè)置的括细,而他沒(méi)有改過(guò),以前就是用的這個(gè)戚啥。而我們的運(yùn)維說(shuō)也沒(méi)改過(guò)奋单。那就奇怪了~
溝通后,客戶的網(wǎng)工設(shè)置mtu值為1560猫十,說(shuō)是最大的值览濒,我其實(shí)想讓他改1500的,但是不聽(tīng)我的拖云。
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
本文使用 文章同步助手 同步