1.HTTP/2較HTTP/1.1優化亮點
HTTP/2是一個二進制協議,其基於“幀”的結構設計,改進了很多HTTP/1.1痛點問題。下面列舉一些最常被津津樂道的改進之處:
多路複用的流
頭部壓縮
資源優先級和依賴設置
服務器推送
流量控制
重置消息
以上列舉的每一項都值得做深入細緻的研究,這裏就只針對“多路複用”功能的實現進行深入的學習。
2.“多路複用”的原理解析
2.1 什麼是多路複用?
網絡上有一張圖能清晰的解釋這個問題:
HTTP/1.1協議的請求-響應模型大家都是熟悉的,我們用“HTTP消息”來表示一個請求-響應的過程,那麼HTTP/1.1中的消息是“管道串形化”的:只有等一個消息完成之後,才能進行下一條消息;而HTTP/2中多個消息交織在了一起,這無疑提高了“通信”的效率。這就是多路複用:在一個HTTP的連接上,多路“HTTP消息”同時工作。
2.2 爲什麼HTTP/1.1不能實現“多路複用”?
簡單回答就是:HTTP/2是基於二進制“幀”的協議,HTTP/1.1是基於“文本分割”解析的協議。
看一個HTTP/1.1簡單的GET請求例子:
GET / HTTP/1.1
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.9,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Cookie:imooc_uuid=b2076a1d-6a14-4cd5-91b0-17a9a2461cf4; imooc_isnew_ct=1517447702; imooc_isnew=2; zg_did=%7B%22did%22%3A%20%221662d799f3f17d-0afe8166871b85-454c092b-100200-1662d799f4015b%22%7D; loginstate=1; apsid=Y4ZmEwNGY3OTUwMTdjZTk0ZTc4YzBmYThmMDBmZDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDEwNzI4OQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5NTMzNjIzNjVAcXEuY29tAAAAAAAAAAAAAAAAAAAAADBmNmM5MzczZTVjMTk3Y2VhMDE2ZjUxNmQ0NDUwY2IxIDPdWyAz3Vs%3DYj; Hm_lvt_fb538fdd5bd62072b6a984ddbc658a16=1541222935,1541224845; Hm_lvt_f0cfcccd7b1393990c78efdeebff3968=1540010199,1541222930,1541234759; zg_f375fe2f71e542a4b890d9a620f9fb32=%7B%22sid%22%3A%201541297212384%2C%22updated%22%3A%201541297753524%2C%22info%22%3A%201541222929083%2C%22superProperty%22%3A%20%22%7B%5C%22%E5%BA%94%E7%94%A8%E5%90%8D%E7%A7%B0%5C%22%3A%20%5C%22%E6%85%95%E8%AF%BE%E7%BD%91%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1%5C%22%2C%5C%22%E5%B9%B3%E5%8F%B0%5C%22%3A%20%5C%22web%5C%22%7D%22%2C%22platform%22%3A%20%22%7B%7D%22%2C%22utm%22%3A%20%22%7B%7D%22%2C%22referrerDomain%22%3A%20%22%22%2C%22cuid%22%3A%20%22Jph3DQ809OQ%2C%22%7D; PHPSESSID=h5jn68k1fcaadn61bpoqa9hch2; cvde=5be7a057c314b-1; IMCDNS=1
Host:www.imooc.com
Referer:https://www.imooc.com/
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
以上就是HTTP/1.1發送請求消息的文本格式:以換行符分割每一條key:value的內容,解析這種數據用不着什麼高科技,相反的,解析這種數據往往速度慢且容易出錯。“服務端”需要不斷的讀入字節,直到遇到分隔符(這裏指換行符,代碼中可能使用\n或者\r\n表示),這種解析方式是可行的,並且HTTP/1.1已經被廣泛使用了二十多年,這事已經做過無數次了,問題一直都是存在的:
一次只能處理一個請求或響應,因爲這種以分隔符分割消息的數據,在完成之前不能停止解析。
解析這種數據無法預知需要多少內存,這會帶給“服務端”很大的壓力,因爲它不知道要把一行要解析的內容讀到多大的“緩衝區”中,在保證解析效率和速度的前提下:內存該如何分配?
2.3 HTTP/2幀結構設計和多路複用實現
前邊提到:HTTP/2設計是基於“二進制幀”進行設計的,這種設計無疑是一種“高超的藝術”,因爲它實現了一個目的:一切可預知,一切可控。
幀是一個數據單元,實現了對消息的封裝。下面是HTTP/2的幀結構:
幀的字節中保存了不同的信息,前9個字節對於每個幀都是一致的,“服務器”解析HTTP/2的數據幀時只需要解析這些字節,就能準確的知道整個幀期望多少字節數來進行處理信息。我們先來了解一下幀中每個字段保存的信息:
名稱 | 長度 | 描述 |
---|---|---|
Length | 3 字節 | 表示幀負載的長度,默認最大幀大小2^14 |
Type | 1 字節 | 當前幀的類型,下面會做介紹 |
Flags | 1 字節 | 具體幀的標識 |
R | 1 位 | 保留位,不需要設置,否則可能帶來嚴重後果 |
Stream Identifier | 31 位 | 每個流的唯一ID |
Frame Payload | 不固定 | 真實幀的長度,真實長度在Length中設置 |
如果使用HTTP/1.1的話,你需要發送完上一個請求,才能發送下一個;由於HTTP/2是分幀的,請求和響應可以交錯甚至可以複用。
爲了能夠發送不同的“數據信息”,通過幀數據傳遞不同的內容,HTTP/2中定義了10種不同類型的幀,在上面表格的Type字段中可對“幀”類型進行設置。下表是HTTP/2的幀類型:
名稱 | ID | 描述 |
---|---|---|
DATA | 0x0 | 傳輸流的核心內容 |
HEADERS | 0x1 | 包含HTTP首部,和可選的優先級參數 |
PRIORITY | 0x2 | 指示或者更改流的優先級和依賴 |
RST_STREAM | 0x3 | 允許一端停止流(通常是由於錯誤導致的) |
SETTINGS | 0x4 | 協商連接級參數 |
PUSH_PROMISE | 0x5 | 提示客戶端,服務端要推送些東西 |
PING | 0x6 | 測試連接可用性和往返時延(RTT) |
GOAWAY | 0x7 | 告訴另外一端,當前端已結束 |
WINDOW_UPDATE | 0x8 | 協商一端要接收多少字節(用於流量控制) |
CONTINUATION | 0x9 | 用以拓展HEADER數據塊 |
有了以上對HTTP/2幀的瞭解,我們就可以解釋多路複用是怎樣實現的了,不過在這之前我們先來了解“流”的概念:HTTP/2連接上獨立的、雙向的幀序列交換。流ID(幀首部的6-9字節)用來標識幀所屬的流
下面兩張圖分別表示了HTTP/2協議上POST請求數據流“複用”的過程,很容易看的明白:
3. 更多特性與簡介
由於HTTP/2消息中“幀”的設計,客戶端和服務端在通信的過程中能夠彼此瞭解更多的信息。下面再簡單說一下其他幾點比較重要的特性,算是一個學習引導方向吧。
3.1 流量控制(滑窗)
HTTP/2的新特性之一是基於流的流量控制。不同於HTTP/1.1,只要客戶端可以處理,服務端就會盡可能快的發送數據,HTTP/2提供了客戶端調整傳輸速度的能力(服務端也可以)。WINDOW_UPDATE幀用來完成這件事情,每個幀告訴對方,發送方想要接收多少字節,它將發送一個WINDOW_UPDATE幀以指示其更新後的處理字節能力。
3.2 設置資源優先級和依賴關係
流的一個重要特性是可以設置優先級和資源數據的依賴關係。HTTP/2通過流的依賴可以實現這些功能。通過HEADERS幀和PRIORITY幀,客戶端可以明確的告訴服務端它最需要什麼,這是通過聲明依賴關係和權重實現的。
依賴關係爲客戶端提供了一種能力,通過指明某些對象對另外一些對象的依賴,告知服務器哪些資源應該被優先傳輸。
權重讓客戶端告訴服務器如何具體確定具有共同依賴關係對象的優先級。
3.3 服務端推送
《孫子兵法》中有一句名言:“兵馬未到,糧草先行”。服務端推送功能就可以實現這樣一個功能。當頁面還沒有開始請求具體的資源時,服務端就已經把一些資源(像css和js)已經推送到客戶端了。當瀏覽器要渲染頁面時,資源已經在緩存中了,聽起來是一件很酷的事情,實際上也正是這樣。服務端推送是通過PUSH_PROMISE幀實現的,當然其實現的細節是非常複雜的,感興趣的同學可以研究一下。
HTTP/2的“多路複用”問題已經說明白了,還補充了一些新特性的介紹。當然想要深入瞭解HTTP/2的一些原理,有太多太多的內容需要閱讀,實踐。比如“首部壓縮”算法HPACK是HTTP/2的關鍵元素之一,是HTTP/2制定開發組長時間的研究成果,其思想內容也是特別值得學習借鑑的。本節所設計到的東西只是HTTP/2協議中的冰山一角,RFC7540可以幫助你充分了解該協議的方方面面。