HTTP個人總結(三)

今天來總結的是關於HTTP的連接管理。

首先來介紹TCP連接是什麼?
這裏寫圖片描述
TCP爲HTTP提供了一條可靠的比特傳輸通道,從TCP連接一端填入的字節會從另一端以原有的順序、正確地傳送出來。

TCP是分流的、由IP分組傳送又是什麼?

TCP的數據是通過名爲IP分組的小數據塊來發送的,HTTPS就是在HTTP和TCP之間插入了一個(稱爲TLS或SSL的)密碼加密層。
這裏寫圖片描述
HTTP要傳送一條報文時,會以流的形式將報文數據的內容通過一條打開的TCP連接按序傳輸。TCP收到數據流之後,會將數據流之後,會將數據流砍成被稱作段的小數據塊,並將段封裝在IP分組中,通過因特網進行傳輸,所有這些工作都是由TCP/ip軟件來處理的,程序員什麼都看不到。
這裏寫圖片描述
每個TCP段都是由IP分組承載,從一個IP地址發送到另一個IP地址,每個IP分組中包括:
1.一個IP分組首部(通常爲20字節)
2.一個TCP段首部(通常爲20字節)
3.一個TCP數據塊(0個或多個字節)
IP首部包含了源和目的IP地址、長度和其他一些標記,TCP段的首部包含了TCP端口號,TCP控制標記以及用於數據排序和完整性檢查的一些數字值。

下面介紹用TCP套接字編程?

操作系統提供了一些操縱其TCP連接的工具。
這裏寫圖片描述
這個套接字API隱藏了TCP和IP所有細節。套接字API允許用戶創建TCP的端點數據結構,將這些端點與遠程服務器的TCP端口進行連接,並對數據流進行讀寫。TCP API隱藏了所有底層網絡協議的握手細節,以及TCP數據流與IP分組之間的分段和重組細節。
這裏寫圖片描述
我們從Web服務器等待連接開始,客戶端根據URL判定出IP地址和端口號,並建立一條到服務器的TCP連接。建立連接可能要花費一些時間,時間長短取決於服務器距離的遠近、服務器的負載情況以及因特網的擁擠程度。一旦建立連接,就可以進行客戶端與服務器的交互。

對TCP性能的考慮?

HTTP事務的性能在很大程度上取決於底層TCP通道的性能。
如HTTP事務的時延:
這裏寫圖片描述
注意,與建立TCP連接,以及傳輸請求和響應報文的時間相比,事務處理時間可能是很短的。HTTP事務時延的幾種主要原因:
1.客戶端首先需要根據URI確定Web服務器的IP地址和端口號,如果最近沒有對URI中的主機名進行訪問,通過DNS解析系統將URI主機名轉換成一個IP地址可能要花費數十秒的時間。
2.客戶端會向服務器發送一條TCP連接請求,並等待服務器回送一個請求接收應答,每條新的TCP連接都會有連接建立時延,這個值通常最多隻有一兩秒鐘,但如果有數百個HTTP事務的話,這個值就會快速疊加起來。
3.一旦建立連接,客戶端就會通過新建立的TCP管道來發送HTTP請求。數據到達,Web服務器會從TCP連接中讀取請求報文,並對請求進行處理,因特網傳輸請求報文,以及服務器處理請求報文都需要時間。
4.Web服務器回送響應報文也需要時間。

下面列出會對程序員產生影響的最常見的TCP相關時延:
1.TCP連接建立握手
2.TCP慢啓動擁塞控制
3.數據聚集的Nagle算法
4.用於捎帶確認的TCP延遲確認算法
5.TIME_WAIT時延和端口耗盡

下面進行各個原因的詳細介紹:
首先介紹TCP連接的握手時延:
這裏寫圖片描述
需要經歷以下步驟:
1.請求新的TCP連接時,客戶端要向服務器發送一個小的TCP分組,這個分組中設置了一個特殊的SYN標記,說明這是一個連接請求。
2.如果服務器接收了連接,就會對一些連接參數進行計算,並向客戶端回送一個TCP分組,這個分組中的SYN的ACK標記都被置位,說明連接請求已被接收。
3.最後,客戶端向服務器回送一條確認小心,通知它連接已成功建立。現代的TCP棧都允許客戶端在這個確認分組中發送數據。

接下來介紹TCP慢啓動:
TCP數據傳輸的性能還取決於TCP連接的使用期。TCP連接會隨着時間進行自我“調整”。起初會限制連接的最大速度,如果數據成功傳輸、會隨着時間的推移提高傳輸的速度。用於防止因特網的突然過載和擁塞。

然後是Nagle算法與TCP_NODELAY:
TCP有一個數據流接口,應用程序可以通過它將任意尺寸的數據放入TCP棧中——即使一次只放一個字節也可以,但是每個TCP段中都至少裝載了40個字節的標記和首部,所以如果TCP發送大量包含少量數據的分組,網絡性能就會嚴重下降。
Nagle算法鼓勵發送全尺寸(LAN上最大尺寸的分組約是1500字節,在因特網上是幾百字節)的端,只有當所有其他分組都被確認之後,Nagle算法才允許發送非全尺寸的分組,如果其他分組仍然在傳輸過程中,將要將那部分數據緩存起來,只有當掛起分組被確認,或者緩存中積累了足夠發送一個全尺寸分組的數據時,纔會將緩存的數據發送出去。
Nagle算法會引發幾種HTTP性能問題,首先,小的HTTP報文可能無法填滿一個分組,可能會因爲等待那些永遠不會到來的額外數據而產生時延,其次,Nagle算法與延遲確認之間的交互存在問題——Nagle算法會阻止數據的發送,知道有確認分組抵達爲止,但確認分組自身會被延遲確認算法延遲100~200毫秒。
HTTP可以再自己的棧中設置參數TCP_NODELAY,禁止Nagle算法,提高性能,彈藥保證會向TCP寫入大塊的數據。

接下來是用於捎帶確認的TCP延遲確認算法。由於因特網自身無法確認可靠的分組傳輸(因特網路由器超負荷的話,可以隨意丟棄分組),所有TCP實現了自己的確認機制來確保數據的成功傳輸。
每個TCP段都有一個序列號和數據完整性校驗和,每個段的接受者收到完好的端時,都會向發送者回送小的確認分組,如果發送者沒有在指定窗口時間內收到確認消息,發送者就認爲分組已被破壞或損毀,並重發數據。
由於確認報文很小,所以TCP允許在發往相同方向的輸出數據分組中對其進行“捎帶”,TCP將返回確認信息與輸出的數據分組結合在一起,可以有效的利用網絡。爲了增加確認報文找到同向傳輸數據分組的可能性,很多TCP棧都實現了一種“延遲確認”的算法。延遲確認算法會在一個特定的窗口時間內將輸出確認存放在緩衝區,以尋找到能夠捎帶它的輸出數據分組,如果在那個時間段內沒有輸出數據分組,就將確認信息放在單獨的分組中發送。但是這個延遲確認算法會引入相當大的延遲,所以根據操作系統的不同,可以調整或者禁止這個算法。

最後介紹TIME_WAIT累積和端口耗盡:
TIME_WAIT端口耗盡時很嚴重的性能問題,會影響到性能基準,但現實中相對較少出現,大多數遇到性能基準問題的人最終都會碰到這個問題。
當某個TCP端點關閉TCP連接時,會在內存中維護一個小的控制塊,用來記錄最近所關閉連接IP地址和端口號,這類信息只會維持一小段時間,通常是所顧忌的最大分段使用期的兩倍(稱爲2MSL,通常是2分鐘)左右,以確保在這段時間內不會創建具有相同地址和端口號的新連接,就是昂志在兩分鐘內創建、關閉並重新創建兩個具有相同IP地址的端口號的連接。
這個通常不是什麼問題,但是在性能基準環境下可能會成爲一個問題。進行性能基準測試時,通過只有一臺或幾臺來產生流量的計算機連接到某系統中去,這樣就限制了連接到服務器的客戶端IP地址數,而且服務器通常會在HTTP的默認TCP端口80上進行監聽。
在只有一個客戶端和一臺Web服務器的異常情況下,構建一條TCP連接的4個值:
source-IP-address,source-port,destination-IP-adress,destination-port
其中三個是固定的,只有源端口號可以隨便改變,客戶端每次連接到服務器上去是,都會獲得一個新的端口,以實現連接的唯一性,但是由於可用源端口的數量有限(比如60000個),而且在2MSL秒(比如120秒)內連接是無法重用的,連接率就被限制在了60000/120=500次/秒,要修正這個問題,可以增加客戶端負載生成機器的數量或者確保客戶端和服務器在循環使用幾個虛擬IP地址以增加更多的連接組合。
當大量端口連接處於打開狀態的情況,有些操作系統的速度會嚴重減緩。

下面介紹HTTP連接的處理?

串行事務處理時延:
如果只對連接進行簡單的管理,TCP性能時延可能會疊加起來,比如一個頁面請求4個HTTP事務,如果每個事務都需要(串行地建立)一條新的連接,那麼連接時延和慢啓動時延就會疊加起來。
這裏寫圖片描述
串行加載的另一個缺點:有些瀏覽器在對象加載完畢之前無法獲知對象的尺寸,而且他們可能需要尺寸信息來決定將對象放在屏幕上的什麼位置,所以在加載了足夠多的對象之前,無法再屏幕上顯式任何內容,在這種情況下,可能瀏覽器串行裝載對象的進展很正常,但是用戶面對面的確實一個空白的屏幕,對裝載的進度一無所知。

還有幾種可以提高HTTP連接性能的技術:
1.並行連接:通過多次TCP連接發起並行地HTTP請求
2.持久連接:重用TCP連接,以消除連接及關閉時延
3.管道化連接:通過共享的TCP連接發起的併發的HTTP請求
4.複用的連接:交替傳送請求和響應報文(實驗階段)

下面進行詳細介紹:
並行連接:
HTTP允許客戶端打開多條連接,並行地執行多個HTTP事務。如:
這裏寫圖片描述
這裏寫圖片描述
但是並行速度並不一定總是更快,如果客戶端的網絡帶寬不足,一個連接到速度較快的服務器上的HTTP的事務耗盡了所有可用的帶寬,那麼每個對象都會以較慢的速度按比例加載,這樣帶來的性能提升很小,甚至沒有,而且打開大量連接消耗更多的內存資源,從而引發自身的性能問題。但是即使沒有增快,但是給用戶覺得頁面加載更快,因爲多個組件對象同時出現在屏幕上,用戶可以看到加載的進展。
並行地缺點:
1.每個事務都會打開/關閉一條新的連接,會耗費時間和帶寬
2.由於TCP慢啓動特性的存在,每條新連接的性能都會有所下降
3.可打開的並行連接數量實際上是有限的

持久連接:
HTTP/1.1(以及HTTP/1.0的各種增強版本)允許HTTP設備在事務處理結束之後將TCP連接保持在打開狀態,以便爲未來的HTTP請求重用現存的連接,這叫持久連接。
重用已對目標服務器打開的空閒持久連接,就可以避開緩慢的連接建立階段,而且已經打開的連接還可以避免慢啓動的擁塞適應階段,以便更快速的進行數據的傳輸。
持久連接與並行連接配合使用可能是最高效的方式。比較老的HTTP/1.0+使用“keep-alive”連接,HTTP/1.1使用persistent連接
這裏寫圖片描述
keep-alive已經不再使用了,而且在當前的HTTP/1.1規範中也沒有對它的說明了。但瀏覽器和服務器丟keep-alive握手的使用仍然相當廣泛,因此HTTP實現者應該做好與之交互操作的準備。客戶端可以通過包含contection:keep-alive首部請求將一條連接保持在打開狀態,如果服務器願意爲下一條連接請求將連接保持在打開狀態,將在響應中包含相同的首部,如:
這裏寫圖片描述
keep-alive首部只是請求將連接保持在活躍狀態,客戶端和服務器不一定會同意keep-alive會話,它們可以在任意時刻關閉空閒的keep-alive連接,並可以隨意限制keep-alive連接所處理事務的數量。

下面介紹的是keep-alive和啞代理?

首先先介紹connection首部和盲中繼:
盲中繼:它們只是將字節從一個連接轉發到另一個連接中去,不對connection首部進行特殊的處理,如:
這裏寫圖片描述
圖中發生瞭如下的問題:
1.Web客戶端向代理髮送了一條報文,其中包含了connetion:keep-alive首部,如果可能的話請求建立一條keep-alive連接。客戶端等待響應,以確定對方是否認可它對keep-alive信道的請求
2.啞代理收到了這條HTTP請求,但它不理解Connection首部(只是將它當做一個擴展首部對待),因此只是沿着轉發鏈路將報文一字不漏地發送給服務器,但Connection首部是個逐跳首部,只適用於單條傳輸鏈路
3.經過中繼的HTTP請求抵達了Web服務器,當Web服務器收到經過代理轉發的Connection:Keep-alive首部時,會誤以爲代理(對服務器來說,這個代理看起來就和其他客戶端一樣)希望進行keep-alive對話,Web服務器會送了一個Connection:keep-alive響應首部,所以此時Web服務器認爲他在和代理進行keep-alive對話,遵循keep-alive規則,但代理其實卻對keep-alive一無所知
4.啞代理將Web服務器的響應報文回送給客戶端,並將來自Web服務器的Connection:keep-alvie首部一起傳送回去。客戶端看到這個首部就會認爲代理同意了keep-alive對話,此時客戶端和服務器都認爲它們在進行keep-alive對話,但是與它們進行對話的代理卻對keep-alive一無所知
5.由於代理對於keep-alive一無所知,所以會將收到的所有數據都回送給客戶端,然後等源端服務器關閉連接。但源端服務器會認爲代理已經顯示的請求它連接保持在打開狀態,所以不會去關閉連接,這樣代理會掛在那裏等待連接關閉。
6.客戶端收到回送的響應報文,會立即轉向下一條請求,在keep-alive連接上向代理髮送另一條請求,而代理並不認爲同一條連接上會有其他請求到來,請求被忽略,瀏覽器就在這裏轉圈,不會有任何進展
7.這種錯誤的通信方式會使瀏覽器一直處於掛起狀態,直到客戶端或服務器將連接超時,並將其關閉。

爲避免此類代理通信問題,現代的代理都決不能轉發Connection首部和所有名字出現connection值中的首部。

爲了解決這個問題,引入了一個名爲Proxy-connection的新首部,解決了客戶端後面緊跟着一個盲中繼所帶來的問題——但沒有解決所有其他情況下存在的問題。
所以之後會出現這種情況:瀏覽器會向代理髮送非標準的Proxy-Connection擴展首部。如果代理是盲中繼,它將會無意義的Proxy-connnection首部轉發給Web服務器,服務器會忽略此首部,不會帶來任何問題,但如果代理是聰明的(能夠理解持久連接的握手動作),就會使用一個Connection首部取代無意義的Proxy-connection首部,然後將其發送給服務器,以收到預期的效果。
這裏寫圖片描述
但如果在啞代理任意一側還有一個聰明的代理,那麼問題又會產生:
這裏寫圖片描述

下面介紹HTTP/1.1持久連接?

HTTP/1.1逐漸停止了對keep-alive連接的支持,用一種名爲持久連接(persisetent connection)的改進型。與HTTP/1.0的keep-alive連接不同,HTTP/1.1假定所有連接都是持久的,要在事務處理結束之後將連接關閉,HTTP/1.1應用程序必須向報文顯式地增加一個Connection:close首部,但是客戶端和服務器仍然可以隨時關閉空閒的連接。

接下來介紹管道化連接?

HTTP/1.1允許在持久連接上可選地使用請求管道。在響應到達之前,可以將多條請求放入隊列,當第一條請求通過網絡流向服務器時,第二條和第三條也可以開始發送了。在高延時的網絡條件下,這樣做可以降低網絡的環回時間,提高性能。
這裏寫圖片描述

最後來介紹關閉連接?

所有的HTTP客戶端、服務器或代理都可以在任意時刻關閉一條TCP傳輸連接。服務器永遠都無法確定在它關閉“空閒”連接那一段時間,在線路的那一頭客戶端有沒有數據要發送。如果出現這種情況,客戶端就會在寫入半截請求報文時發現出現了連接錯誤。
即使在非錯誤情況下,連接也可以任意時刻關閉。HTTP應用橫須要做好正確處理非預期關閉的準備,如果在客戶端執行事務的過程中,傳輸連接關閉了,那麼除非事務處理會帶來一些副作用,否則客戶端應該重新打開連接,並重試一次。
副作用指的是如冪等性問題。如果一個事務,不管是執行一次還是很多次,得到的結果都相同,這個事務就是冪等的,要發送一條非冪等性請求,就需要等待來自前一條請求的響應。正確關閉連接時,TCP連接是雙向的,如:
這裏寫圖片描述
有完全關閉:套接字調用close()
半關閉:套接字調用shutdown()
如:
這裏寫圖片描述
關閉連接的輸出信道總是安全的,連接另一端的對等實體會在其緩衝區中讀出所有數據之後收到一條通知,說明流結束了,這樣他就知道你將連接關閉了。
關閉連接的輸入信道比較危險,除非你知道另一端不打算再發送其他數據了,如果另一端向你已關閉的輸入信道發送數據,操作系統就會向另一端機器會送一條TCP”連接被對端重置”的報文,大部分操作系統都會刪除對端還未讀取的所有緩存數據。
這裏寫圖片描述
比如你已經在一條持久連接上發送了10條管道式請求了,響應也已經收到了,正在操作系統的緩衝區中存着(但應用程序還未將其讀走)。現在假設你發送第11條請求,但服務器認爲你使用這條連接的時間已經夠長了,決定將其關閉,那麼你的第11條請求就會被髮送到一條已關閉的連接上去,並會向你會送一條重置信息,這個重置信息會清空你的輸入緩衝區,當你最終去讀取數據的時候,會得到一個連接被對端重置的錯誤,已緩存的未讀響應數據都丟失了,儘管其中大部分都已經成功抵達你的機器了。

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