一、描述
? ? ?HTTP/2大致可以分為兩部分:分幀層,即h2多路復用能力的核心部分;數據或http層,h2的分幀層是基于幀的二進制協(xié)議,分幀的目的,為了將重要信息都封裝起來,讓協(xié)議的解析方可以輕松閱讀、解析并還原信息。 相比之下, h1 不是基于幀的,而是以文本分隔。通過窗口大小調節(jié),依賴樹構建,維持首部信息的靜態(tài) / 動態(tài)表,壓縮 / 解壓縮首部,優(yōu)先級調整(h2 允許客戶端多次調整單一請求的優(yōu)先級), 預先推送客戶端尚未請求的數據流 ,來縮短響應時間,調高頁面請求渲染效率。
二、HttP2協(xié)議
2.1)連接
? ? ? 首先雙重確認客戶端支持h2,客戶端會發(fā)送一個叫作connection preface(連接前奏)的魔法字節(jié)流該字節(jié)流用十六進制表示如下:0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a 解碼為ASCII是:PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n?這個字符串的用處是,如果服務器(或者中間網絡設備)不支持h2,就會產生一個顯式錯誤。這個魔法字符串會有一個SETTINGS幀緊隨其后。服務器為了確認它可以支持h2,會聲明收到客戶端的SETTINGS幀,并返回一個它自己的SETTINGS幀(反過來也需要確認),然后確認環(huán)境正常,可以開始使用h2。
如何判斷H2
? ? 在連接不加密的情況下,客戶端會利用 Upgrade 首部來表明期望使用 h2。如果服務器也可以支持 h2,它會返回一個“101 Switching Protocols”(協(xié)議轉換)響應響應來接受升級請求。在101空內容響應終止后,服務端可以開始發(fā)送HTTP/2幀。這些幀必須包含一個發(fā)起升級的請求的響應,第一個被服務端發(fā)送的HTTP/2幀是一個設置(SETTINGS)幀。在收到101響應后,客戶端發(fā)送一個包含設置(SETTINGS)幀的連接序言,為了避免不必要的延遲,允許客戶端在發(fā)送客戶端連接序言之后立即發(fā)送其他額外的幀,不需要等待收到服務端連接序言。不過需要注意的是,服務端連接序言設置(SETTINGS)幀可能包含一些關于期望客戶端如何與服務端通信的所必須修改的參數。在收到這些設置(SETTINGS)幀之后,客戶端應當遵守所有設置的參數如果不支持HTTP/2的服務端對請求返回一個不包含升級的報頭字段的響應。
如 果 連 接 基 于 TLS, 情 況 就 不 同 了。 客 戶 端 在 ClientHello 消 息 中 設 置 ALPN(Application-Layer Protocol Negotiation,應用層協(xié)議協(xié)商)擴展來表明期望使用 h2 協(xié)議,服務器用同樣的方式回復。如果使用這種方式,那么 h2 在創(chuàng)建 TLS 握手的過程中完成協(xié)商。
2.2)幀
? ? ? HTTP/2是基于幀 (frame)的協(xié)議。有了幀,處理協(xié)議的程序就能預先知道會收到什么。基于幀的協(xié)議,特別是h2,開始有固定長度的字節(jié),其中包含表示整幀長度的字段,這樣一來,實現和維護都會簡單很多,請求和響應可以交錯甚至多路復用,最大幀主體的絕對長度是 $2^{14}-1$ (16,383)字節(jié),如果一個幀大小超過設定的限制,或者太小無法包含必須的基礎幀數據,這個端點必須發(fā)送一個幀大小錯誤(FRAME_SIZE_ERROR)。如果幀大小錯誤可能修改整個連接狀態(tài),必須作為一個連接錯誤處理,幀的結構如下:

前9個字節(jié)對于每個幀是一致的

h2協(xié)議中有10種不同的幀類型如下:

2.3 流
? ? ?可以將流看作在連接上的一系列幀,它們構成了單獨的HTTP請求和響應。如果客戶端想要發(fā)出請求,它會開啟一個新的流。然后,服務器將在這個流上回復。這與h1的請求/響應流程類似,重要的區(qū)別在于,因為有分幀,所以多個請求和響應可以交錯,而不會互相阻塞。流ID用來標識幀所屬的流??蛻舳说椒掌鞯膆2連接建立之后,通過發(fā)送HEADERS幀來啟動新的流,如果首部需要跨多個幀,可能還發(fā)會送CONTINUATION幀(更多信息參見下面的附注欄“CONTINUATIONS幀”)。該HEADERS幀可能來自HTTP請求,也可能來自響應,具體取決于發(fā)送方。后續(xù)流啟動的時候,會發(fā)送一個帶有遞增流ID的新HEADERS幀,流內發(fā)送幀的順序很重要。它們將按被接收的順序處理。特別是報頭及數據幀的順序語義上是有意義的。
2.3.1 流量控制?
h2提供了客戶端調整傳輸速度的能力,WINDOW_UPDATE幀用來指示流量控制信息。每個幀告訴對方,發(fā)送方想要接收多少字節(jié)。當一端接收并消費被發(fā)送的,數據時,它將發(fā)出一個WINDOW_UPDATE幀以指示其更新后的處理字節(jié)的能力,一個新建立的流標識符必須數值大于任何終端已經打開或者保留的流標識符。規(guī)則適用于使用報頭幀打開的流以及使用推送承諾幀保留的流。終端收到不規(guī)范的流標識符必須響應一個類型為協(xié)議錯誤(PROTOCOL_ERROR)的連接錯誤,流標識符不能被重復使用。生存期長的連接可能導致流標識符可用范圍耗盡??蛻舳瞬荒苄陆鳂俗R符時可以針對新流建立一個新的連接。服務端不能新建流標識符時可以發(fā)送一個超時幀(GOAWAY)強制客戶端對新的流使用新的連接。設置幀里面的SETTINGS_MAX_CONCURRENT_STREAMS參數來限制流的并發(fā)量。初始化流量控制大小是65,535字節(jié)。
流量控制是有方向性的,由接收端全權掌握。接收端可以選擇針對流及整個連接設置任意的窗口大小。發(fā)送端必須遵守接收端的流量控制限制??蛻舳恕⒎斩思爸卸舜碜鳛榻邮照邥r都獨立的向外廣播他們各自的流量控制窗口,作為發(fā)送者時遵守接收端的限制。每個新的流及整個連接的流量控制窗口初始值是65,535字節(jié),只有DATA幀受流量控制。
2.3.2?優(yōu)先級
? ?在沒有多路復用的時候,在發(fā)出對新對象的請求之前,需要等待前一個響應完成。有了 h2,客戶端就可以一次發(fā)出所有資源的請求,服務端也可以立即著手處理這些請求,在沒有多路復用的時候,在它可以發(fā)出對新對象的請求之前,需要等待前一個響應完成。h2通過流的依賴關系來解決這個問題。通過HEADERS幀和PRIORITY幀,客戶端可以明確地和服務端溝通它需要什么,以及它需要這些資源的順序。
2.3.3??流依賴
每個流可以顯式依賴其他流。包含一個依賴偏好設置表示分配資源給特定的流而不是所依賴的流,不依賴任何流的流的流依賴為0x0。
2.3.4??依賴權重
所有有依賴的流都會被分配一個1-256(含)的整數來標識權重,流的優(yōu)先級是通過使用優(yōu)先級幀改變的。設置一個依賴將使流變得依賴某個特定的父節(jié)點流。
2.4 服務端推送服務
如果服務器決定要推送一個對象,會構造一個PUSH_PROMISE幀,PUSH_PROMISE幀的首部塊與客戶端請求推送對象時發(fā)送的首部塊是相似的。所以客戶端有辦法放心檢查將要發(fā)送的請求,如果客戶端對PUSH_PROMISE的任何元素不滿意,就可以按照拒收原因選擇重置這個流(使用RST_STREAM),或者發(fā)送PROTOCOL_ERROR(在GOAWAY幀中)。
2.5?首部壓縮
? ?HPACK(http://www.itdecent.cn/p/f44b930cfcac) 是種表查找壓縮方案,它利用霍夫曼編碼獲得接近 GZIP 的壓縮效率。Hpack
2.6 請求示例
https://www.akamai.com/,www.facebook.com,www.yahoo.com,https://akah2san.h2book.com/

三、性能的影響
3.1、延遲
影響因素:端點之間的距離 和傳輸的介質。

3.2、丟包
? ? ? ?如果網絡中傳輸的數據包沒有成功到達目的地,就會發(fā)生丟包;這通常是由網絡擁堵造成。丟包通過丟包總數與已發(fā)送包總數的比值來衡量。頻繁丟包會影響h2的頁面?zhèn)鬏?,主要是因為h2開啟單一TCP連接,每次有丟包/擁堵時,TCP協(xié)議就會縮減TCP窗口。
對于包含很多小型資源(365個,每個2KB)的Web頁面,h2加載頁面的時間比h1更短。這是因為h1下(有6個TCP連接)服務器只能并行發(fā)送6個資源(由于隊頭阻塞),而h2下多個流(stream)可以共用連接。進一步說,隨著網絡條件變差,h1和h2下PLT都會增加;但是對h2的影響更值得注意,因為這是單連接架構造成的。如果唯一的連接發(fā)生了丟包,所有工作都會受影響。
3.3、服務端推送
服務端推送讓服務器具備了在客戶端請求之前就推送資源的能力。測試表明,如果合理使用推送,頁面渲染時間可以減少20%~50%。盡管如此,推送也會浪費帶寬,這是因為服務端可能試圖推送那些在客戶端已經緩存的資源,導致客戶端收到并不需要的數據。
3.4、第三方資源
? ? ?第三方調用會讓網站變慢,第三方請求往往通過不同域名發(fā)送;由于瀏覽器需要解析DNS、建立TCP連接、協(xié)商TLS,這將嚴重影響性能, 因為第三方資源在不同域名下,所以請求不能從服務端推送、資源依賴、請求優(yōu)先級等h2特性中受益。這些特性僅是為請求相同域名下的資源設計的; 你無法控制第三方資源的性能,也無法決定它們是否會通過h2傳輸。
3.5、不利用H2性能的H1優(yōu)化經驗
3.5.1、域名拆分
? ? ? 域名拆分是為了利用瀏覽器對每個域名開啟多個連接的能力,以便實現資源的并行下載,繞過h1的串行化下載的限制因為HTTP/2采取多路復用,所以域名拆分就不是必要的了。
3.5.2、資源內斂
資源內聯包括把JavaScript、樣式,甚至圖片插入到HTML頁面中,目的是省掉加載外部資源所需的新連接以及請求響應的時間,因為這樣會損失更有價值的特性,比如緩存。
3.5.3 、資源合并
資源合并意味著把幾個小文件合并成一個大文件。它與內聯很相似,旨在省掉那些加載外部資源的請求響應時間,以及解碼/執(zhí)行那些資源所消耗的CPU資源。
3.5.4、生成精靈圖
? ? 目前,生成精靈圖仍是一種避免小資源請求過多的技術為了生成精靈圖,開發(fā)者把較小的圖片拼合成較大的圖片,然后用CSS選擇圖片中某個部分展示出來多路復用和首部壓,縮去掉了大量的請求開銷。
四、實現
4.1、桌面版

? ? ?如果需要建立一個新連接,而瀏覽器支持連接歸并,那么通過復用之前已經存在的連接,就能夠提升請求性能。這意味著可以跳過TCP和TLS的握手過程,改善首次請求新域名的性能。
4.2、移動端

4.3、移動端應用支持
自從2015年6月XCode更新2之后,蘋果公司在iOS中提供了對h2的支持;默認情況下,在使用NSURLSession時,網絡傳輸安全模塊就會允許原生iOS應用充分利用h2協(xié)議。對于安卓應用,它們需要使用兼容h2的第三方庫,比如OkHttp3,并且必須通過TLS連接到支持h2的Web服務器上。
4.4、內容分發(fā)網絡
? ? ? 內容分發(fā)網絡(CDN)是反向代理服務器的全球性分布式網絡,它部署在多個數據中心。CDN的目標是通過縮短與最終用戶的距離來減少請求往返次數,以此為最終用戶提供高可用、高性能的內容服務,大多數主流CDN是支持HTTP/2的,雖然協(xié)議支持的完整程度以及功能特性會因廠商不同而有所差異。
五、調試
5.1、瀏覽器
? ? ? chrome://net-internals
5. 2、webpagetest
5.3、openssl
? ? ? echo | openssl s_client -connect? www.tdw.cn:443 -servername www.tdw.cn?
? ? -alpn spdy/2,h2,h2-14 | grep ALPN
? 5.4、nghttp2
? ? ? nghttp -v -n --no-dep -w 14 -a -H "Header1: Foo" https://www.akamai.com
? 5.5、curl
? ? ? curl -v --http2 https://akah2san.h2book.com/hello-world.html? -w
? 5.6、h2i
? ? ? h2i www.google.com
?5.7、Wireshark
? ? ? tshark port 443 and host www.example.com