再談HTTP2性能提高之背後原理—HTTP2歷史解剖

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

由於理論相比於 HTTP 1.x ,在同時兼容 HTTP/1.1 徹底語義,進一步減小了網絡延遲。php

對於前端開發人員來講,無疑減小了在前端方面的優化工做。好比雪碧圖&文件合併||內容內嵌||域名分片html

http1.0的缺點

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

鏈接沒法複用會致使每次請求都經歷三次握手和慢啓動。三次握手在高延遲的場景下影響較明顯,慢啓動則對文件類大請求影響較大java

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

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

解決鏈接沒法複用

http1.0協議頭裏能夠設置Connection:Keep-Alive。在header裏設置Keep-Alive能夠在必定時間內複用鏈接,具體複用時間的長短能夠由服務器控制,通常在15s左右。到http1.1以後Connection的默認值就是Keep-Alive,若是要關閉鏈接複用須要顯式的設置Connection:Close。一段時間內的鏈接複用對PC端瀏覽器的體驗幫助很大,由於大部分的請求在集中在一小段時間之內。但對移動app來講,成效不大,app端的請求比較分散且時間跨度相對較大。因此移動端app通常會從應用層尋求其它解決方案,長鏈接方案或者僞長鏈接方案:git

方案一:基於tcp的長連接

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

方案二:http long-polling

客戶端在初始狀態就會發送一個polling請求到服務器,服務器並不會立刻返回業務數據,而是等待有新的業務數據產生的時候再返回。因此鏈接會一直被保持,一旦結束立刻又會發起一個新的polling請求,如此反覆,因此一直會有一個鏈接被保持。服務器有新的內容產生的時候,並不須要等待客戶端創建一個新的鏈接。作法雖然簡單,但有些難題須要攻克才能實現穩定可靠的業務框架:web

  • 和傳統的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)之間增長一個二進制分幀層。


在二進制分幀層中, 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 鏈接的減小而使網絡擁塞情況得以改善,同時慢啓動時間的減小,使擁塞和丟包恢復速度更快

多路複用 (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 鏈接發起多重的請求-響應消息。



所以 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,減小發送包的數量從而下降延遲。


服務端推送(Server Push)

服務端推送是一種在客戶端請求以前發送數據的機制。在 HTTP/2 中,服務器能夠對客戶端的一個請求發送多個響應。Server Push 讓 HTTP1.x 時代使用內嵌資源的優化手段變得沒有意義;若是一個請求是由你的主頁發起的,服務器極可能會響應主頁內容、logo 以及樣式表,由於它知道客戶端會用到這些東西。這至關於在一個 HTML 文檔內集合了全部的資源,不過與之相比,服務器推送還有一個很大的優點:能夠緩存!也讓在遵循同源的狀況下,不一樣頁面之間能夠共享緩存資源成爲可能。

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的發展歷史

1、http

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

  • HTTP的鏈接問題,HTTP客戶端和服務器之間的交互是採用請求/應答模式,在客戶端請求時,會創建一個HTTP鏈接,而後發送請求消息,服務端給出應答消息,而後鏈接就關閉了。(後來的HTTP1.1支持持久鏈接)

  • 由於TCP鏈接的創建過程是有開銷的,若是使用了SSL/TLS開銷就更大。

  • 在瀏覽器裏,一個網頁包含許多資源,包括HTML,CSS,JavaScript,圖片等等,這樣在加載一個網頁時要同時打開鏈接到同一服務器的多個鏈接。

  • HTTP消息頭問題,如今的客戶端會發送大量的HTTP消息頭,因爲一個網頁可能須要50-100個請求,就會有至關大的消息頭的數據量。

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


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

2、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的設計。

3、WebSocket

WebSocket則提供使用一個TCP鏈接進行雙向通信的機制,包括網絡協議和API,以取代網頁和服務器採用HTTP輪詢進行雙向通信的機制。


本質上來講,WebSocket是不限於HTTP協議的,可是因爲現存大量的HTTP基礎設施,代理,過濾,身份認證等等,WebSocket借用HTTP和HTTPS的端口。

因爲使用HTTP的端口,所以TCP鏈接創建後的握手消息是基於HTTP的,由服務器判斷這是一個HTTP協議,仍是WebSocket協議。 WebSocket鏈接除了創建和關閉時的握手,數據傳輸和HTTP沒丁點關係了。
WebSocket也有本身一套幀協議。

4、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 - 周陸軍的我的網站

相關文章
相關標籤/搜索