背景
近年來,http網絡請求量日益添加,以下是httparchive統計,從2012-11-01到2016-09-01的請求數量和傳輸大小的趨勢圖:
當前大部份客戶端&服務端架構的應用程序,都是用http/1.1連接的,現代瀏覽器與單個域最大連接數,都在4-6個左右,由上圖Total Requests數據,如果不用CDN分流,平均有20個左右的串行請求。
HTTP2 是1999年發布http1.1后的一次重大的改進,在協議層面改善了以上問題,減少資源占用,來,直接感受一下差異:
HTTP/2 is the future of the Web, and it is here!
這是 Akamai 公司建立的一個官方的演示,用以說明 HTTP/2 相比於之前的 HTTP/1.1 在性能上的大幅度提升。 同時請求 379 張圖片,從Load time 的對比可以看出 HTTP/2 在速度上的優勢。
HTTP/2 源自 SPDY/2
SPDY 系列協議由谷歌開發,於 2009 年公開。它的設計目標是降低 50% 的頁面加載時間。當下很多著名的互聯網公司都在自己的網站或 APP 中采用了 SPDY 系列協議(當前最新版本是 SPDY/3.1),因為它對性能的提升是顯而易見的。主流的瀏覽器(谷歌、火狐、Opera)也都早已經支持 SPDY,它已經成為了工業標准,HTTP Working-Group 最終決定以 SPDY/2 為基礎,開發 HTTP/2。HTTP/2標准於2015年5月以RFC 7540正式發表。
但是,HTTP/2 跟 SPDY 仍有不同的地方,主要是以下兩點:
HTTP/2 支持明文 HTTP 傳輸,而 SPDY 強制使用 HTTPS
HTTP/2 消息頭的壓縮算法采用 HPACK ,而非 SPDY 采用的 DEFLATE(感謝網友 逸風之狐指正)
協議文檔請見:rfc7540:HTTP2
HTTP2特性概覽
1. 二進制協議
HTTP/2 采用二進制格式傳輸數據,而非 HTTP/1.x 的文本格式
由上圖可以看到HTTP2在原來的應用層和HTTP層添加了一層二進制傳輸。
二進制協議的一個好處是,可以定義額外的幀。
HTTP/2 定義了近十種幀(詳情可分析抓包文件),為將來的高級應用打好了基礎。如果使用文本實現這種功能,解析數據將會變得非常麻煩,二進制解析則方便得多。
RFC7540:Frame Definitions
協議中定義的幀
2. 多路復用
HTTP/2 復用TCP連接,在一個連接里,客戶端和瀏覽器都可以同時發送多個請求或回應,而且不用按照順序一一對應,這樣就避免了"隊頭堵塞"(見TCP/IP詳解卷一)。
每個 Frame Header 都有一個 Stream ID 就是被用於實現該特性。每次請求/響應使用不同的 Stream ID。就像同一個 TCP 鏈接上的數據包通過 IP: PORT 來區分出數據包去往哪里一樣。
rfc7540: HTTP2 Multiplexing中對Multiplexing的說明
Streams and Multiplexing
A "stream" is an independent, bidirectional sequence of frames
exchanged between the client and server within an HTTP/2 connection.
Streams have several important characteristics:
o A single HTTP/2 connection can contain multiple concurrently open
streams, with either endpoint interleaving frames from multiple
streams.
o Streams can be established and used unilaterally or shared by
either the client or server.
o Streams can be closed by either endpoint.
o The order in which frames are sent on a stream is significant.
Recipients process frames in the order they are received. In
particular, the order of HEADERS and DATA frames is semantically
significant.
o Streams are identified by an integer. Stream identifiers are
assigned to streams by the endpoint initiating the stream.
3. 數據流
數據流發送到一半的時候,客戶端和服務器都可以發送信號(RST_STREAM幀),取消這個數據流。1.1版取消數據流的唯一方法,就是關閉TCP連接。這就是說,HTTP/2 可以取消某一次請求,同時保證TCP連接還打開着,可以被其他請求使用。
4. 頭信息壓縮:
HTTP/2 對消息頭采用 HPACK 進行壓縮傳輸,能夠節省消息頭占用的網絡的流量。而 HTTP/1.x 每次請求,都會攜帶大量冗余頭信息,浪費了很多帶寬資源。
HTTP2對http頭建立索引表,相同的頭只發送hash table 的index, 同時還用了霍夫曼編碼和傳統的gzip壓縮。
5. 服務器推送
服務端能夠更快的把資源推送給客戶端。例如服務端可以主動把 JS 和 CSS 文件推送給客戶端,而不需要客戶端解析 HTML 再發送這些請求。當客戶端需要的時候,它已經在客戶端了。
那么存在一個問題,如果客戶端設置了緩存怎么辦。有三種方式(來自社區)
- 客戶端可以通過設置SETTINGS_ENABLE_PUSH為0值通知服務器端禁用推送
- 發現緩存后,客戶端和服務器都可以發送信號(RST_STREAM幀),取消這個數據流。
- cache-digest(提案)
6. 流優先級
HTTP2允許瀏覽器指定資源的優先級。
瀏覽器支持
主流瀏覽器都只支持 HTTP/2 Over TLS
node中啟用http2
node中可以用spdy模塊來啟動應用,spdy的api,與https是一致的且主流瀏覽器只支持HTTP/2 Over TLS,需要配置 私鑰和證書,本地自簽名服務器配置可參考引用6,7。
const express = require('express');
const fs = require('fs');
const http2 = require('spdy');
const path = require('path');
const options = {
key: fs.readFileSync('./keys/privatekey.pem'),
cert: fs.readFileSync('./keys/certificate.pem')
};
const app = new express();
http2
.createServer(options, app)
.listen(8080, ()=>{
console.log(`Server is listening on https://localhost:8080.
You can open the URL in the browser.`)
}
)
app.use("/",(req,res)=>{
res.send("hello http2!");
})
如上,對於已存在的項目只要修改幾行代碼就可以使用http2.0了。
請求頭和響應頭:
說明:新版的Chrome,對不安全的證書(如本地的自簽名服務)會降級到http1.1,firefox不會出現此問題。
啟動server push
app.get("/",(req,res)=>{
var stream = res.push('/app.js', { //服務器推送
status: 200, // optional
method: 'GET', // optional
request: {
accept: '*/*'
},
response: {
'content-type': 'application/javascript'
}
})
stream.on('error', function() {
})
stream.end('console.log("http2 push stream, by Lucien ");')
res.send(`hello http2!
<script src="/app.js"></script>`);//express 並沒有host static ,這個app.js 來自push
})
響應
抓包分析
可以用chrome 內部自帶的工具(chrome://net-internals/)查看http2流量,但這個包信息量比較少,結構不如我們熟悉的Fiddler or Wireshark清晰。
Fiddler是直接作為中間代理,可以作為客戶端直接與服務端通訊,可以像瀏覽器那樣直接解密https,直接看到https報文,
但是由於受限於.NET Framework暫不支持Http2.
用wireshark直接抓包 https:443端口的流量是這樣的:
數據被加密了,協議細節完全看不到。
這里介紹了一種方法獲取私鑰解包。
抓包https包時要把代理關了,不然私鑰不是同一個,wireshark不能解包(被這個坑了兩小時T T)。
一個包內有多個不同的Steam ID
追蹤解密后TCP流可以看到,由於多路復用,各個不同的請求交替傳輸不同的幀,所以流數據是亂的。但在同一幀內數據還是正常的。
最后
最后,HTTP2有更高的傳輸速度,更少的資源占用,可以去除各種性能優化tricks(如css sprite,inline-image.)
轉向WEB開發的美好未來T.T