日常問題排查-空閑一段時(shí)間再請求就超時(shí)
當(dāng)前位置:點(diǎn)晴教程→知識管理交流
→『 技術(shù)文檔交流 』
問題現(xiàn)場問題是喜聞樂見的調(diào)用超時(shí)。這個(gè)問題的顯著特征是:
猜想1:服務(wù)端關(guān)閉了連接一個(gè)非常直觀的猜想就是服務(wù)端關(guān)閉了這個(gè)鏈接,請求直接被拒絕了。但熟悉tcp協(xié)議的筆者很快否定了這個(gè)猜想,如果連接被關(guān)閉了,會有下面圖中所示的兩種情況: 第一種情況,client端沒有收到服務(wù)端返回的FIN包,那么在請求發(fā)送后應(yīng)該是直接被對端Reset,立刻感知到報(bào)錯(cuò)。 第二種情況,client端收到了服務(wù)端返回的FIN包,那么在請求發(fā)送前會直接報(bào)socket has already closed,立刻感知到報(bào)錯(cuò)。 根據(jù)上面的判斷,無論什么情況都是立刻返回,而不是等待很長時(shí)間之后超時(shí),和特征2不符,于是可以否定由于服務(wù)端關(guān)閉連接導(dǎo)致。 猜想2:偶發(fā)性路由翻動(dòng)因?yàn)檫^了非常長的時(shí)間才超時(shí),這時(shí)候,我們的就可以考慮是在網(wǎng)絡(luò)層丟包了。那么到底為什么丟包呢?難道是偶發(fā)性的路由翻動(dòng)?這個(gè)想法立馬被筆者否決了。因?yàn)?,如果是路由翻?dòng)一般會在分鐘級別的收斂,而我們觀察到在5s超時(shí)后的重試都是成功的。而且一旦路由翻動(dòng)這段時(shí)間內(nèi)所有的請求都應(yīng)該收到影響,而問題現(xiàn)場其它請求確實(shí)正常的。這就和特征3/特征4不符合。 猜想3(真正的原因)其實(shí)這個(gè)問題筆者一直遇到,而且解決方案也一直有,但從沒有真正的仔細(xì)思考過。但最近讀《tcpip路由技術(shù)》卷二突然靈光一閃,將書中的一些闡述和這個(gè)問題莫名的關(guān)聯(lián)想通了其中的關(guān)竅。人們由于IPv4地址即將耗盡而不得不開發(fā)出NAT技術(shù),而NAT畢竟只是個(gè)補(bǔ)丁,其無法完整的融合進(jìn)TCP導(dǎo)致出現(xiàn)種種因?yàn)檫@個(gè)補(bǔ)丁而出現(xiàn)的問題。我們通過NAT設(shè)備中的轉(zhuǎn)發(fā)表項(xiàng)維護(hù)內(nèi)網(wǎng)的ip:port和外網(wǎng)的ip:port之間的映射,入下圖所示: 很明顯的,由于client和server的數(shù)量是非常多的(因?yàn)槎鄠€(gè)服務(wù)可能公用一個(gè)公網(wǎng)IP),所以轉(zhuǎn)發(fā)表是一個(gè)非常寶貴的資源,一旦轉(zhuǎn)發(fā)表滿了,就無法創(chuàng)建新的連接路徑了。所以,一些長期沒有流量需要有一個(gè)定時(shí)的清理機(jī)制騰出轉(zhuǎn)發(fā)表以供新的連接創(chuàng)建。如下圖所示,在tcp連接estalbish狀態(tài)后一定時(shí)間內(nèi)沒有任何流量,NAT會直接清空這個(gè)轉(zhuǎn)發(fā)表項(xiàng),而client和server端無法感知到這一點(diǎn),于是client端只好在多次NAT重傳后超時(shí)。這個(gè)和Bug現(xiàn)場的各種特征完全一致。當(dāng)然無論是NAT-1和NAT-2都有可能清理轉(zhuǎn)發(fā)表,只要有一個(gè)過期那么這個(gè)連接就會出現(xiàn)超時(shí)。 使用LVS做NAT的默認(rèn)超時(shí)時(shí)間那么我們看一下我們最常用的使用LVS做NAT的默認(rèn)超時(shí)時(shí)間是多少,讓我們來番一下LVS源代碼:
從上面代碼中我們可以看到,LVS通過設(shè)置的timeout_table來設(shè)置轉(zhuǎn)發(fā)表項(xiàng)超時(shí)時(shí)間,而不同的tcp狀態(tài)會有不同的超時(shí)時(shí)間,而默認(rèn)的established的超時(shí)時(shí)間是15 * 60 * HZ也就是15min。也就是說,在默認(rèn)不設(shè)置的情況下,15min中之后這個(gè)連接就會GG。 解決方案好了,了解完原理之后,我們就可以有解決方案了。第一種方案,就是使用短連接。也就是每次請求的時(shí)候新建一個(gè)連接,NAT本身對tcp的FIN包做了處理,一旦發(fā)生四次揮手會自動(dòng)清理表項(xiàng)。用完即回收,即減少了NAT設(shè)備轉(zhuǎn)發(fā)表的壓力也不會產(chǎn)生過一段時(shí)間超時(shí)的問題。但這個(gè)方案有個(gè)缺陷,也是短連接的固有缺陷。由于復(fù)用不了連接,短時(shí)候有海量的請求過來產(chǎn)生大量的短連接,由于TCP 2MSL機(jī)制的存在,client即有可能出現(xiàn)端口耗盡。而端口耗盡后會導(dǎo)致Kernel在搜索可用端口號的時(shí)候性能急劇劣化(每次搜索端口從數(shù)次循環(huán)急劇劣化到每次搜多端口都要數(shù)萬次循環(huán)),這會導(dǎo)致client端的機(jī)器CPU利用率急劇上升,一直陷在搜索端口號的循環(huán)里面導(dǎo)致整體不可用! 如下圖所示: 具體分析可以見筆者的另一篇博客: https://my.oschina.net/alchemystar/blog/4436558 為了解決第一種的方案的問題,我們可以依舊復(fù)用連接,只不過這個(gè)復(fù)用時(shí)間特別短,例如6s之內(nèi)復(fù)用,超過6s的連接就直接丟棄。這樣既能在大量請求涌過來的時(shí)候扛住,又能解決長時(shí)間不用的超時(shí)問題。HttpClient其實(shí)提供這個(gè)機(jī)制,如下所示:
第三種方案,我們可以輪詢每一個(gè)connection發(fā)送心跳包,但這個(gè)實(shí)現(xiàn)起來比較麻煩,遠(yuǎn)沒有上面的HttpClient內(nèi)置方案省心。 還有一個(gè)需要提到的是Http的Keep-alive,連接的保持時(shí)間是在Server端設(shè)置的。而這個(gè)Keep-alive timeout可能 > NAT的清理時(shí)間。對于Client端來說很難約束Server端的配置。所以筆者還是建議采用第二種方案。 總結(jié)NAT雖然大幅度延長了IPV4地址耗盡的時(shí)間,但由于只是打了補(bǔ)丁,它的固有缺陷會導(dǎo)致很多問題。不過我們會根據(jù)遇到問題的原因給出各種解決的方案,從而讓系統(tǒng)穩(wěn)定的運(yùn)行。如果具備相應(yīng)的基礎(chǔ)知識,這個(gè)問題非常容易解決。但如果沒有對整個(gè)通信過程有一個(gè)大致的理解,會無從著手,所以系統(tǒng)化的學(xué)習(xí)非常重要。 轉(zhuǎn)自https://www.cnblogs.com/alchemystar/p/18860539 該文章在 2025/5/7 8:54:29 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |