深度 | 只有IT人才能讀懂的《西游記》
作為一部被重播數(shù)千次的電視劇,86版《西游記》毫無疑問是一部經典中的經典!對于絕大多數(shù)人來說都是童年最深的記憶。
本文作者以西游記為背景,為大家講述一個有關計算機網絡協(xié)議的故事。
我佛造經傳極樂
話說我佛如來為度化天下蒼生,有三藏真經,可勸人為善。
就如圖中所示,真經所藏之處,在于云端。佛祖所管轄之下,有四個區(qū)域 Region,稱為四大部洲,一是東勝神洲,二是南贍部洲,三是西牛賀洲,四是北俱盧洲。
我佛所在西牛賀洲,是主站點。
在每個區(qū)域 Region,為保證真經永固,設置多個藏經樓,稱為可用區(qū)(Available Zone)。
每個藏經樓里面是一排一排的柜子,稱為機柜,里面有一排一排的格子,稱為服務器,經文就擺放在格子中。
在藏經樓中,柜子根據(jù)經文分門別類的組織起來,由不同的神仙進行管理,管理一個柜子的經文的神仙,訪問這里面經文的鑰匙就在他手里,稱為接入層神仙(接入層交換機)。
多個接入層神仙被一組匯聚層神仙(匯聚層交換機)管著,多個匯聚層的神仙被一組核心層神仙(核心交換機)管著。
神仙體系組織嚴格,層次分明,不同的接入層神仙交換經文,要通過匯聚層神仙同意,不同的匯聚層神仙交換經文,需要核心層神仙同意。
經文的看守要萬無一失,因而每一層都是分組看護,互相監(jiān)督,互相備份,稱為堆疊。
雖說每個柜子里面放滿了經文,為了防止經文被偷聽偷看,經文的內容是被仙術封裝在一個虛擬的私密空間里面。
雖然有人可能會偷到物質的經文,但是沒有仙術打開這個私密空間,看到的經文如同空白的一樣。這個虛擬的私密空間稱為 VPC。
要解讀經文,需要使用每一格中一個不起眼的法寶,就是稱為 Openvswitch 的虛擬交換機,顧名思義就是起到經文在虛擬私密空間和物理空間之間的轉換作用。
Openvswitch 如何轉換呢?使用的是一種稱為 VXLAN 的封裝技術,但是必須要事先知道芝麻開門的 ID,也即 VXLAN ID,才能看到經文的真正內容。
在虛擬的空間中,放著真正可以解讀的真經。
真經有法一藏,談天;論一藏,說地;經一藏,度鬼;三藏共計三十五部,該一萬五千一百四十四卷,乃是修真之徑,正善之門。
看來已經前中后臺分離,分為基礎服務層,組合服務層,Controller 層,共三十五個模塊,一萬五千多個服務,真是微服務架構啊。
如何能夠不要迷失在這個一萬五千卷經文中,也是很有挑戰(zhàn)的事情,需要一個索引和指南,這就是常說的 RPC 框架和服務注冊與發(fā)現(xiàn)中心。
為了方便諸多僧侶前來取經,靈山腳下會有一個統(tǒng)一的入口地址,這里有一個神仙,稱為金頂大仙,專門來接應取經人的。
由于前來取經的人很多,同時經文也很多,所以金頂大仙多起到負載均衡的作用,將不同的取經人***到不同的藏經樓,訪問不同的經文。
金頂大仙所在的靈山腳下,是一個世界知名的地址,稱為外網 IP 地址,這個地址是全球可定位的,所有的取經人都先到這個地方。
金頂大仙通過 NAT 規(guī)則,將外網 IP 地址,變成藏經樓的私有 IP 地址,例如 2 號藏經樓三樓,4 號藏經樓五樓等。
在靈山藏經樓里面,是通過私有 IP 地址定位的。真經已經準備好,就差東土取經人了。
觀音奉旨上長安
可是佛祖愁啊,是這樣說的:我待要送上東土,叵耐那方眾生愚蠢,毀謗真言,不識我法門之要旨,怠慢了瑜迦之正宗。
怎么得一個有法力的,去東土尋一個善信,教他苦歷千山,遠經萬水,到我處求取真經,永傳東土,勸他眾生,卻乃是個山大的福緣,海深的善慶、誰肯去走一遭來?
真經就在靈山,可是東土之人愚鈍,不知道靈山咋辦呢?要一個法力無邊的人告訴他們呀。而且***能夠告訴全世界,靈山這里有真經。
好在有觀音菩薩,道:“弟子不才,愿上東土尋一個取經人來也。”,觀音菩薩有什么法力呢?當然是 BGP 協(xié)議了。
剛才那張圖畫的是一個可用區(qū)的情況,對于多個可用區(qū)的情況,我們可以隱去計算節(jié)點的情況,將外網訪問區(qū)域放大。
外網 IP 是放在虛擬網關的外網網口上的,這個 IP 如何讓全世界知道呢?在核心交換外面是安全設備,然后就是邊界路由器。
邊界路由器會和多個運營商連接,從而每個運營商都能夠訪問到這個網站。邊界路由器可以通過 BGP 協(xié)議,將自己數(shù)據(jù)中心里面的外網 IP 向外廣播,也就是告訴全世界,如果要訪問這些外網 IP,都來我這里。
每個運營商也有很多的路由器、很多的點,于是就可以將如何到達這些 IP 地址的路由信息,廣播到全國乃至全世界。
厲害吧,這是我佛如來告訴觀音菩薩的:“這一去。要踏看路道,不許在霄漢中行,須是要半云半霧;目過山水,謹記程途遠近之數(shù),叮嚀那取經人。“
就是說你去東土的路上,經過了哪些道路,要記住路徑,要記住遠近,才能告訴取經人這一路應該怎么走。
玄奘秉誠建大會
當觀音菩薩來到東土大唐,正看到玄奘法師坐在高臺上,帶領眾人誦經,念一會《受生度亡經》,談一會《安邦天寶篆》,又宣一會《勸修功卷》。
菩薩近前來,叫道:“那和尚,你只會談小乘教法,可會談大乘么?”玄奘聞言,心中大喜,翻身跳下臺來,對菩薩起手道:“老師父,弟子失瞻,多罪。見前的蓋眾僧人,都講的是小乘教法,卻不知大乘教法如何。”
菩薩道:“你這小乘教法,度不得亡者超升,只可渾俗和光而已。我有大乘佛法三藏,能超亡者升天,能度難人脫苦,能修無量壽身,能作無來無去。”
你看,在西方極樂凈土,我佛已經有了更牛的佛經,遙遠的東方,還在讀本土的僧人早期從西方傳過來的經。
這種模式,稱為 CDN。
我們部署應用的時候,一般會把靜態(tài)資源保存在兩個地方,一個是 Nginx 后面的 varnish 緩存里面,一般是靜態(tài)頁面。
對于比較大的、不經常更新的靜態(tài)圖片,會保存在對象存儲里面。這兩個地方的靜態(tài)資源都會配置 CDN,將資源下發(fā)到邊緣節(jié)點。
最初佛祖?zhèn)鹘?,都是口口相傳,經文都會記在高僧大德的心里,隨著高僧云游天下,隨著廟宇遍布天下,佛經從而遍布天下。這就相當于將佛經緩存在邊緣節(jié)點。
配置了 CDN 之后,權威 DNS 服務器上,會為靜態(tài)資源設置一個 CNAME 別名,指向另外一個域名 cdn.com,返回給本地 DNS 服務器。
當本地 DNS 服務器拿到這個新的域名時,需要繼續(xù)解析這個新的域名。這個時候,再訪問的時候就不是原來的權威 DNS 服務器了,而是 cdn.com 的權威 DNS 服務器。這是 CDN 自己的權威 DNS 服務器。
在這個服務器上,還是會設置一個 CNAME,指向另外一個域名,也即 CDN 網絡的全局負載均衡器。
本地 DNS 服務器去請求 CDN 的全局負載均衡器解析域名,全局負載均衡器會為用戶選擇一臺合適的緩存服務器提供服務,將 IP 返回給客戶端,客戶端去訪問這個邊緣節(jié)點,下載資源。緩存服務器響應用戶請求,將用戶所需內容傳送到用戶終端。
如果這臺緩存服務器上并沒有用戶想要的內容,那么這臺服務器就要向它的上一級緩存服務器請求內容,直至追溯到網站的源服務器,將內容拉到本地。
CDN 的全局負載均衡策略,就相當于當僧人們想讀佛經的時候,不必要都去西天,而是可以就近去問,周圍有沒有廟宇,然后向廟宇的師傅去請教佛經。
然而緩存的佛經當然是比不上西天取到的經文更新,所以東土由于離西天較遠,緩存的還是小乘佛教,要讀大乘佛教,就要去西天取經,稱為回源。
觀音顯像化金蟬
觀音菩薩打算度化玄奘法師,回源去西天取經。
可是怎么去呢,地址在哪里呢?玄奘法師只聽說西天,不知道具體的地址,這就要問觀音菩薩了。
這個時候,大家才知道,西天在靈山大雷音寺,距此十萬八千里。這個過程稱為 DNS 解析。
當在手機上面打開一個 App 的時候,首先要做的事情就是解析這個網站的域名。
在手機運營商所在的互聯(lián)網區(qū)域里,有一個本地的 DNS,手機會向這個 DNS 請求解析 DNS。當這個 DNS 本地有緩存,則直接返回。
如果沒有緩存,本地 DNS 才需要遞歸地從根 DNS 服務器,查到 .com 的***域名服務器,最終查到權威 DNS 服務器。
如果你使用云平臺的時候,配置了智能 DNS 和全局負載均衡,在權威 DNS 服務中,一般是通過配置 CNAME 的方式,我們可以起一個別名,例如 vip.yourcomany.com。
然后告訴本地 DNS 服務器,讓它請求 GSLB 解析這個域名,GSLB 就可以在解析這個域名的過程中,通過自己的策略實現(xiàn)負載均衡。
GSLB 通過查看請求它的本地 DNS 服務器所在的運營商和地址,就知道用戶所在的運營商和地址,然后在距離用戶位置比較近的 Region 里面,將三個本地負載均衡的公網 IP 地址,返回給本地 DNS 服務器。
本地 DNS 解析器將結果緩存后,返回給客戶端。對于手機 App 來說,可以繞過剛才的傳統(tǒng) DNS 解析機制,直接只要 HTTP DNS 服務,通過直接調用 HTTP DNS 服務器,得到這三個本地負載均衡的公網 IP 地址。
這個公網 IP 地址,就是金頂大仙所在的位置。其實這個時候,金頂大仙已經在等待了。
這個時候,李世民突然開始說話了,曰:“誰肯領朕旨意,上西天拜佛求經?“ 并愿意買下觀音手中的兩件寶物,“錦瀾袈裟”一領,“九環(huán)錫杖”一根,佛祖說過:”若有取經人堅心來此,穿我的袈裟,免墮輪回;持我的錫枚,不遭毒害。“
玄奘法師回答:“貧僧不才,愿效犬馬之勞,與陛下求取真經,祈保我王江山永固。”
這個時候,菩薩說了:“西天路遠,更多虎豹妖魔。只怕有去無回,難保身命。”
玄奘道:“我已發(fā)了弘誓大愿,不取真經,永墮沉淪地獄。“
其實這里的對話是很有意思的,玄奘法師回復李世民的和回復觀音菩薩的不同。
這個時候,李世民作為世俗的君王,已經想求取真經了,也就是東土大唐作為客戶端,要發(fā)起對于服務端的請求了。但是玄奘法師知道,唐王李世民去取經,求的是江山永固。
所以李世民的請求是應用層的,發(fā)起的是 HTTP 的協(xié)議,在 HTTP 的請求正文中,怕是寫的“江山永固”四個字。
而玄奘法師回復觀音菩薩的時候,說的就不同了,是一種對于真經和佛法本身的堅持。
所以玄奘法師是 TCP 層的,TCP 是面向連接的,TCP 是靠譜的協(xié)議,但是這不能說明它面臨的網絡環(huán)境好。
從 IP 層面來講,如果網絡狀況的確那么差,是沒有任何可靠性保證的,而作為 IP 的上一層 TCP 也無能為力,唯一能做的就是更加努力,不斷重傳,通過各種算法保證。
也就是說,對于 TCP 來講,IP 層你丟不丟包,我管不著,但是我在我的層面上,會努力保證可靠性。
這一點在流沙河有了驗證。觀音菩薩度化沙悟凈的時候,沙悟凈說:“菩薩,我在此間吃人無數(shù),向來有幾次取經人來,都被我吃了。凡吃的人頭,拋落流沙,竟沉水底(這個水,鵝毛也不能浮),惟有九個取經人的骷髏,浮在水面,再不能沉。我以為異物,將索兒穿在一處,閑時拿來頑耍,這去,但恐取經人不得到此,卻不是反誤了我的前程也?”
菩薩日:“豈有不到之理?你可將骷髏地掛在頭頂下,等候取經入,自有用處。”
所以沙悟凈脖子上這九個骷髏,是唐三藏的前九輩子,一旦吃了,就不斷的重試。
為了能夠實現(xiàn)重試,實現(xiàn) TCP 的可靠性,客戶端和服務器需要建立連接。
HTTPS 協(xié)議是基于 TCP 協(xié)議的,因而要先建立 TCP 的連接。在這個例子中,TCP 的連接是從手機上的 App 和負載均衡器 SLB 之間的。也就是唐僧和金頂大仙之間,到了金頂大仙,就不怕了,會指引到佛祖那里的。
盡管中間要經過很多的路由器和交換機,但是 TCP 的連接是端到端的。
TCP 這一層和更上層的 HTTPS 無法看到中間的包的過程。盡管建立連接的時候,所有的包都逃不過在這些路由器和交換機之間的轉發(fā),轉發(fā)的細節(jié)我們放到那個下單請求的發(fā)送過程中詳細解讀,這里只看端到端的行為。
對于 TCP 連接來講,需要通過三次握手建立連接,為了維護這個連接,雙方都需要在 TCP 層維護一個連接的狀態(tài)機。
一開始,客戶端和服務端都處于 CLOSED 狀態(tài)。服務端先是主動監(jiān)聽某個端口,處于 LISTEN 狀態(tài)。
然后客戶端主動發(fā)起連接 SYN,之后處于 SYN-SENT 狀態(tài)。服務端收到發(fā)起的連接,返回 SYN,并且 ACK 客戶端的 SYN,之后處于 SYN-RCVD 狀態(tài)。
客戶端收到服務端發(fā)送的 SYN 和 ACK 之后,發(fā)送 ACK 的 ACK,之后處于 ESTABLISHED 狀態(tài)。
這是因為,它一發(fā)一收成功了。服務端收到 ACK 的 ACK 之后,處于 ESTABLISHED 狀態(tài),因為它的一發(fā)一收也成功了。
當 TCP 層的連接建立完畢之后,接下來輪到 HTTPS 層建立連接了,在 HTTPS 的交換過程中,TCP 層始終處于 ESTABLISHED。
對于 HTTPS,客戶端會發(fā)送 Client Hello 消息到服務器,用明文傳輸 TLS 版本信息、加密套件候選列表、壓縮算法候選列表等信息。另外,還會有一個隨機數(shù),在協(xié)商對稱密鑰的時候使用。
然后,服務器會返回 Server Hello 消息,告訴客戶端,服務器選擇使用的協(xié)議版本、加密套件、壓縮算法等。這也有一個隨機數(shù),用于后續(xù)的密鑰協(xié)商。
然后,服務器會給你一個服務器端的證書,然后說:“Server Hello Done,我這里就這些信息了。”
客戶端當然不相信這個證書,于是你從自己信任的 CA 倉庫中,拿 CA 的證書里面的公鑰去解密電商網站的證書。
如果能夠成功,則說明電商網站是可信的。這個過程中,你可能會不斷往上追溯 CA、CA 的 CA、CA 的 CA 的 CA,反正直到一個授信的 CA,就可以了。
其實觀音菩薩手里的錫杖和袈裟,就相當于佛祖頒發(fā)的證書,保證西行路上的安全,玄奘法師這個網絡包別被別人吃了,或者篡改。
就像誤入小雷音一集中,白眉老佛想吃了唐僧肉,自己披上袈裟,西天取經,求得正果。
當然,一開始觀音菩薩拿出錫杖和袈裟這個證書的時候,大家也不相信,所以需要觀音菩薩現(xiàn)出真身,作為 CA,證明給客戶端,唐王李世民和玄奘法師才下拜。
證書驗證完畢之后,覺得這個服務端是可信的,于是客戶端計算產生隨機數(shù)字 Pre-master,發(fā)送 Client Key Exchange,用證書中的公鑰加密,再發(fā)送給服務器,服務器可以通過私鑰解密出來。
接下來,無論是客戶端還是服務器,都有了三個隨機數(shù),分別是:自己的、對端的,以及剛生成的 Pre-Master 隨機數(shù)。通過這三個隨機數(shù),可以在客戶端和服務器產生相同的對稱密鑰。
有了對稱密鑰,客戶端就可以說:“Change Cipher Spec,咱們以后都采用協(xié)商的通信密鑰和加密算法進行加密通信了。”
然后客戶端發(fā)送一個 Encrypted Handshake Message,將已經商定好的參數(shù)等,采用協(xié)商密鑰進行加密,發(fā)送給服務器用于數(shù)據(jù)與握手驗證。
同樣,服務器也可以發(fā)送 Change Cipher Spec,說:“沒問題,咱們以后都采用協(xié)商的通信密鑰和加密算法進行加密通信了”,并且也發(fā)送 Encrypted Handshake Message 的消息試試。
當雙方握手結束之后,就可以通過對稱密鑰進行加密傳輸了。
唐王素酒送三藏
玄奘這個網絡包要發(fā)出了。太宗設朝,聚集文武,要去送行。李世民送給玄奘三個東西。
上一節(jié)說了太宗是應用層,關注保大唐江山永固,玄奘是 TCP 層,要通過堅定的意志到達西天。
李世民給的***個東西是通關文牒,這個是 IP 層的,將來要通過這個文牒通過一個個城關。
第二個東西是紫金缽盂,這個用于玄奘法師到了某個城市里面化齋,同時打聽路的時候使用,這個是一個 MAC 層的。
第三個東西是白馬一匹,作為遠程腳力,這個是物理層的。
***,太宗敬了玄奘一杯素酒,言道:寧戀本鄉(xiāng)一捻土,莫愛他鄉(xiāng)萬兩金。三藏方悟捻土之意,復謝恩飲盡,辭謝出關而去。
當客戶端和服務端之間建立了連接后,接下來就要發(fā)送下單請求的網絡包了。
在用戶層發(fā)送的是 HTTP 的網絡包,因為服務端提供的是 RESTful API,因而 HTTP 層發(fā)送的就是一個請求。
- POST /purchaseOrder HTTP/1.1
- Host: www.xxxxxx.com
- Content-Type: application/json; charset=utf-8
- Content-Length: nnn
- {
- "order": {
- "date": "2018-07-01",
- "className": "趣談網絡協(xié)議",
- "Author": "劉超",
- "price": "68"
- }
- }
HTTP 的報文大概分為三大部分。***部分是請求行,第二部分是請求的首部,第三部分才是請求的正文實體。
在請求行中,URL 就是 www.xxxxxx.com/purchaseOrder ,版本為 HTTP 1.1。
請求的類型叫作 POST,它需要主動告訴服務端一些信息,而非獲取。需要告訴服務端什么呢?一般會放在正文里面。正文可以有各種各樣的格式,常見的格式是 JSON。
請求行下面就是我們的首部字段。首部是 key value,通過冒號分隔。
Content-Type 是指正文的格式。例如,我們進行 POST 的請求,如果正文是 JSON,那么我們就應該將這個值設置為 JSON。
接下來是正文,這里是一個 JSON 字符串,里面通過文本的形式描述了,要買一個課程,作者是誰,多少錢。
這樣,HTTP 請求的報文格式就拼湊好了。接下來瀏覽器或者移動 App 會把它交給下一層傳輸層。
怎么交給傳輸層呢?也是用 Socket 進行程序設計。如果用的是瀏覽器,這些程序不需要你自己寫,有人已經幫你寫好了;如果在移動 App 里面,一般會用一個 HTTP 的客戶端工具來發(fā)送,并且?guī)湍惴庋b好。
HTTP 協(xié)議是基于 TCP 協(xié)議的,所以它使用面向連接的方式發(fā)送請求,通過 Stream 二進制流的方式傳給對方。當然,到了 TCP 層,它會把二進制流變成一個的報文段發(fā)送給服務器。
在 TCP 頭里面,會有源端口號和目標端口號,目標端口號一般是服務端監(jiān)聽的端口號,源端口號在手機端,往往是隨機分配一個端口號。這個端口號在客戶端和服務端用于區(qū)分請求和返回,發(fā)給那個應用。
在 IP 頭里面,都需要加上自己的地址(即源地址)和它想要去的地方(即目標地址)。
當一個手機上線的時候,PGW 會給這個手機分配一個 IP 地址,這就是源地址,而目標地址則是云平臺的負載均衡器的外網 IP 地址。
在 IP 層,客戶端需要查看目標地址和自己是否是在同一個局域網,計算是否是同一個網段,往往需要通過 CIDR 子網掩碼來計算。
對于這個下單場景,目標 IP 和源 IP 不會在同一個網段,因而需要發(fā)送到默認的網關。一般通過 DHCP 分配 IP 地址的時候,也會同時配置默認網關的 IP 地址。
但是客戶端不會直接使用默認網關的 IP 地址,而是發(fā)送 ARP 協(xié)議,來獲取網關的 MAC 地址,然后將網關 MAC 作為目標 MAC,自己的 MAC 作為源 MAC,放入 MAC 頭,發(fā)送出去。
一個完整的網絡包的格式是這樣的:
接下來,網絡包就正式發(fā)出了。如果你是用手機打開 App,下單購物發(fā)送網絡包,一般通過手機運營商的網絡。
客戶的手機開機以后,在附近尋找基站 eNodeB,發(fā)送請求,申請上網。基站將請求發(fā)給 MME,MME 對手機進行認證和鑒權,還會請求 HSS 看有沒有錢,看看是在哪里上網。
當 MME 通過了手機的認證之后,開始建立隧道,建設的數(shù)據(jù)通路分兩段路,其實是兩個隧道。一段是從 eNodeB 到 SGW,第二段是從 SGW 到 PGW,在 PGW 之外,就是互聯(lián)網。
PGW 會為手機分配一個 IP 地址,手機上網都是帶著這個 IP 地址的。
對于手機來講,默認的網關在 PGW 上。在移動網絡里面,從手機到 SGW,到 PGW 是有一條隧道的。
在這條隧道里面,會將上面的這個包作為隧道的乘客協(xié)議放在里面,外面 SGW 和 PGW 在核心網機房的 IP 地址。網絡包直到 PGW(PGW 是隧道的另一端)才將里面的包解出來,轉發(fā)到外部網絡。
所以,從手機發(fā)送出來的時候,網絡包的結構為:
- 源 MAC:手機也即 UE 的 MAC。
- 目標 MAC:網關 PGW 上面的隧道端點的 MAC。
- 源 IP:UE 的 IP 地址。
- 目標 IP:SLB 的公網 IP 地址。
進入隧道之后,要封裝外層的網絡地址,因而網絡包的格式為:
- 外層源 MAC:E-NodeB 的 MAC。
- 外層目標 MAC:SGW 的 MAC。
- 外層源 IP:E-NodeB 的 IP。
- 外層目標 IP:SGW 的 IP。
- 內層源 MAC:手機也即 UE 的 MAC。
- 內層目標 MAC:網關 PGW 上面的隧道端點的 MAC。
- 內層源 IP:UE 的 IP 地址。
- 內層目標 IP:SLB 的公網 IP 地址。
當隧道在 SGW 的時候,切換了一個隧道,為從 SGW 到 PGW 的隧道,因而網絡包的格式為:
- 外層源 MAC:SGW 的 MAC。
- 外層目標 MAC:PGW 的 MAC。
- 外層源 IP:SGW 的 IP。
- 外層目標 IP:PGW 的 IP。
- 內層源 MAC:手機也即 UE 的 MAC。
- 內層目標 MAC:網關 PGW 上面的隧道端點的 MAC。
- 內層源 IP:UE 的 IP 地址。
- 內層目標 IP:SLB 的公網 IP 地址。
在 PGW 的隧道端點將包解出來,轉發(fā)出去的時候,一般在 PGW 出外部網絡的路由器上,會部署 NAT 服務,將手機的 IP 地址轉換為公網 IP 地址,當請求返回的時候,再 NAT 回來。
因而在 PGW 之后,相當于做了一次歐洲十國游型的轉發(fā),網絡包的格式為:
- 源 MAC:PGW 出口的 MAC。
- 目標 MAC:NAT 網關的 MAC。
- 源 IP:UE 的 IP 地址。
- 目標 IP:SLB 的公網 IP 地址。
在 NAT 網關,相當于做了一次玄奘西游型的轉發(fā),網絡包的格式變成:
- 源 MAC:NAT 網關的 MAC。
- 目標 MAC:A2 路由器的 MAC。
- 源 IP:UE 的公網 IP 地址。
- 目標 IP:SLB 的公網 IP 地址。
在手機運營商的網絡里面,網絡狀況是比較好的。對于玄奘法師,在大唐國境之內,還是比較平安的。
原文說:師徒們行了數(shù)日,到了鞏州城。早有鞏州合屬官吏人等,迎接入城中。安歇一夜,次早出城前去。
一路饑餐渴飲,夜住曉行,兩三日,又至河州衛(wèi)。早有鎮(zhèn)邊的總兵與本處僧道,聞得是欽差御弟法師上西方見佛,無不恭敬,接至里面供給了,著僧綱請往福原寺安歇。
本寺僧人,一一參見,安排晚齋。齋畢,吩咐二從者飽喂馬匹,天不明就行。
真的是有接有送。行經半日,只見對面處,有一座大山,真?zhèn)€是高接青霄,崔巍險峻。
此山喚做兩界山,東半邊屬我大唐所管,西半邊乃是韃靼的地界。過了這座山,就不是大唐的土地了。
歷經千山與萬險
離開大唐的國土,接下來的路應該怎么走呢?
好在此去西天,要經過一個個國家,每個國家有一個個城關,玄奘法師只要到處問路,只要這些城關的守門人知道大概路怎么走,就能一個個國家的走下去,如果遇到國家,還有通關文牒,還能保護玄奘法師在國內的安全。
這里有兩個問題要解決,***個是每個城關的守門人和每個國家,是怎么知道去西天怎么走的。第二個問題是玄奘如何問路,如何走。
我們先***個問題,這個觀音菩薩從西天來東土的時候,已經通過一種法術告訴這些國家和城關了。
菩薩的法術主要分兩種情況,一種情況是在一個國家內部如何走,另一種情況在國家之間,在野外如何走的問題。
在一個國家內部,菩薩主要遵循最短路徑原則,就是走得路越少越好,道路越短越好。
但是國家之間,菩薩不但要考慮遠近的問題,還要考慮政策的問題。例如有的國家路近,但是路過的國家看不慣僧人,見了僧人就抓。例如滅法國,連光頭都要抓。這樣的情況即便路近,也***繞遠點走。
菩薩的法術是什么呢?咱們在大學里面學習計算機網絡與數(shù)據(jù)結構的時候,知道求最短路徑常用的有兩種方法:
- Bellman-Ford 算法
- Dijkstra 算法
在計算機網絡中基本也是用這兩種方法計算的。距離矢量路由(distance vector routing),它是基于 Bellman-Ford 算法的。鏈路狀態(tài)路由(link state routing),基于 Dijkstra 算法。
最常用的兩種路由協(xié)議:
- OSPF(Open Shortest Path First,開放式最短路徑優(yōu)先)就是這樣一個基于鏈路狀態(tài)路由協(xié)議,廣泛應用在數(shù)據(jù)中心中的協(xié)議,稱為內部網關協(xié)議(Interior Gateway Protocol,簡稱 IGP)。
- BGP 協(xié)議使用的算法是路徑矢量路由協(xié)議(path-vector protocol)。它是距離矢量路由協(xié)議的升級版,稱為外網路由協(xié)議(Border Gateway Protocol,簡稱 BGP)。
路由協(xié)議是城關之間相互溝通到哪里應該怎么走的協(xié)議。
第二個問題,也就是玄奘如何問路,如何走。這就是 IP 協(xié)議。
這就要靠通關文牒了,里面寫著貧僧來自東土大唐(就是源 IP 地址),欲往西天拜佛求經(指的是目標 IP 地址)。路過寶地,借宿一晚,明日啟行,請問接下來該怎么走啊?
在解決***個問題的時候,每個城關已經通過菩薩的法術,和鄰近的城關進行溝通,知道了下面的信息。
這個叫路由表,根據(jù)這個表格,可以告訴唐僧怎么走。接下來我們看完整故事。
出了 NAT 網關,就從核心網到達了互聯(lián)網。在網絡世界,每一個運營商的網絡成為自治系統(tǒng) AS。每個自治系統(tǒng)都有邊界路由器,通過它和外面的世界建立聯(lián)系。
對于云平臺來講,它可以被稱為 Multihomed AS,有多個連接連到其他的 AS,但是大多拒絕幫其他的 AS 傳輸包。例如一些大公司的網絡。
對于運營商來說,它可以被稱為 Transit AS,有多個連接連到其他的 AS,并且可以幫助其他的 AS 傳輸包,比如主干網。
如何從出口的運營商到達云平臺的邊界路由器?在路由器之間需要通過 BGP 協(xié)議實現(xiàn),BGP 又分為兩類,eBGP 和 iBGP。自治系統(tǒng)間,邊界路由器之間使用 eBGP 廣播路由。內部網絡也需要訪問其他的自治系統(tǒng)。
邊界路由器如何將 BGP 學習到的路由導入到內部網絡呢?通過運行 iBGP,使內部的路由器能夠找到到達外網目的地***的邊界路由器。
網站的 SLB 的公網 IP 地址早已經通過云平臺的邊界路由器,讓全網都知道了。
于是這個下單的網絡包選擇了下一跳是 A2,也即將 A2 的 MAC 地址放在目標 MAC 地址中。
到達 A2 之后,從路由表中找到下一跳是路由器 C1,于是將目標 MAC 換成 C1 的 MAC 地址。
到達 C1 之后,找到下一跳是 C2,將目標 MAC 地址設置為 C2 的 MAC。到達 C2 后,找到下一跳是云平臺的邊界路由器,于是將目標 MAC 設置為邊界路由器的 MAC 地址。
你會發(fā)現(xiàn),這一路,都是只換 MAC,不換目標 IP 地址。這就是所謂下一跳的概念。
在云平臺的邊界路由器,會將下單的包轉發(fā)進來,經過核心交換,匯聚交換,到達外網網關節(jié)點上的 SLB 的公網 IP 地址。
我們可以看到,手機到 SLB 的公網 IP,是一個端到端的連接,連接的過程發(fā)送了很多包。
所有這些包,無論是 TCP 三次握手,還是 HTTPS 的密鑰交換,都是要走如此復雜的過程到達 SLB 的,當然每個包走的路徑不一定一致。
當網絡包走在這個復雜的道路上,很可能一不小心就丟了,怎么辦?這就需要借助 TCP 的機制重新發(fā)送。
既然 TCP 要對包進行重傳,就需要維護一個 Sequence Number,看哪些包到了,哪些沒到,哪些需要重傳,傳輸?shù)乃俣葢摽刂频蕉嗌伲@就是 TCP 的滑動窗口協(xié)議。
整個 TCP 的發(fā)送,一開始會協(xié)商一個 Sequence Number,從這個 Sequence Number 開始,每個包都有編號。
滑動窗口將接收方的網絡包分成四個部分:
- 已經接收,已經 ACK,已經交給應用層的包。
- 已經接收,已經 ACK,未發(fā)送給應用層。
- 已經接收,尚未發(fā)送 ACK。
- 未接收,尚有空閑的緩存區(qū)域。
對于 TCP 層來講,每一個包都有 ACK。ACK 需要從 SLB 回復到手機端,將上面的那個過程反向來一遍,當然路徑不一定一致,可見 ACK 也不是那么輕松的事情。
如果發(fā)送方超過一定的時間沒有收到 ACK,就會重新發(fā)送。只有 TCP 層 ACK 過的包,才會發(fā)給應用層,并且只會發(fā)送一份,對于下單的場景,應用層是 HTTP 層。
你可能會問了,TCP 老是重復發(fā)送,會不會導致一個單下了兩遍?是否要求服務端實現(xiàn)冪?
從 TCP 的機制來看,是不會的。只有收不到 ACK 的包才會重復發(fā),發(fā)到接收端,在窗口里面只保存一份,所以在同一個 TCP 連接中,不用擔心重傳導致二次下單。
但是 TCP 連接會因為某種原因斷了,例如手機信號不好,這個時候手機把所有的動作重新做一遍,建立一個新的 TCP 連接,在 HTTP 層調用兩次 RESTful API。這個時候可能會導致兩遍下單的情況,因而 RESTful API 需要實現(xiàn)冪等。
當 ACK 過的包發(fā)給應用層之后,TCP 層的緩存就空了出來,這會導致上面圖中的大三角,也即接收方能夠容納的總緩存,整體順時針滑動。
小的三角形,也即接收方告知發(fā)送方的窗口總大小,也即還沒有完全確認收到的緩存大小,如果把這些填滿了,就不能再發(fā)了,因為沒確認收到,所以一個都不能扔。
功成行滿見真如
唐僧經歷九九八十一難,終于到達了西天。發(fā)現(xiàn)金頂大仙已經在等他們了。
網絡包從手機端經歷千難萬險,終于到了 SLB 的公網 IP 所在的公網網口。由于匹配上了 MAC 地址和 IP 地址,因而將網絡包收了進來。
到了西天,唐僧度過***一條河凌云仙渡的時候,發(fā)現(xiàn)滾浪飛流,約有八九里寬闊,四無人跡。
好不容易盼來一條船,還沒有底。原來駕船的是接引佛祖,玄奘法師的肉體隨著河水飄走,從而脫胎換骨,成就金身。
在虛擬網關節(jié)點的外網網口上,會有一個 NAT 規(guī)則,將公網 IP 地址轉換為 VPC 里面的私網 IP 地址,這個私網 IP 地址就是 SLB 的 HAProxy 所在的虛擬機的私網 IP 地址。
從而網絡包也脫胎換骨,實現(xiàn)公網 IP 到私有網絡 IP 的轉換。
當然為了承載比較大的吞吐量,虛擬網關節(jié)點會有多個,物理網絡會將流量分發(fā)到不同的虛擬網關節(jié)點。
同樣 HAProxy 也會是一個大的集群,虛擬網關會選擇某個負載均衡節(jié)點,將某個請求分發(fā)給它,負載均衡之后是 Controller 層,也是部署在虛擬機里面的。
當網絡包里面的目標 IP 變成私有 IP 地址之后,虛擬路由會查找路由規(guī)則,將網絡包從下方的私網網口發(fā)出來。
這個時候包的格式為:
- 源 MAC:網關 MAC。
- 目標 MAC:HAProxy 虛擬機的 MAC。
- 源 IP:UE 的公網 IP。
- 目標 IP:HAProxy 虛擬機的私網 IP。
在***部分,我們說佛經是存放在一個虛擬空間里面的,要打開這個虛擬空間,解讀經文,需要一個芝麻開門的 ID。接引佛祖會給玄奘法師一個 ID。
在虛擬路由節(jié)點上,也會有 OVS,將網絡包封裝在 VXLAN 隧道里面,VXLAN ID 就是給你的租戶創(chuàng)建 VPC 的時候分配的。
VXLAN ID 就是 VPC 虛擬空間的 ID,OVS 就是那個能夠封裝和解開私密空間的法寶。
包的格式為:
- 外層源 MAC:網關物理機 MAC。
- 外層目標 MAC:物理機 A 的 MAC。
- 外層源 IP:網關物理機 IP。
- 外層目標 IP:物理機 A 的 IP。
- 內層源 MAC:網關 MAC。
- 內層目標 MAC:HAProxy 虛擬機的 MAC。
- 內層源 IP:UE 的公網 IP。
- 內層目標 IP:HAProxy 虛擬機的私網 IP。
在物理機 A 上,OVS 會將包從 VXLAN 隧道里面解出來,發(fā)給 HAProxy 所在的虛擬機。
HAProxy 所在的虛擬機發(fā)現(xiàn) MAC 地址匹配,目標 IP 地址匹配,就根據(jù) TCP 端口,將包發(fā)給 HAProxy 進程,因為 HAProxy 是在監(jiān)聽這個 TCP 端口的。
因而 HAProxy 就是這個 TCP 連接的服務端,客戶端是手機。對于 TCP 的連接狀態(tài),滑動窗口等,都是在 HAProxy 上維護的。
在這里 HAProxy 是一個四層負載均衡,也即他只解析到 TCP 層,里面的 HTTP 協(xié)議他不關心,就將請求轉發(fā)給后端的多個 Controller 層的一個。
HAProxy 發(fā)出去的網絡包就認為 HAProxy 是客戶端了,看不到手機端了,網絡包格式如下:
- 源 MAC:HAProxy 所在虛擬機的 MAC。
- 目標 MAC:Controller 層所在虛擬機的 MAC。
- 源 IP:HAProxy 所在虛擬機的私網 IP。
- 目標 IP:Controller 層所在虛擬機的私網 IP。
當然這個包發(fā)出去之后,還是會被物理機上的 OVS 放入 VXLAN 隧道里面,網絡包格式為:
- 外層源 MAC:物理機 A 的 MAC。
- 外層目標 MAC:物理機 B 的 MAC。
- 外層源 IP:物理機 A 的 IP。
- 外層目標 IP:物理機 B 的 IP。
- 內層源 MAC:HAProxy 所在虛擬機的 MAC。
- 內層目標 MAC:Controller 層所在虛擬機的 MAC。
- 內層源 IP:HAProxy 所在虛擬機的私網 IP。
- 內層目標 IP:Controller 層所在虛擬機的私網 IP。
在物理機 B 上,OVS 會將包從 VXLAN 隧道里面解出來,發(fā)給 Controller 層所在的虛擬機。
Controller 層所在的虛擬機發(fā)現(xiàn) MAC 地址匹配,目標 IP 地址匹配,就根據(jù) TCP 端口,將包發(fā)給 Controller 層的進程,因為他是在監(jiān)聽這個 TCP 端口的。
在 HAProxy 和 Controller 層之間,維護一個 TCP 的連接。Controller 層收到包之后,他是關心 HTTP 里面是什么的,于是解開 HTTP 的包,發(fā)現(xiàn)是一個 POST 請求,內容是下單購買一個課程。
取得真經成金身
玄奘法師終于到達西天大雷音寺,見到了我佛如來。
佛祖愿意傳經給玄奘,于是讓玄奘去藏經樓取經文,誰知道西天也有西天的規(guī)矩,如果不懂這里的規(guī)矩,就很難和管理經文的人溝通,取不到真經。
同理,在電商服務里面,往往在組合服務層會有一個專門管理下單的服務,Controller 層雖然對外暴露的是標準的 RESTful 協(xié)議,但是對內會通過 RPC 協(xié)議調用這個組合服務層。如果不懂這個協(xié)議,就沒法通信。
假設我們使用的是 Dubbo,則 Controller 層需要讀取注冊中心,將下單服務的進程列表拿出來,選出一個來調用。
Dubbo 中默認的 RPC 協(xié)議是 Hessian2。Hessian2 將下單的遠程調用序列化為二進制進行傳輸。
Netty 是一個非阻塞的基于事件的網絡傳輸框架。Controller 層和下單服務之間,使用了 Netty 的網絡傳輸框架。
有了 Netty,就不用自己編寫復雜的異步 Socket 程序了。Netty 使用的方式,就是咱們講 Socket 編程的時候,一個項目組支撐多個項目(IO 多路復用,從派人盯著到有事通知)這種方式。
Netty 還是工作在 Socket 這一層的,發(fā)送的網絡包還是基于 TCP 的。在 TCP 的下層,還是需要封裝上 IP 頭和 MAC 頭。
如果跨物理機通信,還是需要封裝的外層的 VXLAN 隧道里面。當然底層的這些封裝,Netty 都不感知,它只要做好它的異步通信即可。
在 Netty 的服務端,也即下單服務中,收到請求后,先用 Hessian2 的格式進行解壓縮。然后將請求分發(fā)到線程中進行處理,在線程中,會調用下單的業(yè)務邏輯。
玄奘師徒好在后來碰到了懂得內情的注冊中心——彌勒佛,從而回到靈山,還是按照人家的規(guī)矩辦了,才將無字經文,換成有字經文。
下單的業(yè)務邏輯比較復雜,往往要調用基礎服務層里面的庫存服務、優(yōu)惠券服務等,將多個服務調用完畢,才算下單成功。
下單服務調用庫存服務和優(yōu)惠券服務,也是通過 Dubbo 的框架,通過注冊中心拿到庫存服務和優(yōu)惠券服務的列表,然后選一個調用。
調用的時候,統(tǒng)一使用 Hessian2 進行序列化,使用 Netty 進行傳輸,底層如果跨物理機,仍然需要通過 VXLAN 的封裝和解封裝。
咱們以庫存為例子的時候,講述過冪等的接口實現(xiàn)的問題。因為如果扣減庫存,僅僅是誰調用誰減一。
這樣存在的問題是,如果扣減庫存因為一次調用失敗,而多次調用,這里指的不是 TCP 多次重試,而是應用層調用的多次重試,就會存在庫存扣減多次的情況。
這里常用的方法是,使用樂觀鎖(Compare and Set,簡稱 CAS)。CAS 要考慮三個方面,當前的庫存數(shù)、預期原來的庫存數(shù)和版本,以及新的庫存數(shù)。
在操作之前,查詢出原來的庫存數(shù)和版本,真正扣減庫存的時候,判斷如果當前庫存的值與預期原值和版本相匹配,則將庫存值更新為新值,否則不做任何操作。
這是一種基于狀態(tài)而非基于動作的設計,符合 REST 的架構設計原則。這樣的設計有利于高并發(fā)場景。
當多個線程嘗試使用 CAS 同時更新同一個變量時,只有其中一個線程能更新變量的值,而其他線程都失敗,失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次嘗試。
最終,當下單更新到分布式數(shù)據(jù)庫中之后,整個下單過程才算真正告一段落。
當然,這個下單調用要返回一個結果。我們下單成功啦!!!!!!