[HTTP權威指南讀書筆記]第四章—連接管理

本文章是轉載自:源地址。文章作者對於此章的敘述比我好很多,所以就不出醜而是直接借鑑他人的文章。


HTTP規範對HTTP報文解釋得很清楚,但對HTTP連接介紹並不多,而HTTP連接時HTTP報文傳輸的關鍵通道。本文將介紹HTTP是如何使用TCP連接,HTTP的優化以及管理連接時應該注意的問題等。

1. TCP連接

世界上幾乎所有的HTTP通信都是由TCP/IP承載的,TCP/IP是一種常用的分組交換網絡分層協議集。客戶端應用程序可以打開一條TCP/IP連接,連接到服務器應用程序。一旦連接建立,客戶端和服務器計算機之間交換報文就永遠不會丟失、受損或失序。

TCP爲HTTP提供了一條可靠的比特傳輸管道。從TCP連接一端流入的字節會從另一端以原有的順序正確地傳送出來。TCP的數據是通過名爲IP分組的小數據塊來發送的。每個TCP段由IP分組承載,IP分組包括:IP分組首部、TCP段首部、TCP數據塊。在任意時刻計算機都可以有幾條TCP連接處於打開狀態。TCP是通過端口號來保持所有連接正常運行的,它由<源IP地址、源端口號、目的IP地址、目的端口號>唯一確定。

操作系統通常提供了一些用套接字操縱TCP連接的API,允許用戶創建TCP的端點數據結構,與遠程服務器的TCP端點進行連接,並對數據流進行讀寫。TCP API隱藏了所有底層網絡協議細節。下圖展示了客戶端和服務器如何通過TCP套接字接口進行通信,至於套接字API編程則不再贅述。


注:原書簡要介紹了對TCP的性能的考慮,但TCP是個很複雜的話題,屬於計算機網絡的範疇,因此此處略過TCP性能考慮的詳細介紹。最常見的TCP的相關時延包括:TCP連接建立握手、TCP慢啓動擁塞控制、數據聚集的Nagle算法、用於捎帶確認的TCP延遲確認算法、TIME_WAIT時延和端口耗盡。

2. HTTP連接的處理

2.1 常被誤解的Connection首部

Connection首部可以承載3種不同類型的標籤:

  • HTTP首部字段名,列出了只與此連接有關的首部。在將報文轉發出去之前,必須刪除列出的所有首部字段。
  • 任意標籤值,用於描述此連接的非標準選項
  • 值close,說明操作完成之後需關閉這條持久連接

2.2 串行連接

如果只對連接進行簡單的管理,TCP的性能時延可能會疊加起來。比如,假設有一個包含3個圖片的Web頁面,瀏覽器需要發起4個HTTP事務來顯示。加載一副圖片時,頁面上其他地方都沒有動靜會讓人覺得速度很慢,此外,瀏覽器在對象加載完畢前無法獲知對象的尺寸,所以串行事務會讓用戶面對一個空白的屏幕,對裝載進度一無所知。爲了提高HTTP的連接性能,有並行連接,持久連接,管道化連接等技術可以改善。

2.3 並行連接

HTTP允許客戶端打開多條連接,並行地執行多個HTTP事務。並行連接可能會提高頁面的加載速度,但有的情況下反而更慢。比如客戶端的網絡帶寬不足時(如通過28.8kbps的Modem上網),一個連接到速度較快的服務器的HTTP事務就會很容易耗盡所有可用帶寬。而且打開大量連接會消耗很多內存,這會造成服務器性能嚴重下降。實際上,瀏覽器確實使用了並行連接,但並行的總數會限制爲一個較小的值(通常是4個)。服務器也可以關閉來自特定客戶端的過量連接。

2.4 持久連接

HTTP在允許事務處理結束後將TCP連接保持在打開狀態,以便未來的HTTP請求重用現存的連接,這被稱爲TCP的持久連接。相比並行連接,持久連接降低了時延和連接建立的開銷,減少了打開連接的潛在數量。但是要小心管理這些連接,不然會累積出大量的空閒連接,耗費機器資源。現在,很多Web應用程序都會打開少量的並行連接,其中每一個都是持久連接。持久連接有兩種類型:較老的HTTP/1.0+“keep-alive”連接和現代HTTP/1.1“persistent”連接。

2.4.1 Keep-Alive持久連接

Keep-Alive已經不再使用了,但是有一些瀏覽器和服務器對keep-alive握手的使用仍然相當廣泛,因此這裏簡單敘述一下keep-alive操作。首先客戶端可以通過包含Connection: Keep-Alive首部請求將一條連接保持在打開狀態,如果服務器同意,就在響應中包含相同的首部。若響應中沒有相應的首部,客戶端就會認爲服務器不支持keep-alive,接收到響應後關閉連接。注意,Keep-Alive首部只是請求將連接保持在活躍狀態,發出請求後,雙端不一定會同意進行keep-alive會話,它們可以在任意時刻關閉或限制空閒的keep-alive連接。下面給出一個用Keep-Alive通用首部指定選項來調節keep-alive行爲的例子,該例子說明服務器最多還會爲另外5個事務保持連接打開狀態,將打開狀態保持到連接空閒了2分鐘之後:


使用keep-alive連接有一些限制:Connection: Keep-Alive首部必須隨所有希望保持持久連接的報文一起發送;實體的主體部分必須有正確的Content-Length,才能打開持久連接;代理和網管必須在將報文轉發出去之前,刪除在Connection首部及其中命名的所有首部字段(Connection首部應該只對離開客戶端的TCP鏈路產生影響,但很多老的或簡單的代理都是盲中繼,即只將字節從一個連接轉發到另一個連接而不對首部進行處理,代理對keep-alive對話毫不知情,而客戶端和服務器都認爲它們在進行keep-alive對話,這就會造成錯誤的通信方式,具體情況如下圖所示)。爲了避免此類代理通信問題的發生,現代的代理都決不能轉發Connection首部和所有名字出現在Connection值中的首部。


對於單個盲中繼問題,Netscape的瀏覽器引入一個名爲Proxy-Connection的新首部,即使盲中繼代理轉發這個首部,Web服務器也會將其狐狸。如果代理是“聰明”的,收到這個首部後就會發送自己的Connection: Keep-Alive來建立keep-alive連接。當然,這種做法只能解決只有一個代理的情況,如果客戶端和服務器之間有多個代理,同樣會有問題。透明的Web應用程序正確實現持久連接時非常重要的。

2.4.2 HTTP/1.1持久連接

HTTP/1.1逐漸停止了對keep-alive連接的支持,用一種名爲persistent connection的改進型設計取代了它。在HTTP/1.1下,持久連接默認情況下是激活的,除非響應報文中包含Connection: close,否則連接就維持在打開狀態。當然,客戶端和服務器仍然可以隨時關閉空閒的連接。

使用persistent連接同樣有一些限制:發送Connection: close請求首部後,客戶端就無法在那條連接上發送更多請求了;實體主體部分長度都和相應的Content-Length一致,連接才能持久保持;服務器不應試圖在傳輸報文的過程中關閉連接,而且關閉之前應該至少響應一條請求;客戶端收到整條響應之前連接關閉了,客戶端都要重新發起請求,除非重複發請求會產生副作用。

2.4.3 管道化連接

HTTP/1.1允許在持久連接上可選地使用請求管道,在響應到達之前,可以將多條請求放入隊列,當第一條請求發出之後,後面的請求也可以開始陸續發送。這樣可以在高時延網絡條件下降低網絡的環回時間,提高性能。管道化連接也有一些限制:如果HTTP客戶端無法確認連接時持久的,就不應該使用管道;必須按照與請求相同的順序回送HTTP響應;客戶端必須做好連接會在任意時刻關閉的準備,並準備好重發所有未完成的管道化請求;客戶端不應該用管道化的方式發送會產生副作用的請求(如POST)。

下圖是各種連接的時延對比。


2.5 關閉連接

無論是錯誤還是非錯誤情況,連接都可能在任意時刻關閉。HTTP應用程序要做好正確處理非預期關閉的準備。通常來說當傳輸連接意外關閉時,客戶端會重新打開並重試。注意,有些事務比如GET一個靜態頁面,反覆執行多次也不會有什麼變化(冪等請求),但對於POST(非冪等請求),就不能重複執行。

TCP連接是雙向的,連接的每一端都有一個輸入隊列和一個輸出隊列。應用程序可以關閉TCP輸入和輸出信道中的任一個,或者兩者都關閉,分別稱爲“完全關閉”和“半關閉”。其中,關閉連接的輸出信道總是安全的,而關閉輸入信道則比較危險,除非你知道另一端不打算再發送其他數據。如果另一端向你已關閉的輸入信道發送數據,操作系統就會向另一端的機器回送一條TCP“連接被對端重置”的報文。這個重置信息會清空輸入緩衝區,儘管其中的大部分都已經成功抵達你的機器。

總之,實現正常關閉的應用程序首先應該關閉它們的輸出信道,然後等待連接另一端關閉它的輸出信道。當兩端都告訴對方它們不會再發送任何數據之後,連接就會被完全關閉,而不會有重置的危險。

參考文獻:人民郵電出版社《HTTP權威指南》第4章


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