白話http隊頭阻塞

  • 2019 年 10 月 5 日
  • 筆記

題圖 From Bing By clm

http協議的1.0版本與1.1版本最大的一個區別就是http1.1增加了長連接功能,那什麼是http的長連接呢?

在了解http的長連接之前,我們來看一下http1.0的請求是如何建立連接的,首先我們要清楚的是,http不論哪個版本,都是建立在tcp協議上的,而tcp的連接需要經歷三次握手,tcp的關閉需要四次揮手,而http1.0協議中每個http請求都需要經歷三次握手和四次揮手,頁面中都多少個http請求,就需要建立多少次握手和揮手,流程如圖:

但是在http1.1中新增了長連接功能,這個長連接功能通常被叫做http長連接,筆者認為這個叫法不太準確,應該叫做tcp長連接,為什麼這樣說呢,因為在http1.1中,如果頁面中發起了多個http請求,此時只需要建立一個tcp連接就可以了,多個http請求響應會共用這一個tcp連接通道。

此時http請求中會攜帶一個Http請求頭:Connection:keep-alive,現在大部分的web伺服器都默認支援tcp長連接,也就是網頁中的請求不攜帶Connection:keep-alive請求頭,默認就是長連接請求,如果不想支援長連接的話,需要顯示的添加Connection:closed請求頭。

http1.1請求鏈接過程,如下:

通過對比兩張流程圖,我們發現,tcp保持長連接大大提高了傳輸效率,但是這裡還是有一個問題,那就是http的對頭阻塞問題。

在一般情況下,HTTP遵守「請求-響應」的模式,也就是客戶端每次發送一個請求到服務端,服務端返迴響應,這種模式很簡單,但是有一個致命缺陷那就是頁面中有多個請求,每個請求必須等到前一個請求響應之後才能發送,並且當前請求的響應返回之後,當前請求的下一個請求才能發送,流程如下圖:

仔細觀察上圖:在tcp鏈接中,http請求必須等待前一個請求響應之後,才能發送,後面的依次類推,由此可以看出,如果在一個tcp通道中如果某個http請求的響應因為某個原因沒有及時返回,後面的響應會被阻塞,這就是隊頭阻塞。

為了提高速度和效率,在持久連接的基礎上,HTTP1.1進一步地支援在持久連接上使用管道化(pipelining)特性。管道化允許客戶端在已發送的請求收到服務端的響應之前發送下一個請求,藉此來減少等待時間提高吞吐,如果多個請求能在同一個TCP分節發送的話,還能提高網路利用率,流程如圖:

仔細觀察上圖,我們發現,同一個tcp連接中可以同時發送多個http請求,也就是並發,但是在響應的時候,必須排隊響應,誰先到達的誰先響應,相比不支援管道化的http請求確實提高了效率,但是還是有局限性,加入其中某個響應因為某種原因延遲了幾秒,後面的響應都會被阻塞,如圖:

觀察上圖紅線標識的響應,因為紅線標識的響應被阻塞了,它後面的所有響應都會被阻塞,這就是隊頭阻塞

並且使用HTTP管道化還有一些限制:

1、管道化要求服務端按照請求發送的順序返迴響應(FIFO),原因很簡單,HTTP請求和響應並沒有序號標識,無法將亂序的響應與請求關聯起來。

2、當客戶端在支援管道化時需要保持未收到響應的請求,當連接意外中斷時,需要重新發送這部分請求。如果這個請求只是從伺服器獲取數據,那麼並不會對資源造成任何影響,而如果是一個提交資訊的請求如post請求,那麼可能會造成資源多次提交從而改變資源,這是不允許的。而不會對伺服器資源產生影響的請求有個專業名詞叫做冪等請求。客戶端在使用管道化的時候請求方式必須是冪等請求。

我將http不支援管道化與管道化的圖放在一起,大家比較一下:

因為HTTP管道化本身可能會導致隊頭阻塞的問題,以及上面提到的一些限制,現代瀏覽器默認都關閉了管道化,並且大部分伺服器也是默認不支援管道化的

那麼如何解決隊頭阻塞呢?

HTTP 協議建議客戶端使用並髮長連接,注意這個並髮指的是tcp並發連接接。RFC2616 里明確限制每個客戶端可以建立兩個長連接,這裡著重說明一下,客戶端建立長連接的個數是針對域名發起的,舉例說明,當我們訪問a.com網站的時候,客戶端與a.com伺服器建立的長鏈接就是2個。

但是一般瀏覽器會把並發鏈接數增加到6到8個,Google瀏覽器是6個,也就是頁面中如果針對同一個域名有多個http請求,Google瀏覽器會針對這個域名建立6個tcp長連接,在每個長連接裡面再去處理http請求,但是這種方案其實對伺服器的挑戰非常大,有些web優化方案中還會突破6到8的限制,那就是域名切片,因為長連接是針對的同一個域名,那麼如果開發人員將資源分布在不同的域名上,那麼長連接的數量也是可以被突破的。

假設頁面中有100張圖片,基於這個案例,咱們用圖示將http1.0到http1.1的變遷用三張圖來表示一下:

http1.0時代:100個http請求建立100個tcp連接。

http1.1時代,tcp支援了長連接,每個tcp可以處理多個http請求。

前面說過了,tcp的並發數可以通過域名切片來增大,但這樣做會增大伺服器的連接數,當伺服器面對海量請求的話,可能會現問題,那麼怎麼辦呢,這是就需要使用http2協議了。我們下期再聊。

下面我給大家總結一下本篇文章的內容:

1、首先我們釐清了一個概念,那就是http長連接其實指的是tcp長連接。

2、隊頭阻塞是一種現象,http因為請求-響應模型會有隊頭阻塞的現象出現,隊頭阻塞指的是在同一個tcp鏈接中,如果先發送的http請求如果沒有響應的話,後面的http請求也不會響應。

3、解決隊頭阻塞的第一個方案就是並髮長連接,瀏覽器默認是6-8個長連接,我們可以用域名分片的技術突破這個數值。

4、並髮長連接雖然在一定程度上解決了http的隊頭阻塞,但是會對伺服器的性能有較高的要求。