HTTP2特性預覽和抓包分析

背景

近年來,http網絡請求量日益添加,如下是httparchive統計,從2012-11-01到2016-09-01的請求數量和傳輸大小的趨勢圖:javascript

chart

當前大部份客戶端&服務端架構的應用程序,都是用http/1.1鏈接的,現代瀏覽器與單個域最大鏈接數,都在4-6個左右,由上圖Total Requests數據,若是不用CDN分流,平均有20個左右的串行請求。
HTTP2 是1999年發佈http1.1後的一次重大的改進,在協議層面改善了以上問題,減小資源佔用,來,直接感覺一下差別:php

HTTP/2 is the future of the Web, and it is here!
這是 Akamai 公司創建的一個官方的演示,用以說明 HTTP/2 相比於以前的 HTTP/1.1 在性能上的大幅度提高。 同時請求 379 張圖片,從Load time 的對比能夠看出 HTTP/2 在速度上的優點。css

image

本文全部源碼和抓包文件在githubhtml

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正式發表。java

可是,HTTP/2 跟 SPDY 仍有不一樣的地方,主要是如下兩點:node

HTTP/2 支持明文 HTTP 傳輸,而 SPDY 強制使用 HTTPS
HTTP/2 消息頭的壓縮算法採用 HPACK ,而非 SPDY 採用的 DEFLATE(感謝網友 逸風之狐指正)git

協議文檔請見:rfc7540:HTTP2github

HTTP2特性概覽

1. 二進制協議

HTTP/2 採用二進制格式傳輸數據,而非 HTTP/1.x 的文本格式算法

image

由上圖能夠看到HTTP2在原來的應用層和HTTP層添加了一層二進制傳輸。chrome

二進制協議的一個好處是,能夠定義額外的幀。

HTTP/2 定義了近十種幀(詳情可分析抓包文件),爲未來的高級應用打好了基礎。若是使用文本實現這種功能,解析數據將會變得很是麻煩,二進制解析則方便得多。
RFC7540:Frame Definitions
image
協議中定義的幀

2. 多路複用

HTTP/2 複用TCP鏈接,在一個鏈接裏,客戶端和瀏覽器均可以同時發送多個請求或迴應,並且不用按照順序一一對應,這樣就避免了"隊頭堵塞"(見TCP/IP詳解卷一)。
每一個 Frame Header 都有一個 Stream ID 就是被用於實現該特性。每次請求/響應使用不一樣的 Stream ID。就像同一個 TCP 連接上的數據包經過 IP: PORT 來區分出數據包去往哪裏同樣。
image

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(提案)

    rfc7540: HTTP2 Server Push

    6. 流優先級

    HTTP2容許瀏覽器指定資源的優先級。

    rfc7540: Stream Priority

瀏覽器支持

image

主流瀏覽器都只支持 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了。

請求頭和響應頭:
image

說明:新版的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 
})

源碼在github

響應

image

image

抓包分析

能夠用chrome 內部自帶的工具(chrome://net-internals/)查看http2流量,但這個包信息量比較少,結構不如咱們熟悉的Fiddler or Wireshark清晰。

Fiddler是直接做爲中間代理,能夠做爲客戶端直接與服務端通信,能夠像瀏覽器那樣直接解密https,直接看到https報文,
可是因爲受限於.NET Framework暫不支持Http2.

用wireshark直接抓包 https:443端口的流量是這樣的:
image

數據被加密了,協議細節徹底看不到。
這裏介紹了一種方法獲取私鑰解包。
抓包https包時要把代理關了,否則私鑰不是同一個,wireshark不能解包(被這個坑了兩小時T T)。

image

image

一個包內有多個不一樣的Steam ID

image

追蹤解密後TCP流能夠看到,因爲多路複用,各個不一樣的請求交替傳輸不一樣的幀,因此流數據是亂的。但在同一幀內數據仍是正常的。

最後

最後,HTTP2有更高的傳輸速度,更少的資源佔用,能夠去除各類性能優化tricks(如css sprite,inline-image.)
轉向WEB開發的美好將來T.T

參考資料

  1. Turn-on HTTP/2 today!
  2. Hypertext Transfer Protocol Version 2 (HTTP/2)
  3. npm spdy
  4. npm spdy push
  5. How to create a self-signed SSL Certificate
  6. HPACK: Header Compression for HTTP/2
  7. 用Node.js建立自簽名的HTTPS服務器
相關文章
相關標籤/搜索