再談HTTP2效能提升之背後原理—HTTP2歷史解剖

zhoulujun發表於2019-01-28

即使千辛萬苦,還是把網站升級到http2了,遇坑如《phpcms v9站http升級到https加http2遇到到坑》。

因為理論相比於 HTTP 1.x ,在同時相容 HTTP/1.1 完全語義,進一步減少了網路延遲。

對於前端開發人員來說,無疑減少了在前端方面的優化工作。比如雪碧圖&檔案合併||內容內嵌||域名分片

http1.0的缺點

http1.0被抱怨最多的就是連線無法複用,和head of line blocking這兩個問題。理解這兩個問題有一個十分重要的前提:客戶端是依據域名來向伺服器建立連線,一般PC端瀏覽器會針對單個域名的server同時建立6~8個連線,手機端的連線數則一般控制在4~6個。顯然連線數並不是越多越好,資源開銷和整體延遲都會隨之增大。

連線無法複用會導致每次請求都經歷三次握手和慢啟動。三次握手在高延遲的場景下影響較明顯,慢啟動則對檔案類大請求影響較大。

head of line blocking會導致頻寬無法被充分利用,以及後續健康請求被阻塞。假設有5個請求同時發出,對於http1.0的實現,在第一個請求沒有收到回覆之前,後續從應用層發出的請求只能排隊,請求2,3,4,5只能等請求1的response回來之後才能逐個發出。一旦請求1的request因為什麼原因沒有抵達伺服器,或者response因為網路阻塞沒有及時返回,影響的就是所有後續請求,問題就變得比較嚴重了。

對於http1.0的缺點優化方案

解決連線無法複用

http1.0協議頭裡可以設定Connection:Keep-Alive。在header裡設定Keep-Alive可以在一定時間內複用連線,具體複用時間的長短可以由伺服器控制,一般在15s左右。到http1.1之後Connection的預設值就是Keep-Alive,如果要關閉連線複用需要顯式的設定Connection:Close。一段時間內的連線複用對PC端瀏覽器的體驗幫助很大,因為大部分的請求在集中在一小段時間以內。但對移動app來說,成效不大,app端的請求比較分散且時間跨度相對較大。所以移動端app一般會從應用層尋求其它解決方案,長連線方案或者偽長連線方案:

方案一:基於tcp的長連結

現在越來越多的移動端app都會建立一條自己的長連結通道,通道的實現是基於tcp協議。基於tcp的socket程式設計技術難度相對複雜很多,而且需要自己制定協議,但帶來的回報也很大。資訊的上報和推送變得更及時,在請求量爆發的時間點還能減輕伺服器壓力(http短連線模式會頻繁的建立和銷燬連線)。不止是IM app有這樣的通道,像淘寶這類電商類app都有自己的專屬長連線通道了。現在業界也有不少成熟的方案可供選擇了,google的protobuf就是其中之一。

方案二:http long-polling

客戶端在初始狀態就會傳送一個polling請求到伺服器,伺服器並不會馬上返回業務資料,而是等待有新的業務資料產生的時候再返回。所以連線會一直被保持,一旦結束馬上又會發起一個新的polling請求,如此反覆,所以一直會有一個連線被保持。伺服器有新的內容產生的時候,並不需要等待客戶端建立一個新的連線。做法雖然簡單,但有些難題需要攻克才能實現穩定可靠的業務框架:

  • 和傳統的http短連結相比,長連線會在使用者增長的時候極大的增加伺服器壓力

  • 移動端網路環境複雜,像wifi和4g的網路切換,進電梯導致網路臨時斷掉等,這些場景都需要考慮怎麼重建健康的連線通道。

  • 這種polling的方式穩定性並不好,需要做好資料可靠性的保證,比如重發和ack機制。

  • polling的response有可能會被中間代理cache住,要處理好業務資料的過期機制。

http long-polling

long-polling方式還有一些缺點是無法克服的,比如每次新的請求都會帶上重複的header資訊,還有資料通道是單向的,主動權掌握在server這邊,客戶端有新的業務請求的時候無法及時傳送。

方案三:http streaming

同long-polling不同的是,server並不會結束初始的streaming請求,而是持續的通過這個通道返回最新的業務資料。顯然這個資料通道也是單向的。streaming是通過在server response的頭部裡增加”Transfer Encoding: chunked”來告訴客戶端後續還會有新的資料到來。除了和long-polling相同的難點之外,streaming還有幾個缺陷:

  • 有些代理伺服器會等待伺服器的response結束之後才會將結果推送到請求客戶端。對於streaming這種永遠不會結束的方式來說,客戶端就會一直處於等待response的過程中。

  • 業務資料無法按照請求來做分割,所以客戶端沒收到一塊資料都需要自己做協議解析,也就是說要做自己的協議定製。

streaming不會產生重複的header資料。

方案四:web socket

WebSocket和傳統的tcp socket連線相似,也是基於tcp協議,提供雙向的資料通道。WebSocket優勢在於提供了message的概念,比基於位元組流的tcp socket使用更簡單,同時又提供了傳統的http所缺少的長連線功能。不過WebSocket相對較新,2010年才起草,並不是所有的瀏覽器都提供了支援。各大瀏覽器廠商最新的版本都提供了支援。

解決head of line blocking

Head of line blocking(以下簡稱為holb)是http2.0之前網路體驗的最大禍源。健康的請求會被不健康的請求影響,而且這種體驗的損耗受網路環境影響,出現隨機且難以監控。為了解決holb帶來的延遲,協議設計者設計了一種新的pipelining機制。

20160911174859654955.png

不過pipelining並不是救世主,它也存在不少缺陷:

  • pipelining只能適用於http1.1,一般來說,支援http1.1的server都要求支援pipelining。

  • 只有冪等的請求(GET,HEAD)能使用pipelining,非冪等請求比如POST不能使用,因為請求之間可能會存在先後依賴關係。

  • head of line blocking並沒有完全得到解決,server的response還是要求依次返回,遵循FIFO(first in first out)原則。也就是說如果請求1的response沒有回來,2,3,4,5的response也不會被送回來。

  • 絕大部分的http代理伺服器不支援pipelining。

  • 和不支援pipelining的老伺服器協商有問題。

  • 可能會導致新的Front of queue blocking問題。

正是因為有這麼多的問題,各大瀏覽器廠商要麼是根本就不支援pipelining,要麼就是預設關掉了pipelining機制,而且啟用的條件十分苛刻。可以參考chrome對於pipeling的問題描述

HTTP2的優勢

二進位制分幀

http1.x誕生的時候是明文協議,其格式由三部分組成:start line(request line或者status line),header,body。要識別這3部分就要做協議解析,http1.x的解析是基於文字。基於文字協議的格式解析存在天然缺陷,文字的表現形式有多樣性,要做到健壯性考慮的場景必然很多,二進位制則不同,只認0和1的組合。基於這種考慮http2.0的協議解析決定採用二進位制格式,實現方便且健壯。

http2.0用binary格式定義了一個一個的frame,和http1.x的格式對比如下圖:


20160911174955780620.png

http2.0的格式定義更接近tcp層的方式,這張二機制的方式十分高效且精簡。

  • length定義了整個frame的開始到結束

  • type定義frame的型別(一共10種)

  • flags用bit位定義一些重要的引數

  • stream id用作流控制

  • payload就是request的正文了

為什麼麼能在不改動 HTTP/1.x 的語義、方法、狀態碼、URI 以及首部欄位….. 的情況下, HTTP/2 是如何做到「突破 HTTP1.1 的效能限制,改進傳輸效能,實現低延遲和高吞吐量」?

關鍵之一就是在 應用層(HTTP/2)和傳輸層(TCP or UDP)之間增加一個二進位制分幀層。


再談HTTP2效能提升之背後原理—HTTP2歷史解剖在二進位制分幀層中, HTTP/2 會將所有傳輸的資訊分割為更小的訊息和幀(frame),並對它們採用二進位制格式的編碼 ,其中 HTTP1.x 的首部資訊會被封裝到 HEADER frame,而相應的 Request Body 則封裝到 DATA frame 裡面。
HTTP/2 通訊都在一個連線上完成,這個連線可以承載任意數量的雙向資料流。

在過去, HTTP 效能優化的關鍵並不在於高頻寬,而是低延遲。TCP 連線會隨著時間進行自我「調諧」,起初會限制連線的最大速度,如果資料成功傳輸,會隨著時間的推移提高傳輸的速度。這種調諧則被稱為 TCP 慢啟動。具體複習:《再深談TCP/IP三步握手&四步揮手原理及衍生問題—長文解剖IP》、《從網路卡傳送資料再談TCP/IP協議—網路傳輸速度計算-網路卡構造

由於這種原因,讓原本就具有突發性和短時性的 HTTP 連線變的十分低效。

HTTP/2 通過讓所有資料流共用同一個連線,可以更有效地使用 TCP 連線,讓高頻寬也能真正的服務於 HTTP 的效能提升。
總結:

  • 單連線多資源的方式,減少服務端的連結壓力,記憶體佔用更少,連線吞吐量更大

  • 由於 TCP 連線的減少而使網路擁塞狀況得以改善,同時慢啟動時間的減少,使擁塞和丟包恢復速度更快

再談HTTP2效能提升之背後原理—HTTP2歷史解剖

多路複用 (Multiplexing)||連線共享

多路複用允許同時通過單一的 HTTP/2 連線發起多重的請求-響應訊息。

眾所周知 ,在 HTTP/1.1 協議中 「瀏覽器客戶端在同一時間,針對同一域名下的請求有一定數量限制。超過限制數目的請求會被阻塞」。

Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users. These guidelines are intended to improve HTTP response times and avoid congestion.

source:RFC-2616-8.1.4 Practical Considerations

比如TCP建立連線時三次握手有1.5個RTT(round-trip time)的延遲,為了避免每次請求的都經歷握手帶來的延遲,應用層會選擇不同策略的http長連結方案。又比如TCP在建立連線的初期有慢啟動(slow start)的特性,所以連線的重用總是比新建連線效能要好

下圖總結了不同瀏覽器對該限制的數目。

來源:Roundup on Parallel Connections

這也是為何一些站點會有多個靜態資源 CDN 域名的原因之一

上面協議解析中提到的stream id就是用作連線共享機制的:

一個request對應一個stream並分配一個id,這樣一個連線上可以有多個stream,每個stream的frame可以隨機的混雜在一起,接收方可以根據stream id將frame再歸屬到各自不同的request裡面。因而 HTTP/2 能多路複用(Multiplexing) ,允許同時通過單一的 HTTP/2 連線發起多重的請求-響應訊息。

再談HTTP2效能提升之背後原理—HTTP2歷史解剖



因此 HTTP/2 可以很容易的去實現多流並行而不用依賴建立多個 TCP 連線,HTTP/2 把 HTTP 協議通訊的基本單位縮小為一個一個的幀,這些幀對應著邏輯流中的訊息。並行地在同一個 TCP 連線上雙向交換訊息。

前面還提到過連線共享之後,需要優先順序和請求依賴的機制配合才能解決關鍵請求被阻塞的問題。http2.0裡的每個stream都可以設定又優先順序(Priority)和依賴(Dependency)。優先順序高的stream會被server優先處理和返回給客戶端,stream還可以依賴其它的sub streams。優先順序和依賴都是可以動態調整的。動態調整在有些場景下很有用,假想使用者在用你的app瀏覽商品的時候,快速的滑動到了商品列表的底部,但前面的請求先發出,如果不把後面的請求優先順序設高,使用者當前瀏覽的圖片要到最後才能下載完成,顯然體驗沒有設定優先順序好。同理依賴在有些場景下也有妙用。

首部壓縮(Header Compression)

http1.x的header由於cookie和user agent很容易膨脹,而且每次都要重複傳送。

HTTP/1.1並不支援 HTTP 首部壓縮,為此 SPDY 和 HTTP/2 應運而生

這裡普及一個小知識點。現在大家都知道tcp有slow start的特性,三次握手之後開始傳送tcp segment,第一次能傳送的沒有被ack的segment數量是由initial tcp window大小決定的。這個initial tcp window根據平臺的實現會有差異,但一般是2個segment或者是4k的大小(一個segment大概是1500個位元組),也就是說當你傳送的包大小超過這個值的時候,要等前面的包被ack之後才能傳送後續的包,顯然這種情況下延遲更高。intial window也並不是越大越好,太大會導致網路節點的阻塞,丟包率就會增加,具體細節可以參考IETF這篇文章。http的header現在膨脹到有可能會超過這個intial window的值了,所以更顯得壓縮header的重要性。

壓縮演算法的選擇

SPDY/2使用的是gzip壓縮演算法,但後來出現的兩種攻擊方式BREACH和CRIME使得即使走ssl的SPDY也可以被破解內容,最後綜合考慮採用的是一種叫HPACK的壓縮演算法。這兩個漏洞和相關演算法可以點選連結檢視更多的細節,不過這種漏洞主要存在於瀏覽器端,因為需要通過javascript來注入內容並觀察payload的變化。

現在SPDY 使用的是通用的DEFLATE 演算法,而 HTTP/2 則使用了專門為首部壓縮而設計的 HPACK 演算法。

http2.0使用encoder來減少需要傳輸的header大小,通訊雙方各自cache一份header fields表,既避免了重複header的傳輸,又減小了需要傳輸的大小。高效的壓縮演算法可以很大的壓縮header,減少傳送包的數量從而降低延遲。

再談HTTP2效能提升之背後原理—HTTP2歷史解剖
服務端推送(Server Push)

服務端推送是一種在客戶端請求之前傳送資料的機制。在 HTTP/2 中,伺服器可以對客戶端的一個請求傳送多個響應。Server Push 讓 HTTP1.x 時代使用內嵌資源的優化手段變得沒有意義;如果一個請求是由你的主頁發起的,伺服器很可能會響應主頁內容、logo 以及樣式表,因為它知道客戶端會用到這些東西。這相當於在一個 HTML 文件內集合了所有的資源,不過與之相比,伺服器推送還有一個很大的優勢:可以快取!也讓在遵循同源的情況下,不同頁面之間可以共享快取資源成為可能。

再談HTTP2效能提升之背後原理—HTTP2歷史解剖

http2.0引入RST_STREAM型別的frame,可以在不斷開連線的前提下取消某個request的stream,表現更好。

重置連線表現更好

很多app客戶端都有取消圖片下載的功能場景,對於http1.x來說,是通過設定tcp segment裡的reset flag來通知對端關閉連線的。這種方式會直接斷開連線,下次再發請求就必須重新建立連線。http2.0引入RST_STREAM型別的frame,可以在不斷開連線的前提下取消某個request的stream,表現更好。

流量控制(Flow Control)

TCP協議通過sliding window的演算法來做流量控制。傳送方有個sending window,接收方有receive window。http2.0的flow control是類似receive window的做法,資料的接收方通過告知對方自己的flow window大小表明自己還能接收多少資料。只有Data型別的frame才有flow control的功能。對於flow control,如果接收方在flow window為零的情況下依然更多的frame,則會返回block型別的frame,這張場景一般表明http2.0的部署出了問題。

更安全的SSL

HTTP2.0使用了tls的擴充ALPN來做協議升級,除此之外加密這塊還有一個改動,HTTP2.0對tls的安全性做了近一步加強,通過黑名單機制禁用了幾百種不再安全的加密演算法,一些加密演算法可能還在被繼續使用。如果在ssl協商過程當中,客戶端和server的cipher suite沒有交集,直接就會導致協商失敗,從而請求失敗。在server端部署http2.0的時候要特別注意這一點。


關於 HTTP/2 的 Server Push 以及 HTTP/2 的快取策略

典型問題:

「如果客戶端早已在快取中有了一份 copy 怎麼辦?」還要 Push 嗎?

詳情參考另一個答案:

HTTP/2 對現在的網頁訪問,有什麼大的優化呢?體現在什麼地方

PS:
強烈推薦閱讀 Mark Nottingham 在 Velocity Beijing 2015 的 speech:HTTP/2 for Front-End Developers ,關於 HTTP/2 下的前端效能優化相關。
Slide 地址:HTTP/2 for Front-End Developers


按照OSI網路分層模型,IP是網路層協議,TCP是傳輸層協議,而HTTP是應用層的協議。在這三者之間,SPDY和WebSocket都是與HTTP相關的協議,而TCP是HTTP底層的協議。

HTTP2的發展歷史

一、http

HTTP協議經過多年的使用,發現了一些不足,主要是效能方面的,包括:

  • HTTP的連線問題,HTTP客戶端和伺服器之間的互動是採用請求/應答模式,在客戶端請求時,會建立一個HTTP連線,然後傳送請求訊息,服務端給出應答訊息,然後連線就關閉了。(後來的HTTP1.1支援持久連線)

  • 因為TCP連線的建立過程是有開銷的,如果使用了SSL/TLS開銷就更大。

  • 在瀏覽器裡,一個網頁包含許多資源,包括HTML,CSS,JavaScript,圖片等等,這樣在載入一個網頁時要同時開啟連線到同一伺服器的多個連線。

  • HTTP訊息頭問題,現在的客戶端會傳送大量的HTTP訊息頭,由於一個網頁可能需要50-100個請求,就會有相當大的訊息頭的資料量。

  • HTTP通訊方式問題,HTTP的請求/應答方式的會話都是客戶端發起的,缺乏伺服器通知客戶端的機制,在需要通知的場景,如聊天室,遊戲,客戶端應用需要不斷地輪詢伺服器。


而SPDY和WebSocket是從不同的角度來解決這些不足中的一部分。除了這兩個技術,還有其他技術也在針對這些不足提出改進。

二、SPDY

SPDY的主要目的是減少50%以上的頁面載入時間,但是呢不增加部署的複雜性,不影響客戶端和服務端的Web應用,只需要瀏覽器和Web伺服器支援SPDY。主要有以下幾:

  • 多路複用,一個TCP連線上同時跑多個HTTP請求。請求可設定優先順序。

  • 去除不需要的HTTP頭,壓縮HTTP頭,以減少需要的網路頻寬。

  • 使用了SSL作為傳輸協議提供資料安全。

  • 對傳輸的資料使用gzip進行壓縮

  • 提供服務方發起通訊,並向客戶端推送資料的機制。

實質上,SPDY就是想不影響HTTP語義的情況下,替換HTTP底層傳輸的協議來加快頁面載入時間。

SPDY的解決辦法就是設計了一個會話層協議--幀協議,解決多路複用,優先順序等問題,然後在其上實現了HTTP的語義。

SPDY的誕生和表現說明了兩件事情:一是在現有網際網路設施基礎和http協議廣泛使用的前提下,是可以通過修改協議層來優化http1.x的。二是針對http1.x的修改確實效果明顯而且業界反饋很好。正是這兩點讓IETF(Internet Enginerring Task Force)開始正式考慮制定HTTP2.0的計劃,最後決定以SPDY/3為藍圖起草HTTP2.0,SPDY的部分設計人員也被邀請參與了HTTP2.0的設計。

三、WebSocket

WebSocket則提供使用一個TCP連線進行雙向通訊的機制,包括網路協議和API,以取代網頁和伺服器採用HTTP輪詢進行雙向通訊的機制。


本質上來說,WebSocket是不限於HTTP協議的,但是由於現存大量的HTTP基礎設施,代理,過濾,身份認證等等,WebSocket借用HTTP和HTTPS的埠。

由於使用HTTP的埠,因此TCP連線建立後的握手訊息是基於HTTP的,由伺服器判斷這是一個HTTP協議,還是WebSocket協議。 WebSocket連線除了建立和關閉時的握手,資料傳輸和HTTP沒丁點關係了。
WebSocket也有自己一套幀協議。

四、SPDY和WebSocket的關係

SPDY和WebSocket的關係比較複雜。

  1. 補充關係,二者側重點不同。SPDY更側重於給Web頁面的載入提速,而WebSocket更強調為Web應用提供一種雙向的通訊機制以及API。

  2. 競爭關係,二者解決的問題有交集,比如在伺服器推送上SPDY和WebSocket都提供了方案。

  3. 承載關係,試想,如果SPDY的標準化早於WebSocket,WebSocket完全可以側重於API,利用SPDY的幀機制和多路複用機制實現該API。 Google提出草案,說WebSocket可以跑在SPDY之上。WebSocket的連線建立在SPDY的流之上,將WebSocket的幀對映到SPDY的幀上。

  4. 融合關係,如微軟在HTTP Speed+Mobility中所做的。


http2的競爭兄弟

1. HTTP Speed+Mobility

還有一個有趣的技術叫做HTTP Speed+Mobility,和SPDY一樣都是HTTP 2.0標準的競爭者,HTTP Speed+Mobility來自微軟。HTTP SM借鑑了SPDY和WebSocket的協議,將二者揉為一體,又有所取捨。


HTTP SM的設計原則包括:

  • 保留HTTP的語義,這一點和SPDY一致,但也正應如此,拋棄了SPDY裡的ServerPush。

  • 遵守分層的網路架構,TCP能做的,HTTP SM不做,因此去除了SPDY的流控。

  • 使用現有標準,因此使用HTTP/1.1 Upgrade header機制,借用了WebSocket的握手機制和幀格式(RFC6455)。

  • 客戶端掌握內容的控制,因此不強制使用壓縮和SSL/TLS。

  • 考慮到網路的費用和電力,這點考慮到了移動裝置以及物聯網,提供了Credit Control機制。


HTTP SM分以下幾層:

  • 會話層和幀協議,這部分取自WebSocket協議。包括握手機制,以及幀格式。

  • 流層(包括多路複用),這部分主要借鑑SPDY,包括多路複用,流優先順序,但增加了Credit Control。這部分作為 WebSocket協議的擴充套件。

  • HTTP層,在流層上實現HTTP語義,這部分也借鑑自SPDY。

2. Network-Friendly HTTP

NF是HTTP 2.0候選方案之一,主要提出以下改進:

  • 對HTTP頭的名稱進行二進位制編碼

  • 對通用HTTP頭進行分組

  • 請求/應答的多路複用

  • 分層模型

NF同樣定義了幀和流,


3. WAKA

WAKA也是HTTP 2.0候選方案之一,是HTTP協議原作者Roy Fielding提出的一個提案。

WAKA支援多路複用,支援優先順序。WAKA提出了兩個新的HTTP方法,RENDER和MONITOR。

參考資料:

本文主要內容來源:《HTTP 2.0的那些事

文章由本人精煉而成,原文:再談HTTP2效能提升之背後原理-HTTP2歷史解剖 - Network - 周陸軍的個人網站


相關文章