ping 可是個好東西啊!

平時,我們想要知道,自己的機器到目的機器之間,網絡通不通,一般會執行 ping 命令

一般對於狀況良好的網絡來說,你能看到它對應的loss丟包率爲0%,也就是所謂的能 ping 通。如果看到丟包率100%,也就是 ping 不通

ping 正常

ping 不通

那麼問題來了,假設我能 ping 通某臺機器,那這時候如果我改用 TCP 協議去發數據到目的機器,也一定能通嗎?

或者換個問法,ping 和 tcp 協議走的網絡路徑是一樣的嗎?

這時候第一反應就是不一定,因爲 ping 完之後中間鏈路裏的某個路由器可能會掛了(斷電了),再用 TCP 去連就會走別的路徑。

也沒錯。但假設,中間鏈路沒發生任何變化呢?

我先直接說答案。

不一定,走的網絡路徑還是有可能是不同的。

今天就來聊聊爲什麼。

我之前寫過一篇《斷網了,還能 ping 通 127.0.0.1 嗎?》, 裏面提到過 ping 數據包和 tcp 數據包的區別

ping 和 TCP 發消息的區別

我們知道網絡是分層的,每一層都有對應協議。

五層網絡協議對應的消息體變化分析

而這網絡層就像搭積木一樣,上層協議都是基於下層協議搭出來的。

不管是 ping(用了 ICMP 協議)還是 tcp 本質上都是基於網絡層 IP 協議的數據包,而到了物理層,都是二進制 01 串,都走網卡發出去了。

如果網絡環境沒發生變化,目的地又一樣,那按道理說他們走的網絡路徑應該是一樣的,什麼情況下會不同呢?

我們就從路由這個話題聊起吧。

網絡路徑

在我們的想象中,當我們想在兩臺機器之間傳輸數據。本機和目的機器之間會建立一條連接,像一條管道一樣,數據從這頭到那頭。這條管道其實是我們爲了方便理解而抽象出來的概念。

實際上,我們將數據包從本地網卡發出之後,會經過各種路由器(或者交換機),才能到達目的機器。

這些路由器數量衆多,相互之間可以互連,連起來之後就像是一張大網,所以叫 "網絡" 可以說是非常的形象。

路由器構成的網絡

考慮到交換機有的功能,路由器基本上都支持,所以我們這邊只討論路由器。

那麼現在問題來了,路由器收到數據後,怎麼知道應該走哪條路徑,傳給哪個路由器?

路徑由什麼決定?

在上面的那麼大一張網絡中,隨便一個路由器都有可能走任何一個路徑,將數據發到另外一個路由器上,

但路由和路由之間距離,帶寬啥的可能都不同。

於是就很需要知道,兩點之間走哪條路纔是最優路徑

於是問題就變成了這樣一個圖狀結構。每條邊都帶有成本或權重,算這上面任意兩點的最短距離

路由器和 Dijkstra

這時候想必大家回憶壓不住要上來了。

這題我熟,這就是大學時候刷的 Dijkstra 算法。菊花廠的 OJ 筆試題集裏也經常出現,現在終於明白爲什麼他們家的筆試題裏圖類題目比別的大廠貌似要多一些了吧,因爲菊花廠就是搞通信的,做路由器的老玩家了。

路由表的生成

基於 Dijkstra 算法,封裝出了一個新的協議,OSPF 協議Open Shortest Path First, 開放最短路徑優先)。

有了 OSPF,路由器就得到了網絡圖裏自己到其他點之間的最短距離,於是就知道了數據包要到某個點,該走哪條最優路徑

將這些信息匯成一張表,也就是我們常說的路由表

路由表裏記錄了到什麼 IP 需要走什麼端口,以及走這條路徑的成本(metric)。

可以通過 route 命令查看到。

route 表

路由表決定數據包路徑

數據包在發送的過程中,會在網絡層加入目標地址 IP

路由器會根據這個 IP路由表去做匹配。

然後路由表,會告訴路由器,什麼樣的消息該轉發到什麼端口。

舉個例子。

通過路由錶轉發數據

假設 A 要發消息到 D。也就是192.168.0.105/24要發消息到192.168.1.11/24

那麼 A 會把消息經發到路由器。

路由器已知目的地 IP192.168.1.11/24 ,去跟路由表做匹配,發現192.168.1.0/24, 就在 e2 端口,那麼就會把消息從 e2 端口發出,(可能還會經過交換機)最後把消息打到目的機器。

當然,如果路由表裏找不到,那就打到默認網關吧,也就是從 e1 口發出,發到 IP192.0.2.1這個路由器的路由表不知道該去哪,說不定其他路由器知道

路由表的匹配規則

上面的例子裏,是隻匹配上了路由表裏的一項,所以只能是它了。

但是,條條大路通羅馬。實際上能到目的地的路徑肯定有很多。

如果路由表裏有很多項都被匹配上了,會怎麼選?

如果多個路由項都能到目的地,那就優先選匹配長度更長的那個。比如,還是目的地192.168.1.11,發現路由表裏的 192.168.1.0/24192.168.0.0/16 都能匹配上,但明顯前者匹配長度更長,所以最後會走 192.168.1.0/24 對應的轉發端口。

但如果兩個表項的匹配長度都一樣呢?

那就會看生成這個路由表項的協議是啥,選優先級高的,優先級越高也就是所謂的管理距離ADAdministrativeDistance)越小。比如說優先選手動配的靜態(static)路由,次優選 OSPF 動態學習過來的表項。

如果還是相同,就看度量值 metrics,其實也就是路徑成本 cost,成本越小,越容易被選中。

路由器能選的路線有很多,但按道理,最優的只有 "一條",所以到這裏爲止,我們都可以認爲,對於同一個目的地,ping 和 TCP 走的路徑是相同的。

但是。

**如果連路徑成本都一樣呢?**也就是說有多條最優路徑呢。

那就都用

這也就是所謂的等價多路徑,ECMPEqual Cost MultiPath)。

我們可以通過traceroute看下鏈路是否存在等價多路徑的情況。

可以看到,中間某幾行,有好幾個 IP,也就是說這一跳裏同時可以選好幾個目的機器,說明這段路徑支持 ECMP

ECMP 有什麼用

利用等價多路徑,我們可以增加鏈路帶寬

舉個例子。

沒有 ECMP 時只能選擇某一條路徑

從 A 點到 B 點,如果這兩條路徑成本不同,帶寬都是1千兆。那數據包肯定就選成本低的那條路了,如果這條路出故障了,就走下面那條路。但不管怎麼樣,同一時間,只用到了一條路徑。另外一條閒置就有些浪費了,有沒有辦法可以利用起來呢?

有,將它們兩條路徑的成本設置成一樣,那它們就成了等價路由,然後中間的路由器開啓 ECMP 特性,就可以同時利用這兩條鏈路了。帶寬就從原來的1千兆變成了2千兆。數據就可以在兩條路徑中隨意選擇了。

利用 ECMP 可以同時使用兩條鏈路

但這也帶來了另外一個問題。加劇了數據包亂序

原來我只使用一條網絡路徑,數據依次發出,如無意外,也是依次到達。

現在兩個數據包走兩條路徑,先發的數據包可能後到。這就亂序了。

那麼問題又又來了。

亂序會有什麼問題?

對於我們最最最常使用的 TCP 協議來說,它是個可靠性網絡的協議,這裏提到的可靠,不僅是保證數據要能送到目的地,還要保證數據順序要跟原來發送端的一樣。

實現也很簡單,TCP 爲每個數據包(segment)做上編號。數據到了接收端後,根據數據包編號發現是亂序數據包,就會扔到亂序隊列中對數據包進行排序。如果前面的數據包還沒到,哪怕後面的數據包先到了,也得在亂序隊列中一直等,到齊後才能被上層拿到。

舉個例子,發送端發出三個數據包,編號1,2,3,假設在傳輸層2和3先到了,1還沒到。那此時應用層是沒辦法拿到2和3的數據包的,必須得等1來了之後,應用層才能一次性拿到這三個包。因爲這三個包原來可能表示的是一個完整的消息,少了 1, 那麼消息就不完整,應用層拿到了也毫無意義。

像這種,由於前面的數據丟失導致後面的數據沒辦法及時給到應用層的現象,就是我們常說的 TCP 隊頭阻塞

亂序隊列等待數據包的到來

亂序發生時2和3需要待在亂序隊列中,而亂序隊列其實用的也是接收緩衝區的內存,而接收緩衝區是有大小限制的。通過下面的命令可以看到接收緩衝區的大小。

# 查看接收緩衝區
$ sysctl net.ipv4.tcp_rmem
net.ipv4.tcp_rmem = 4096(min)    87380(default)  6291456(max)
# 緩衝區會在min和max之間動態調整

亂序的情況越多,接收緩衝區的內存就被佔用的越多,對應的接收窗口就會變小,那正常能收的數據就變少了,網絡吞吐就變差了,也就是性能變差了。

因此,我們需要儘量保證所有同一個 TCP 連接下的所有 TCP 包都走相同路徑,這樣才能最大程度避免丟包

ECMP 的路徑選擇策略

當初開啓 ECMP 就是爲了提升性能,現在反而加重了亂序,降低了 TCP 傳輸性能。

這怎麼能忍。

爲了解決這個問題,我們需要有一個合理的路徑選擇策略。爲了避免同一個連接裏的數據包亂序,我們需要保證同一個連接裏的數據包,都走同樣的路徑。

這好辦。我們可以通過連接的五元組(發送方的 IP端口,接收方的 IP端口,以及通信協議)信息定位到唯一一條連接。

五元組

然後對五元組信息生成哈希鍵,讓同一個哈希鍵的數據走同一條路徑,問題就完美解決了。

五元組映射成 hash 鍵

根據五元組選擇 ECMP 路徑

TCP 和 Ping 走的網絡路徑一樣嗎

現在我們回到文章開頭的問題。

對於同樣的發送端和接收端,TCP 和 Ping 走的網絡路徑一樣嗎?

不一定一樣,因爲五元組裏的信息裏有一項是通信協議。ping 用的是 ICMP 協議,跟 TCP 協議不同,並且 ping 不需要用到端口,所以五元組不同,生成的哈希鍵不同,通過 ECMP 選擇到的路徑也可能不同。

TCP 和 ping 的五元組差異

同樣都用 TCP 協議,數據包走的網絡路徑一樣嗎

還是同樣的發送端和接收端,同樣是 TCP 協議,不同 TCP 連接走的網絡路徑是一樣的嗎?

跟上面的問題一樣,其實還是五元組的問題,同樣都是 TCP 協議,對於同樣的發送端和接收端,他們的 IP 和接收端的端口肯定是一樣的,但發送方的端口是可以隨時變化的,因此通過 ECMP 走的路徑也可能不同。

不同 TCP 連接的五元組差異

但問題又來了。

我知道這個有什麼用呢?我做業務開發,又沒有設置網絡路由的權限。

利用這個知識點排查問題

對於業務開發,這絕對不是個沒用的知識點。

如果某天,你發現,你能 ping 通目的機器,但用 TCP 去連,卻偶爾連不上目的機器。而且兩端機器都挺空閒,沒什麼性能上的瓶頸。實在走投無路了。

你就可以想想,會不會是網絡中用到了ECMP,其中一條鏈路有問題導致的。

ping 能成功但部分 TCP 連接失敗

排查方法也很簡單。

你是知道本機的 IP 以及目的機器的 IP 和端口號的,也知道自己用的是 TCP 連接。

只要你在報錯的時候打印下錯誤信息,你就知道了發送端的端口號了。

這樣五元組是啥你就知道了。

下一步就是指定發送端的端口號重新發起 TCP 請求,同樣的五元組,走同樣的路徑,按理說如果鏈路有問題,就肯定會復現。

如果不想改自己的代碼,你可以用 nc 命令指定客戶端端口看下能不能正常建立 TCP 連接。

nc -p 6666 baidu.com 80

-p 6666是指定發出請求的客戶端端口是6666,後面跟着的是連接的域名80 端口

通過 nc 成功建立 tcp 連接

假設用了6666端口的五元組去連接總是失敗,改用6667或其他端口卻能成功,你可以帶着這個信息去找找負責網絡的同事。

總結

參考資料

《網絡排查案例課》 ——極客時間

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/rnA-qVCgv0G5NFEUCS3M6w