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