TCP粘合技術原理
本部分,將對面向內容交換的負載平衡中,使用的主要網路通訊技術手段進行分析。其中,關於通訊的半工(TCP HandOff)和雙工(TCP Splicing粘合模式)是目前Content Switch(面向內容交換)集群系統使用的主要技術。傳統的負載平衡技術主要有應用層協議代理伺服器、三層和四層交換等。其中,應用層代理技術,面向特定的應用層協議,對客戶端和伺服器的資料流進行轉換;三層和四層交換通過識別資料報文的有效地址資訊進行雙向的對映和排程。不管採用哪一種技術,其根本模式都是在網路連線的基礎上進一步對資料包進行業務級分流。
1、應用級代理
應用級代理廣泛的應用於HTTP代理,HTTP快取等領域,扮演著當今重要的網路服務角色。
一般的應用層代理都採用應用層連線粘合代理(split-connection),代理伺服器位於客戶端和被訪問的服務站點之間,代理伺服器透明的將客戶請求和伺服器響應進行雙向轉換,以協助雙方完成通訊的全過程。從HTTP快取伺服器(眾所周知的Squid)到安全防火牆,代理伺服器始終保持了對現有網路服務協議的相容性(主要是HTTP等)以及對現有應用框架的整合性。
當用戶提交請求,代理將請求轉發給伺服器,對於伺服器而言,代理充當了客戶端的角色;當伺服器響應請求,代理收到後將資料返回客戶端,對於客戶端而言,充當服務端的角色。
但是,連線粘合代理存在著幾大致命的問題:首先,代理伺服器的效能始終是大規模通訊的瓶頸;其次,由於代理伺服器對任何方向上的資料都要進行轉換,大大增加了通訊延遲;最後,代理本質上對端到端的傳輸協議進行了修改,難免會存在傳輸協議的相容性問題。
因此,在叢集應用中,特別是大規模負載平衡集群系統中很難看到應用級代理的身影。
圖一:應用層代理原理示意圖
當客戶端向伺服器發起請求的時候,連線被重定向到代理伺服器的指定埠上(例如IE瀏覽器中設定的代理一樣);代理接收到這一連線請求以後,根據請求的目標地址和埠,向被訪問的伺服器發出另一個連線請求,並將客戶端發來的請求轉發到目標伺服器上;隨後,代理伺服器強制在兩個連線上進行互相轉發請求資料和應答資料,成為客戶端和伺服器之間必經之路。因此,代理實現客戶端對伺服器的透明訪問。本質上,代理將客戶端的請求進行適當的修改,重新通過另一個連線傳送到伺服器端,反之依然。這樣子的缺點是顯然的:資料的雙向拷貝需要經由核心的TCP/IP協議棧到使用者空間,修改後再由使用者空間到TCP/IP層進行處理轉發,頻繁的上下文交換導致代理的效率低下。而代理伺服器也不得不維護至少雙倍於請求數量的連線,這對於代理伺服器的記憶體和CPU排程都有極高的要求。
這類的代理伺服器大多數針對特定的應用層協議進行雙向網路連線的交換工作,比如特定的HTTP、HTTPS代理,FTP代理等,都要求代理伺服器理解對應的高層協議的通訊原語,因此往往通過一個多執行緒的使用者程序來實現。
2、核心級的TCP粘合交換技術 ― TCP Splicing
大多數的四層交換產品,都是在網路傳輸協議層(TCP)進行工作。它們通過轉換資料包的埠和IP地址資訊,將資料重新定向到服務節點上。比如,將HTTP請求定向到Web Cache伺服器上,或者將請求均勻分配到不同的節點實現負載平衡。而面向內容排程的提出,要求在四層交換的基礎上進一步識別交換資料的內容,以實現智慧交換和更高的效能。
傳統的四層交換,在客戶端發出第一個Syn包後,均衡器對服務節點群進行選擇,並將該SYN後續的報文直接發往選定的節點。處理的模式固然簡單並且有效率,但是對於需要解析報文內容的交換機而言,需要在連線建立後(SYN開始之後並完成三次握手),才從接下來的報文中獲得負載平衡所需的資訊(比如,一個HTTP協議的Get的請求,在客戶端完成了連線建立的操作之後,均衡器才能獲得具體的Get指令內容)。這意味著,均衡器不能僅僅根據SYN報文的地址和埠資訊就做出排程判斷;而要把排程的決定延遲到相關業務資料到達的時候進行――整體上講,交換被”延遲”了。
於是,原理上,負載均衡器(也就是應用級代理),需要監聽客戶端的連線請求,並在客戶端發出連線的請求之後(從SYN開始),建立客戶端到均衡器之間的連線(通過TCP的三次握手協議完成)。並在隨後的請求報文中分析資料並決定真正被訪問的服務節點,然後才與服務節點建立另一個連線,將兩個連線粘合在一起(Splicing)。
圖二:TCP
Splicing原理示意圖
TCP粘合連線的原理如上圖所示。該結構於應用級代理的最大不同在於:客戶端和叢集節點之間的連線在作業系統的核心層進行連線粘合。也就是說,TCP粘合避免了資料包從核心空間到使用者空間的上下文交換這一耗時的過程,並且可以利用作業系統在核心層TCP/IP協議棧的多執行緒處理能力,提高TCP連線的交換速度,比如Linux核心2.4。
3、TCP Splicing 處理流程簡要描述
相比於應用層代理而言,TCP Splicing技術避免了資料報文的上下文切換工作,減少了核心空間到使用者程序的通訊開銷,整體效率可以作的很好。這一類的技術目前應用的非常穩定有效率,在小型閘道器裝置和簡單的負載平衡集群系統中,足夠支撐叢集的整體執行。
簡單說,要實現TCP連線的粘合,要經過至少三個步驟:
1、客戶端發起連線請求,並且由均衡器截獲請求,完成和客戶端的三次握手協議,等待客戶端的請求資料(比如Get指令)。
圖三:第一步,客戶端發起連線
2、均衡器接收到客戶端傳送的Get請求資料,選定叢集節點後,偽裝成客戶端向叢集節點發起請求,完成三次握手協議。並記錄下兩個連線的本地響應埠,用於對映兩個TCP連線。
圖四:第二步,均衡器向服務節點發起連線
3、經過前面的兩次連線,均衡器在客戶端和被選節點之前建立一個連線的對映關係。而後續兩方的通訊就無需再次被分析模組處理,直接在對映端互相交換就可以了。
圖五:第三步,TCP連線粘合完成,資料直接交換
4、TCP Splicing資料包通訊流程
這裡我們詳細分析一下TCP粘合處理流程中,對於兩回的三次握手協議之後,再建立起連線對映的具體細節。
從前面的敘述我們知道,負載均衡器在客戶端發起TCP連線請求並完成三次的握手確認後,並沒有馬上將資料轉發,而是先偽裝為服務節點響應客戶端的請求,並在隨後接收到第一個資料包中獲取負載均衡所需的資料,決定實際的服務節點;並在完成與服務節點的通訊確認工作時候才開始雙方的資料轉換工作。
TCP
Splicing資料處理流程
圖中,藍色框線包含起來的部分表示負載均衡器,包括了專門負責監聽客戶端請求的埠和負責傳送請求到服務節點的埠。而TCP粘合工作完成之後,實際上客戶端和服務節點雙方的資料包就在兩個埠上直接交換,不需要經過複雜的使用者空間的排程模組處理了,相當於核心級的報文交換機。
Step1:客戶端向叢集發起請求,典型的請求以一個Syn(CSEQ表示客戶端報文的初始序列)報文為開始,標記一個TCP連線開始並請求迴應一個三次握手協議過程。該請求到達負載均衡器的客戶埠。
Step2:負載均衡器內建的網路輸入流處理例程將截獲客戶端的SYN請求報文,解析後轉發給連線管理模組,並記錄下這一次的請求資訊:源地址、源埠號、目標地址和目標埠號。
Step3:負載均衡器迴應一個對應的ACK(CSEQ+1,表示對應於初始序列的應答序列)到客戶埠,回送客戶端監聽口。同時,發起一個Syn(DSEQ表示新的報文序列號)。
Step4:Ack應答資料以及新的Syn報文從客戶端監聽口回送客戶端,客戶端接收響應,第一次握手完成。
眾所周知的是,由於IP網路是分組轉發的,而接收端試用的緩衝區也會導致收到非完全序列的報文。因此,TCP為了保證報文傳送序列與接收序列的同步,定義了一個報文序列協議規範。關鍵在於:每次傳送的報文中所包含的序列,一定是準備接下來接收的ACK應答報文的序列。
Step5:客戶端在收到叢集的正確應答資料後,認為連線已經建立(實際上,客戶端僅僅是和負載均衡器建立了連線,它被欺騙了J)。向服務端發出資料請求報文DATA(CSEQ+1),並且對均衡器的SYN(DSEQ)進行應答,同時傳送ACK(DSEQ+1)。至此完成了三次握手的全部過程,並且開始進行資料通訊。
由於面向內容的負載平衡必須在收到客戶端發出的含有協議內容的資料之後(例如Get指令),才能進行負載排程的判斷工作,因此,需要針對客戶端的連線請求進行”欺騙”,待於客戶端的連線建立之後將其掛起,準備排程。這樣的操作,顯然延遲了客戶端到實際節點之間的連線程序,也有文獻稱之為TCP Delay Binding。
Step6:關鍵的一步。真正的請求資料第一次到達了負載均衡器的排程模組。排程根據請求中的有效協議資訊做出負載平衡判斷。判斷的依據可以是請求的檔案型別,或者是服務級別。另外,負載平衡器也要根據當前服務節點的權值佇列,選擇目前符合服務條件的負載最輕的節點。而客戶端的請求在這裡將被掛起,從客戶端的角度看,連線請求被隱蔽的延遲了。關於負載平衡策略的討論不是本文的重點,具體的排程演算法和平衡策略將在後續篇章中介紹。
Step7:負載均衡器選擇完實際的服務節點,以客戶端的”身份”向服務節點發起SYN連線請求,特別一提的是,該請求使用了客戶端原先發起的請求初始序列號CSEQ。這麼做的目的是為了後面進行TCP粘合所必須的TCP Header資料轉換時,儘可能減少均衡器的CPU計算量,也方便管理被粘合的兩個連線。
Step8:SYN(CSEQ)由服務端監聽口到達服務節點,從整個會話過程來看,客戶端Step1所發出的SYN(CSEQ)被延遲地傳送到了最終目的地!
Step9:服務端接受SYN(CSEQ),送回應答資料包ACK(CSEQ)+1),同時發起SYN(SSEQ)以完成TCP的握手例程。
Step10:值得注意的一個步驟,服務節點的SYN(SSEQ)報文序列被重新對映為SYN(DSEQ),和step3的報文序列相同。這樣,從負載均衡器的角度來講,他正確的獲得了一個應答報文,並且這個被轉換過序列的應答報文可以直接和客戶端的報文序列對映在一起了。而ACK(CSEQ+1)應答報文被送回核心模組。
到這裡為止,我們可以發現,服務端監聽口對於均衡器而言,作為虛擬的服務端而存在,通過該埠的報文序列都是轉換過後的序列,能夠直接和客戶端監聽口的序列對應。同理,客戶端監聽口也承擔了相似的角色。
Step11:均衡器確認了ACK(CSEQ+1)報文後,完成和服務節點的三次握手。發出應答報文ACK(DSEQ+1)(注意:均衡器在這裡使用的是客戶端序列,把自己當做客戶端,把服務端監聽口當做伺服器)。同時發出請求資料Data(CSEQ+1)。
到這一步,均衡器完成了對Step5的TCP請求延遲處理。客戶端的請求資料終於被髮送給了服務節點(通過服務端監聽口)。
Step12:經過服務端監聽口的報文,需要被適當的修改序列:即DSEQ序列->SSEQ序列。這樣的轉換是固定偏移的,也就是第一次SYN被接收之後,二者序列之差可以作為整個通訊過程中,序列轉換的偏移量。因此,從埠送回到服務節點的報文是:Data(CSEQ+1)以及ACK(SSEQ+1)。
Step13:服務節點響應資料請求,例如返回請求的HTML檔案。資料DATA(SSEQ+1)以長度len返回,並回送應答ACK(CSEQ+len+1)到均衡器的監聽口。
Step14:監聽口對Data(SSEQ+1)進行轉換,ACK不轉換。並將轉換後的資料,直接傳送到客戶端監聽口,無需經過核心排程模組的處理。由於前面已經記錄了客戶端的連線資訊,因此這樣的傳送非常快速。而客戶端監聽口收到資料之後,即立刻發往客戶端。
Step15:客戶端對剛才收到的資料應答,將ACK(DSEQ+len+1)送往均衡器。均衡器將該報文發往服務端監聽口。
Step16:服務端監聽口對報文進行對映轉換,DSEQ->SSEQ,向服務節點發送應答報文ACK(SSEQ+len+1)。表明一次資料 請求-回送 工作完成。
均衡器在客戶端和服務節點之間扮演了一個透明閘道器的角色。二者互相不可見。而為了順利實現兩個TCP連線資料包的轉換,均衡器設立負責扮演服務的客戶端監聽口和扮演客戶的服務端監聽口。資料包在服務端監聽口進行TCP報文頭的修改工作:序列號的對映,DSEQ<->SSEQ。
並且,均衡器排程核心在適當的時候,根據客戶的請求進行負載平衡排程工作。功能上看,均衡器具備了兩種功能:基於應用層協議解析的負載平衡排程策略,以及基於埠的報文交換。其中,報文交換的物件依賴於負載平衡選擇的服務節點。但報文交換在一次排程策略確定之後就獨立執行(圖中的Step13~Step16),不再經過複雜的處理,可以獲得非常高的效能和擴充套件性。另外,這麼作也保護了服務節點的安全性。而排程核心獨立於報文交換核心,它僅僅處理需要進行排程的部分報文,例如:包含請求不同檔案型別資訊的報文,或者是包含Session資訊的報文。那麼在設計埠對映管理核心程式的時候,需要對報文進行快速分類,決定哪一類報文可以直接通過報文交換核心,而哪一部分需要交給排程核心處理。
原文連結:http://517sou.net/archives/tcp%E7%B2%98%E5%90%88%E6%8A%80%E6%9C%AF%E5%8E%9F%E7%90%86/