HTTP pipelining
HTTP pipelining技術指的是:把多個HTTP請求放到一個TCP連接中一一發送,在發送過程中不需要等待服務器對前一個請求的相應。但是客戶端還是要按照發送請求的順序來接受相應。
但是,服務器是要按照順序處理請求,如果前一個請求非常耗時,那麼後續請求都會受到影響,這就是所謂的線頭阻塞(head-of-line blocking)。
靠大量新建連接是不能有效解決線頭阻塞的問題,因爲新建一個TCP連接的開銷非常大。
正是由於HTTP pipelining不能徹底解決線頭阻塞的問題,所以大部分桌面瀏覽器會默認關閉HTTP pipelining這一功能。
克服延遲的技術
在過去開發者會使用大量的技術降低延遲對用戶帶來的影響,其中很多技術直到現在都仍然在使用,其核心的原則就是:減少請求數(和體積),增大請求的併發數。
這些技術包括:
(1)雪碧圖
(2)使用Base64格式的內聯圖片
(3)合併瑣碎文件(將零散的JavaScript文件合併爲一個大的文件,用一個請求完成下載)
(4)Domain Hash(將服務分散在儘可能多的主機上,來破解一個客戶端只能和每個主機建立6-8個連接的限制)
(5)Cookie Free(將圖片或其他資源分發到不同的域名,減少不必要的cookie傳輸)
HTTP2介紹
HTTP2就是爲了解決HTTP1.1存在的各種問題,它起源於SPDY(這是一個由Google牽頭開發的開源的協議)
HTTP2保留了HTTP1的URL結構,但是在實際的實現上,它只支持TLS協議,基於TLS協議進行HTTP2協商時有兩種協議NPM和ALPN,二者的區別就是由誰(客戶端還是服務器)來決定通信協議,ALPN讓客戶端發送協議優先級列表給服務器,有服務器選擇合適的,NPN正好相反。
協商HTTP2協議時,會給服務器發送一個帶升級(upgrade
)頭部的保溫,如果服務器支持HTTP2就會返回101 Switching
作爲狀態碼,並且從此開始在該連接上使用HTTP2。
HTTP2的特性
(1)二進制
HTTP2是一個二進制協議,相比於基於文本的HTTP1.1,HTTP2更容易識別幀的起始和結束,此外基於二進制幀可以更便捷的從幀結構中分離出協議本身的內容。
HTTP2定義了10中不同類型的幀,最基礎的兩種分別對應HTTP1.1的DATA
和HEADERS
(2)多路複用
HTTP2連接上闡傳輸的每個幀都會關聯到一個“流”中。流是一個獨立的、雙向的幀序列,數據通過流在客戶端和服務端之間進行交換。
每個獨立的HTTP2連接都可以包含多個併發的流,這些流中同時包含着來自兩端的幀,流可以被客戶端/服務端單方面的建立和使用,也可共享,或被任意一方關閉。接收方會按照收到幀的順序來進行處理。
留的多路複用以爲這在同一連接中來自各個流的數據包會被混合在一起,就像多個獨立的數據車廂被拼湊到了一個一列列車上,但它們會在終點站被分開。
HTTP1.1中的
keep-alive
技術已經實現了在一次TCP連接中完成多個HTTP請求,但是每個請求仍然要單獨發HTTP header。它與HTTP2的多路複用有何區別呢?Keep-alive的TCP連接複用仍然是阻塞的,在同一個TCP連接上,客戶端必須等到服務端響應了之後才能發起第二次請求。按順序發送請求,按順序接受請求,這樣客戶端纔不會亂掉
HTTP2的TCP複用可以同時發送多個請求,不一定要按照順序,也不用等待上一個請求的響應就可以發出下一個請求。這些請求都有唯一標識,所以不會亂掉。
HTTP/1.1不能實現多路複用是因爲HTTP/1.1是基於文本分隔解析的協議, 一個HTTP/1.1的請求是通過換行符來每一條
key: value
的內容,這種方式存在的缺點是:
- 以分隔符分隔消息的數據,在完成之前不能停下,所以一次只能處理一個請求或者響應
- 解析這種數據無法預知需要多少內存,會帶給服務器很大壓力
而HTTP/2是基於二進制幀進行設計的,這樣設計實現了一切可預知,一切可控。
一幀就是一個數據單元,實現了對消息的封裝,服務器解析數據幀時可以通過解析前九個字節就能知道整個幀需要多少個字節數來處理信息。
而且由於HTTP/2是分幀的,請求和相應可以交錯甚至複用。
HTTP/2定義了10種不同類型的幀,來傳遞不同的內容。幀通過“流”來進行交換,所謂的流就是HTTP/2連接上獨立的、雙向的幀序列交換,在流中進行幀的雙向交換,就是實現多路複用的基礎:
(3)優先級和依賴性
每個流都包含一個優先級(也就是權重),它用來告訴對端哪個流更重要。當資源有限時服務器會優先處理權重較高的流。
藉助PRIORITY幀,客戶端會告訴服務端當前流依賴於哪個流。
優先級和依賴關係會在傳輸過程被動態改變,比如瀏覽器可以指定哪個資源有更高的優先級,或者在切換標籤頁的時候瀏覽器可以提升切換到的新頁面包含的流的優先級。
(4)頭壓縮
HTTP是無狀態的協議,所以每個請求必須攜帶服務器需要的所有細節,HTTP2依然如此。
這保證了的HTTP的可重複性,很多請求(例如請求頁面的圖片)看起來是幾乎一致的,這些請求可以被壓縮,同時請求中包含的cookie在很多情況下時相同的,它們也值得被壓縮。
HTTP2壓縮是一個很棘手的課題,即需要實現較高的壓縮比,又需要放置被破解,HTTP2採用的壓縮格式是HPACK,它是專門爲HTTP2頭部設計的壓縮格式。
(5)可重置(後悔藥)
HTTP1.1中,但一個HTTP信息發送後,很難去中斷它(除非斷開整個TPC連接),HTTP2中可以通過發送RST_STREAME幀來終止當前傳輸的消息並且發送一個新的消息。
(6)服務器推送
這個技術與Websocket的推送是不一樣的,這個功能更準確的名稱叫做“緩存推送”:但客戶端請求資源時,服務器知道客戶端可能也需要其他的資源,這時服務端就可以在客戶端請求之前將這個資源主動推送給客戶端,讓客戶端放進緩存中以備將來之需。
服務端推送需要客戶端顯式的允許服務端提供該功能,並且客戶端能夠通過發送一個RST_STREAME幀來主動中斷推送的流。
Websocket是雙向通信的協議,它也具有主動推送的功能,Websocket的主動推送和HTTP2的服務器推送有何區別呢?
HTTP2的服務器推送,推送的是靜態文件(CSS/JS/HTML),而不是數據,如果需要實時的交互數據,還是需要使用Websocket的。
Websocket與HTTP2的異同:
主要區別:
- HTTP2推送靜態文件,Websocket推送數據
- HTTP2使用HTTPS加密,Websocket可以使用加密協議(wss)也可以使用非加密協議(ws)
- HTTP2會對HTTP header進行壓縮,Websocket不會
基於此,HTTP/2並不能取代Websocket技術。
(7)流量控制
每個HTTP2流都有自己的公示的流量窗口,它可以限制另一端發送數據。對於每個流來說,兩端都必須告訴對方自己還有足夠的空間來處理新的數據,而在該窗口被擴大前,另一端只允許被髮送這麼多的數據。
只有數據幀會受到流量控制。
擴展
HTTP2允許添加擴展,接收方和發送方可以再逐跳原則的基礎上協商使用新的幀,但是這些幀的狀態無法被改變,也不受流的控制。現在有兩種廣泛流行的幀可能會被納入擴展協議中:
(1)備選服務(Alternative Service)
服務器會通過發送ALt-Svc頭(或者HTTP2的ALTSVC幀)告知客戶端另一個備選服務(即另外一條指向不同的服務源、主機或者端口,卻能夠獲得同樣內容的URL)
客戶端收到後,應該嘗試異步的去連接到該服務,如果連接成功的話,就可以使用該備選服務
(2)阻塞(Blocked)
當服務端存在需要發送的內容,但流控制卻禁止發送任何數據時,那麼此類型的幀將會被髮送且僅發送一次。
收到這種幀,那麼連接中必然有錯誤發生或者傳輸速度低於預期。
HTTP2的影響
(1)對於用戶
對於用戶來說,HTTP2減少了網絡往返傳輸的數量,通過多路複用和快速丟棄無效流的辦法來避免線頭阻塞的困擾。
同時HTTP2支持大量並行流,所以網站的數據分發在各處也不會影響傳輸速度。
此外通過合理利用流的優先級,可以讓客戶端看可能優先收到重要的數據。
總的來說,頁面載入時間和站點響應速度都會更快,簡而言之,用戶體驗都會得到提升。
(2)對於開發者
上面提到的,有的原本廣泛使用的、用來提升網絡傳輸的速度的手段,可能會降低HTTP2的性能,例如HTTP2的多路複用傾向於使用更少的連接,所以Domain Hash可能會造成反效果,而且雪碧圖和內聯圖片產生的優化效果也會因此降低。
但是目前HTTP2的完全部屬還差的很遠,所以開發者需要同時部屬一套前端來支持HTTP1.1和HTTP2的訪問,在兩種條件下同時獲得高性能,這將會是一種挑戰。
HTTP2的實現
(1)瀏覽器
各大主流瀏覽器在2014年後就都路西增加了HTTP2的支持。
(2)服務器
Nginx在1.9.5版本起增加了HTTP2的支持。
NodeJS在10.0版本後正式添加了HTTP2模塊,相關API可以參考文檔,具體實踐可以參考這篇文章《基於Node.js的HTTP/2 Server實踐》