深刻淺出:HTTP/2

上篇文章深入淺出:5G和HTTP裏給本身挖了一根深坑,說是要寫一篇關於HTTP/2的文章,今天來還帳了。css

本文分爲如下幾個部分:html

  1. HTTP/2的背景
  2. HTTP/2的特色
  3. HTTP/2的協議分析
  4. HTTP/2的支持 

HTTP/2簡介

HTTP/2主要是爲了解決現HTTP 1.1性能很差的問題纔出現的。當初Google爲了提升HTTP性能,作出了SPDY,它就是HTTP/2的前身,後來也發展成爲HTTP/2的標準。nginx

HTTP/2兼容HTTP 1.1,例如HTTP Method,Status code,URI以及大部分Header Fields。git

HTTP/2經過如下方法減小latency,用來改進頁面加載的速度,github

  1. HTTP Header的壓縮,採用的是HPack算法。
  2. HTTP/2的Server Push,很是重要的一個特性。
  3. 請求的pipeline。
  4. 修復在HTTP 1.x的隊頭阻塞問題。
  5. 在單個TCP鏈接裏多工複用請求。

HTTP/2支持HTTP 1.1裏的大部分use case,例如桌面瀏覽器、移動瀏覽器、Web API、Web Server、代理服務器、反向代理服務器、防火牆和CDN等。web

HTTP/2 頭部壓縮(HPack)

HPack是HTTP/2 裏HTTP頭壓縮的算法,具體能夠參看https://tools.ietf.org/html/rfc7541。下面簡單介紹一下HPack是如何工做的。算法

見下圖,該圖來自Google 的性能專家 Ilya Grigorik 的文章HTTP/2 is here, let's optimize!,它很是直觀地描述了 HTTP/2 中頭部壓縮的原理:chrome

簡單說,HTTP頭壓縮須要在HTTP/2 Client和服務端之間:瀏覽器

  • 維護一份相同的靜態表(Static Table),包含常見的頭部名稱,以及特別常見的頭部名稱與值的組合;
  • 維護一份相同的動態表(Dynamic Table),能夠動態地添加內容;
  • 基於靜態哈夫曼碼錶的哈夫曼編碼(Huffman Coding);

在HTTP頭裏,有些key:value是固定,例如:緩存

 :method: GET
 :scheme: http

在編碼時,它們直接用一個index編號代替,例如:method:GET是2,這些在一個靜態表定義。靜態表的定義以下,總共61個Header Name,點擊URL https://tools.ietf.org/html/rfc7541#appendix-A查看全部靜態表的定義。

 

Index Header Name Header Value
1 :authority  
2 :method GET
3 :method POST
4 :path /
5 :path /index.html
6 :scheme http
7 :scheme https
8 :status 200
... ... ...
32 cookie  
... ... ...
60 via  
61 www-authenticate  

 

使用靜態表、動態表、以及Huffman編碼能夠極大地提高壓縮效果。對於靜態表裏的字段,原來須要N個字符表示的,如今只須要一個索引便可,對於靜態、動態表中不存在的內容,還可使用哈夫曼編碼來減少體積。HTTP/2 標準裏也給出了一份詳細的靜態哈夫曼碼錶(https://tools.ietf.org/html/rfc7541#appendix-B),它們須要內置在客戶端和服務端之中。

關於HPack的算法和實現,後面專門抽一篇文章來寫。

HTTP/2 ALPN

HTTP/2協議裏有個negotiation的機制,讓客戶端和服務器選擇使用HTTP 1.1仍是2.0,這個是由ALPN來實現,關於ALPN,能夠參看

ALPN(Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension,https://tools.ietf.org/html/rfc7301。 

下面是抓包截圖,在TLS裏的Client Hello的包裏,咱們能夠看到ALPN裏由H2和HTTP/1.1,這就是說客戶端支持HTTP2以及HTTP 1.1.

當Server收到後,會識別Client發過來的協議列表,若是不認識就忽略掉。若是認識多個,則選擇一個最合適的協議發佈給Client。也是在Server Hello裏的ALPN返回,見下圖。

HTTP/2 Server Push機制

Server Push是HTTP 2最重要的一個特性。

在HTTP 1.1裏,在同一個 TCP 鏈接裏面,上一個迴應(response)發送完了,服務器才能發送下一個,但在HTTP/2裏,能夠將多個迴應一塊兒發送。

下圖是PUSH模式,當請求一個HTML時,若是HTML裏有CSS文件,server會一併推給client,而不像在HTTP 1.1下,還須要再發一個CSS的請求。

根據上圖,從理論上PUSH模式下性能會好不少。

舉個例子解釋一下。下面是一個簡單的HTML頁面,假說是index.html 。

<html>
<head>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <p>This is a sample to illustrate how HTTP/2 works</p>
  <img src="example.png">
</body>
</html>

這裏有三個文件須要處理:該HTML頁面、CSS文件style.css以及圖片example.png。在HTTP 1.1裏爲了處理這三個文件,Client須要發三個請求給Server。

首先,發送一個請求index.html,

GET /index.html HTTP/1.1

Client解析該HTML文件,繼而知道有2個style.css和example.png資源文件下載。

Client繼續發送2個請求下載他們。

GET /style.css  HTTP/1.1

以及

GET /example.png  HTTP/1.1

通常爲了解決這兩個問題,像CSS文件,能夠把CSS code直接放在HTML裏,也能夠把example.png轉化爲base64 code嵌入在HTML裏,以上只是把外部資源文件合併到HTML裏。

除了上述方法,還有一個優化的方法,就是Preload(預加載),能夠參看這裏,https://w3c.github.io/preload/。 

因此咱們能夠把HTML代碼改爲以下:

<link rel="preload" href="/styles.css" as="style">
<link rel="preload" href="/example.png" as="image">

那Preload是什麼意思呢?就是說下載前一個頁面時,能夠把相關的資源文件預先加載好,這樣感受起來會快一些。可是有一個關鍵問題須要注意,即使是預加載的狀況下,也不能減小HTTP請求次數。 

針對上面的問題,咱們引出服務器推送(server push)。根據上面的圖,咱們能夠看出,Server尚未收到Client的請求,就把各類資源推送給Client。

拿上面例子繼續舉例,當Client只請求index.html,可是Server把index.htmlstyle.cssexample.png所有發送給瀏覽器。這樣只須要一輪 HTTP 通訊,Client就獲得了所有資源。

 

HTTP/2的支持

如今主流的軟件都支持HTTP/2.

瀏覽器

基本上大部分瀏覽器在2015年末都支持HTTP/2了,包括Chrome、Opera、Firefox、IE 十一、Safari,Edge。

在Chrome上,能夠下載插件HTTP Indicator,判斷訪問的網站是否支持HTTP/2.

也能夠打開Chrome的開發者工具,打開Network tab,能夠看到Protocol爲h2的就是HTTP/2請求。若是Initiator爲push的,說明開啓了Server Push模式。

 

經常使用Server軟件

  1. Apache HTTPd,從版本2.4.12開始支持,經過模塊mod_h2來支撐。
  2. Apache Tomcat,從版本8.5開始支持。
  3. Jetty從9.3開始支持。
  4. Netty從4.1開始。
  5. IIS在Win10和WIndows Server 2016支持。
  6. Ngnix從1.9.5開始支持HTTP2,但Server Push功能則在1.13.9纔開始。

硬件

  1. Ctrix NetScaler從11.x開始支持
  2. F5 BIG-IP從11.6開始。

CDN/Cloud

  1. Akamai
  2. AWS
  3. Azure
  4. Aliyun
  5. Tecent Cloud

緩存問題

若是開啓了Server Push模式,咱們很容易意識到一個問題,那就是緩存問題。Server見到HTML頁面就把外部資源push給Client,若是沒有緩存,其實很浪費。爲了解決這個問題,能夠在第一次請求時push,後面的請求都不push了。

服務器推送有一個很麻煩的問題。所要推送的資源文件,若是瀏覽器已經有緩存,推送就是浪費帶寬。即便推送的文件版本更新,瀏覽器也會優先使用本地緩存。下面是 Nginx 官方給出的示例,根據 Cookie 判斷是否爲第一次訪問(https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/)。

server {
    listen 443 ssl http2 default_server;

    ssl_certificate ssl/certificate.pem;
    ssl_certificate_key ssl/key.pem;

    root /var/www/html;
    http2_push_preload on;

    location = /demo.html {
        add_header Set-Cookie "session=1";
        add_header Link $resources;
    }
}

map $http_cookie $resources {
    "~*session=1" "";
    default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, </image2.jpg>; as=image; rel=preload";

HTTP/2的性能

有人專門作過測試,https://www.smashingmagazine.com/2017/04/guide-http2-server-push/#measuring-server-push-performance,借用該文的一張圖片,

能夠看出,啓用HTTP/2後性能並未大幅度提高,因此在使用HTTP/2仍是謹慎一些,若是使用不當,反而會使性能降低。

另外,Ngnix專門撰文描述7個提升HTTP/2的技巧https://www.nginx.com/blog/7-tips-for-faster-http2-performance/ 。

參考文章:

  1. https://en.wikipedia.org/wiki/HTTP/2
  2. https://tools.ietf.org/html/rfc7301
  3. https://tools.ietf.org/html/rfc7541 (HPack)
  4. http://www.ruanyifeng.com/blog/2018/03/http2_server_push.html
  5. https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/
  6. https://www.smashingmagazine.com/2017/04/guide-http2-server-push/#measuring-server-push-performance
  7. https://www.nginx.com/blog/7-tips-for-faster-http2-performance/ 
  8. https://w3c.github.io/preload/
  9. http://velocityconf.com/devops-web-performance-2015/public/schedule/detail/42385
相關文章
相關標籤/搜索