原來你是這樣的http2......

歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~css

本文由mariolu發表於雲+社區專欄html

序言

目前HTTP/2.0(簡稱h2)已經在普遍使用(截止2018年8月根據Alexa流行度排名的頭部1千萬網站中,h2佔比約29%,w3techs.com/technologie…android

第一話、追蹤溯源

img

圖一、HTTP年鑑圖ios

早在1991年,伴隨WWW誕生之初,HTTP/0.9協議已經提出。HTTP0.9是簡單且應用受限的協議。支持去網絡主機獲取對應路徑的資源。可是沒有擴展屬性。其協議之簡單甚至只用下面一個訪問谷歌主機的例子歸納了HTTP/0.9的所有。以下所示,協議只支持GET,沒有http頭;響應只能是超文本。nginx

telnet google.com 80算法

Connected to x.x.x.xchrome

GET /aboutapache

(Hyper text)編程

(Conection closed)瀏覽器

隨着人們對富媒體信息的渴望以及瀏覽器的普及,HTTP/1.0在1996年被提出來。HTTP/1.0的不少特性目前還被普遍使用,可是仍然像HTTP/0.9同樣一次請求須要建立一次的tcp鏈接。隨即短短几年時間內,HTTP/1.1以RFC標準形式再次展示在人們眼前。此時的HTTP協議1.1版本已經從新設計了長鏈接、options請求方法、cache頭、upgrade頭、range頭、transfer-encoding頭, 以及pieline(in order)等概念。

而咱們另外一個所熟知的HTTPS的SSL/TLS技術各個版本差很少在後來的十年間逐漸被提出。出於安全考慮,互聯網通訊間的防火牆路由交換機等設備,這些設備通常僅會開發有限的端口(如80和443)。各類版本的通訊協議只能複用這些端口。HTTP1.1的80端口設計了upgrade請求頭升級到更高級的協議,而443端口爲了不多消耗個網絡RTT,在tls握手過程當中使用了NPN/ALPN技術直接在通訊以前保持CS兩端的協議一致。NPN/ALPN是是TLS協議擴展,其中NPN是Google爲實現spdy提出的。由服務端提供可支持的協議,供客戶端選擇。ALPN則是更接近於HTTP交互的方式,由客戶端先發出使用某種協議的請求,由服務端確認是否支持協議。ALPN爲了HTTP2誕生作鋪墊。

另一個不得不提的是spdy協議。Spdy旨在解決HTTP1.1的線頭阻塞問題(後面章節有詳細討論)於2009被google提出。同時分別於2012提出了spdy3.0實現了流控制,2013-2014期間提出了流優先級,server push等概念。Spdy的存在乎義更像是http2.0的體驗服。它爲探索HTTP繼續演進道路作了鋪墊。

第二話、人機交互

彙編是效率最高的語言之一,可是又是最晦澀難懂的語言之一。而人腦易懂的編程語言每每犧牲性能做爲折衷。簡單說,HTTP/1.x協議就是爲了人類語言習慣所設計的協議,可是轉換成機器執行協議並非高效的。讓咱們在回顧下計算機執行解析HTTP1.x的流程。

GET / HTTP/1.1<crlf>

Host: xxx.aa.com<crlf>

<crlf>

對應的解析僞代碼是

loop

while(! CRLF)

read bytes

end while

if line 1:

parse line as Request-Line

else if empty line:

Break out and We have done

else If start with non-whitespace

parse header

else if space

continue with last heade

end if

end loop

在僞代碼解析流程能夠看到,肉眼看起來簡潔的協議解析起來是這麼的費勁。並且在HTTP服務器中還要考慮這種問題:字節行的長度是未知的,也不知道預先分配多大內存。

HTTP/2.0使用了計算機易懂的二進制編碼信息,並且得向上兼容HTTP的涵義。具體咱們來看下他是如何作到的。像大多數通訊協議同樣,楨是傳輸最小單位。楨分爲數據幀和控制楨。數據幀做爲數據的載體,控制楨控制信道的信令。h2楨的通用格式爲首部9字節+額外的字符。正如你能想到的那樣,楨的第一個部分是描述長度,第二個部分描述了楨的類型,第三個部分描述了標誌Flag,第四個部分是惟一序列號。這是全部楨的通用頭。通用頭緊接的是楨的實體。圖4展現了楨的結構。

img

圖二、通用楨的格式

這樣設計有什麼好處呢。再來看一下楨的解析流程,你就會發現對計算機來講更簡潔。

loop

read 9 byte

payload_Length=first 3 bytes

read payload

swith type:

Take action

end loop

HTTP2.0使用header楨表達HTTP header+request line,data楨表達Body。header楨和data楨使用相同的stream id組成一個完整的HTTP請求/響應包。這裏的stream描述了一次請求和響應,至關於完成了一次HTTP/1.x的短鏈接請求和響應。

第三話、並行不悖

上節講到咱們用h2楨完整表達了HTTP/1.x。可是h2協議抱負遠不止於此。它的真正目的是解決以前HTTP1.x的線頭阻塞問題、改善網絡延遲和頁面加載時間。

咱們知道一個完整的網頁包含了主頁請求和數次或數十次的子請求。HTTP/1.1已經能夠並行發出全部請求.可是HTTP自己是無狀態的協議,它依賴於時間的順序來識別請求和響應直接的對應關係。先來的請求必須先給響應。那麼若是後面的響應資源對瀏覽器構建DOM或者CSSOM更重要。那它必須阻塞等待前者完成。固然這也難不倒咱們,咱們能夠多開幾條tcp鏈接(瀏覽器規定一個origin(協議+host+port)最多6個)或者合併資源來減小沒必要要的阻塞。這是有代價的。首先tcp建連的開銷,其實合併資源帶來一小塊子資源過時致使整個合併資源的緩存過時。對此,h2有一攬子的解決方案,接下來一一道來。

h2在一個tcp鏈接建立多個流。每一個流能夠有從屬關係,好比說根據瀏覽器加載的優先級順序(主請求>CSS>能改變DOM結構的JS文件>圖片和字體資源文件)創建一條依賴關係鏈。處於同一等級的依賴關係中能夠設置權重。權重用於分配傳輸信道資源多少。

圖6例子說明了有一次主頁請求index.html、一次main.css,一次jq.js以及一些image文件和字體文件qq.tff

img

圖三、h2請求的依賴樹

HTML的優先級最高,在HTML傳輸完成以前,其餘文件不會被傳輸。HTML傳輸完成後,JS和CSS根據其分配的權重佔比分配信息傳輸資源。若是CSS傳輸完成後,TFF和PNG若是是相同權重,那麼他們將佔有1/4的信道資源。

這裏拋出3個問題和答案。

  • 若是CSS被阻塞了,那麼 JS 獲得本屬於CSS的通訊資源
  • 若是CSS傳輸完成但沒有被移依賴樹, TFF和PNG繼承CSS的通訊份額 (假設TFF和PNG權重同樣,那麼各分得1/4通訊資源).
  • 若是CSS在依賴數被移除,JS, TFF, PNG平分通訊資源(假設3個權重同樣,那麼三者各分得1/3通訊資源)

第四話、衆星捧月

HTTP2還設計了一系列方案來改善網絡的性能、包括流量控制,HPack壓縮,Server Push。

什麼你說TCP已經有流量控制了,HTTP不是畫蛇添足嗎?沒錯,可是在單條TCP內部,各個流但是沒有流量控制。流量控制使用了Update Frame不斷告知發送方更新發送的窗口大小(上限)。流量控制一個現實用途是阻塞不重要的請求,以騰出更大的通訊資源給重要的請求使用。流量控制是不能夠被關閉的,流量大小能夠設置2的31次方-1(2GB)。不一樣的中間網絡設備有不同的吞吐能力。流控的另外一個用途在於同步全部的中間設備交換機最小的上限。流量窗口初始大小爲65535(2的16次方-1)。

就像世界上的大多數財富彙集在少數人身上同樣。在一份對48,452,989個請求的統計中,如下11個頭佔據了99%的數量,依名次遞減分別是user-agent、accetp-encoding、accept-language、accept、referer、host、connection、cookie、origin、upgrade-inseure-request、content-type。http header的值也有很大類似性,好比說」/index.html「, 「gzip, deflate」。Cookie也攜帶冗餘的信息。

這些都組成了http header大量能夠壓縮的內容。

而在一份GET請求和一個304響應或者content-length不多的響應中,這些頭佔據了很大比例的通訊資源。2016年發佈的一份HTTP報告中,請求頭大約在460bytes,對一個一般的網頁,平均會有140個請求對象。這些頭總共須要63KB。這些量頗有可能會是首屏和頁面加載時間優化的瓶頸。

可能你會說用gzip等壓縮算法這些請求頭,不就完了嗎?的確spdy就這樣幹過,直到2013年BREACH攻擊暴露了gzip壓縮在https應用的安全性,這種攻擊讓攻擊者很容易得到session cookie等數據。因而纔有了HPACK。

HPACK簡單來講就是索引表,包括靜態表和動態表。靜態表由RFC定義,從不改變,靜態表預留了62個表項。每一個鏈接的通訊雙方維護着動態表。

H2協議使用索引號表明http中的name、value或者name-value。假設被索引的是name,value沒有索引,那麼value還能夠用霍夫曼編碼壓縮。

  • 在預約的頭字段靜態映射表 中已經有預約義的 Header Name 和 Header Value值,這時候的二進制數據格式如圖4, 第一位固定爲1, 後面7位爲映射的索引值。圖8的83就是這樣的,83的二進制字節標示1000 0011,抹掉首位就是 3 , 對應的靜態映射表中的method:POST。

img

圖四、index索引name和value

img

圖五、抓包示意

  • 預約的頭字段靜態映射表中有 name,須要設置新值。圖6所示例子,一個指定 path的Header,首字符 爲 44 ,對應的二進制位0100 0100。前兩個字符爲 01 ,Index 爲 4 ,即對應靜態映射表中的 path 頭。第二個字符爲 95對應的二進制位 1001 0101,排除首字符對應的 Value Length 爲 十進制的21。即 算上 44,一共23個字符來記錄這個信息。

img

圖六、index索引name和自定義value

img

圖七、抓包示意

  • 預約的頭字段靜態映射表中沒有 name,須要設置新name和新值。40 的二進制是0100 0000,02 的二進制是0000 0010,後七位的十進制值是 2,86 的二進制是1000 0110,後7位的十進制值是6。

img

圖八、index索引自定義name和自定義value

img

圖九、抓包示意

  • 明確要求該請求頭不作hpack的index

HTTP2.0還有個大殺器是Server Push,Server Push利用閒置的帶寬資源能夠向瀏覽器預推送頁面展現的關鍵資源,Server push有效得下降了頁面加載時間。具體詳情參考筆者的另外一篇文章https://cloud.tencent.com/developer/article/1159626。

第五話、庖丁解牛

HTTP2相比HTTP1更適合計算機執行。可是其二進制特性不易於人腦理解。這一話咱們專門來說講關於http2的調試工具。

  • Chrome(qq瀏覽器)能夠按住F12查看h2協議。圖13所示爲瀏覽器網絡時序圖,列出了具體協議名稱。chrome還能夠在地址欄敲入chrome://net-internals/#http2查看到h2協議細節,如圖11所示。點擊相應的host就能夠看到h2協商過程,如圖12所示。

img

img

圖十、瀏覽器的網絡時序圖

chrome://net-internals/#http2

img

圖十一、chrome調試h2

img

圖十二、h2協商過程

  • wireshark(需和chrome或firefox搭配使用)。設置環境變量SSLKEYLOGFILE=c:\temp\sslkeylog.log。而後在Wireshark->Preferences->Protocols->SSL配置key所在路徑。

img

圖1三、wireshark配置

img

img

img

圖1四、wireshark抓h2包

  • Nghttp2是一個完整的http2協議實現的組件。做者也參與過spdy實現。目前nghttp2庫被不少知名軟件做爲h2協議實現庫使用。另外nghttp2也自帶了h2協議的分析工具。圖18展現了在明文狀態使用upgrade頭升級到h2c。圖19展現了在https基礎上升級到h2。

img

圖1五、明文狀態使用upgrade頭升級到h2c

img

圖1六、展現了在https基礎上升級到h2

Curl的—http2選項(須要和nghttp2一塊兒編譯)

img

img

圖1七、支持h2的curl客戶端調試

  • Github還有些實用的http2工具組件,諸如chrome-http2-log-parser、http2-push-manifest等組件,筆者後續會專門開篇文章介紹這些工具。對於移動端的調試,ios能夠用charles proxy作代理,android須要開發者模式使用移動端的chrome,筆者在移動端使用較少,這裏就不作展開。

第六話、雕欄玉砌

H2怎麼部署呢,目前主流服務端像nginx、apache都已經支持http2,主流的客戶端curl和各類瀏覽器(包括移動端safari和chrome-android)基本也支持http2。代理服務器如ATS、Varnish,Akamai、騰訊雲等CDN服務也支持http2。那麼怎麼把一套網站部署到h2。或者說部署h2網站和以前h1網站有什麼不同?

若是是本身的源站,那麼請確保服務器支持TLS1.2已經RFC7540所要求的加密套件,h2須要保證支持alpn。你可使用ssllabs等網站檢查。對於h2服務器的要求是h2必須瞭解如何設置流的優先級,h2服務器須要支持server push。h2客戶端須要儘可能多的發送請求。

若是你的網站是從http1.x遷移過來的,那麼以前對於http1.x所作的優化可能無任何幫助甚至更差。合併小文件不在須要,由於額外的小文件請求在h2看來只是開銷不多。而且若是大文件的局部更改使得整個大文件緩存失效。在http1.0時代使用多個域名來併發http鏈接,在http2也毫無必要,由於http2天生就是併發的。http1.x作的優化好比說圖片資源文件不使用cookie來減小請求大小,http2的header壓縮功能也減小了這種影響。即便不作這種優化也亦可。像合併css、小圖片帶來的增益在http2.0也是可忽略的。

若是網頁使用第三方網站組件,那麼請儘量減小使用第三方網站組件。第三方網站不能保證支持h2,因此它可能成爲木桶理論的最大短板。

謹慎使用2.0-1.x的部署方案,h2流轉化成h1請求。由於這樣沒法發揮h2性能。

img

圖1八、2.0-1.x的部署方案

CDN代理服務器的h2支持,能夠屏蔽h2強制走tls的代理服務器。如圖19,代理能夠在與各類協議客戶端的網絡環境下,切斷和客戶端的tls鏈接,和服務器新建鏈接。也能夠做爲load balancer,至關於HTTP2.0用戶和HTTP2.x服務器直接通訊。

img

圖1九、帶tls客戶端功能的代理

圖20列舉若是繞過proxy到達h2服務器。此時的proxy至關於tcp轉發的load balance功能的設備。若是該proxy支持tls的alpn協議,那麼它也能夠選擇HTTP代理功能,和h2服務器能夠創建加密鏈接。若是即不支持alpn,也不支持tcp轉發。那麼proxy只能用upgrade升級成h2協議。

img

圖20、通過代理服務器的H2部署方案

第七話、十全九美

HTTP2.0是創建在TCP之上,因此TCP的全部缺點他都有,因此H2能發揮最大性能得益於調優的tcp協議棧。TCP的慢啓動特性,決定h2一開始的併發流量不會太大,TCP以及SSL的握手鍊接也會拖慢h2的首包網絡耗時。QUIC則徹底地拋棄TCP,在UDP基礎上實現了HTTP2的一系列特性。同時作了應用層的如TCP的可靠性保障。同時這些TLS1.3傳輸更快更簡潔。這些都爲HTTP2.0進化到HTTP3.0提供了一些思路。

總結

以上內容都來源於筆者的實踐經驗和理論總結。篇幅所限不能涵蓋各個細節。具體能夠繼續參考RFC7540和RFC7541協議。

問答

沒有「http | https」的網址怎麼實現?

相關閱讀

我是怎麼一步步用go找出壓測性能瓶頸

HTTP/2之服務器推送(Server Push)最佳實踐

低於0.01%的極致Crash率是怎麼作到的?

【每日課程推薦】新加坡南洋理工大學博士,帶你深度學習NLP技術

此文已由做者受權騰訊雲+社區發佈,更多原文請點擊

搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社區

相關文章
相關標籤/搜索