Linux服務(wù)器時(shí)不時(shí)丟包,它憑啥能精準(zhǔn)解決?
在日常使用中,Linux服務(wù)器丟包會(huì)以多種令人困擾的形式展現(xiàn)出來。當(dāng)你嘗試加載網(wǎng)頁時(shí),原本應(yīng)該瞬間呈現(xiàn)的頁面,進(jìn)度條卻像是陷入了泥沼,緩慢地向前挪動(dòng),圖片長時(shí)間無法顯示,文字也變得斷斷續(xù)續(xù),仿佛在與你玩捉迷藏 。進(jìn)行文件傳輸時(shí),可能會(huì)突然遭遇傳輸中斷,進(jìn)度瞬間歸零,之前的等待和努力付諸東流,一切都得重新開始。
對于依賴 Linux 服務(wù)器的業(yè)務(wù)而言,丟包帶來的負(fù)面影響是極為嚴(yán)重的。在電商平臺(tái)中,丟包可能導(dǎo)致用戶在下單過程中出現(xiàn)卡頓、數(shù)據(jù)提交失敗等情況,不僅破壞了用戶的購物體驗(yàn),還可能直接導(dǎo)致訂單流失,造成經(jīng)濟(jì)損失。在在線教育領(lǐng)域,丟包會(huì)使直播課程畫面卡頓、聲音中斷,嚴(yán)重影響教學(xué)質(zhì)量和學(xué)生的學(xué)習(xí)效果,甚至可能導(dǎo)致學(xué)生和家長對平臺(tái)失去信任。
一、網(wǎng)絡(luò)丟包概述
對于 Linux 系統(tǒng)的使用者來說,網(wǎng)絡(luò)性能的優(yōu)劣直接關(guān)系到系統(tǒng)的整體表現(xiàn)。而在網(wǎng)絡(luò)性能問題中,網(wǎng)絡(luò)丟包堪稱最為棘手的難題之一,它就像隱藏在暗處的殺手,悄無聲息地侵蝕著系統(tǒng)的性能。想象一下,當(dāng)你在服務(wù)器上部署了一個(gè)關(guān)鍵的應(yīng)用服務(wù),滿懷期待地等待用戶的訪問和使用。然而,用戶卻頻繁反饋訪問速度極慢,甚至出現(xiàn)連接中斷的情況。經(jīng)過一番排查,你發(fā)現(xiàn)罪魁禍?zhǔn)拙谷皇蔷W(wǎng)絡(luò)丟包。這時(shí)候,你就會(huì)深刻地意識(shí)到,網(wǎng)絡(luò)丟包問題絕不是一個(gè)可以忽視的小麻煩。
從專業(yè)角度來看,網(wǎng)絡(luò)丟包會(huì)帶來一系列嚴(yán)重的后果。最直觀的就是網(wǎng)絡(luò)延遲的顯著增加。當(dāng)數(shù)據(jù)包在傳輸過程中被丟棄,接收方就無法及時(shí)收到完整的數(shù)據(jù),這就需要發(fā)送方重新發(fā)送這些丟失的數(shù)據(jù)包。重傳的過程無疑會(huì)消耗額外的時(shí)間,導(dǎo)致數(shù)據(jù)傳輸?shù)难舆t大幅上升。在一些對實(shí)時(shí)性要求極高的應(yīng)用場景中,如在線游戲、視頻會(huì)議等,哪怕是幾毫秒的延遲增加都可能帶來極差的用戶體驗(yàn)。在在線游戲中,延遲的增加可能導(dǎo)致玩家的操作出現(xiàn)卡頓,無法及時(shí)響應(yīng)游戲中的各種事件,嚴(yán)重影響游戲的流暢性和競技性;在視頻會(huì)議中,延遲則可能使畫面出現(xiàn)卡頓、聲音不同步等問題,讓溝通變得異常困難。
網(wǎng)絡(luò)丟包還會(huì)導(dǎo)致吞吐量的降低。吞吐量是指單位時(shí)間內(nèi)成功傳輸?shù)臄?shù)據(jù)量,它是衡量網(wǎng)絡(luò)性能的重要指標(biāo)之一。當(dāng)丟包發(fā)生時(shí),一部分?jǐn)?shù)據(jù)無法正常傳輸,這就必然會(huì)導(dǎo)致實(shí)際的吞吐量下降。對于一些大數(shù)據(jù)傳輸?shù)膱鼍埃缥募螺d、數(shù)據(jù)備份等,吞吐量的降低會(huì)大大延長傳輸時(shí)間,降低工作效率。如果你需要從遠(yuǎn)程服務(wù)器下載一個(gè)大型文件,原本預(yù)計(jì)幾個(gè)小時(shí)就能完成的下載任務(wù),可能因?yàn)榫W(wǎng)絡(luò)丟包導(dǎo)致下載時(shí)間延長數(shù)倍,甚至可能因?yàn)閬G包過于嚴(yán)重而導(dǎo)致下載失敗,需要重新開始。
對于基于 TCP 協(xié)議的應(yīng)用來說,丟包更是意味著網(wǎng)絡(luò)擁塞和重傳。TCP 協(xié)議具有可靠性機(jī)制,當(dāng)它檢測到數(shù)據(jù)包丟失時(shí),會(huì)自動(dòng)觸發(fā)重傳機(jī)制,以確保數(shù)據(jù)的完整性。然而,頻繁的重傳不僅會(huì)增加網(wǎng)絡(luò)流量,還會(huì)進(jìn)一步加劇網(wǎng)絡(luò)擁塞,形成一種惡性循環(huán)。在高并發(fā)的網(wǎng)絡(luò)環(huán)境中,這種惡性循環(huán)可能會(huì)導(dǎo)致整個(gè)網(wǎng)絡(luò)的癱瘓,使所有依賴網(wǎng)絡(luò)的應(yīng)用都無法正常運(yùn)行;網(wǎng)絡(luò)丟包對 Linux 系統(tǒng)性能的影響是多方面的,它不僅會(huì)降低用戶體驗(yàn),還會(huì)影響業(yè)務(wù)的正常運(yùn)行,給企業(yè)帶來巨大的損失。
在開始之前,我們先用一張圖解釋 linux 系統(tǒng)接收網(wǎng)絡(luò)報(bào)文的過程:
- 首先網(wǎng)絡(luò)報(bào)文通過物理網(wǎng)線發(fā)送到網(wǎng)卡
- 網(wǎng)絡(luò)驅(qū)動(dòng)程序會(huì)把網(wǎng)絡(luò)中的報(bào)文讀出來放到 ring buffer 中,這個(gè)過程使用 DMA(Direct Memory Access),不需要 CPU 參與
- 內(nèi)核從 ring buffer 中讀取報(bào)文進(jìn)行處理,執(zhí)行 IP 和 TCP/UDP 層的邏輯,最后把報(bào)文放到應(yīng)用程序的 socket buffer 中
- 應(yīng)用程序從 socket buffer 中讀取報(bào)文進(jìn)行處理
圖片
二、丟包可能發(fā)生在哪?
當(dāng)網(wǎng)絡(luò)丟包問題出現(xiàn)時(shí),就如同一場懸疑案件,我們需要抽絲剝繭,從各個(gè)層面去探尋 “兇手”,也就是丟包發(fā)生的原因。在 Linux 系統(tǒng)中,丟包可能發(fā)生在網(wǎng)絡(luò)協(xié)議棧的各個(gè)層次,每個(gè)層次都有其獨(dú)特的丟包原因和排查方法。
2.1 收包流程:數(shù)據(jù)包的 “入境之路”
圖片
當(dāng)網(wǎng)卡接收到報(bào)文時(shí),這場 “入境之旅” 就開啟了。首先,網(wǎng)卡通過 DMA(直接內(nèi)存訪問)技術(shù),以極高的效率將數(shù)據(jù)包拷貝到 RingBuf(環(huán)形緩沖區(qū))中,就好比貨物被快速卸到了一個(gè)臨時(shí)倉庫。緊接著,網(wǎng)卡向 CPU 發(fā)起一個(gè)硬中斷,就像吹響了緊急集合哨,通知 CPU 有數(shù)據(jù)抵達(dá) “國門”。
CPU 迅速響應(yīng),開始執(zhí)行對應(yīng)的硬中斷處理例程,在這個(gè)例程里,它會(huì)將數(shù)據(jù)包的相關(guān)信息放入每 CPU 變量 poll_list 中,隨后觸發(fā)一個(gè)收包軟中斷,把后續(xù)的精細(xì)活兒交給軟中斷去處理。對應(yīng) CPU 的軟中斷線程 ksoftirqd 就登場了,它負(fù)責(zé)處理網(wǎng)絡(luò)包接收軟中斷,具體來說,就是執(zhí)行 net_rx_action () 函數(shù)。
在這個(gè)函數(shù)的 “指揮” 下,數(shù)據(jù)包從 RingBuf 中被小心翼翼地取出,然后進(jìn)入?yún)f(xié)議棧,開啟層層闖關(guān)。從鏈路層開始,檢查報(bào)文合法性,剝?nèi)^、幀尾,接著進(jìn)入網(wǎng)絡(luò)層,判斷包的走向,若是發(fā)往本機(jī),再傳遞到傳輸層。最終,數(shù)據(jù)包被妥妥地放到 socket 的接收隊(duì)列中,等待應(yīng)用層隨時(shí)來收取,至此,數(shù)據(jù)包算是順利 “入境”,完成了它的收包流程。
2.2 發(fā)包流程:數(shù)據(jù)包的 “出境之旅”
圖片
應(yīng)用程序要發(fā)送數(shù)據(jù)時(shí),數(shù)據(jù)包的 “出境之旅” 便啟程了。首先,應(yīng)用程序調(diào)用 Socket API(比如 sendmsg)發(fā)送網(wǎng)絡(luò)包,這一操作觸發(fā)系統(tǒng)調(diào)用,使得數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間,同時(shí),內(nèi)核會(huì)為其分配一個(gè) skb(sk_buff 結(jié)構(gòu)體,它可是數(shù)據(jù)包在內(nèi)核中的 “代言人”,承載著各種關(guān)鍵信息),并將數(shù)據(jù)封裝其中。接著,skb 進(jìn)入?yún)f(xié)議棧,開始自上而下的 “闖關(guān)升級”。
在傳輸層,會(huì)為數(shù)據(jù)添加 TCP 頭或 UDP 頭,進(jìn)行擁塞控制、滑動(dòng)窗口等一系列精細(xì)操作;到了網(wǎng)絡(luò)層,依據(jù)目標(biāo) IP 地址查找路由表,確定下一跳,填充 IP 頭中的源和目標(biāo) IP 地址、TTL 等關(guān)鍵信息,還可能進(jìn)行 skb 切分,同時(shí)要經(jīng)過 netfilter 框架的 “安檢”,判斷是否符合過濾規(guī)則。
之后,在鄰居子系統(tǒng)填充目的 MAC 地址,再進(jìn)入網(wǎng)絡(luò)設(shè)備子系統(tǒng),skb 被放入發(fā)送隊(duì)列 RingBuf 中,等待網(wǎng)卡發(fā)送。網(wǎng)卡發(fā)送完成后,會(huì)向 CPU 發(fā)出一個(gè)硬中斷,告知 “任務(wù)完成”,這個(gè)硬中斷又會(huì)觸發(fā)軟中斷,在軟中斷處理函數(shù)中,對 RingBuf 進(jìn)行清理,把已經(jīng)發(fā)送成功的數(shù)據(jù)包殘留信息清除掉,就像清理運(yùn)輸后的車廂,為下一次運(yùn)輸做好準(zhǔn)備,至此,數(shù)據(jù)包順利 “出境”,完成了它的發(fā)包流程。
三、丟包原因深度剖析
3.1 物理鏈路層
在網(wǎng)絡(luò)的物理鏈路層,有諸多因素可能導(dǎo)致 Linux 服務(wù)器丟包。網(wǎng)線就像是網(wǎng)絡(luò)世界中的高速公路,承擔(dān)著數(shù)據(jù)傳輸?shù)闹厝?。然而,隨著時(shí)間的推移,網(wǎng)線會(huì)逐漸老化,就像年久失修的高速公路,路面變得坑坑洼洼。老化的網(wǎng)線內(nèi)部結(jié)構(gòu)會(huì)發(fā)生變化,金屬導(dǎo)線可能會(huì)出現(xiàn)氧化、斷裂等情況,這會(huì)大大增加信號(hào)在傳輸過程中的衰減 。當(dāng)信號(hào)衰減到一定程度時(shí),數(shù)據(jù)就無法被準(zhǔn)確地傳輸,從而導(dǎo)致丟包現(xiàn)象的發(fā)生。
網(wǎng)絡(luò)設(shè)備之間的接口連接也至關(guān)重要。如果接口松動(dòng),就好比高速公路上的連接點(diǎn)出現(xiàn)了松動(dòng),數(shù)據(jù)傳輸?shù)耐ǖ谰蜁?huì)變得不穩(wěn)定。接口松動(dòng)可能會(huì)導(dǎo)致接觸不良,信號(hào)時(shí)有時(shí)無,數(shù)據(jù)包在傳輸過程中就容易丟失 。在一些服務(wù)器機(jī)房中,由于設(shè)備的頻繁插拔或者震動(dòng),接口松動(dòng)的情況時(shí)有發(fā)生,這也是需要重點(diǎn)排查的一個(gè)點(diǎn)。
網(wǎng)絡(luò)設(shè)備如路由器、交換機(jī)等,在網(wǎng)絡(luò)中扮演著交通樞紐的角色。它們負(fù)責(zé)轉(zhuǎn)發(fā)數(shù)據(jù)包,確保數(shù)據(jù)能夠準(zhǔn)確地到達(dá)目的地。一旦這些設(shè)備出現(xiàn)故障,就像交通樞紐發(fā)生了擁堵或者癱瘓,整個(gè)網(wǎng)絡(luò)的通信都會(huì)受到嚴(yán)重影響。路由器的硬件故障,如內(nèi)存損壞、CPU 過熱等,可能會(huì)導(dǎo)致其無法正常處理數(shù)據(jù)包,從而引發(fā)丟包 。交換機(jī)的端口故障、背板帶寬不足等問題,也會(huì)使數(shù)據(jù)包在交換過程中丟失。
當(dāng)鏈路層由于緩沖區(qū)溢出等原因?qū)е戮W(wǎng)卡丟包時(shí),Linux 會(huì)在網(wǎng)卡收發(fā)數(shù)據(jù)的統(tǒng)計(jì)信息中記錄下收發(fā)錯(cuò)誤的次數(shù)。鏈路層是網(wǎng)絡(luò)通信的基礎(chǔ),它負(fù)責(zé)將網(wǎng)絡(luò)層傳來的數(shù)據(jù)封裝成幀,并通過物理介質(zhì)進(jìn)行傳輸。鏈路層丟包通常是由于硬件故障、網(wǎng)絡(luò)擁塞或者配置錯(cuò)誤等原因?qū)е碌?。?dāng)鏈路層由于緩沖區(qū)溢出等原因?qū)е戮W(wǎng)卡丟包時(shí),Linux 會(huì)在網(wǎng)卡收發(fā)數(shù)據(jù)的統(tǒng)計(jì)信息中記錄下收發(fā)錯(cuò)誤的次數(shù)。
我們可以通過 ethtool 或者 netstat 命令,來查看網(wǎng)卡的丟包記錄:
netstat -i
Kernel Interface table
Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 100 31 0 0 0 8 0 0 0 BMRU
lo 65536 0 0 0 0 0 0 0 0 LRU
RX-OK、RX-ERR、RX-DRP、RX-OVR ,分別表示接收時(shí)的總包數(shù)、總錯(cuò)誤數(shù)、進(jìn)入 Ring Buffer 后因其他原因(如內(nèi)存不足)導(dǎo)致的丟包數(shù)以及 Ring Buffer 溢出導(dǎo)致的丟包數(shù)。
TX-OK、TX-ERR、TX-DRP、TX-OVR 也代表類似的含義,只不過是指發(fā)送時(shí)對應(yīng)的各個(gè)指標(biāo)。
這里我們沒有發(fā)現(xiàn)任何錯(cuò)誤,說明虛擬網(wǎng)卡沒有丟包。不過要注意,如果用 tc 等工具配置了 QoS,那么 tc 規(guī)則導(dǎo)致的丟包,就不會(huì)包含在網(wǎng)卡的統(tǒng)計(jì)信息中。所以接下來,我們還要檢查一下 eth0 上是否配置了 tc 規(guī)則,并查看有沒有丟包。添加 -s 選項(xiàng),以輸出統(tǒng)計(jì)信息:
tc -s qdisc show dev eth0
qdisc netem 800d: root refcnt 2 limit 1000 loss 30%
Sent 432 bytes 8 pkt (dropped 4, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
可以看到, eth0 上配置了一個(gè)網(wǎng)絡(luò)模擬排隊(duì)規(guī)則(qdisc netem),并且配置了丟包率為 30%(loss 30%)。再看后面的統(tǒng)計(jì)信息,發(fā)送了 8 個(gè)包,但是丟了 4個(gè)??磥響?yīng)該就是這里導(dǎo)致 Nginx 回復(fù)的響應(yīng)包被 netem 模塊給丟了。
既然發(fā)現(xiàn)了問題,解決方法也很簡單,直接刪掉 netem 模塊就可以了。執(zhí)行下面的命令,刪除 tc 中的 netem 模塊:
tc qdisc del dev eth0 root netem loss 30%
刪除后,重新執(zhí)行之前的 hping3 命令,看看現(xiàn)在還有沒有問題:
hping3 -c 10 -S -p 80 192.168.0.30
HPING 192.168.0.30 (eth0 192.168.0.30): S set, 40 headers + 0 data bytes
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=0 win=5120 rtt=7.9 ms
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=2 win=5120 rtt=1003.8 ms
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=5 win=5120 rtt=7.6 ms
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=6 win=5120 rtt=7.4 ms
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=9 win=5120 rtt=3.0 ms
--- 192.168.0.30 hping statistic ---
10 packets transmitted, 5 packets received, 50% packet loss
round-trip min/avg/max = 3.0/205.9/1003.8 ms
不幸的是,從 hping3 的輸出中可以看到還是 50% 的丟包,RTT 的波動(dòng)也仍舊很大,從 3ms 到 1s。顯然,問題還是沒解決,丟包還在繼續(xù)發(fā)生。不過,既然鏈路層已經(jīng)排查完了,我們就繼續(xù)向上層分析,看看網(wǎng)絡(luò)層和傳輸層有沒有問題。
3.2 網(wǎng)卡及驅(qū)動(dòng)層面
網(wǎng)卡硬件故障:網(wǎng)卡作為服務(wù)器與網(wǎng)絡(luò)連接的關(guān)鍵硬件,其狀態(tài)直接影響著網(wǎng)絡(luò)通信的質(zhì)量。當(dāng)網(wǎng)卡芯片過熱時(shí),就像電腦在長時(shí)間高負(fù)荷運(yùn)行后會(huì)變得卡頓一樣,網(wǎng)卡的工作也會(huì)變得不穩(wěn)定 。過熱可能會(huì)導(dǎo)致芯片內(nèi)部的電子元件性能下降,無法準(zhǔn)確地接收或發(fā)送數(shù)據(jù)包,從而造成丟包。如果網(wǎng)卡受到物理損壞,如接口損壞、電路板短路等,那么數(shù)據(jù)傳輸就會(huì)中斷,丟包現(xiàn)象也會(huì)隨之出現(xiàn) 。在一些服務(wù)器中,由于散熱不良或者硬件質(zhì)量問題,網(wǎng)卡過熱和損壞的情況并不少見。
驅(qū)動(dòng)程序異常:驅(qū)動(dòng)程序就像是網(wǎng)卡的指揮官,負(fù)責(zé)協(xié)調(diào)網(wǎng)卡與操作系統(tǒng)之間的通信。如果驅(qū)動(dòng)版本與內(nèi)核不兼容,就好比指揮官和士兵之間的指令傳達(dá)出現(xiàn)了錯(cuò)誤,網(wǎng)卡可能無法正常工作。驅(qū)動(dòng)版本過舊,可能不支持新的內(nèi)核功能,或者存在一些已知的漏洞,這都可能導(dǎo)致在處理網(wǎng)絡(luò)數(shù)據(jù)時(shí)出現(xiàn)丟包的情況 。驅(qū)動(dòng)程序損壞也會(huì)影響網(wǎng)卡的正常運(yùn)行。在系統(tǒng)更新、病毒感染等情況下,驅(qū)動(dòng)程序可能會(huì)被破壞,導(dǎo)致無法正確處理中斷請求,數(shù)據(jù)包也就無法及時(shí)被處理和傳輸,最終造成丟包。
3.3 網(wǎng)絡(luò)協(xié)議棧各層問題
①鏈路層:在鏈路層,幀校驗(yàn)失敗是導(dǎo)致丟包的常見原因之一。每個(gè)網(wǎng)絡(luò)幀都包含一個(gè)校驗(yàn)和,用于驗(yàn)證幀數(shù)據(jù)在傳輸過程中是否被篡改 。當(dāng)接收方接收到幀時(shí),會(huì)根據(jù)校驗(yàn)和算法重新計(jì)算校驗(yàn)和,并與幀中攜帶的校驗(yàn)和進(jìn)行對比。如果兩者不一致,就說明幀數(shù)據(jù)可能被損壞,接收方會(huì)將該幀丟棄,從而導(dǎo)致丟包。
QoS(Quality of Service,服務(wù)質(zhì)量)配置不當(dāng)也會(huì)引發(fā)丟包。QoS 的目的是為不同類型的網(wǎng)絡(luò)流量提供不同的服務(wù)質(zhì)量保證 。當(dāng) QoS 配置不合理時(shí),比如將某些重要的數(shù)據(jù)包設(shè)置為低優(yōu)先級,這些數(shù)據(jù)包在網(wǎng)絡(luò)擁塞時(shí)可能會(huì)被優(yōu)先丟棄,從而導(dǎo)致丟包現(xiàn)象的發(fā)生。在一些企業(yè)網(wǎng)絡(luò)中,由于對 QoS 的配置不夠精細(xì),常常會(huì)出現(xiàn)這種情況。
②網(wǎng)絡(luò)層:路由錯(cuò)誤在網(wǎng)絡(luò)層是一個(gè)比較嚴(yán)重的問題。路由就像是網(wǎng)絡(luò)中的導(dǎo)航系統(tǒng),負(fù)責(zé)為數(shù)據(jù)包指引正確的傳輸路徑 。當(dāng)路由表出現(xiàn)錯(cuò)誤時(shí),比如路由條目丟失、錯(cuò)誤的路由指向等,數(shù)據(jù)包就無法到達(dá)正確的目的地,最終會(huì)被丟棄。如果服務(wù)器的路由表中沒有到某個(gè)目標(biāo)網(wǎng)絡(luò)的正確路由,那么發(fā)往該網(wǎng)絡(luò)的數(shù)據(jù)包就會(huì)被丟棄,這就像導(dǎo)航系統(tǒng)把你指引到了錯(cuò)誤的方向,你永遠(yuǎn)也無法到達(dá)目的地。
MTU(Maximum Transmission Unit,最大傳輸單元)設(shè)置不合理也會(huì)引發(fā)丟包。MTU 是指網(wǎng)絡(luò)中能夠傳輸?shù)淖畲髷?shù)據(jù)包大小 。當(dāng)一個(gè)數(shù)據(jù)包的大小超過了鏈路的 MTU 時(shí),它就需要被分片成多個(gè)小數(shù)據(jù)包進(jìn)行傳輸。在傳輸過程中,如果某個(gè)分片丟失,那么整個(gè)數(shù)據(jù)包就無法被正確重組,從而導(dǎo)致丟包。在一些跨網(wǎng)絡(luò)的通信中,由于不同網(wǎng)絡(luò)的 MTU 可能不同,如果沒有進(jìn)行合理的設(shè)置和調(diào)整,就很容易出現(xiàn)這種情況。
③傳輸層:在傳輸層,端口未監(jiān)聽是一個(gè)常見的丟包原因。每個(gè)網(wǎng)絡(luò)應(yīng)用都需要監(jiān)聽特定的端口來接收數(shù)據(jù) 。如果一個(gè)應(yīng)用程序沒有正確監(jiān)聽其對應(yīng)的端口,那么發(fā)送到該端口的數(shù)據(jù)包就無法被接收,最終會(huì)被丟棄。當(dāng)一個(gè) Web 服務(wù)器沒有監(jiān)聽 80 端口(HTTP 協(xié)議默認(rèn)端口)時(shí),客戶端發(fā)送的 HTTP 請求數(shù)據(jù)包就會(huì)被丟棄,導(dǎo)致無法訪問該網(wǎng)站。
資源占用超過內(nèi)核限制也會(huì)引發(fā)丟包。系統(tǒng)內(nèi)核為每個(gè)網(wǎng)絡(luò)連接分配了一定的資源,如內(nèi)存、文件描述符等 。當(dāng)連接數(shù)過多,或者某個(gè)連接占用了過多的資源時(shí),系統(tǒng)就可能無法為新的連接分配足夠的資源,導(dǎo)致新的數(shù)據(jù)包無法被處理,從而造成丟包。在高并發(fā)的網(wǎng)絡(luò)環(huán)境中,如大型電商網(wǎng)站的促銷活動(dòng)期間,由于大量用戶同時(shí)訪問服務(wù)器,很容易出現(xiàn)這種情況。
執(zhí)行 netstat -s 命令,可以看到協(xié)議的收發(fā)匯總,以及錯(cuò)誤信息:
netstat -s
#輸出
Ip:
Forwarding: 1 //開啟轉(zhuǎn)發(fā)
31 total packets received //總收包數(shù)
0 forwarded //轉(zhuǎn)發(fā)包數(shù)
0 incoming packets discarded //接收丟包數(shù)
25 incoming packets delivered //接收的數(shù)據(jù)包數(shù)
15 requests sent out //發(fā)出的數(shù)據(jù)包數(shù)
Icmp:
0 ICMP messages received //收到的ICMP包數(shù)
0 input ICMP message failed //收到ICMP失敗數(shù)
ICMP input histogram:
0 ICMP messages sent //ICMP發(fā)送數(shù)
0 ICMP messages failed //ICMP失敗數(shù)
ICMP output histogram:
Tcp:
0 active connection openings //主動(dòng)連接數(shù)
0 passive connection openings //被動(dòng)連接數(shù)
11 failed connection attempts //失敗連接嘗試數(shù)
0 connection resets received //接收的連接重置數(shù)
0 connections established //建立連接數(shù)
25 segments received //已接收報(bào)文數(shù)
21 segments sent out //已發(fā)送報(bào)文數(shù)
4 segments retransmitted //重傳報(bào)文數(shù)
0 bad segments received //錯(cuò)誤報(bào)文數(shù)
0 resets sent //發(fā)出的連接重置數(shù)
Udp:
0 packets received
...
TcpExt:
11 resets received for embryonic SYN_RECV sockets //半連接重置數(shù)
0 packet headers predicted
TCPTimeouts: 7 //超時(shí)數(shù)
TCPSynRetrans: 4 //SYN重傳數(shù)
...
etstat 匯總了 IP、ICMP、TCP、UDP 等各種協(xié)議的收發(fā)統(tǒng)計(jì)信息。不過,我們的目的是排查丟包問題,所以這里主要觀察的是錯(cuò)誤數(shù)、丟包數(shù)以及重傳數(shù)??梢钥吹剑挥?TCP 協(xié)議發(fā)生了丟包和重傳,分別是:
- 11 次連接失敗重試(11 failed connection attempts)
- 4 次重傳(4 segments retransmitted)
- 11 次半連接重置(11 resets received for embryonic SYN_RECV sockets)
- 4 次 SYN 重傳(TCPSynRetrans)
- 7 次超時(shí)(TCPTimeouts)
這個(gè)結(jié)果告訴我們,TCP 協(xié)議有多次超時(shí)和失敗重試,并且主要錯(cuò)誤是半連接重置。換句話說,主要的失敗,都是三次握手失敗。不過,雖然在這兒看到了這么多失敗,但具體失敗的根源還是無法確定。
3.4 系統(tǒng)與應(yīng)用層面
系統(tǒng)負(fù)載過高:當(dāng) Linux 服務(wù)器的 CPU、內(nèi)存等資源被大量占用時(shí),就像一個(gè)人同時(shí)要處理多項(xiàng)繁重的任務(wù),會(huì)變得力不從心,網(wǎng)絡(luò)數(shù)據(jù)的處理也會(huì)受到影響。CPU 是服務(wù)器的核心處理器,當(dāng)它被大量占用時(shí),系統(tǒng)無法及時(shí)響應(yīng)網(wǎng)絡(luò)請求 。網(wǎng)絡(luò)數(shù)據(jù)包在緩沖區(qū)中等待處理的時(shí)間會(huì)變長,超過一定時(shí)間后,這些數(shù)據(jù)包就會(huì)被丟棄。內(nèi)存不足也會(huì)導(dǎo)致丟包。當(dāng)內(nèi)存被大量占用時(shí),系統(tǒng)可能會(huì)開始使用交換空間(Swap),而交換空間的讀寫速度比內(nèi)存慢很多,這會(huì)導(dǎo)致網(wǎng)絡(luò)數(shù)據(jù)的處理速度大幅下降,最終造成丟包。在一些運(yùn)行著多個(gè)大型應(yīng)用程序的服務(wù)器中,由于資源競爭激烈,系統(tǒng)負(fù)載過高的情況經(jīng)常發(fā)生。
應(yīng)用程序異常:應(yīng)用程序自身的問題也可能導(dǎo)致丟包。內(nèi)存泄漏是一個(gè)常見的應(yīng)用程序問題,就像一個(gè)容器有漏洞,水會(huì)不斷地漏出去 。當(dāng)應(yīng)用程序發(fā)生內(nèi)存泄漏時(shí),它會(huì)不斷地占用內(nèi)存,導(dǎo)致系統(tǒng)可用內(nèi)存越來越少。隨著可用內(nèi)存的減少,網(wǎng)絡(luò)數(shù)據(jù)的處理會(huì)受到影響,最終可能導(dǎo)致丟包。應(yīng)用程序的邏輯錯(cuò)誤也會(huì)導(dǎo)致丟包。如果應(yīng)用程序在處理網(wǎng)絡(luò)數(shù)據(jù)時(shí)存在邏輯錯(cuò)誤,比如錯(cuò)誤地解析數(shù)據(jù)包、無法正確地發(fā)送響應(yīng)等,那么就會(huì)導(dǎo)致數(shù)據(jù)包的丟失。在一些開發(fā)不完善的應(yīng)用程序中,常常會(huì)出現(xiàn)這些邏輯錯(cuò)誤。
3.5 防火墻與 iptables 規(guī)則
防火墻在網(wǎng)絡(luò)安全中起著重要的防護(hù)作用,但如果策略設(shè)置過于嚴(yán)格,就像一個(gè)過于嚴(yán)厲的門衛(wèi),可能會(huì)把一些合法的網(wǎng)絡(luò)數(shù)據(jù)包當(dāng)作危險(xiǎn)分子攔截在外 。當(dāng)防火墻的規(guī)則設(shè)置不當(dāng),將正常的網(wǎng)絡(luò)通信流量誤判為攻擊行為時(shí),就會(huì)導(dǎo)致這些數(shù)據(jù)包被丟棄,從而引發(fā)丟包現(xiàn)象。在一些企業(yè)網(wǎng)絡(luò)中,為了加強(qiáng)網(wǎng)絡(luò)安全,可能會(huì)設(shè)置非常嚴(yán)格的防火墻策略,但這也可能會(huì)影響到正常的業(yè)務(wù)通信。
iptables 是 Linux 系統(tǒng)中常用的防火墻工具,它通過規(guī)則來控制網(wǎng)絡(luò)數(shù)據(jù)包的進(jìn)出 。當(dāng) iptables 規(guī)則錯(cuò)誤配置時(shí),比如 DROP 規(guī)則設(shè)置不當(dāng),會(huì)使正常的數(shù)據(jù)包被錯(cuò)誤地丟棄。如果錯(cuò)誤地設(shè)置了 iptables 規(guī)則,將某個(gè)應(yīng)用程序的通信端口設(shè)置為 DROP,那么該應(yīng)用程序的所有網(wǎng)絡(luò)數(shù)據(jù)包都會(huì)被丟棄,導(dǎo)致無法正常通信。在配置 iptables 規(guī)則時(shí),需要謹(jǐn)慎操作,確保規(guī)則的正確性,避免因規(guī)則錯(cuò)誤而導(dǎo)致丟包。
首先,除了網(wǎng)絡(luò)層和傳輸層的各種協(xié)議,iptables 和內(nèi)核的連接跟蹤機(jī)制也可能會(huì)導(dǎo)致丟包。所以,這也是發(fā)生丟包問題時(shí)我們必須要排查的一個(gè)因素。
先來看看連接跟蹤,要確認(rèn)是不是連接跟蹤導(dǎo)致的問題,只需要對比當(dāng)前的連接跟蹤數(shù)和最大連接跟蹤數(shù)即可。
# 主機(jī)終端中查詢內(nèi)核配置
$ sysctl net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_max = 262144
$ sysctl net.netfilter.nf_conntrack_count
net.netfilter.nf_conntrack_count = 182
可以看到,連接跟蹤數(shù)只有 182,而最大連接跟蹤數(shù)則是 262144。顯然,這里的丟包,不可能是連接跟蹤導(dǎo)致的。
接著,再來看 iptables?;仡櫼幌?iptables 的原理,它基于 Netfilter 框架,通過一系列的規(guī)則,對網(wǎng)絡(luò)數(shù)據(jù)包進(jìn)行過濾(如防火墻)和修改(如 NAT)。這些 iptables 規(guī)則,統(tǒng)一管理在一系列的表中,包括 filter、nat、mangle(用于修改分組數(shù)據(jù)) 和 raw(用于原始數(shù)據(jù)包)等。而每張表又可以包括一系列的鏈,用于對 iptables 規(guī)則進(jìn)行分組管理。
對于丟包問題來說,最大的可能就是被 filter 表中的規(guī)則給丟棄了。要弄清楚這一點(diǎn),就需要我們確認(rèn),那些目標(biāo)為 DROP 和 REJECT 等會(huì)棄包的規(guī)則,有沒有被執(zhí)行到??梢灾苯硬樵?DROP 和 REJECT 等規(guī)則的統(tǒng)計(jì)信息,看看是否為0。如果不是 0 ,再把相關(guān)的規(guī)則拎出來進(jìn)行分析。
iptables -t filter -nvL
#輸出
Chain INPUT (policy ACCEPT 25 packets, 1000 bytes)
pkts bytes target prot opt in out source destination
6 240 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.29999999981
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 15 packets, 660 bytes)
pkts bytes target prot opt in out source destination
6 264 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.29999999981
從 iptables 的輸出中,你可以看到,兩條 DROP 規(guī)則的統(tǒng)計(jì)數(shù)值不是 0,它們分別在INPUT 和 OUTPUT 鏈中。這兩條規(guī)則實(shí)際上是一樣的,指的是使用 statistic 模塊,進(jìn)行隨機(jī) 30% 的丟包。0.0.0.0/0 表示匹配所有的源 IP 和目的 IP,也就是會(huì)對所有包都進(jìn)行隨機(jī) 30% 的丟包。看起來,這應(yīng)該就是導(dǎo)致部分丟包的“罪魁禍?zhǔn)住绷恕?/span>
執(zhí)行下面的兩條 iptables 命令,刪除這兩條 DROP 規(guī)則。
root@nginx:/# iptables -t filter -D INPUT -m statistic --mode random --probability 0.30 -j DROP
root@nginx:/# iptables -t filter -D OUTPUT -m statistic --mode random --probability 0.30 -j DROP
再次執(zhí)行剛才的 hping3 命令,看看現(xiàn)在是否正常。
hping3 -c 10 -S -p 80 192.168.0.30
#輸出
HPING 192.168.0.30 (eth0 192.168.0.30): S set, 40 headers + 0 data bytes
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=0 win=5120 rtt=11.9 ms
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=1 win=5120 rtt=7.8 ms
...
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=9 win=5120 rtt=15.0 ms
--- 192.168.0.30 hping statistic ---
10 packets transmitted, 10 packets received, 0% packet loss
round-trip min/avg/max = 3.3/7.9/15.0 ms
這次輸出你可以看到,現(xiàn)在已經(jīng)沒有丟包了,并且延遲的波動(dòng)變化也很小??磥?,丟包問題應(yīng)該已經(jīng)解決了。
不過,到目前為止,我們一直使用的 hping3 工具,只能驗(yàn)證案例 Nginx 的 80 端口處于正常監(jiān)聽狀態(tài),卻還沒有訪問 Nginx 的 HTTP 服務(wù)。所以,不要匆忙下結(jié)論結(jié)束這次優(yōu)化,我們還需要進(jìn)一步確認(rèn),Nginx 能不能正常響應(yīng) HTTP 請求。我們繼續(xù)在終端二中,執(zhí)行如下的 curl 命令,檢查 Nginx 對 HTTP 請求的響應(yīng):
$ curl --max-time 3 http://192.168.0.30
curl: (28) Operation timed out after 3000 milliseconds with 0 bytes received
奇怪,hping3 的結(jié)果顯示Nginx 的 80 端口是正常狀態(tài),為什么還是不能正常響應(yīng) HTTP 請求呢?別忘了,我們還有個(gè)大殺器——抓包操作??磥碛斜匾グ纯戳耍ú榭?.5)。
四、實(shí)用排查工具與方法
4.1 ping命令初步檢測
ping 命令是我們排查網(wǎng)絡(luò)丟包問題時(shí)最常用的工具之一,就像是網(wǎng)絡(luò)世界中的 “偵察兵”,能幫助我們快速了解網(wǎng)絡(luò)的連通性和丟包率 。它的工作原理很簡單,通過向目標(biāo) IP 地址發(fā)送 ICMP(Internet Control Message Protocol)回顯請求報(bào)文,并等待接收回顯應(yīng)答報(bào)文,以此來判斷網(wǎng)絡(luò)是否暢通 。如果網(wǎng)絡(luò)存在丟包問題,我們就能從 ping 命令的返回結(jié)果中發(fā)現(xiàn)一些蛛絲馬跡。
在 Linux 系統(tǒng)中使用 ping 命令非常方便。打開終端,輸入 “ping 目標(biāo) IP 地址”,然后按下回車鍵,就能開始測試了 。比如我們要測試與百度服務(wù)器的連接情況,就可以輸入 “ping www.baidu.com” 。命令執(zhí)行后,會(huì)不斷地向百度服務(wù)器發(fā)送 ICMP 請求,并顯示每次請求的響應(yīng)時(shí)間和結(jié)果。在返回結(jié)果中,我們重點(diǎn)關(guān)注 “丟包率” 這一指標(biāo)。如果顯示 “丟失 = 0”,那就說明在測試過程中沒有出現(xiàn)丟包現(xiàn)象,網(wǎng)絡(luò)連接比較穩(wěn)定;如果顯示 “丟失 = X”(X 大于 0),則表示有 X 個(gè)數(shù)據(jù)包在傳輸過程中丟失了,丟包率就是(丟失的數(shù)據(jù)包數(shù) ÷ 發(fā)送的數(shù)據(jù)包總數(shù))× 100% 。
除了基本的使用方法,ping 命令還有一些常用參數(shù),可以幫助我們更全面地測試網(wǎng)絡(luò)?!?c” 參數(shù)用于指定發(fā)送的數(shù)據(jù)包數(shù)量 。使用 “ping -c 50 www.baidu.com”,表示只向百度服務(wù)器發(fā)送 50 個(gè) ICMP 請求,這樣可以在短時(shí)間內(nèi)快速獲取一定數(shù)量的測試數(shù)據(jù),方便我們進(jìn)行分析?!?i” 參數(shù)用于指定每次發(fā)送數(shù)據(jù)包的時(shí)間間隔 。比如 “ping -i 2 www.baidu.com”,表示每隔 2 秒發(fā)送一個(gè)數(shù)據(jù)包,通過調(diào)整這個(gè)時(shí)間間隔,我們可以模擬不同的網(wǎng)絡(luò)負(fù)載情況。“-s” 參數(shù)用于指定發(fā)送數(shù)據(jù)包的大小 。默認(rèn)情況下,ping 命令發(fā)送的數(shù)據(jù)包大小為 64 字節(jié),我們可以使用 “ping -s 1000 www.baidu.com” 來發(fā)送大小為 1000 字節(jié)的數(shù)據(jù)包,這樣可以測試網(wǎng)絡(luò)在傳輸大尺寸數(shù)據(jù)包時(shí)的表現(xiàn)。
4.2 MTR綜合診斷
MTR(My Traceroute)工具是網(wǎng)絡(luò)診斷中的一把 “瑞士軍刀”,它巧妙地結(jié)合了 ping 和 traceroute 的功能,能為我們提供更加全面和深入的網(wǎng)絡(luò)診斷信息 。與 ping 命令只能簡單地檢測網(wǎng)絡(luò)連通性和丟包率不同,MTR 不僅可以實(shí)時(shí)顯示數(shù)據(jù)包在網(wǎng)絡(luò)傳輸過程中的路徑,還能展示每一跳的延遲時(shí)間和丟包情況,就像一個(gè)精準(zhǔn)的導(dǎo)航儀,幫助我們準(zhǔn)確定位丟包發(fā)生的具體節(jié)點(diǎn)。
在 Linux 系統(tǒng)中安裝 MTR 工具也很簡單。對于基于 Debian 或 Ubuntu 的系統(tǒng),我們可以在終端中輸入 “sudo apt-get install mtr” 來進(jìn)行安裝;對于基于 Red Hat 或 CentOS 的系統(tǒng),則可以使用 “sudo yum install mtr” 命令來完成安裝 。安裝完成后,就可以使用 MTR 工具進(jìn)行網(wǎng)絡(luò)診斷了。
使用MTR工具時(shí),只需在終端中輸入 “mtr目標(biāo) IP 地址”,然后按下回車鍵即可 。例如,我們要診斷到騰訊服務(wù)器的網(wǎng)絡(luò)情況,輸入 “mtr www.tencent.com” 。MTR 工具會(huì)立即開始工作,向目標(biāo)服務(wù)器發(fā)送一系列的數(shù)據(jù)包,并實(shí)時(shí)顯示數(shù)據(jù)包在傳輸過程中經(jīng)過的每一個(gè)節(jié)點(diǎn)的信息 。在MTR 的輸出結(jié)果中,我們可以看到多個(gè)重要的信息列。
“Host” 列顯示的是節(jié)點(diǎn)的IP地址和域名,通過它我們可以了解數(shù)據(jù)包經(jīng)過了哪些網(wǎng)絡(luò)設(shè)備;“Loss%”列表示每個(gè)節(jié)點(diǎn)的丟包率,這是我們最關(guān)注的指標(biāo)之一,如果某個(gè)節(jié)點(diǎn)的丟包率較高,那就說明丟包問題很可能發(fā)生在這個(gè)節(jié)點(diǎn)上;“Snt”列表示每秒發(fā)送的數(shù)據(jù)包數(shù);“Last”“Avg”“Best”“Wrst”列分別顯示最近一次、平均、最短和最長的探測延遲時(shí)間,通過這些時(shí)間數(shù)據(jù),我們可以判斷網(wǎng)絡(luò)的延遲情況和穩(wěn)定性 。
在實(shí)際使用 MTR 工具時(shí),我們還可以結(jié)合一些參數(shù)來滿足不同的診斷需求?!?r” 參數(shù)可以讓 MTR 以報(bào)告模式顯示輸出結(jié)果 。使用 “mtr -r www.tencent.com”,MTR 會(huì)在檢測完成后,以報(bào)告的形式展示所有節(jié)點(diǎn)的信息,方便我們進(jìn)行查看和分析?!?n” 參數(shù)用于禁止對 IP 地址進(jìn)行域名反解析 。有些情況下,域名反解析可能會(huì)花費(fèi)一些時(shí)間,影響診斷效率,使用 “mtr -n www.tencent.com” 可以直接顯示 IP 地址,加快診斷速度?!?c” 參數(shù)可以指定探測的次數(shù) 。例如 “mtr -c 50 www.tencent.com”,表示只進(jìn)行 50 次探測,這樣可以在特定次數(shù)的測試后獲取結(jié)果,適用于一些對測試次數(shù)有要求的場景。
4.3 netstat查看網(wǎng)絡(luò)狀態(tài)
netstat 命令是 Linux 系統(tǒng)中一個(gè)功能強(qiáng)大的網(wǎng)絡(luò)工具,它就像是網(wǎng)絡(luò)世界的 “觀察者”,可以用于查看網(wǎng)絡(luò)連接、路由表、網(wǎng)絡(luò)接口統(tǒng)計(jì)信息等,幫助我們從多個(gè)角度了解網(wǎng)絡(luò)的運(yùn)行狀態(tài),進(jìn)而排查網(wǎng)絡(luò)層和傳輸層的丟包原因。
通過 netstat 命令,我們可以清晰地看到當(dāng)前系統(tǒng)的網(wǎng)絡(luò)連接情況。在終端中輸入“netstat -a”,就能列出所有的網(wǎng)絡(luò)連接,包括處于監(jiān)聽狀態(tài)的連接和已經(jīng)建立的連接 。“-a”參數(shù)表示顯示所有連接,無論是正在監(jiān)聽的還是已經(jīng)建立的。在輸出結(jié)果中,“Proto” 列表示套接字使用的協(xié)議,如 tcp、udp 等;“Local Address” 列顯示本地地址和端口號(hào);“Foreign Address” 列顯示遠(yuǎn)程地址和端口號(hào);“State” 列表示連接的狀態(tài),常見的狀態(tài)有 LISTEN(監(jiān)聽)、ESTABLISHED(已建立連接)、TIME_WAIT(等待一段時(shí)間后關(guān)閉連接)等 。通過查看這些信息,我們可以判斷網(wǎng)絡(luò)連接是否正常,是否存在異常的連接狀態(tài),從而找出可能導(dǎo)致丟包的原因。
netstat 命令還可以用來查看路由表,了解數(shù)據(jù)包在網(wǎng)絡(luò)中的傳輸路徑。在終端中輸入 “netstat -r”,就能顯示內(nèi)核的 IP 路由表 ?!?r” 參數(shù)用于顯示路由表。路由表中包含了網(wǎng)絡(luò)目的地址、子網(wǎng)掩碼、網(wǎng)關(guān)、接口等信息 。通過分析路由表,我們可以檢查路由是否正確,是否存在路由錯(cuò)誤導(dǎo)致數(shù)據(jù)包無法正確轉(zhuǎn)發(fā)的情況。如果發(fā)現(xiàn)路由表中有錯(cuò)誤的路由條目,比如路由指向了錯(cuò)誤的網(wǎng)關(guān),就需要及時(shí)進(jìn)行修正,以解決丟包問題。
查看網(wǎng)絡(luò)接口統(tǒng)計(jì)信息也是 netstat 命令的重要功能之一。在終端中輸入 “netstat -s”,可以打印出網(wǎng)絡(luò)協(xié)議的統(tǒng)計(jì)數(shù)據(jù) ?!?s” 參數(shù)用于打印統(tǒng)計(jì)數(shù)據(jù)。在輸出結(jié)果中,我們可以看到各個(gè)網(wǎng)絡(luò)協(xié)議(如 TCP、UDP、ICMP 等)的數(shù)據(jù)包發(fā)送和接收數(shù)量、錯(cuò)誤數(shù)量、丟棄數(shù)量等信息 。通過分析這些統(tǒng)計(jì)信息,我們可以判斷網(wǎng)絡(luò)接口是否正常工作,是否存在大量的數(shù)據(jù)包被丟棄的情況。如果發(fā)現(xiàn)某個(gè)協(xié)議的丟棄數(shù)據(jù)包數(shù)量較多,就需要進(jìn)一步排查原因,可能是網(wǎng)絡(luò)接口故障、網(wǎng)絡(luò)擁塞或者其他問題導(dǎo)致的。
4.4 ethtool檢查網(wǎng)卡狀態(tài)
ethtool 工具是專門用于管理和診斷以太網(wǎng)卡的利器,在排查 Linux 服務(wù)器丟包問題時(shí),它能幫助我們深入了解網(wǎng)卡的配置和運(yùn)行狀態(tài),判斷網(wǎng)卡是否存在硬件或驅(qū)動(dòng)相關(guān)的丟包問題,就像一位專業(yè)的醫(yī)生,為網(wǎng)卡進(jìn)行全面的 “體檢”。
使用 ethtool 工具查看網(wǎng)卡配置非常簡單。在終端中輸入 “ethtool 網(wǎng)卡名稱”,就能獲取網(wǎng)卡的詳細(xì)配置信息 。比如要查看 eth0 網(wǎng)卡的配置,輸入 “ethtool eth0” 。在輸出結(jié)果中,我們可以看到網(wǎng)卡的支持的端口類型、鏈路模式、自動(dòng)協(xié)商狀態(tài)、速度、雙工模式等信息 。通過檢查這些配置信息,我們可以判斷網(wǎng)卡的配置是否正確,是否與網(wǎng)絡(luò)環(huán)境相匹配。如果發(fā)現(xiàn)網(wǎng)卡的配置與網(wǎng)絡(luò)要求不一致,比如網(wǎng)卡被設(shè)置為半雙工模式,而網(wǎng)絡(luò)環(huán)境要求全雙工模式,就需要及時(shí)調(diào)整網(wǎng)卡配置,以避免丟包問題的發(fā)生。
ethtool 工具還可以查看網(wǎng)卡的統(tǒng)計(jì)信息,這對于判斷網(wǎng)卡是否存在丟包問題非常有幫助。在終端中輸入 “ethtool -S 網(wǎng)卡名稱”,可以顯示網(wǎng)卡和驅(qū)動(dòng)特定的統(tǒng)計(jì)參數(shù) ?!?S” 參數(shù)用于顯示統(tǒng)計(jì)參數(shù)。在輸出結(jié)果中,我們重點(diǎn)關(guān)注與丟包相關(guān)的統(tǒng)計(jì)項(xiàng),如 “rx_dropped”(接收丟棄的數(shù)據(jù)包數(shù)量)、“tx_dropped”(發(fā)送丟棄的數(shù)據(jù)包數(shù)量)、“rx_errors”(接收錯(cuò)誤的數(shù)據(jù)包數(shù)量)、“tx_errors”(發(fā)送錯(cuò)誤的數(shù)據(jù)包數(shù)量)等 。如果這些統(tǒng)計(jì)項(xiàng)的數(shù)值不斷增加,就說明網(wǎng)卡可能存在丟包問題。當(dāng) “rx_dropped” 的數(shù)值持續(xù)上升時(shí),可能是網(wǎng)卡接收緩沖區(qū)溢出,導(dǎo)致數(shù)據(jù)包被丟棄;當(dāng) “tx_errors” 的數(shù)值較大時(shí),可能是網(wǎng)卡發(fā)送數(shù)據(jù)時(shí)出現(xiàn)了錯(cuò)誤,需要進(jìn)一步檢查網(wǎng)卡硬件或驅(qū)動(dòng)是否正常。
除了查看配置和統(tǒng)計(jì)信息,ethtool 工具還能對網(wǎng)卡進(jìn)行一些設(shè)置和操作。使用 “ethtool -s 網(wǎng)卡名稱 speed 1000 duplex full autoneg off” 命令,可以強(qiáng)制設(shè)置網(wǎng)卡的速度為 1000Mbps、全雙工模式,并關(guān)閉自動(dòng)協(xié)商 。當(dāng)懷疑網(wǎng)卡的自動(dòng)協(xié)商功能出現(xiàn)問題,導(dǎo)致與網(wǎng)絡(luò)設(shè)備的連接不穩(wěn)定時(shí),可以通過這種方式強(qiáng)制設(shè)置網(wǎng)卡參數(shù),看是否能解決丟包問題。使用 “ethtool -r 網(wǎng)卡名稱” 命令,可以重置網(wǎng)卡到自適應(yīng)模式 。如果網(wǎng)卡在某些情況下出現(xiàn)異常,重置到自適應(yīng)模式可能會(huì)使其恢復(fù)正常工作狀態(tài)。
4.5 tcpdump抓包分析
tcpdump 命令是網(wǎng)絡(luò)數(shù)據(jù)包分析的得力助手,它可以抓取網(wǎng)絡(luò)數(shù)據(jù)包,并對這些數(shù)據(jù)包進(jìn)行分析,幫助我們深入了解網(wǎng)絡(luò)通信的細(xì)節(jié),找出丟包的具體原因,就像一位經(jīng)驗(yàn)豐富的偵探,通過對現(xiàn)場線索的分析來解開謎團(tuán)。
在 Linux 系統(tǒng)中使用 tcpdump 命令抓取網(wǎng)絡(luò)數(shù)據(jù)包時(shí),我們可以通過一些參數(shù)來靈活控制抓取的范圍和條件。在終端中輸入 “tcpdump -i 網(wǎng)卡名稱”,可以指定在特定的網(wǎng)卡上抓取數(shù)據(jù)包 ?!?i” 參數(shù)用于指定網(wǎng)卡名稱。比如要在 eth0 網(wǎng)卡上抓取數(shù)據(jù)包,輸入 “tcpdump -i eth0” 。這樣,tcpdump 就會(huì)開始捕獲 eth0 網(wǎng)卡上傳輸?shù)乃袛?shù)據(jù)包。如果只想抓取特定協(xié)議的數(shù)據(jù)包,可以使用 “-p” 參數(shù) 。使用 “tcpdump -i eth0 -p tcp”,表示只抓取 eth0 網(wǎng)卡上的 TCP 協(xié)議數(shù)據(jù)包。如果要抓取特定 IP 地址或端口的數(shù)據(jù)包,可以使用 “host”“src”“dst”“port” 等參數(shù) 。使用 “tcpdump -i eth0 host 192.168.1.100”,表示只抓取與 IP 地址為 192.168.1.100 的主機(jī)相關(guān)的數(shù)據(jù)包;使用 “tcpdump -i eth0 port 80”,表示只抓取端口號(hào)為 80 的數(shù)據(jù)包,通常用于抓取 HTTP 協(xié)議的數(shù)據(jù)包。
抓取到數(shù)據(jù)包后,就需要對這些數(shù)據(jù)包進(jìn)行分析,以找出丟包的原因。tcpdump 命令的輸出結(jié)果包含了數(shù)據(jù)包的詳細(xì)信息,如時(shí)間戳、源 IP 地址、目的 IP 地址、協(xié)議類型、端口號(hào)、數(shù)據(jù)包內(nèi)容等 。通過分析這些信息,我們可以判斷數(shù)據(jù)包是否正常傳輸,是否存在異常的數(shù)據(jù)包行為。
如果發(fā)現(xiàn)某個(gè)時(shí)間段內(nèi)有大量的重傳數(shù)據(jù)包,就說明可能存在丟包問題,導(dǎo)致數(shù)據(jù)需要重新傳輸 。重傳數(shù)據(jù)包的出現(xiàn)可能是因?yàn)榫W(wǎng)絡(luò)擁塞、信號(hào)干擾、鏈路故障等原因,我們需要進(jìn)一步排查這些因素,找出具體的丟包原因。如果發(fā)現(xiàn)數(shù)據(jù)包的目的 IP 地址或端口號(hào)與預(yù)期不符,也可能是網(wǎng)絡(luò)配置錯(cuò)誤或受到了攻擊,導(dǎo)致數(shù)據(jù)包被錯(cuò)誤地發(fā)送或丟棄,需要及時(shí)進(jìn)行檢查和修復(fù)。
執(zhí)行下面的 tcpdump 命令,抓取 80 端口的包如下:
tcpdump -i eth0 -nn port 80
#輸出
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
然后,切換到終端二中,再次執(zhí)行前面的 curl 命令:
curl --max-time 3 http://192.168.0.30
curl: (28) Operation timed out after 3000 milliseconds with 0 bytes received
等到 curl 命令結(jié)束后,再次切換回終端一,查看 tcpdump 的輸出:
14:40:00.589235 IP 10.255.255.5.39058 > 172.17.0.2.80: Flags [S], seq 332257715, win 29200, options [mss 1418,sackOK,TS val 486800541 ecr 0,nop,wscale 7], length 0
14:40:00.589277 IP 172.17.0.2.80 > 10.255.255.5.39058: Flags [S.], seq 1630206251, ack 332257716, win 4880, options [mss 256,sackOK,TS val 2509376001 ecr 486800541,nop,wscale 7], length 0
14:40:00.589894 IP 10.255.255.5.39058 > 172.17.0.2.80: Flags [.], ack 1, win 229, options [nop,nop,TS val 486800541 ecr 2509376001], length 0
14:40:03.589352 IP 10.255.255.5.39058 > 172.17.0.2.80: Flags [F.], seq 76, ack 1, win 229, options [nop,nop,TS val 486803541 ecr 2509376001], length 0
14:40:03.589417 IP 172.17.0.2.80 > 10.255.255.5.39058: Flags [.], ack 1, win 40, options [nop,nop,TS val 2509379001 ecr 486800541,nop,nop,sack 1 {76:77}], length 0
從 tcpdump 的輸出中,我們就可以看到:
- 前三個(gè)包是正常的 TCP 三次握手,這沒問題;
- 但第四個(gè)包卻是在 3 秒以后了,并且還是客戶端(VM2)發(fā)送過來的 FIN 包,說明客戶端的連接關(guān)閉了
在實(shí)際使用 tcpdump 命令時(shí),為了更好地分析數(shù)據(jù)包,我們還可以將抓取到的數(shù)據(jù)包保存到文件中,然后使用其他工具進(jìn)行進(jìn)一步的分析。使用 “tcpdump -i eth0 -w packet.cap” 命令,可以將抓取到的數(shù)據(jù)包保存到名為 packet.cap 的文件中 ?!?w” 參數(shù)用于指定保存數(shù)據(jù)包的文件名。保存好數(shù)據(jù)包文件后,我們可以使用 Wireshark 等專業(yè)的網(wǎng)絡(luò)協(xié)議分析工具打開這個(gè)文件,進(jìn)行更加詳細(xì)和直觀的分析 。Wireshark 提供了圖形化的界面,能夠以更清晰的方式展示數(shù)據(jù)包的結(jié)構(gòu)和內(nèi)容,方便我們快速定位問題。
根據(jù) curl 設(shè)置的 3 秒超時(shí)選項(xiàng),你應(yīng)該能猜到,這是因?yàn)?curl 命令超時(shí)后退出了。用 Wireshark 的 Flow Graph 來表示,你可以更清楚地看到上面這個(gè)問題:
圖片
這里比較奇怪的是,我們并沒有抓取到 curl 發(fā)來的 HTTP GET 請求。那究竟是網(wǎng)卡丟包了,還是客戶端就沒發(fā)過來呢?
可以重新執(zhí)行 netstat -i 命令,確認(rèn)一下網(wǎng)卡有沒有丟包問題:
netstat -i
Kernel Interface table
Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 100 157 0 344 0 94 0 0 0 BMRU
lo 65536 0 0 0 0 0 0 0 0 LRU
從 netstat 的輸出中,你可以看到,接收丟包數(shù)(RX-DRP)是 344,果然是在網(wǎng)卡接收時(shí)丟包了。不過問題也來了,為什么剛才用 hping3 時(shí)不丟包,現(xiàn)在換成 GET 就收不到了呢?還是那句話,遇到搞不懂的現(xiàn)象,不妨先去查查工具和方法的原理。我們可以對比一下這兩個(gè)工具:
- hping3 實(shí)際上只發(fā)送了 SYN 包;
- curl 在發(fā)送 SYN 包后,還會(huì)發(fā)送 HTTP GET 請求。HTTP GET本質(zhì)上也是一個(gè) TCP 包,但跟 SYN 包相比,它還攜帶了 HTTP GET 的數(shù)據(jù)。
通過這個(gè)對比,你應(yīng)該想到了,這可能是 MTU 配置錯(cuò)誤導(dǎo)致的。為什么呢?
其實(shí),仔細(xì)觀察上面 netstat 的輸出界面,第二列正是每個(gè)網(wǎng)卡的 MTU 值。eth0 的 MTU只有 100,而以太網(wǎng)的 MTU 默認(rèn)值是 1500,這個(gè) 100 就顯得太小了。當(dāng)然,MTU 問題是很好解決的,把它改成 1500 就可以了。
ifconfig eth0 mtu 1500
修改完成后,再切換到終端二中,再次執(zhí)行 curl 命令,確認(rèn)問題是否真的解決了:
curl --max-time 3 http://192.168.0.30/
#輸出
<!DOCTYPE html>
<html>
...
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
非常不容易呀,這次終于看到了熟悉的 Nginx 響應(yīng),說明丟包的問題終于徹底解決了。
五、針對不同原因的解決策略
5.1 物理鏈路問題處理
當(dāng)懷疑是物理鏈路問題導(dǎo)致 Linux 服務(wù)器丟包時(shí),我們可以從以下幾個(gè)方面進(jìn)行處理。首先是更換網(wǎng)線,在更換網(wǎng)線時(shí),要選擇質(zhì)量可靠的網(wǎng)線。優(yōu)質(zhì)的網(wǎng)線在材質(zhì)和工藝上更有保障,能有效減少信號(hào)衰減和干擾。在購買網(wǎng)線時(shí),要注意查看網(wǎng)線的參數(shù)和質(zhì)量認(rèn)證標(biāo)志,選擇符合網(wǎng)絡(luò)環(huán)境需求的規(guī)格。如果網(wǎng)絡(luò)環(huán)境要求高速穩(wěn)定的傳輸,就應(yīng)選擇超五類或六類網(wǎng)線 。更換網(wǎng)線的過程并不復(fù)雜,先將兩端的網(wǎng)線插頭從設(shè)備上拔下,然后插入新的網(wǎng)線插頭即可。在插拔網(wǎng)線時(shí),要注意力度適中,避免損壞設(shè)備接口。
檢查并緊固接口也是非常重要的一步。仔細(xì)檢查網(wǎng)絡(luò)設(shè)備之間的所有接口,包括服務(wù)器網(wǎng)卡接口、交換機(jī)接口、路由器接口等 。查看接口是否有松動(dòng)、氧化、變形等情況。對于松動(dòng)的接口,要重新插拔網(wǎng)線,確保插頭與接口緊密連接。在插拔網(wǎng)線時(shí),可以聽到 “咔噠” 一聲,這表示插頭已經(jīng)正確插入接口 。對于氧化的接口,可以使用橡皮擦輕輕擦拭接口金屬部分,去除氧化物,以提高接口的導(dǎo)電性 。對于變形的接口,要及時(shí)更換相關(guān)設(shè)備,避免影響網(wǎng)絡(luò)連接。
排查和更換故障網(wǎng)絡(luò)設(shè)備同樣不容忽視。如果經(jīng)過檢查發(fā)現(xiàn)是路由器或交換機(jī)出現(xiàn)故障,應(yīng)及時(shí)進(jìn)行維修或更換 。在更換網(wǎng)絡(luò)設(shè)備時(shí),要選擇與原設(shè)備性能相當(dāng)或更好的設(shè)備,并確保新設(shè)備的配置與網(wǎng)絡(luò)環(huán)境相匹配 。如果原交換機(jī)是千兆交換機(jī),那么在更換時(shí)也應(yīng)選擇千兆交換機(jī),以保證網(wǎng)絡(luò)的傳輸速度。在更換設(shè)備前,要備份好原設(shè)備的配置信息,以便在新設(shè)備上快速恢復(fù)配置 。更換設(shè)備后,要重新測試網(wǎng)絡(luò)連接,確保網(wǎng)絡(luò)恢復(fù)正常。
5.2 網(wǎng)卡與驅(qū)動(dòng)修復(fù)
硬件檢查與更換:判斷網(wǎng)卡硬件是否故障,可以通過多種方法。首先觀察網(wǎng)卡指示燈的狀態(tài),如果指示燈不亮或者閃爍異常,就可能表示網(wǎng)卡存在硬件問題 。正常情況下,網(wǎng)卡指示燈應(yīng)該是穩(wěn)定亮起或者有規(guī)律地閃爍。使用硬件檢測工具也能幫助我們判斷網(wǎng)卡硬件是否正常。一些服務(wù)器管理軟件提供了硬件檢測功能,可以對網(wǎng)卡進(jìn)行全面的檢測 。如果確定網(wǎng)卡硬件出現(xiàn)故障,在更換網(wǎng)卡時(shí),要注意選擇與服務(wù)器兼容的網(wǎng)卡 。不同型號(hào)的服務(wù)器對網(wǎng)卡的兼容性可能不同,在購買網(wǎng)卡時(shí),要查看服務(wù)器的硬件兼容性列表,選擇列表中推薦的網(wǎng)卡型號(hào) 。在安裝新網(wǎng)卡時(shí),要先關(guān)閉服務(wù)器電源,然后將新網(wǎng)卡插入服務(wù)器的 PCI 插槽中 。插入時(shí)要注意方向正確,確保網(wǎng)卡與插槽緊密接觸。安裝完成后,再打開服務(wù)器電源,讓系統(tǒng)自動(dòng)識(shí)別新網(wǎng)卡。
驅(qū)動(dòng)更新與修復(fù):要獲取和安裝最新的網(wǎng)卡驅(qū)動(dòng),首先需要確定網(wǎng)卡的型號(hào)??梢酝ㄟ^查看服務(wù)器硬件手冊、在系統(tǒng)中使用命令 “l(fā)spci | grep -i network” 等方式來確定網(wǎng)卡型號(hào) 。確定型號(hào)后,前往網(wǎng)卡制造商的官方網(wǎng)站,在支持頁面中搜索對應(yīng)型號(hào)的最新驅(qū)動(dòng)程序 。下載驅(qū)動(dòng)程序時(shí),要注意選擇與服務(wù)器操作系統(tǒng)版本相匹配的驅(qū)動(dòng) 。如果服務(wù)器使用的是 CentOS 7 系統(tǒng),就需要下載適用于 CentOS 7 的網(wǎng)卡驅(qū)動(dòng)。下載完成后,按照驅(qū)動(dòng)安裝說明進(jìn)行安裝。一般來說,驅(qū)動(dòng)安裝包中會(huì)包含安裝指南,按照指南中的步驟進(jìn)行操作即可 。
如果遇到驅(qū)動(dòng)程序損壞的情況,可以嘗試修復(fù)驅(qū)動(dòng)程序。在 Linux 系統(tǒng)中,可以使用 “modprobe -r 驅(qū)動(dòng)名” 命令先卸載損壞的驅(qū)動(dòng)模塊 。然后重新加載驅(qū)動(dòng)模塊,使用 “modprobe 驅(qū)動(dòng)名” 命令 。如果問題仍然存在,可以嘗試從系統(tǒng)備份中恢復(fù)驅(qū)動(dòng)程序文件。如果在安裝系統(tǒng)時(shí)進(jìn)行了備份,可以從備份中提取出正確的驅(qū)動(dòng)程序文件,然后將其覆蓋到原驅(qū)動(dòng)文件所在的目錄 。在恢復(fù)驅(qū)動(dòng)文件時(shí),要注意文件的權(quán)限和路徑設(shè)置,確保文件能夠被系統(tǒng)正確識(shí)別和使用。
5.3 協(xié)議棧參數(shù)優(yōu)化
①鏈路層優(yōu)化:調(diào)整 QoS 參數(shù)時(shí),需要根據(jù)網(wǎng)絡(luò)中不同應(yīng)用的需求來進(jìn)行設(shè)置。對于實(shí)時(shí)性要求較高的應(yīng)用,如視頻會(huì)議、在線游戲等,要為它們分配較高的優(yōu)先級,確保這些應(yīng)用的數(shù)據(jù)包能夠優(yōu)先傳輸 。在 Linux 系統(tǒng)中,可以使用 tc(traffic control)工具來配置 QoS 參數(shù) 。使用 “tc qdisc add dev eth0 root handle 1: prio” 命令可以在 eth0 網(wǎng)卡上添加一個(gè)優(yōu)先級隊(duì)列 。然后使用 “tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip dport 80 0xffff flowid 1:1” 命令將目的端口為 80(通常用于 HTTP 協(xié)議)的數(shù)據(jù)包設(shè)置為優(yōu)先級 1 。這樣,在網(wǎng)絡(luò)擁塞時(shí),HTTP 協(xié)議的數(shù)據(jù)包會(huì)優(yōu)先被處理,減少丟包的可能性。
修復(fù)鏈路層配置錯(cuò)誤也是優(yōu)化的重要環(huán)節(jié)。仔細(xì)檢查鏈路層的配置文件,如 /etc/network/interfaces(基于 Debian 系統(tǒng))或 /etc/sysconfig/network-scripts/ifcfg-eth0(基于 Red Hat 系統(tǒng)) 。確保文件中的配置項(xiàng)正確無誤,如 MAC 地址、子網(wǎng)掩碼、網(wǎng)關(guān)等 。如果發(fā)現(xiàn)配置錯(cuò)誤,及時(shí)進(jìn)行修改,然后重啟網(wǎng)絡(luò)服務(wù),使新的配置生效 。在 Debian 系統(tǒng)中,可以使用 “sudo systemctl restart networking” 命令重啟網(wǎng)絡(luò)服務(wù);在 Red Hat 系統(tǒng)中,可以使用 “sudo systemctl restart network” 命令 。
②網(wǎng)絡(luò)層優(yōu)化:正確配置路由是網(wǎng)絡(luò)層優(yōu)化的關(guān)鍵。如果是靜態(tài)路由,要確保路由表中的路由條目準(zhǔn)確無誤。使用 “route -n” 命令可以查看當(dāng)前系統(tǒng)的路由表 。如果發(fā)現(xiàn)路由表中有錯(cuò)誤的路由條目,比如路由指向了錯(cuò)誤的網(wǎng)關(guān),就需要使用 “route del -net 目標(biāo)網(wǎng)絡(luò) netmask 子網(wǎng)掩碼 gw 錯(cuò)誤網(wǎng)關(guān)” 命令刪除錯(cuò)誤的路由條目,然后使用 “route add -net 目標(biāo)網(wǎng)絡(luò) netmask子網(wǎng)掩碼gw正確網(wǎng)關(guān)” 命令添加正確的路由 。如果是動(dòng)態(tài)路由,要確保路由協(xié)議的配置正確,如 RIP、OSPF 等 。檢查路由協(xié)議的參數(shù)設(shè)置,如鄰居路由器的 IP 地址、認(rèn)證信息等 。
合理設(shè)置 MTU 值也能有效避免網(wǎng)絡(luò)層丟包。首先需要確定網(wǎng)絡(luò)中最小的 MTU 值??梢酝ㄟ^在網(wǎng)絡(luò)中使用 ping 命令并逐漸增大數(shù)據(jù)包大小的方式來測試 。使用 “ping -s 1400 -M do 目標(biāo) IP 地址” 命令(其中 1400 是數(shù)據(jù)包大小,可以根據(jù)實(shí)際情況調(diào)整),如果返回 “Packet needs to be fragmented but DF set” 錯(cuò)誤,說明當(dāng)前數(shù)據(jù)包大小超過了鏈路的 MTU 值 。逐漸減小數(shù)據(jù)包大小,直到不再出現(xiàn)該錯(cuò)誤,此時(shí)的數(shù)據(jù)包大小就是鏈路的 MTU 值 。確定 MTU 值后,在服務(wù)器的網(wǎng)絡(luò)配置文件中設(shè)置 MTU 值 。在 /etc/network/interfaces 文件中,添加或修改 “mtu 確定的 MTU 值” 配置項(xiàng) 。
③ 傳輸層優(yōu)化:調(diào)整端口監(jiān)聽配置時(shí),要確保應(yīng)用程序正確監(jiān)聽其對應(yīng)的端口。使用 netstat 命令檢查端口的監(jiān)聽狀態(tài),如 “netstat -an | grep 端口號(hào)” 。如果發(fā)現(xiàn)端口未被正確監(jiān)聽,檢查應(yīng)用程序的配置文件,確保監(jiān)聽端口的設(shè)置正確 。對于一些網(wǎng)絡(luò)服務(wù),如 Web 服務(wù)器、FTP 服務(wù)器等,要確保它們的配置文件中指定的監(jiān)聽端口與實(shí)際需求一致 。如果需要修改監(jiān)聽端口,在修改配置文件后,重啟應(yīng)用程序,使新的配置生效 。
優(yōu)化內(nèi)核資源限制參數(shù)也很重要。在 Linux 系統(tǒng)中,內(nèi)核為網(wǎng)絡(luò)連接分配了一定的資源,如內(nèi)存、文件描述符等 。可以通過修改內(nèi)核參數(shù)來優(yōu)化這些資源的分配 。編輯 /etc/sysctl.conf 文件,添加或修改以下參數(shù):“net.core.somaxconn = 65535”(設(shè)置 TCP 監(jiān)聽隊(duì)列的最大長度)、“net.ipv4.tcp_max_tw_buckets = 6000”(設(shè)置 TIME_WAIT 狀態(tài)的最大數(shù)量)、“net.ipv4.tcp_keepalive_time = 1200”(設(shè)置 TCP 連接保持活動(dòng)的時(shí)間)等 。修改完成后,使用 “sudo sysctl -p” 命令使新的參數(shù)生效 。這些參數(shù)的調(diào)整可以根據(jù)服務(wù)器的實(shí)際負(fù)載情況和網(wǎng)絡(luò)環(huán)境進(jìn)行優(yōu)化,以提高傳輸層的性能,減少丟包現(xiàn)象。
5.4 系統(tǒng)與應(yīng)用優(yōu)化
①系統(tǒng)負(fù)載優(yōu)化:降低系統(tǒng)負(fù)載可以從多個(gè)方面入手。優(yōu)化進(jìn)程調(diào)度是其中之一,在 Linux 系統(tǒng)中,可以使用 top 命令查看當(dāng)前系統(tǒng)中各個(gè)進(jìn)程的資源占用情況 。如果發(fā)現(xiàn)某個(gè)進(jìn)程占用了大量的 CPU 或內(nèi)存資源,且該進(jìn)程并非必要進(jìn)程,可以考慮結(jié)束該進(jìn)程 。使用 “kill -9 進(jìn)程 ID” 命令可以強(qiáng)制結(jié)束進(jìn)程 。但在結(jié)束進(jìn)程時(shí)要謹(jǐn)慎操作,確保不會(huì)影響系統(tǒng)的正常運(yùn)行。
關(guān)閉不必要的服務(wù)也是降低系統(tǒng)負(fù)載的有效方法。使用 “systemctl list-unit-files --type=service” 命令可以列出系統(tǒng)中所有的服務(wù) 。檢查這些服務(wù),找出那些當(dāng)前不需要運(yùn)行的服務(wù),如一些默認(rèn)啟動(dòng)但實(shí)際未使用的網(wǎng)絡(luò)服務(wù) 。使用 “systemctl stop 服務(wù)名” 命令可以停止這些服務(wù),使用 “systemctl disable 服務(wù)名” 命令可以禁止這些服務(wù)在系統(tǒng)啟動(dòng)時(shí)自動(dòng)運(yùn)行 。通過關(guān)閉不必要的服務(wù),可以釋放系統(tǒng)資源,使系統(tǒng)有更多的資源用于處理網(wǎng)絡(luò)請求,從而減少丟包現(xiàn)象的發(fā)生。
②應(yīng)用程序修復(fù):排查應(yīng)用程序的內(nèi)存泄漏問題可以使用一些工具,如 valgrind 。valgrind 是一個(gè)功能強(qiáng)大的內(nèi)存調(diào)試工具,它可以檢測出應(yīng)用程序中的內(nèi)存泄漏、內(nèi)存越界等問題 。使用 valgrind 運(yùn)行應(yīng)用程序,如 “valgrind --leak-check=full./ 應(yīng)用程序名” 。valgrind 會(huì)在應(yīng)用程序運(yùn)行結(jié)束后,輸出詳細(xì)的內(nèi)存使用報(bào)告,指出是否存在內(nèi)存泄漏以及泄漏的位置 。根據(jù)報(bào)告中的信息,對應(yīng)用程序進(jìn)行修改,修復(fù)內(nèi)存泄漏問題 。
檢查應(yīng)用程序的邏輯錯(cuò)誤也很關(guān)鍵。仔細(xì)審查應(yīng)用程序的代碼,檢查在處理網(wǎng)絡(luò)數(shù)據(jù)時(shí)是否存在邏輯錯(cuò)誤,如錯(cuò)誤的數(shù)據(jù)包解析、無法正確地發(fā)送響應(yīng)等 ??梢允褂谜{(diào)試工具,如 gdb,對應(yīng)用程序進(jìn)行調(diào)試 。在 gdb 中設(shè)置斷點(diǎn),逐步執(zhí)行代碼,觀察變量的值和程序的執(zhí)行流程,找出邏輯錯(cuò)誤的根源 。一旦發(fā)現(xiàn)邏輯錯(cuò)誤,及時(shí)修改代碼,重新編譯和部署應(yīng)用程序,確保應(yīng)用程序能夠正常處理網(wǎng)絡(luò)數(shù)據(jù),避免因應(yīng)用程序異常導(dǎo)致的丟包問題。
5.5 防火墻與 iptables 規(guī)則調(diào)整
合理配置防火墻策略是確保網(wǎng)絡(luò)正常通信的重要步驟。在配置防火墻時(shí),要遵循最小權(quán)限原則,只允許必要的網(wǎng)絡(luò)流量通過 。如果服務(wù)器只提供 Web 服務(wù),那么只需要開放 80 端口(HTTP 協(xié)議)和 443 端口(HTTPS 協(xié)議)即可 。在 Linux 系統(tǒng)中,可以使用 iptables 命令來配置防火墻策略 。
使用 “iptables -A INPUT -p tcp --dport 80 -j ACCEPT” 命令允許 TCP 協(xié)議的 80 端口的流量進(jìn)入服務(wù)器 。使用 “iptables -A INPUT -p tcp --dport 443 -j ACCEPT” 命令允許 TCP 協(xié)議的 443 端口的流量進(jìn)入服務(wù)器 。同時(shí),要拒絕其他不必要的流量,使用 “iptables -A INPUT -j DROP” 命令拒絕所有未明確允許的流量 。
檢查和修正 iptables 規(guī)則時(shí),要仔細(xì)查看規(guī)則的順序和內(nèi)容 。iptables 規(guī)則是按照順序匹配的,所以規(guī)則的順序非常重要 。確保允許規(guī)則在拒絕規(guī)則之前,否則允許規(guī)則將不會(huì)生效 。使用 “iptables -L -n” 命令可以查看當(dāng)前的 iptables 規(guī)則列表 。如果發(fā)現(xiàn)規(guī)則錯(cuò)誤,如 DROP 規(guī)則設(shè)置不當(dāng),導(dǎo)致正常的數(shù)據(jù)包被丟棄,使用 “iptables -D 鏈名 規(guī)則序號(hào)” 命令刪除錯(cuò)誤的規(guī)則 。
如果要?jiǎng)h除 INPUT 鏈中的第 3 條規(guī)則,使用 “iptables -D INPUT 3” 命令 。然后根據(jù)實(shí)際需求,重新添加正確的規(guī)則 。通過合理配置防火墻策略和檢查修正 iptables 規(guī)則,可以避免因防火墻和規(guī)則錯(cuò)誤導(dǎo)致的丟包問題,確保網(wǎng)絡(luò)的正常運(yùn)行。
六、實(shí)戰(zhàn)演練:排查與解決 Nginx 丟包問題
理論上的分析固然重要,但實(shí)際操作才是檢驗(yàn)真理的關(guān)鍵。下面,我們將通過一個(gè)具體的案例,以 Nginx 應(yīng)用為例,深入探討如何在實(shí)際場景中排查和解決網(wǎng)絡(luò)丟包問題。
6.1 模擬訪問與初步判斷
假設(shè)我們在一臺(tái) Linux 服務(wù)器上部署了 Nginx 應(yīng)用,現(xiàn)在懷疑它存在網(wǎng)絡(luò)丟包問題。我們首先使用 hping3 命令來模擬訪問 Nginx 服務(wù)。hping3 是一個(gè)功能強(qiáng)大的網(wǎng)絡(luò)工具,它可以發(fā)送各種類型的網(wǎng)絡(luò)數(shù)據(jù)包,幫助我們測試網(wǎng)絡(luò)的連通性和性能。執(zhí)行以下命令:
hping3 -c 10 -S -p 80 192.168.0.30
在這個(gè)命令中,-c 10表示發(fā)送 10 個(gè)請求包,-S表示使用 TCP SYN 標(biāo)志位,-p 80指定目標(biāo)端口為 80,即 Nginx 服務(wù)默認(rèn)的端口,192.168.0.30是 Nginx 服務(wù)器的 IP 地址。執(zhí)行命令后,我們得到如下輸出:
HPING 192.168.0.30 (eth0 192.168.0.30): S set, 40 headers + 0 data bytes
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=3 win=5120 rtt=7.5 ms
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=4 win=5120 rtt=7.4 ms
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=5 win=5120 rtt=3.3 ms
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=7 win=5120 rtt=3.0 ms
len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=6 win=5120 rtt=3027.2 ms
--- 192.168.0.30 hping statistic ---
10 packets transmitted, 5 packets received, 50% packet loss
round-trip min/avg/max = 3.0/609.7/3027.2 ms
從輸出結(jié)果中,我們可以清晰地看到,總共發(fā)送了 10 個(gè)請求包,但只收到了 5 個(gè)回復(fù)包,丟包率高達(dá) 50%。而且,每個(gè)請求的 RTT(往返時(shí)間)波動(dòng)非常大,最小值只有 3.0ms,而最大值卻達(dá)到了 3027.2ms,這表明網(wǎng)絡(luò)中很可能存在丟包現(xiàn)象。
6.2 鏈路層排查
初步判斷存在丟包問題后,我們首先從鏈路層開始排查。使用netstat -i命令查看虛擬網(wǎng)卡的丟包記錄:
netstat -i
得到如下輸出:
Kernel Interface table
Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 100 31 0 0 0 8 0 0 0 BMRU
lo 65536 0 0 0 0 0 0 0 0 LRUR
在這個(gè)輸出中,RX-OK表示接收時(shí)的總包數(shù),RX-ERR表示總錯(cuò)誤數(shù),RX-DRP表示進(jìn)入 Ring Buffer 后因其他原因(如內(nèi)存不足)導(dǎo)致的丟包數(shù),RX-OVR表示 Ring Buffer 溢出導(dǎo)致的丟包數(shù),TX-OK至TX-OVR則表示發(fā)送時(shí)的相應(yīng)指標(biāo)。從這里可以看出,虛擬網(wǎng)卡的各項(xiàng)錯(cuò)誤指標(biāo)均為 0,說明虛擬網(wǎng)卡本身沒有丟包。
不過,如果使用tc等工具配置了 QoS(Quality of Service,服務(wù)質(zhì)量),tc規(guī)則導(dǎo)致的丟包不會(huì)包含在網(wǎng)卡的統(tǒng)計(jì)信息中。因此,我們還需要檢查eth0上是否配置了tc規(guī)則,并查看是否有丟包。添加-s選項(xiàng)以輸出統(tǒng)計(jì)信息:
tc -s qdisc show dev eth0
輸出結(jié)果如下:
qdisc netem 800d: root refcnt 2 limit 1000 loss 30%
Sent 432 bytes 8 pkt (dropped 4, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
可以看到,eth0上配置了一個(gè)網(wǎng)絡(luò)模擬排隊(duì)規(guī)則qdisc netem,并且設(shè)置了丟包率為 30%(loss 30%)。從后面的統(tǒng)計(jì)信息可知,發(fā)送了 8 個(gè)包,但丟了 4 個(gè)。這很可能就是導(dǎo)致 Nginx 回復(fù)的響應(yīng)包被netem模塊丟棄的原因。既然找到了問題,解決方法就很簡單,直接刪除netem模塊:
tc qdisc del dev eth0 root netem loss 30%
刪除后,重新執(zhí)行hping3命令,看看問題是否解決。然而,從hping3的輸出中發(fā)現(xiàn),仍然有 50% 的丟包,RTT 波動(dòng)依舊很大,說明問題還未得到解決,需要繼續(xù)向上層排查。
6.3 網(wǎng)絡(luò)層和傳輸層排查
接下來,我們排查網(wǎng)絡(luò)層和傳輸層。在這兩層中,引發(fā)丟包的因素眾多,但確認(rèn)是否丟包卻相對簡單,因?yàn)?Linux 已經(jīng)為我們提供了各個(gè)協(xié)議的收發(fā)匯總情況。執(zhí)行netstat -s命令,查看 IP、ICMP、TCP 和 UDP 等協(xié)議的收發(fā)統(tǒng)計(jì)信息:
netstat -s
輸出結(jié)果非常豐富,這里我們重點(diǎn)關(guān)注與丟包相關(guān)的信息。例如,從 TCP 協(xié)議的統(tǒng)計(jì)信息中,我們看到有多次超時(shí)和失敗重試,并且主要錯(cuò)誤是半連接重置,這表明可能存在三次握手失敗的問題。這可能是由于網(wǎng)絡(luò)擁塞、端口被占用、防火墻限制等原因?qū)е碌?。此時(shí),我們需要進(jìn)一步分析具體的錯(cuò)誤原因,可以結(jié)合其他工具和命令,如lsof查看端口占用情況,檢查防火墻規(guī)則等。
6.4 iptables 排查
iptables 是 Linux 系統(tǒng)中常用的防火墻工具,其規(guī)則配置不當(dāng)可能導(dǎo)致數(shù)據(jù)包被丟棄。首先,我們檢查內(nèi)核的連接跟蹤機(jī)制,查看當(dāng)前的連接跟蹤數(shù)和最大連接跟蹤數(shù):
cat /proc/sys/net/nf_conntrack_count
cat /proc/sys/net/nf_conntrack_max
假設(shè)連接跟蹤數(shù)只有 182,而最大連接跟蹤數(shù)是 262144,說明連接跟蹤數(shù)沒有達(dá)到上限,不存在因連接跟蹤數(shù)滿而導(dǎo)致丟包的問題。
接著,查看 iptables 規(guī)則,使用iptables -L -n命令:
iptables -L -n
在輸出的規(guī)則列表中,我們發(fā)現(xiàn)有兩條DROP規(guī)則,使用了statistic模塊進(jìn)行隨機(jī) 30% 的丟包。這顯然是導(dǎo)致丟包的一個(gè)重要原因。我們將這兩條規(guī)則直接刪除,然后重新執(zhí)行hping3命令。此時(shí),hping3的輸出顯示已經(jīng)沒有丟包,這說明 iptables 的錯(cuò)誤規(guī)則是導(dǎo)致之前丟包的原因之一。
6.5 端口狀態(tài)檢查與進(jìn)一步排查
雖然hping3驗(yàn)證了 Nginx 的 80 端口處于正常監(jiān)聽狀態(tài),但還需要檢查 Nginx 對 HTTP 請求的響應(yīng)。使用curl命令:
curl -w 'Http code: %{http_code}\\nTotal time:%{time_total}s\\n' -o /dev/null --connect-timeout 10 http://192.168.0.30/
結(jié)果發(fā)現(xiàn)連接超時(shí),這表明雖然端口監(jiān)聽正常,但 Nginx 在處理 HTTP 請求時(shí)可能存在問題。為了進(jìn)一步分析,我們使用tcpdump命令抓包:
tcpdump -i eth0 -n tcp port 80
在另一個(gè)終端執(zhí)行curl命令,然后查看tcpdump的輸出。發(fā)現(xiàn)前三個(gè)包是正常的 TCP 三次握手,但第四個(gè)包卻是在 3 秒后才收到,并且是客戶端發(fā)送過來的 FIN 包,這說明客戶端的連接已經(jīng)關(guān)閉。
重新執(zhí)行netstat -i命令,檢查網(wǎng)卡是否有丟包,發(fā)現(xiàn)果然是在網(wǎng)卡接收時(shí)丟包了。進(jìn)一步檢查最大傳輸單元 MTU(Maximum Transmission Unit):
ifconfig eth0 | grep MTU
發(fā)現(xiàn)eth0的 MTU 只有 100,而以太網(wǎng)的 MTU 默認(rèn)值是 1500。MTU 過小可能導(dǎo)致數(shù)據(jù)包在傳輸過程中需要分片,從而增加丟包的風(fēng)險(xiǎn)。我們將 MTU 修改為 1500:
ifconfig eth0 mtu 1500
再次執(zhí)行curl命令,問題得到解決,Nginx 能夠正常響應(yīng) HTTP 請求。