【讀】這一次,讓我們再深入一點 - HTTP的連結管理

_高洋_發表於2018-01-29

這是網路系列的第七篇文章,接下來會有更多精彩內容.敬請期待! 讓我們一起乘風破浪!

前言

在上篇(這一次,讓我們再深入一點 - HTTP報文)中我們瞭解到了HTTP的報文格式,也就是客戶端伺服器對話的約定.那麼該篇將介紹客戶端伺服器如何將對話內容傳送到對方的.通過本篇,可以瞭解到如下內容:

  • HTTP是使用TCP連線的
  • TCP連線的延遲、瓶頸以及存在的障礙
  • HTTP的優化,包括並行連線、持久連線、管道化連線

TCP連線

其實TCP連線在介紹TCP協議(這一次,讓我們再深入一點 - TCP協議)時已經說明了. 這裡在重複說下連線建立和釋放的過程, 加深下印象. HTTP連線實際是TCP連線和使用連線的規則組成。TCP的可靠服務是HTTP報文完整有序到達對方的基礎。對於傳輸層的TCP來說,其依靠下層(網路層)的IP協議進行資料的傳送,以IP資料包的形式。也就是說,在HTTP想要傳送一條報文時,會將報文資料通過開啟的TCP連線按序傳輸;TCP在接收的資料後,將其分成多個資料塊,並將其封裝在IP資料包中進行傳輸。對於HTTPS(相關介紹看這裡)來說,HTTP的報文會先交付給安全層進行包裝,然後交付給TCP。HTTP和HTTPS使用的協議棧看上去類似下面這個圖:

HTTP和HTTPS協議棧

一臺計算機可能再同一時刻有幾條TCP連線處於開啟狀態,為了彼此不干擾,TCP使用埠號來區分;而通過IP地址可以確定網路上的一臺主機,所以一條連線可以由這些因素確定: <源IP地址、源埠號、目的IP地址、目的埠號> 那麼這個TCP連線是如何建立的呢,當然是被人說爛了的三次握手:

三次握手建立連線模型

  • 伺服器是處於監聽狀態的,以便及時發現客戶端建立連線的需求。
  • 客戶端TCP程式主動發出Flag段SYN=1,報文序列號seq=x的報文段(A),請求建立連線。狀態變為SYN-SENT(同步已傳送)。
  • 伺服器收到對應報文段(A)後,會發出確認報文段(B)。該報文(B)的Flag段的SYN和ACK都是1,確認號ack=x+1(意為對A的確認),同時設定自己的初始序列號seq=y。狀態由LISTEN(監聽)變為SYN-RCVD(同步收到)。
  • 客戶端收到伺服器的確認後,還需向伺服器傳送確認。報文段(C)的Flag的ACK=1,確認號ack=y+1(意為對B的確認),序列號seq=x+1。狀態變為ESTABLISHED(已建立連線)。伺服器在收到報文段後狀態也變為ESTABLISHED。
  • 客戶端的最後確認是必要的,可以防止以失效的請求建立連線報文突然到達伺服器而產生錯誤。

下面再來看看連線是如何釋放的:

四次握手釋放連線模型

  • 在連線建立完成,資料傳輸完畢後,通訊的雙發都是可以釋放連線的。但通常情況,伺服器都是被動的一端,客戶端才知道,自己是不是真的沒有資料要傳送了。
  • 在資料傳輸過程中,客戶端和伺服器都處於已建立連線的狀態。
  • 客戶端TCP程式先發出連線釋放報文(A),並停止傳送資料。該報文的Flag位的FIN=1,seq=u(等於其上一個序號+1)。客戶端進入FIN-WAIT-1狀態,等待伺服器確認。
  • 伺服器接收到釋放連線報文段後發出確認報文(B)Flag位ACK=1,確認號ack=u+1,序列號seq=v(等於其上一個序號+1)。伺服器進入WAIT(關閉等待)狀態,這條TCP連線處於半關閉狀態,也就是客戶端到伺服器方向的通道被關閉。
  • 客戶端在收到伺服器的確認報文(B)後,進入FIN-WAIT-2狀態,等待伺服器發出連線關閉的報文。
  • 若伺服器也沒有其他資料需要傳送,就會向客戶端發出釋放連線的報文(C),Flag端FIN=1,ACK=1,確認號ack=u+1(和報文B一樣),序列號seq=w(伺服器可能在發出報文B之後又傳送了資料,w的值可能不是v+1)。伺服器進入LAST-ACK狀態,等待客戶端的確認。
  • 客戶端在收到伺服器釋放連線報文(C)後,發出確認報文(D)。Flag段ACK=1,確認號ack=w+1,序列號seq=u+1(報文A的seq+1)。客戶端進入TIME-WAIT狀態。現在TCP連線並沒有釋放掉,必須等待2倍MSL(最長報文段壽命Maximum Segment Lifetime,該時長是由時間等待計時器設定)後才能關閉。
  • 伺服器收到報文D後,就可以關閉連線。
  • 為何要等待2MSL,客戶端才能關閉連線
    • 保證客戶端發出的報文D能夠到達伺服器。在報文D丟失的情況下,伺服器未收到客戶端的響應,所以會觸發TCP的超時重傳,而客戶端可以在2MSL時間內收到重傳的報文,並對之響應且重新啟動2MSL計時器。最終,該連線可以正常關閉。
    • 防止失效的連線請求報文出現。可以保證在建立該TCP連線中發出的報文都在網路中消失。
  • 關於TCP的保活計時器:在客戶端伺服器之間的TCP連線建立後,客戶端突然故障,伺服器也就無法再收到來自該客戶端的任何報文,為了使伺服器不會白白等待客戶端而使用的措施是保活計時器。伺服器每次收到客戶端的資料就會重置保活計時器,時間2小時。若2小時未收到客戶端的任何資料,伺服器傳送探測報文,每隔75秒一次,若連續10個探測報文都沒有客戶端的響應,該連線就會被伺服器關閉。

從TCP的特點看HTTP效能

由於HTTP依賴TCP,其效能是否優越,很大層度上取決於TCP的效能。回顧下HTTP事務過程:

HTTP事務流程概覽

從上圖可以看出一下幾個點會影響HTTP的表現:

  • 根據URI確定伺服器的地址和埠號。也就是DNS系統的效能。
  • TCP連線的建立。
  • 請求資料,處理資料過程
  • TCP連線的關閉。

接下來重點看下和TCP有關的,畢竟這部分是個大頭。

  • TCP連線的握手時延;從上一部分可以瞭解到TCP在傳送資料之前需要通過3次握手建立連線,即使HTTP需要傳送一個很小的資料也要有這個過程。這種情況下,一個HTTP事務可能在建立連線上花費50%或更多時間。
  • TCP的延遲確認;TCP需要確定報文的送達,存在自己的確認機制。確認報文一般會在另一個報文中進行“捎帶”;為了找到這個能夠“捎帶”的報文,TCP存在一個“延遲確認”演算法,該演算法也會影響其上層的HTTP。
  • TCP慢啟動;為了防止大量資料短時間進入網路,造成網路堵塞,TCP在開始傳送資料時會限制最大速度,隨著時間推移,TCP檢測到之前傳送的資料都被確認後,會逐漸提高速度(當然也有可能減慢速度)。這個特性使得新建的連線不如傳送過資料的連線速度快。
  • Nagle演算法與TCP_NODELAY;由於TCP包結構包含標記和首部欄位,若TCP傳送了大量的包含少量資料的分組,網路的效能會下降。Nagle演算法試圖在傳送分組之前將大量的資料繫結在一起,提高效率。在較小的HTTP報文可能無法填滿一個分組,可能會因為等待那些永遠無法達到的額外資料而產生時延。同時,Nagle演算法會阻止資料的傳送,直到有確認的分組到達,但確認分組自身會被延遲確認演算法延遲。HTTP應用程式可以設定引數TCP_NODELAY,禁用Nagle演算法,這樣的話,一定要確保向TCP寫入大資料塊。
  • TIME-WAIT累積和埠耗盡;在TCP釋放連線時,客戶端會在最後一個確認報文發出後等待2MSL時長在斷開連線,若這種狀態的連線太多,會造成埠耗盡。

HTTP的處理辦法

  • Connection首部;不僅可以用來控制持久連線(Connection:Keep-Alive/Close),而且可以說明不需要進行傳輸的頭部欄位(Connection:首部名稱)。

  • 序列事務,一個事務是否能開始,取決於上一個事務是否完成。比如一個包含3個圖片的Web頁面,瀏覽器需要發起4個事務來完成資料請求。序列事務如下:

    序列事務示例

  • 並行連線,同時開啟多個連線,執行多個事務。但是,多事務會對頻寬資源進行搶奪,導致每個資源都會以較小的速度載入;另外,大量的連線會消耗自身的硬體資源,引起效能問題。所以,連線數量要有較好的控制才行。

  • 持久連線,在事務處理結束後任然保持開啟狀態的TCP連線稱為持久連線。這樣可以避開連線的重複建立以及TCP慢啟動的特點。

    • HTTP/1.0 keep-alive;客戶端可以通過包含Connection:Keep-Alive首部請求將一條連線保持在開啟狀態,若伺服器願意為下一請求將連線保持在開啟狀態,就在響應中包含相同的首部;若響應中沒有Connection:Keep-Alive首部,客戶端就認為伺服器不支援keep-alive。下面是調節keep-alive行為的選項:
      • timeout,在響應首部發出。說明了伺服器希望將連線保持在活躍狀態的時間。參考值。

      • max,在響應首部發出。說明了伺服器希望為多少個事務保持連線的活躍狀態。參考值。

         Connection: Keep-Alive
         Keep-Alive: max=5, timeout=120
         伺服器希望為另外5個事務或2分鐘之內保持連線的活躍狀態。
        複製程式碼
      在HTTP/1.0,keep-alive不是預設使用的。
    • HTTP/1.1 persistent;預設開啟持久連線,可以在請求首部中包含Connection:close來關閉。
  • 管道化連線,在持久化連線的前提上建立。將多個請求放入佇列中,當第一個請求到達伺服器後,後續的請求也就可以傳送了。注意點:

    • 若HTTP客戶端無法確認連線是持久的,就不應該使用管道。
    • 伺服器要按序響應。
    • 客戶端必須做好連線在任意時刻關閉的準備,未發出的請求,要能重新發出。
    • 客戶端不應該使用管道化方式發出帶有副作用的請求(如POST)。

    下面是序列連線、持久連線、管道連線在完成4個事務的區別:

    【讀】這一次,讓我們再深入一點 - HTTP的連結管理

結語

該篇我們主要了解了TCP是如何影響HTTP的, 以及HTTP所作出的優化.希望大家能理解相關概念.

  • 部分圖片來源於網路,如有侵權,請告知。
  • 如有錯誤,還請指出。共勉!
  • 您的喜歡是最大的讚賞。

相關文章