HTTP2特性預(yù)覽和抓包分析
背景
近年來(lái),http網(wǎng)絡(luò)請(qǐng)求量日益添加,以下是httparchive統(tǒng)計(jì),從2012-11-01到2016-09-01的請(qǐng)求數(shù)量和傳輸大小的趨勢(shì)圖:

當(dāng)前大部份客戶端&服務(wù)端架構(gòu)的應(yīng)用程序,都是用http/1.1連接的,現(xiàn)代瀏覽器與單個(gè)域最大連接數(shù),都在4-6個(gè)左右,由上圖Total Requests數(shù)據(jù),如果不用CDN分流,平均有20個(gè)左右的串行請(qǐng)求。
HTTP2 是1999年發(fā)布http1.1后的一次重大的改進(jìn),在協(xié)議層面改善了以上問(wèn)題,減少資源占用,來(lái),直接感受一下差異:
HTTP/2 is the future of the Web, and it is here!
這是 Akamai 公司建立的一個(gè)官方的演示,用以說(shuō)明 HTTP/2 相比于之前的 HTTP/1.1 在性能上的大幅度提升。 同時(shí)請(qǐng)求 379 張圖片,從Load time 的對(duì)比可以看出 HTTP/2 在速度上的優(yōu)勢(shì)。

HTTP/2 源自 SPDY/2
SPDY 系列協(xié)議由谷歌開發(fā),于 2009 年公開。它的設(shè)計(jì)目標(biāo)是降低 50% 的頁(yè)面加載時(shí)間。當(dāng)下很多著名的互聯(lián)網(wǎng)公司都在自己的網(wǎng)站或 APP 中采用了 SPDY 系列協(xié)議(當(dāng)前最新版本是 SPDY/3.1),因?yàn)樗鼘?duì)性能的提升是顯而易見的。主流的瀏覽器(谷歌、火狐、Opera)也都早已經(jīng)支持 SPDY,它已經(jīng)成為了工業(yè)標(biāo)準(zhǔn),HTTP Working-Group 最終決定以 SPDY/2 為基礎(chǔ),開發(fā) HTTP/2。HTTP/2標(biāo)準(zhǔn)于2015年5月以RFC 7540正式發(fā)表。
但是,HTTP/2 跟 SPDY 仍有不同的地方,主要是以下兩點(diǎn):
HTTP/2 支持明文 HTTP 傳輸,而 SPDY 強(qiáng)制使用 HTTPS
HTTP/2 消息頭的壓縮算法采用 HPACK ,而非 SPDY 采用的 DEFLATE(感謝網(wǎng)友 逸風(fēng)之狐指正)
協(xié)議文檔請(qǐng)見:rfc7540:HTTP2
HTTP2特性概覽
1. 二進(jìn)制協(xié)議
HTTP/2 采用二進(jìn)制格式傳輸數(shù)據(jù),而非 HTTP/1.x 的文本格式

由上圖可以看到HTTP2在原來(lái)的應(yīng)用層和HTTP層添加了一層二進(jìn)制傳輸。
二進(jìn)制協(xié)議的一個(gè)好處是,可以定義額外的幀。
HTTP/2 定義了近十種幀(詳情可分析抓包文件),為將來(lái)的高級(jí)應(yīng)用打好了基礎(chǔ)。如果使用文本實(shí)現(xiàn)這種功能,解析數(shù)據(jù)將會(huì)變得非常麻煩,二進(jìn)制解析則方便得多。
RFC7540:Frame Definitions

協(xié)議中定義的幀
2. 多路復(fù)用
HTTP/2 復(fù)用TCP連接,在一個(gè)連接里,客戶端和瀏覽器都可以同時(shí)發(fā)送多個(gè)請(qǐng)求或回應(yīng),而且不用按照順序一一對(duì)應(yīng),這樣就避免了"隊(duì)頭堵塞"(見TCP/IP詳解卷一)。
每個(gè) Frame Header 都有一個(gè) Stream ID 就是被用于實(shí)現(xiàn)該特性。每次請(qǐng)求/響應(yīng)使用不同的 Stream ID。就像同一個(gè) TCP 鏈接上的數(shù)據(jù)包通過(guò) IP: PORT 來(lái)區(qū)分出數(shù)據(jù)包去往哪里一樣。

rfc7540: HTTP2 Multiplexing中對(duì)Multiplexing的說(shuō)明
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. 數(shù)據(jù)流
數(shù)據(jù)流發(fā)送到一半的時(shí)候,客戶端和服務(wù)器都可以發(fā)送信號(hào)(RST_STREAM幀),取消這個(gè)數(shù)據(jù)流。1.1版取消數(shù)據(jù)流的唯一方法,就是關(guān)閉TCP連接。這就是說(shuō),HTTP/2 可以取消某一次請(qǐng)求,同時(shí)保證TCP連接還打開著,可以被其他請(qǐng)求使用。
4. 頭信息壓縮:
HTTP/2 對(duì)消息頭采用 HPACK 進(jìn)行壓縮傳輸,能夠節(jié)省消息頭占用的網(wǎng)絡(luò)的流量。而 HTTP/1.x 每次請(qǐng)求,都會(huì)攜帶大量冗余頭信息,浪費(fèi)了很多帶寬資源。
HTTP2對(duì)http頭建立索引表,相同的頭只發(fā)送hash table 的index, 同時(shí)還用了霍夫曼編碼和傳統(tǒng)的gzip壓縮。
5. 服務(wù)器推送
服務(wù)端能夠更快的把資源推送給客戶端。例如服務(wù)端可以主動(dòng)把 JS 和 CSS 文件推送給客戶端,而不需要客戶端解析 HTML 再發(fā)送這些請(qǐng)求。當(dāng)客戶端需要的時(shí)候,它已經(jīng)在客戶端了。
那么存在一個(gè)問(wèn)題,如果客戶端設(shè)置了緩存怎么辦。有三種方式(來(lái)自社區(qū))
- 客戶端可以通過(guò)設(shè)置SETTINGS_ENABLE_PUSH為0值通知服務(wù)器端禁用推送
- 發(fā)現(xiàn)緩存后,客戶端和服務(wù)器都可以發(fā)送信號(hào)(RST_STREAM幀),取消這個(gè)數(shù)據(jù)流。
- cache-digest(提案)
6. 流優(yōu)先級(jí)
HTTP2允許瀏覽器指定資源的優(yōu)先級(jí)。
瀏覽器支持

主流瀏覽器都只支持 HTTP/2 Over TLS
node中啟用http2
node中可以用spdy模塊來(lái)啟動(dòng)應(yīng)用,spdy的api,與https是一致的且主流瀏覽器只支持HTTP/2 Over TLS,需要配置 私鑰和證書,本地自簽名服務(wù)器配置可參考引用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!");
})
如上,對(duì)于已存在的項(xiàng)目只要修改幾行代碼就可以使用http2.0了。
請(qǐng)求頭和響應(yīng)頭:

說(shuō)明:新版的Chrome,對(duì)不安全的證書(如本地的自簽名服務(wù))會(huì)降級(jí)到http1.1,firefox不會(huì)出現(xiàn)此問(wèn)題。
啟動(dòng)server push
app.get("/",(req,res)=>{
var stream = res.push('/app.js', { //服務(wù)器推送
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 ,這個(gè)app.js 來(lái)自push
})
響應(yīng)


抓包分析
可以用chrome 內(nèi)部自帶的工具(chrome://net-internals/)查看http2流量,但這個(gè)包信息量比較少,結(jié)構(gòu)不如我們熟悉的Fiddler or Wireshark清晰。
Fiddler是直接作為中間代理,可以作為客戶端直接與服務(wù)端通訊,可以像瀏覽器那樣直接解密https,直接看到https報(bào)文,
但是由于受限于.NET Framework暫不支持Http2.
用wireshark直接抓包 https:443端口的流量是這樣的:

數(shù)據(jù)被加密了,協(xié)議細(xì)節(jié)完全看不到。
這里介紹了一種方法獲取私鑰解包。
抓包https包時(shí)要把代理關(guān)了,不然私鑰不是同一個(gè),wireshark不能解包(被這個(gè)坑了兩小時(shí)T T)。


一個(gè)包內(nèi)有多個(gè)不同的Steam ID

追蹤解密后TCP流可以看到,由于多路復(fù)用,各個(gè)不同的請(qǐng)求交替?zhèn)鬏敳煌膸粤鲾?shù)據(jù)是亂的。但在同一幀內(nèi)數(shù)據(jù)還是正常的。
最后
最后,HTTP2有更高的傳輸速度,更少的資源占用,可以去除各種性能優(yōu)化tricks(如css sprite,inline-image.)
轉(zhuǎn)向WEB開發(fā)的美好未來(lái)T.T


浙公網(wǎng)安備 33010602011771號(hào)