連接管理
重點
- http是如何使用tcp連接的;
- tcp連接的時延、瓶頸以及存在的障礙
- http的優化包括并行連接、keep-alive(持久連接)和管道化管理
- 管理連接是應該以及不應該做的事情
4.1tcp連接
世界上幾乎所有的http通信都是由tcp承載的,tcp是全球計算機及網絡設備都在是用的一種常用的分組交換網絡分層協議集。客戶端可以打開一條tcp連接,連接到服務器,一旦連接建立,客戶如和服務器的交換報文就不會丟失,受損或失序。
4.11tcp可靠數據管道
http連接實際上就是tcp連接和一些使用連接的規則,tcp為http提供了一條可靠的比特傳輸管道。從tcp連接的一端填入的字節會以原有的方式傳送出來
4.1.2tcp流是分段的由ip分組傳送
http要傳送一條報文時,會以流的形式將報文數據的內容通過一條打開的tcp連接按序傳輸tcp收到數據流后,會將數據流分成更小的數據塊稱為段,將段封裝在ip分組中
一個ip分組包括
- 一個ip分組首部(20字節)包含源和目的ip地址,長度
- 一個tcp首部(20字節)包含tcp端口,控制標記,校驗和
- 一個tcp數據塊(0-n個字節)
4.1.3保持tcp連接的正確運行
tcp通過端口號保持連接的正確,tcp連接通過四個值<源ip、源端口號、目的ip,目的端口號>
4.1.4用tcp套接字編程
套接字編程api允許用戶創建tcp的端點數據結構,這些端點與遠程服務器連接,并對數據流進行讀寫。
4.2對tcp性能的考慮
4.2.1http事務的時延
注意,與建立tcp連接,以及傳輸請求和響應報文相比,事務處理可能是很短的。除非客戶端或服務器超載,或正在處理復雜的動態資源,否則http時延是由tcp網絡時延構成的。
- 客戶端首先根據url確定服務器的ip和端口號。如果最近沒有對該主機訪問,通過dns解析系統把主機名轉換為ip可能需要數十秒
- 接下來,客戶端會發送一條tcp連接請求,同時等待服務器的接受應答。每條tcp都會有建立時延,一般最多一兩秒,但如果有數百個http事務,這個值會快速的增加
- 一旦連接建立,客戶端通過建立的tcp管道發發送http請求。數據到達時,web服務器會從tcp連接中讀取請求報文,并處理。
4.然后服務器會回送http響應。
tcp網絡的時延取決于硬件速度、網絡和服務器的負載,請求和響應的尺寸,客戶端和服務器的距離。tcp協議的技術復雜想也會對時延產生影響。
4.2.2性能聚焦區域
可能對http程序員產生影響的tcp時延
- tcp連接建立握手;
- tcp慢啟動擁塞控制;
- 數據聚集的nagle算法;
- 用于捎帶確認的tcp延遲確認算法;
- time_wait時延和端口耗盡;
4.2.3tcp連接的握手時延
tcp連接需要以下幾步
- 請求新的tcp連接,客戶端向服務器發送一個小的tcp分組。這個分組設置了syn標記,說明這是一個連接請求
- 如果服務器接受了連接,就會對參數進行計算,并向客戶端發送一個tcp分組,這個分組的syn、ack都被置位,表明連接被接受
- 最后客戶端向服務器發送一條確認信息,通知它連接建立成功。現代的tcp棧都允許客戶端在這個確認分組中發送數據。
4.2.4延遲確認
由于因特網自身無法確保可靠地分組傳輸,所以tcp實現了自己的確認機制來確認劇場來確保數據的成功傳輸。
每個tcp端都有一個序列號和數據完整性的校驗和。每個接受者收到完整的段,都會發送確認分組,如果接收方沒有在指定的時間內收到確認信息就會認為分組已經破壞,重發數據。
由于確認報文小,所以tcp允許在發往相同方向的輸出數據進行“捎帶”。tcp將返回的確認信息與輸出的數據分組結合在一起,可以更有效的利用網絡。。為了增加確認報文找到同向傳輸數據,很多tcp棧實現了一種“延遲確認”算法。
但是,http具有雙峰特征的請求-應答行為降低了捎帶信息的可能(什么雙峰,不懂)。通常延遲確認算法會引入比較大的時延,根據操作系統不同,可以調整或禁止延遲確認算法。
4.2.5tcp慢啟動
tcp數據傳輸的性能還取決于tcp連接的使用期(age)。tcp連接句隨著時間進行自我“調諧”,起初會限制最大連接速度,如果數據成功,會隨著時間提高傳輸的速度。這種調諧被稱為tcp慢啟動。
4.2.6nagle算法與tcp_nodelay
tcp只有一個數據流接口,應用程序可以將任意尺寸的數據放入其中,如果tcp發送了大量包含少量數據的分組,網絡性能會下降。
nagle算法試圖在發送一個分組前,將大量的tcp數據綁到一起,以提高網絡效率。
nagle鼓勵發送全尺寸的段,只有當其它分組都被確認后,nagle才允許發送非全尺寸的分組。
nagle可能印發集中http性能問題。首先,小的http分組可能無法填滿一個分組,可能因為等待產生時延,其次nagle與延遲確認算法交互存在問題。nagle會阻止數據發送,直到確認分組到達,但確認分組自身被延遲確認你算法延遲100-200毫秒。
4.2.7time_wait積累與端口耗盡
time_wait端口耗盡是很嚴重的問題,會影響到性能基準,現實中出現較少。大多數遇到性能基準問題的人最終會遇到這個問題,而且性能會變得很差。
當tcp關閉時,在內存中會有一個小的控制塊,記錄最近關閉的tcp連接的ip地址和端口號。這個方法可以防止兩分鐘內創建,關閉并重新創建兩個相同ip和端口號的連接。
即使沒有遇到端口耗盡的問題,也要小心大量連接處于打開狀態。,有些操作系統會因此性能下降。
4.3http連接的處理
4.3.1常被誤解的connection首部
http允許客戶端和最終的源服務器之間存在一串http中間實體(代理,高速緩存,等)可以從客戶端開始,逐跳的將http報文經過這些中間設備,轉發到源服務器上。
某些情況下,兩個相鄰的http應用會為它們共享的連接應用一組選項。http的connection首部字段中有一個有逗號分隔的連接標簽列表,這些標簽為此連接必定了一些不會傳播到其它連接的選項。
connection可以承載三種不同類型的標簽:
- http首部字段名,列出了只與次連接相關的首部;
- 任意標簽值,用于描述此連接的非標準選項;
- 值close,說明操作完成之后需關閉這條持久連接;
如果連接標簽中包含了一個http首部字段的名稱,那么這個首部字段就包含了一些與連接有關的信息,不能將其轉發出去。在將報文轉發之前,必須刪除connection首部列出的所有首部字段。
4.3.2串行事務處理時延
如果只是對連接 進行簡單的管理,tcp的性能時延可能會疊加。還有一個缺點,有些瀏覽器在對象加載之前,無法獲得對象的尺寸,所以屏幕會空白,導致用戶對加載進度一無所知。
提高性能的幾種方法
- 并行連接
通過多條tcp連接發起并發的http請求; - 持久連接
重用tcp連接,以消除連接及關閉時延; - 管道化連接
通過共享的tcp連接發起并發的http請求; - 復用的連接
交替傳送請求和響應報文(實驗階段)
4.4并行連接
4.4.1并行連接可能提高頁面的加載速度
包含嵌入對象的組合頁面,如果能克服單條連接的空載時間和帶寬限制,加載速度也會提高。
4.4.2并行連接不一定更快
即使并行連接的速度可能更快,但并不一定總是更快。例如:客戶端的帶寬不足。而且大量連接會消耗內存,從而引發自身性能問題。
4.4.3并行連接可能讓人“感覺更快”
如果屏幕多個動作用戶能夠看到進度,在心里會感覺更快。
4.5持久連接
web客戶端經常打開同一個網站的連接。因此,初始化了對某個服務器的http請求的應用程序可能在不就的將來發起更多的請求。這種性質被稱為站點本地性(site locality)
因此http1.1允許http設備在事務處理之后保持tcp連接在打開狀態。
重用服務器打開的空閑持久連接,就可以避免緩慢的建立連接階段。而且已經打開的連接還可以避免慢啟動的擁塞控制階段。
4.5.1持久以及并行連接
并行連接的缺點:
- 每個事務都會打開/關閉一條新的連接,會耗費時間和帶寬。
- 由于tcp慢啟動特性的存在,每個新連接的性能都會下降。
- 可打開的并行連接數量實際上有限。
持久連接有一些比并行連接更好的地方。持久連接降低了時延和連接建立的開銷,將連接保持在已協調狀態,而且減少了打開連接的潛在數量。但是,管理持久連接一定要特別小心,不然就會出現積累大量的空閑連接。
4.5.2http1.0 keep-alive 連接
很多http1.0的瀏覽器和服務器進行擴展,支持keep-alive連接。
4.5.3keep-alive操作
keep-alive已經不在使用,而且在http1.1也不存在,但瀏覽器和服務器對keep-alive仍然在使用。
客戶端發送包含 connection keep-alive首部請求將一條連接保持在打開狀態。如果服務器允許,就在響應中包含同樣的頭部。
4.5.4keep-alive選項
可以用keep-alive通用首部中指定、有逗號分隔的選項來調節keep-alive的行為。
- 參數timeout是在keep-alive響應首部發送的。它估計了服務器希望保持在活躍狀態的時間。這不是一個承諾值。
- 參數max是在keep-alive響應首部發送的,它估計了服務器希望為多少各事務保持連接的活躍狀態。這不是一個承諾值。
- keep-alive還可以支持未經處理的屬性,這些屬性主要用于診斷和調試。語法為 name[=value]
4.5.5keep-alive連接的限制和規則
- 在http1.0中keep-alive不是默認啟用的。
- connection:keep-alive首部必須隨著所有希望保持持久連接的報文一起發送。
- 客戶端探明響應中是否有connection:keep-alive就可以知道服務器發出響應后是否關閉連接。
- 只有在無需檢測到連接的關閉即可確定報文實體主體長度的情況下,才可以將連接保持在打開狀態。
- 代理和網關必須執行connection首部的規則(刪除connection首部中命名的所有首部字段以及connection首部自身)
- 嚴格來說,不應該與無法確定是否支持connection的代理服務器建立keep-alive連接,以防出現啞代理。
- 從技術上講,應該忽略所有來自http1.0的connection首部字段,因為他們可能是老的代理服務器誤轉發的。實際上,盡管可能導致老代理掛起的危險。
- 除非重復發送請求會導致一些副作用,否則如果在客戶端接受完整的響應之前連接就關閉了,客戶端就一定要做好重試請求的準備。
4.5.6keep-alive和啞代理
1.connection首部和盲中繼
問題出現在代理上-尤其不理解connection首部,而且不知道沿著轉發鏈路將其發送出去之前,應該將首部刪除的代理。很多老的代理都是盲中繼(blind relay),他們只是將字節從一個連接轉發到另一個去,不對connection進行特殊處理。
- 客戶端向代理發送一條報文,包含了connection首部。
- 啞代理收到了這個請求,但不理解connection首部,把它轉發給服務器。
- 服務器收到這個請求,允許了進行keep-alive,并把含有connection的首部發送給啞代理。
- 啞代理把服務器的報文回給客戶端,客戶端認為,它正在進行keep-alive對話。
- 此時,代理完成了轉發,等待服務器關閉連接,然而服務器認為,代理已經請求了keep-alive,所以代理就會掛在那里等待連接關閉。
- 客戶端收到報文,即可轉向下一條請求,然而,代理不認為這條連接會有請求,請求被忽略。
- 這種錯誤的通信方式會使瀏覽器處于掛起狀態,直到客戶端或服務器連接超時。
2.代理和逐跳首部
為避免此類通信問題的發生,現代的代理都不能轉發connection首部和所有名字出現在connection值的首部。
另外還有幾個不能作為connection首部值列出,也不能被代理轉發或作為緩存響應使用的首部。其中包括 proxy-authenticate、proxy-connection、transfer-encoding和upgrade。參考4.3.1.
4.5.7插入proxy-connection
為了解決這個問題,一些瀏覽器配置了proxy-connection,很多代理能夠理解他。
如果是啞代理,它轉發了該首部,服務器會忽略該選項。如果是聰明的代理,則會用connection替代它。但如果在啞代理的一側還有聰明的代理,這個問題又會出現。
4.5.8http1.1持久連接
http1.1逐漸停止了對keep-alive連接的支持,用一種名為持久連接(persistent connection)的改進型設計取代了它。
http1.1持久連接在默認情況下是激活的,要在處理事務之后關閉連接,http1.1必須顯示的添加一個connection:close首部。但是,不發送connection:close不意味著服務器承諾永遠保持連接。
4.5.9持久連接的限制和規則
發送了connection:close首部之后,客戶端就無法在那條連接上發送更多的請求。
如果客戶端不想發送其它請求,應該在最后一條請求中發送connection:close首部。
只有連接上的所有報文都正確,自定義報文長度時,實體主體的長的與相應的content-length一致,或者用分塊傳輸編碼方式編碼的-連接才能持久。
http的代理必須能夠分別管理客戶端和服務器的持久連接-每個持久連接都只適用于一跳傳輸
http1.1的代理服務器不應與http1.0的客戶端建立持久連接,除非他們了解客戶端的處理能力。盡管服務器不應該試圖在傳輸的過程中關閉連接,而且關閉之前應該響應一條請求,但不管connection首部取了什么值,http1.1可以在任意時刻關閉連接。
http1.1應用程序必須能夠從異步的關閉中恢復出來。只要不存在可能會積累起來的副作用,客戶端都應該重試這條請求。
除非重復請求會產生副作用,否則如果客戶端收到整個響應之前連接關閉,客戶端必須重新發送請求。
一個客戶端對任何服務器或代理最多只能維持兩條持久連接,以防止服務器過載。
4.6管道化連接
http1.1允許持久連接可選的使用請求管道。在響應到達之前,可以將多條請求放入隊列。
管道化連接的限制
- 如果http客戶端無法確認連接是持久的,不應使用管道。
- 必須按照請求相同的順序會送http響應。如果響應失序,沒辦法與請求匹配。
- http客戶端必須做好連接會在任意時刻關閉的準備,還要準備重發未完成的管道化請求。
- http客戶端不應該用管道化的方式發送會產生副作用的請求(比如post)。總之,出錯的時候,管道化的方式會阻止客戶端了解服務器執行的是一系列管道化請求的哪些,由于無法安全的重試post這樣的非冪等請求,所以出錯時,就存在某些方法永遠無法執行的風險。
4.7關閉連接的奧秘
連接管理-尤其知道什么時候關閉連接,是http的實用魔法之一。
4.7.1任意解除連接
所有的http客戶端服務器或代理,都可以在任意時刻關閉一條tcp連接。通常在一條報文結束的時候關閉連接,但出錯的時候,也可能在首部行的中間,或者其它奇怪的地方。
4.7.2content-length及截尾操作
每條http響應都應該有精確的content-length首部,用于描述響應主體的尺寸。客戶端或代理收到一條隨連接關閉而結束的http響應,而且實際傳輸的實體長度與content-length不匹配,接收端就應該質疑長度的真實性。如果是緩存代理,接收端不應緩存這條響應,代理應該把報文原封不動的轉發出去,不應該試圖去校正content-length,以維護語義的透明性。
4.7.3連接關閉容限、重試以及冪等性
即使在非錯誤的請求下,連接也可以在任意時刻關閉。http應用程序要做好正確處理非預期關閉的準備。如果客戶端在執行事務的過程中,傳輸連接關閉了,那么除非事務處理會帶來一些副作用,否則客戶端應該重新打開連接,并重試一次。
如果一個事務不管是執行一次還是多次,得到的結果都相同。這個事務就是冪等的。實現者認為get head put delete trace options方法都是冪等的。客戶端不應該以管道化的方式傳送非冪等請求。
4.7.4正常關閉連接
tcp連接是雙向的。tcp連接的每一端都有一個輸入隊列和一個輸出隊列,用于數據的讀寫。放入一端輸出隊列的數據最終會出現在另一端的輸入隊列中。
1.完全關閉和半關閉
應用程序可以關閉輸入和輸出信道的任意一個,或者兩者都關閉了。
2.tcp關閉及重置錯誤
簡單的http應用可以只使用完全關閉。但當應用程序開始與很多其他類型的http客戶端、服務器和代理進行對話切開始使用管道化持久連接時,使用半關閉來防止對實體收到非預期的寫入錯誤就變得很重要了
3.正常關閉
http規范建議,當客戶端或服務器突然要關閉一條連接時,應該正常的關閉。
總之,實現正常關閉的應用程序首先應該關閉輸出信道,然后等待另一端的對等實體關閉它的輸出信道。
但不幸的是,無法確保對等實體會實現半關閉,或對其進行檢查。因此要想正常關閉連接,應先關閉輸出信道,然后周期性的檢查信道狀態。