【轉】http://www.syyong.com/architecture/http2.html
HTTP/2(最初名為HTTP/2.0)是 WWW 使用的 HTTP 網絡協議的主要版本。 它來自早先由 Google 開發的實驗性 SPDY 協議。 HTTP / 2由互聯網工程任務組的超文本傳輸協議工作組 httpbis(其中 bis 表示“第二個”)開發。 HTTP / 2是 HTTP 1.1以來的第一個新版本,於1997年在 RFC 2068中進行了標准化。工作組於2014年12月向 IESG 提交了 HTTP / 2作為建議標准的審議意見,IESG 批准其發布為提議 標准於2015年2月17日發布。HTTP / 2規范於2015年5月發布為 RFC 7540。
Chrome,Opera,Firefox,Internet Explorer 11,Safari,Amazon Silk 和 Edge 瀏覽器支持標准化工作。 到2015年底,大多數主流瀏覽器都增加了HTTP / 2支持。
官方協議
RFC 7540 - Hypertext Transfer Protocol Version 2(HTTP/2)
RFC 7541 - HPACK: Header Compression for HTTP/2
演示
HTTP/1缺陷
- 一個TCP鏈接上只能有一個請求/響應
- 頭部消耗
HTTP/1 協議頭部使用純文本格式,沒有任何壓縮,且包含很多冗余信息(例如 Cookie、UserAgent 每次都會攜帶),所以一個頁面的請求數越多,頭部帶來的額外開銷就越大。
如打開http://www.oschina.net/question/1397765_172789?fromerr=VbVRMMxc
傳輸頭部的開銷超過了5%
更有為了獲得2字節的數據,在頭部傳輸上花費了幾十倍的流量:
Why do we need header compression?
Mozilla的Patrick McManus 通過計算消息頭對平均頁面負載的影響,給出下面的說明:
假定一個頁面有80個資源需要加載(這個數量對於今天的Web而言還是挺保守的), 而每一次請求都有1400字節的消息頭(同樣這也並不少見,因為Cookie和引用等東西的存在), 至少要7到8個來回去“在線”獲得這些消息頭。
這還不包括響應時間——那只是從客戶端那里獲取到它們所花的時間而已.
這全都由於TCP的慢啟動機制,它會基於對已知有多少個包,來確定還要來回去獲取哪些包 – 這很明顯的限制了最初的幾個來回可以發送的數據包的數量.
相比之下,即使是頭部輕微的壓縮也可以是讓那些請求只需一個來回就能搞定——有時候甚至一個包就可以了。
這種開銷是可以被節省下來的,特別是當你考慮移動客戶端應用的時候,即使是良好條件下,一般也會看到幾百毫秒的來回延遲。
HTTP/2改進
1.多路復用,只需一個連接即可實現並行
HTTP/2引入"流"概念支持多路復用。
流:
是服務器和客戶端在HTTP/2連接內用於交換幀數據的獨立雙向序列,邏輯上可看做一個較為完整的交互處理單元,即表達一次完整的資源請求-響應數據交換流程。
一個業務處理單元,在一個流內進行處理完畢,這個流生命周期完結。
流與HTTP2連接關系
實際傳輸可能是這樣的
流的一些屬性:
- 一個HTTP/2連接可同時保持多個打開的流,任一端點交換幀;
- 流可被客戶端或服務器單獨或共享創建和使用;
- 流可被任一端關閉;
- 在流內發送和接收數據都要按照順序;
- 流的標識符自然數表示,1~2^31-1區間,由創建流的終端分配;
- 流與流之間邏輯上是並行、獨立存在;
2.服務器"推送"
"推送"是HTTP/2添加的一種新的交互模式,允許服務端“推送”那些它認為客戶端將會需要的內容到客戶端的緩存中,以此來避免往返的延遲。比如:客戶端瀏覽頁面請求page.html,服務器在發回page.html后,認為客戶端還需要JavaScript和CSS,則把strict.js和style.css也推送給客戶端。當然這個“推送”其實不是你想推就能推的,服務器要遵循請求-響應這個模型,只不過服務器對同一請求可以推送多個響應。客戶端在交換 SETTINGS 幀時,設置字段 SETTINGS_ENABLE_PUSH(0x2) 為1顯式允許服務器推送。
3.頭部壓縮
為了減少傳輸頭部在整個HTTP請求中所占的比重,HTTP/1 時代,有很多優化方案可以嘗試,例如合並請求、啟用 Cookie-Free 域名等等,但是這些方案或多或少會引入一些新的問題。
HTTP/2協議使用了HPACK算法壓縮頭部,能顯著壓縮頭部大小。
4.優先級
瀏覽器請求優先級與 HTTP 2.0
- 瀏覽器在渲染頁面時,並非所有資源都具有相同的優先級:HTML 文檔本身對構建 DOM 不可或缺,CSS 對構建 CSSOM 不可或缺,而 DOM 和 CSSOM 的構 建都可能受到 JavaScript 資源的阻塞,其他資源(如圖片)的優先級都可以降低。
- 為加快頁面加載速度,所有現代瀏覽器都會基於資源的類型以及它在頁面中的位置排定請求的優先次序,甚至通過之前的訪問來學習優先級模式——比如,之前的渲染如果被某些資源阻塞了,那么同樣的資源在下一次訪問時可能就會被賦予更高的優先級。
- 在 HTTP 1.x 中,瀏覽器極少能利用上述優先級信息,因為協議本身並不支持多路復用,也沒有辦法向服務器通告請求的優先級。此時瀏覽器只能依賴並行連接,且最多只能同時向一個域名發送 6 個請求。於是,在等連接可用期間,請求只能在客戶端排隊,從而增加了不必要的網絡延遲。理論上,HTTP 管道可以解決這 個問題,只是由於缺乏支持而無法付諸實踐。
- HTTP 2.0 一舉解決了所有這些低效的問題:瀏覽器可以在發現資源時立即分派請求,指定每個流的優先級,讓服務器決定最優的響應次序。這樣請求就不必排隊了,既節省了時間,也最大限度地利用了每個連接。
客戶端對HTTP/2的支持
iOS
- NSURLConnection
-
NSURLSession (HTTP/2 is only supported by NSURLSession. NSURLConnection has been deprecated in iOS 9. The HTTP/2 support is part of iOS 9, and thus won't be available on earlier OS releases.)
- on iOS 9, via HTTP/2
- on iOS 8, via HTTP/1.1 or SPDY
- on iOS 7, via HTTP/1.1
- AFNetworking 3.0版本已經支持NSURLSession(即 支持HTTP/2)
- SDWebImage 要到4.0.0版本 才會引入 NSURLSession Refernce
Android
- HttpUrlConnection 不支持
- OkHttp 完整支持
- Volley 可配置使用OkHttp傳輸數據
Windows
HTTP/2 support is present in Windows 10 and the Server 2016 Technical Preview
瀏覽器
參見:CanIUse
服務端對HTTP/2的支持
Nginx
Nginx 1.9.5+ 完整支持:
版本要求:
openssl 1.0.2+ (OpenSSL 1.0.2 開始支持 ALPN) Nginx 1.9.5+
Nginx 編譯安裝:
./configure \
--prefix=/usr/local/nginx \ --with-openssl=/usr/local/openssl-1.0.2e \ --with-pcre \ --with-stream --with-stream_ssl_module \ --with-http_ssl_module \ --with-http_v2_module \ make make install
Moving to HTTP/2 with NGINX 1.9.5
Tengine
Tengine 2.1.2+ 支持 http2。
協議協商
在完全過度到 http/2 協議,http1.1 的服務器和客戶端依然大量存在,這就注定新老協議的一個共存。這樣,瀏覽器和服務器就需要協商使用何種協議進行通訊。
h2c
即:HTTP/2 ClearText。直接在標准TCP之上建立HTTP2,采用“h2c”作為協議標識符,在未知服務器是否提供HTTP/2支持之前,可以依賴現有HTTP/1.1進行試探。
GET /page HTTP/1.1 Host: server.example.com Connection: Upgrade, HTTP2-Settings Upgrade: h2c HTTP2-Settings: (SETTINGS payload) HTTP/1.1 200 OK Content-length: 243 Content-type: text/html (... HTTP/1.1 response ...) (or) HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: h2c (... HTTP/2 response ...)
h2
HTTP/2安全版本在TLS上構建,使用ALPN擴展協議進行協商,采用“h2”作為協議標識符,在TLS層協商過程中一並完成對是否支持HTTP2的試探。
ALPN 和 NPN 區別:
NPN(Next Protocol Negotiation) 是一個使 SPDY 在 TLS 服務器上對使用何種應用層協議進行協商的協議。ALPN(Application Layer Protocol Negotiation) 則是 IETF(h2的標准化組織)修改 NPN 而成的一個標准。兩者區別在於誰持有會話協議的決定權,ALPN 是由客戶端給服務器發送一個協議清單,由服務器來最終選擇一個,而 NPN 則正好相反。
h2 和 h2c 的區別
理論上來說,http2 可以架構在 tls 上,也可以架構在 tcp 上(運行於非加密通道之上的 http2)。協議文本也確實沒有限定或者強制使用 tls 信道。但是 http2 的前身是 spdy,而 spdy 是在 tls 之上的。目前 chrome/firefox 也只支持 tls 。這就讓架構於 tls 之上就成為 http2 的事實上的標准。
調試工具
Charles
Charles4.0+開始支持http2,如使用Charles4.2抓街電的包:
Wireshark
Wireshark 是一個無比強大的網絡封包分析工具。新版 Wireshark 才增加了對 HTTP/2 的支持,下載地址:
https://www.wireshark.org/#download
安裝后,輸入過濾條件進行抓包,如:
由於使用TLS,數據是加過密了的,看不到協議細節。為了解密,首先需要保存ssl key,然后 wireshark 通過 key 來解密數據包。具體操作請參考>>
解密后如圖所示:
CURL
執行命令 curl --version
查看curl是否支持 http2。如返回:
curl 7.54.0 (x86_64-apple-darwin16.0) libcurl/7.54.0 SecureTransport zlib/1.2.8
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets
可以看到當前 curl 的版本及支持的協議以及功能特性沒有支持 HTTP2。為了讓 curl 支持 HTTP2 需要安裝 nghttp2(http2 的 C 語言庫)。\
編譯安裝nghttp2(MAC 可以直接通過 brew install nghttp2 安裝):
git clone https://github.com/tatsuhiro-t/nghttp2.git cd nghttp2 autoreconf -i automake autoconf ./configure make sudo make install
升級curl版本:
wget https://curl.haxx.se/download/curl-7.56.0.tar.bz2 tar -xvjf curl-7.56.0.tar.bz2 cd curl-7.56.0 ./configure --with-nghttp2 --with-ssl sudo make && make install
檢查是否安裝成功:
curl --version
測試curl with http2:
$ curl --http2 -I https://www.jd.com
HTTP/2.0 200
server:JDWS/2.0 date:Tue, 17 Oct 2017 07:53:14 GMT content-type:text/html; charset=utf-8 content-length:122981 vary:Accept-Encoding vary:Accept-Encoding expires:Tue, 17 Oct 2017 07:53:00 GMT cache-control:max-age=30 ser:130.26 via:BJ-M-YZ-NX-76(HIT), http/1.1 CD-CT-2-JCS-25 ( [cRs f ]) age:30 strict-transport-security:max-age=360
nghttp
nghttp 做為一個功能完整的 HTTP/2 客戶端,非常適合用來查看和調試 HTTP/2 流量。它支持的參數很多,通過官方文檔或者 nghttp -h 都能查看。最常用幾個參數如下:
- -v, --verbose,輸出完整的 debug 信息;
- -n, --null-out,丟棄下載的數據;
- -a, --get-assets,下載 html 中的 css、js、image 等外鏈資源;
- -H, --header=<HEADER>,添加請求頭部字段,如 -H':method: PUT';
- -u, --upgrade,使用 HTTP 的 Upgrade 機制來協商 HTTP/2 協議,用於 h2c,詳見下面的例子;
以下是使用 nghttp 訪問 nghttp2 官網的結果。從調試信息中可以清晰看到 h2c 協商以及 Server Push 的整個過程:
$ nghttp -nvu http://nghttp2.org [ 0.244] Connected [ 0.244] HTTP Upgrade request GET / HTTP/1.1 host: nghttp2.org connection: Upgrade, HTTP2-Settings upgrade: h2c http2-settings: AAMAAABkAAQAAP__ accept: */* user-agent: nghttp2/1.24.0 [ 1.833] HTTP Upgrade response HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: h2c [ 1.833] HTTP Upgrade success [ 1.835] recv SETTINGS frame <length=18, flags=0x00, stream_id=0> (niv=3) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):1048576] [SETTINGS_HEADER_TABLE_SIZE(0x01):8192] [ 1.835] recv (stream_id=1) :status: 200 [ 1.835] recv (stream_id=1) date: Tue, 17 Oct 2017 08:07:13 GMT [ 1.835] recv (stream_id=1) content-type: text/html [ 1.835] recv (stream_id=1) last-modified: Wed, 20 Sep 2017 14:04:09 GMT [ 1.835] recv (stream_id=1) etag: "59c27559-19e1" [ 1.835] recv (stream_id=1) accept-ranges: bytes [ 1.835] recv (stream_id=1) content-length: 6625 [ 1.835] recv (stream_id=1) x-backend-header-rtt: 0.001153 [ 1.835] recv (stream_id=1) server: nghttpx [ 1.835] recv (stream_id=1) via: 2 nghttpx [ 1.835] recv (stream_id=1) x-frame-options: SAMEORIGIN [ 1.835] recv (stream_id=1) x-xss-protection: 1; mode=block [ 1.835] recv (stream_id=1) x-content-type-options: nosniff [ 1.835] recv HEADERS frame <length=198, flags=0x04, stream_id=1> ; END_HEADERS (padlen=0) ; First response header [ 1.835] recv DATA frame <length=6625, flags=0x01, stream_id=1> ; END_STREAM [ 1.835] send SETTINGS frame <length=12, flags=0x00, stream_id=0> (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [ 1.835] send GOAWAY frame <length=8, flags=0x00, stream_id=0> (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])
h2load
nghttp2 帶有 h2load,它是一個支持 HTTP/2 的壓測工具,可以用來測試 HTTP/2 服務的穩定性、QPS 等信息。它支持的參數也可以通過官網文檔或者 h2load -h 來查看。下面是一個簡單例子:
$ h2load https://example.com -n 100 -c 10
starting benchmark...
spawning thread #0: 10 total client(s). 100 total requests TLS Protocol: TLSv1.2 Cipher: ECDHE-RSA-AES128-GCM-SHA256 Server Temp Key: ECDH P-256 256 bits Application protocol: h2 progress: 10% done progress: 20% done progress: 30% done progress: 40% done progress: 50% done progress: 60% done progress: 70% done progress: 80% done progress: 90% done progress: 100% done finished in 2.67s, 37.52 req/s, 49.48KB/s requests: 100 total, 100 started, 100 done, 100 succeeded, 0 failed, 0 errored, 0 timeout status codes: 100 2xx, 0 3xx, 0 4xx, 0 5xx traffic: 131.87KB (135038) total, 5.50KB (5628) headers (space savings 79.16%), 124.02KB (127000) data min max mean sd +/- sd time for request: 174.06ms 219.98ms 188.81ms 8.90ms 61.00% time for connect: 558.91ms 606.67ms 572.44ms 14.39ms 80.00% time to 1st byte: 737.95ms 813.75ms 762.35ms 22.60ms 60.00% req/s : 3.75 4.30 4.07 0.16 60.00%
Chrome開發者工具
Chrome 40版本就開始支持http2了,如圖所示:
Firefox開發者工具
Firefox 開發者工具也支持http2,如圖所示:
瀏覽器插件
HTTP/2 and SPDY indicator 是一款可以為每個網站提供HTTP/2,SPDY 和 QUIC 支持的指示按鈕的插件,也有管理頁面便於查看更詳細的信息。
Chrome瀏覽器安裝好插件后,在地址欄中輸入 chrome://net-internals/#http2
會列出瀏覽器當前所有活躍的 HTTP/2 Session,點擊具體的 Session ID,可以看到全部幀信息,如圖所示:
參考
- https://en.wikipedia.org/wiki/HTTP/2
- http2協議譯文
- HTTP/2的已知實現
- https://www.zhihu.com/question/34074946
- http://www.blogjava.net/yongboy/archive/2015/03/19/423611.html
- https://imququ.com/post/http2-resource.html
- https://imququ.com/post/http2-and-wpo-1.html
- https://imququ.com/post/http2-and-wpo-2.html
- https://imququ.com/post/http2-and-wpo-3.html
- https://imququ.com/post/header-compression-in-http2.html
- http://httpwg.org/specs/rfc7541.html#huffman.code
- http://www.jianshu.com/p/68ef142c7ee2
- http://nginx.org/en/docs/http/ngx_http_v2_module.html
- https://wiki.wireshark.org/HTTP2