http啞代理

一.TCP連接

TCPHTTP提供了一條可靠的比特傳輸管道。從TCP連接一端填入的字節會從另一端以原有的順序、正確地傳送出來。

TCP的數據是通過名爲IP分組(或IP數據報)的小數據塊來發送的。

HTTP就是“HTTP over TCP over IP”這個“協議棧”中的最頂層了。其安全版本HTTPS就是在HTTPTCP之間插入了一個(稱爲TLSSSL的)密碼加密層。


HTTP要傳送一條報文時,會以流的形式將報文數據的內容通過一條打開的TCP連接按序傳輸。TCP收到數據流之後,會將數據流砍成被稱作段的小數據塊,並將段封裝在IP分組中,通過因特網進行傳輸 。

每個 TCP段都是由IP分組承載,從一個IP地址發送到另一個IP地址的。每個IP分組中都包括:

一個IP分組首部(通常爲20字節);

一個TCP段首部(通常爲20字節);

一個TCP數據塊(0個或多個字節)。

IP首部包含了源和目的IP地址、長度和其他一些標記。TCP段的首部包含了TCP端口號、TCP控制標記,以及用於數據排序和完整性檢查的一些數字值。 



二、對TCP性能的考慮

最常見的 TCP相關時延:

  • TCP 連接建立握手;

  • TCP 慢啓動擁塞控制;

  • 數據聚集的Nagle算法;

  • 用於捎帶確認的TCP延遲確認算法;

  • TIME_WAIT 時延和端口耗盡。 

1.TIME_WAIT和端口耗盡

當某個 TCP端點關閉TCP連接時,會在內存中維護一個小的控制塊,用來記錄最近所關閉連接的IP地址和端口號。這類信息只會維持一小段時間,通常是所估計的最大分段使用期的兩倍(稱爲2MSL,通常爲2分鐘8)左右,以確保在這段時間內不會創建具有相同地址和端口號的新連接。實際上,這個算法可以防止在兩分鐘內創建、關閉並重新創建兩個具有相同IP地址和端口號的連接。 (TCP分節可能由於路由器異常而“迷途”,在迷途期間,TCP發送端可能因確認超時而重發這個分節,迷途的分節在路由器修復後也會被送到最終目的地,這個遲到的迷途分節到達時可能會引起問題。在關閉“前一個連接”之後,馬上又重新建立起一個相同的IP和端口之間的“新連接”,“前一個連接”的迷途重複分組在“前一個連接”終止後到達,而被“新連接”收到了。爲了避免這個情況,TCP協議不允許處於TIME_WAIT狀態的連接啓動一個新的可用連接,因爲TIME_WAIT狀態持續2MSL,就可以保證當成功建立一個新TCP連接的時候,來自舊連接重複分組已經在網絡中消逝。

在只有一個客戶端和一臺 Web服務器的異常情況下,構建一條TCP連接的4個值:<source-IP-address, source-port, destination-IP-address, destination-port>

其中的 3個都是固定的——只有源端口號可以隨意改變:<client-IP, source-port, server-IP, 80>

客戶端每次連接到服務器上去時,都會獲得一個新的源端口,以實現連接的唯一性。但由於可用源端口的數量有限(比如,60 000個),而且在2MSL秒(比如,120秒)內連接是無法重用的,連接率就被限制在了60 000/120=500/秒。如果再不斷進行優化,並且服務器的連接率不高於500/秒,就可確保不會遇到TIME_W AIT端口耗盡問題。 


2.延遲確認算法

由於因特網自身無法確保可靠的分組傳輸(因特網路由器超負荷的話,可以隨意丟棄分組),所以TCP實現了自己的確認機制來確保數據的成功傳輸。

每個 TCP段都有一個序列號和數據完整性校驗和。每個段的接收者收到完好的段時,都會向發送者回送小的確認分組。如果發送者沒有在指定的窗口時間內收到確認信息,發送者就認爲分組已被破壞或損毀,並重發數據。

由於確認報文很小,所以 TCP允許在發往相同方向的輸出數據分組中對其進行“捎帶”。TCP將返回的確認信息與輸出的數據分組結合在一起,可以更有效地利用網絡。爲了增加確認報文找到同向傳輸數據分組的可能性,很多TCP棧都實現了一種 “延遲確認”算法。延遲確認算法會在一個特定的窗口時間(通常是100200毫秒)內將輸出確認存放在緩衝區中,以尋找能夠捎帶它的輸出數據分組。如果在那個時間段內沒有輸出數據分組,就將確認信息放在單獨的分組中傳送。

但是,HTTP具有雙峯特徵的請求應答行爲降低了捎帶信息的可能。當希望有相反方向回傳分組的時候,偏偏沒有那麼多。通常,延遲確認算法會引入相當大的時延。根據所使用操作系統的不同,可以調整或禁止延遲確認算法。 

3.TCP慢啓動

TCP數據傳輸的性能還取決於TCP連接的使用期(age)。TCP連接會隨着時間進行自我“調諧”,起初會限制連接的最大速度,如果數據成功傳輸,會隨着時間的推移提高傳輸的速度。這種調諧被稱爲TCP慢啓動(slow start),用於防止因特網的突然過載和擁塞。

TCP慢啓動限制了一個TCP端點在任意時刻可以傳輸的分組數。簡單來說,每成功接收一個分組,發送端就有了發送另外兩個分組的權限。如果某個HTTP事務有大量數據要發送,是不能一次將所有分組都發送出去的。必須發送一個分組,等待確認;然後可以發送兩個分組,每個分組都必須被確認,這樣就可以發送四個分組了,以此類推。這種方式被稱爲“打開擁塞窗口”。

由於存在這種擁塞控制特性,所以新連接的傳輸速度會比已經交換過一定量數據的、“已調諧”連接慢一些。 

4.Nagle算法與TCP_NODELAY

TCP有一個數據流接口,應用程序可以通過它將任意尺寸的數據放入TCP棧中——即使一次只放一個字節也可以!但是,每個TCP段中都至少裝載了40個字節的標記和首部,所以如果TCP發送了大量包含少量數據的分組,網絡的性能就會嚴重下降。 

Nagle算法(根據其發明者John Nagle命名)試圖在發送一個分組之前,將大量TCP數據綁定在一起,以提高網絡效率。 

Nagle算法鼓勵發送全尺寸(LAN上最大尺寸的分組大約是1500字節,在因特網上是幾百字節)的段。只有當所有其他分組都被確認之後,Nagle算法才允許發送非全尺寸的分組。如果其他分組仍然在傳輸過程中,就將那部分數據緩存起來。只有當掛起分組被確認,或者緩存中積累了足夠發送一個全尺寸分組的數據時,纔會將緩存的數據發送出去。6

Nagle算法會引發幾種HTTP性能問題。首先,小的HTTP報文可能無法填滿一個分組,可能會因爲等待那些永遠不會到來的額外數據而產生時延。其次,Nagle算法與延遲確認之間的交互存在問題——Nagle算法會阻止數據的發送,直到有確認分組抵達爲止,但確認分組自身會被延遲確認算法延遲100~200毫秒。 

HTTP應用程序常常會在自己的棧中設置參數TCP_NODELA Y,禁用Nagle算法,提高性能。如果要這麼做的話,一定要確保會向TCP寫入大塊的數據,這樣就不會產生一堆小分組了。 


三、提高HTTP連接性能的幾種方法

• 並行連接
通過多條 TCP連接發起併發的HTTP請求。

• 持久連接
重用 TCP連接,以消除連接及關閉時延。

• 管道化連接
通過共享的 TCP連接發起併發的HTTP請求。 

• 複用的連接交替傳送請求和響應報文(實驗階段)。 


1.並行連接

HTTP允許客戶端打開多條連接,並行地執行多個HTTP事務。在這個例子中,並行加載了四幅嵌入式圖片,每個事務都有自己的TCP連接。 




2.持久連接

Web客戶端經常會打開到同一個站點的連接。比如,一個Web頁面上的大部分內嵌圖片通常都來自同一個Web站點,而且相當一部分指向其他對象的超鏈通常都指向同一個站點。因此,初始化了對某服務器HTTP請求的應用程序很可能會在不久的將來對那臺服務器發起更多的請求(比如,獲取在線圖片)。這種性質被稱爲站點局部性(site locality)。 

因此,HTTP/1.1(以及HTTP/1.0的各種增強版本)允許HTTP設備在事務處理結束之後將TCP連接保持在打開狀態,以便爲未來的HTTP請求重用現存的連接。在事務處理結束之後仍然保持在打開狀態的TCP連接被稱爲持久連接。非持久連接會在每個事務結束之後關閉。持久連接會在不同事務之間保持打開狀態,直到客戶端或服務器決定將其關閉爲止。 

重用已對目標服務器打開的空閒持久連接,就可以避開緩慢的連接建立階段。而且,已經打開的連接還可以避免慢啓動的擁塞適應階段,以便更快速地進行數據的傳輸。 

持久連接與並行連接配合使用可能是最高效的方式。現在,很多Web應用程序都會打開少量的並行連接,其中的每一個都是持久連接。持久連接有兩種類型:比較老 

的 HTTP/1.0+keep-alive”連接,以及現代的HTTP/1.1persistent”連接

1) keep-alive操作

keep-alive已經不再使用了,而且在當前的HTTP/1.1規範中也沒有對它的說明了。但瀏覽器和服務器對keep-alive握手的使用仍然相當廣泛。

實現HTTP/1.0 keep-alive連接的客戶端可以通過包含Connection: Keep-Alive首部請求將一條連接保持在打開狀態。

如果服務器願意爲下一條請求將連接保持在打開狀態,就在響應中包含相同的首部。如果響應中沒有Connection: Keep-Alive首部,客戶端就認爲服務器不支持keep-alive,會在發回響應報文之後關閉連接。 

Keep-Alive首部必須隨所有希望保持持久連接的報文一起發送。如果客戶端沒有發送Connection: Keep-Alive首部,服務器就會在那條請求之後關閉連接。 

2)keep-alive選項

注意,keep-Alive首部只是請求將連接保持在活躍狀態。發出keep-alive請求之後,客戶端和服務器並不一定會同意進行keep-alive會話。它們可以在任意時刻關閉空閒的keep-alive連接,並可隨意限制keep-alive連接所處理事務的數量。 

可以用 Keep-Alive通用首部中指定的、由逗號分隔的選項來調節keep-alive的行爲。

參數timeout是在Keep-Alive響應首部發送的。它估計了服務器希望將連接保持在活躍狀態的時間。這並不是一個承諾值。

參數max是在Keep-Alive響應首部發送的。它估計了服務器還希望爲多少個事務保持此連接的活躍狀態。這並不是一個承諾值。 

Keep-Alive首部還可支持任意未經處理的屬性,這些屬性主要用於診斷和調試。語法爲name [=value]。 


Keep-Alive首部完全是可選的,但只有在提供Connection: Keep-Alive時才能使用它。這裏有個Keep-Alive響應首部的例子,這個例子說明服務器最多還會爲另外5個事務保持連接的打開狀態,或者將打開狀態保持到連接空閒了2分鐘之後。

Connection: Keep-AliveKeep-Alive: max=5, timeout=120 


3)keep-alive和啞代理

很多老的或簡單的代理都是盲中繼(blind relay),它們只是將字節從一個連接轉發到另一個連接中去,不對Connection首部進行特殊的處理。(不理解Connection首部,而且不知道在沿着轉發鏈路將其發送出去之前,應該將該首部刪除 ) 


     (1)在圖4-15a中 Web客戶端向代理髮送了一條報文,其中包含了Connection:Keep-Alive首部,如果可能的話請求建立一條keep-alive連接。客戶端等待響應,以確定對方是否認可它對keep-alive信道的請求。

    (2)  啞代理收到了這條HTTP請求,但它並不理解 Connection首部(只是將其作爲一個擴展首部對待)。代理不知道keep-alive是什麼意思,因此只是沿着轉發鏈路將報文一字不漏地發送給服務器(圖4-15b)。但Connection首部是個逐跳首部,只適用於單條傳輸鏈路,不應該沿着傳輸鏈路向下傳輸。接下來,就要發生一些很糟糕的事情了。

  1. (3)  在圖4-15b中,經過中繼的HTTP請求抵達了Web服務器。當Web服務器收到經過代理轉發的Connection: Keep-Alive首部時,會誤以爲代理(對服務器來說,這個代理看起來就和所有其他客戶端一樣)希望進行keep-alive對話!對Web服務器來說這沒什麼問題——它同意進行keep-alive對話,並在圖4-15c回送了一個Connection: Keep-Alive響應首部。所以,此時W eb服務器認爲它在與代理進行keep-alive對話,會遵循keep-alive的規則。但代理卻對keep-alive一無所知。不妙。

  2. (4)  在圖4-15d中,啞代理將Web服務器的響應報文回送給客戶端,並將來自Web服務器的Connection: Keep-Alive首部一起傳送過去。客戶端看到這個首部,就會認爲代理同意進行keep-alive對話。所以,此時客戶端和服務器都認爲它們在進行keep-alive對話,但與它們進行對話的代理卻對keep-alive一無所知。

  3. (5)  由於代理對keep-alive一無所知,所以會將收到的所有數據都回送給客戶端,然後等待源端服務器關閉連接。但源端服務器會認爲代理已經顯式地請求它將連接保持在打開狀態了,所以不會去關閉連接。這樣,代理就會掛在那裏等待連接的關閉。

  4. (6)  客戶端在圖4-15d中收到了回送的響應報文時,會立即轉向下一條請求,在keep-alive連接上向代理髮送另一條請求(參見圖4-15e)。而代理並不認爲同一條連接上會有其他請求到來,請求被忽略,瀏覽器就在這裏轉圈,不會有任何進展了。

  5. (7)  這種錯誤的通信方式會使瀏覽器一直處於掛起狀態,直到客戶端或服務器將連接超時,並將其關閉爲止。 

爲避免此類代理通信問題的發生,現代的代理都絕不能轉發Connection首部和所有名字出現在Connection值中的首部。因此,如果一個代理收到了一個Connection: Keep-Alive首部,是不應該轉發Connection首部,或所有名爲Keep-Alive的首部的。 

在網景的變通做法是,瀏覽器會向代理髮送非標準的Proxy-Connection擴展首部,而不是官方支持的著名的Connection首部。如果代理是盲中繼,它會將無意義的Proxy-Connection首部轉發給W eb服務器,服務器會忽略此首部,不會帶來任何問題。但如果代理是個聰明的代理(能夠理解持久連接的握手動作),就用一Connection首部取代無意義的Proxy-Connection首部,然後將其發送給服務器,以收到預期的效果。 





4)persistent連接

HTTP/1.1 逐漸停止了對keep-alive連接的支持,用一種名爲持久連接(persistentconnection)的改進型設計取代了它。持久連接的目的與keep-alive連接的目的相同,但工作機制更優一些。

HTTP/1.0+keep-alive連接不同,HTTP/1.1持久連接在默認情況下是激活的。除非特別指明,否則HTTP/1.1假定所有連接都是持久的。要在事務處理結束之後將連接關閉,HTTP/1.1應用程序必須向報文中顯式地添加一個Connection:close首部。這是與以前的HTTP協議版本很重要的區別,在以前的版本中,keep-alive連接要麼是可選的,要麼根本就不支持。

HTTP/1.1 客戶端假定在收到響應後,除非響應中包含了Connection: close部,不然HTTP/1.1連接就仍維持在打開狀態。但是,客戶端和服務器仍然可以隨時關閉空閒的連接。不發送Connection: close並不意味着服務器承諾永遠將連接保持在打開狀態。 

  • 只有當連接上所有的報文都有正確的、自定義報文長度時——也就是說,實體主體部分的長度都和相應的Content-Length一致,或者是用分塊傳輸編碼方式編碼的——連接才能持久保持。 (因爲是持久連接,無法通過連接的關閉來判斷報文發送結束)

3.管道化連接

HTTP/1.1允許在持久連接上可選地使用請求管道。這是相對於keep-alive連接的又一性能優化。在響應到達之前,可以將多條請求放入隊列。當第一條請求通過網絡流向地球另一端的服務器時,第二條和第三條請求也可以開始發送了。在高時延網絡條件下,這樣做可以降低網絡的環回時間,提高性能。 

圖 4-18a-c顯示了持久連接是怎樣消除TCP連接時延,以及管道化請求(參見圖4-18c)是如何消除傳輸時延的。

對管道化連接有幾條限制:

  • 如果HTTP客戶端無法確認連接是持久的,就不應該使用管道。

  • 必須按照與請求相同的順序回送HTTP響應。HTTP報文中沒有序列號標籤,因

    此如果收到的響應失序了,就沒辦法將其與請求匹配起來了。

  • HTTP 客戶端必須做好連接會在任意時刻關閉的準備,還要準備好重發所有未完

    成的管道化請求。如果客戶端打開了一條持久連接,並立即發出了10條請求,服務器可能在只處理了,比方說,5條請求之後關閉連接。剩下的條請求會失敗,客戶端必須能夠應對這些過早關閉連接的情況,重新發出這些請求。

  • HTTP 客戶端不應該用管道化的方式發送會產生副作用的請求(比如POST)。總之,出錯的時候,管道化方式會阻礙客戶端了解服務器執行的是一系列管道化請求中的哪一些。由於無法安全地重試POST這樣的非冪等請求,所以出錯時,就存在某些方法永遠不會被執行的風險。 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章