天天看點

HTTP/2深入學習

一、http/2背景

二、http/2特性

三、http/2連接配接過程

四、http/2基石frame

五、其它

HTTP/2深入學習

http/2 采用二進制格式傳輸資料,而非 http/1.x 的文本格式。二進制格式在協定的解析和優化擴充上帶來更多的優勢和可能

http/2 對消息頭采用 hpack 進行壓縮傳輸,能夠節省消息頭占用的網絡的流量。不像 http/1.x 每次請求,都會攜帶大量備援頭資訊,浪費了很多帶寬資源。頭壓縮能夠很好的解決該問題

http/1.x 有個問題叫隊頭堵塞,它是指一個連接配接(connection)一次隻送出一個請求的效率比較高, 多了就會變慢,在應對并發的時候,往往用戶端必須通過建立連接配接池實作,而http/2 複用tcp連接配接,在一個連接配接裡,用戶端和浏覽器都可以同時發送多個請求或回應,而且不用按照順序一一對應,這樣就避免了”隊頭堵塞”

當連接配接建立時,服務端也能夠更快的把資源主動推送給用戶端。例如服務端可以主動把 js 和 css 檔案推送給用戶端,而不需要用戶端解析 html 再發送這些請求

http/2上面每個流都擁有自己的公示的流量視窗,它可以限制另一端發送資料。對于每個流來說,兩端都必須告訴對方自己還有更多的空間來接受新的資料,而在該視窗被擴大前,另一端隻被允許發送這麼多資料

HTTP/2深入學習

上圖詳細展示了用戶端和服務端采用http/2協定通信整個過程,包括建立連接配接、發送接受消息、結束。為了說明上述流程我們先來了解流(stream)、消息(message) 和幀(frame)

stream 處于一個連接配接中的雙向二進制資料流,可以包含一個或者多個 message。

message 一個完整的請求或者響應,包含多個 frame 序列。

frame http/2 通訊中的最小傳輸機關,至少含有一個 frame header,能夠表示它屬于哪一個 stream。

1.首先用戶端會發一個字元串(pri * http/2.0)到服務端,表示即将要通過http/2來通信

2.接着用戶端發送settings幀給服務端,服務端收到後也會傳回settings幀

3.用戶端會發送流控給服務端,同樣服務端會傳回。

4.最終用戶端會發送settings幀确認ack=true給服務端,同樣服務端也會傳回ack=true,到此通道建立完成

5.用戶端發送消息請求的時候,分headers和data幀發送,并且幀裡面帶有streamid來辨識是哪個消息,同樣服務端也會傳回headers和data幀,帶有用戶端請求過來的streamid

6.關閉連接配接發送go_away幀

frame 是 http/2 二進制格式的基礎,基本可以把它了解為它 tcp 裡面的資料包一樣。http/2 之是以能夠有如此多的新特性,正是因為底層資料格式的改變,其格式如下。

HTTP/2深入學習

幀包括以下字段(從左往右):

payloadlength(負載長度3byte)+type(frame類型1byte)+flags(标志位1byte)+streamid(消息id3byte)+framepayload(負載内容payloadlength/byte)

eg.一串二進制資料 00 00 00 04 01 00 00 00 00

我們可以分析出它的負載長度0,幀的類型是4代表settings幀,标志為1表示true,streamid為0,表示是建立連接配接時候的settings 幀内容ack=true

注意:關于幀長度,需要稍加關注: - 0 ~ 2^14(16384)為預設約定長度,所有端點都需要遵守 - 2^14 (16,384) ~ 2^24-1(16,777,215)此區間數值,需要接收方設定settings_max_frame_size參數單獨指派 - 一端接收到的幀長度超過設定上限或幀太小,需要發送frame_size_err錯誤 - 當幀長錯誤會影響到整個連接配接狀态時,須以連接配接錯誤對待之;比如headers,push_promise,continuation,settings,以及幀辨別符不該為0的幀等,都需要如此處理 - 任一端都沒有義務必須使用完一個幀的所有可用空間 - 大幀可能會導緻延遲,針對時間敏感的幀,比如rst_stream, window_update, priority,需要快速發送出去,以免延遲導緻性能等問題

設定幀frame type=4,接收者向發送者通告己方設定,伺服器端在連接配接成功後必須第一個發送的幀。

字段identifier定義了如下參數: - settings_header_table_size (0x1),通知接收者報頭表的位元組數最大值,報頭塊解碼使用;初始值為4096個位元組,預設可不用設定 - settings_enable_push (0x2),0:禁止伺服器推送,1:允許推送;其它值非法,protocol_error錯誤 - settings_max_concurrent_streams (0x3),發送者允許可打開流的最大值,建議值100,預設可不用設定;0值為禁止建立新流 - settings_initial_window_size (0x4),發送端流控視窗大小,預設值2^16-1 (65,535)個位元組大小;最大值為2^31-1個位元組大小,若溢出需要報flow_control_error錯誤 - settings_max_frame_size (0x5),單幀負載最大值,預設為2^14(16384)個位元組,兩端所發送幀都會收到此設定影響;值區間為2^14(16384)-2^24-1(16777215) - settings_max_header_list_size (0x6),發送端通告自己準備接收的報頭集合最大值,即位元組數。此值依賴于未壓縮報頭字段,包含字段名稱、字段值以及每一個報頭字段的32個位元組的開銷等;文檔裡面雖說預設值不受限制,因為受到報頭集合大小不限制的影響,個人認為不要多于2 settings_max_frame_size(即2^142=32768),否則標頭太大,隐患多多

标志位: * ack (0x1),表示接收者已經接收到setting幀,作為确認必須設定此标志位,此時負載為空,否則需要報frame_size_error錯誤

流量控制幀frame type=8,流量控制幀,作用于單個流以及整個連接配接,但隻能影響兩個端點之間傳輸的data資料幀

字段清單: - window size increment,31個比特位無符号自然數,範圍為1-2^31-1(2,147,483,647)個位元組數,表明發送者可以發送的最大位元組數,以及接收者可以接收到的最大位元組數。

頭幀frame type=1,報頭主要載體,請求頭或響應頭,同時呢也用于打開一個流,在流處于打開"open"或者遠端半關閉"half closed (remote)"狀态都可以發送。

字段清單: - pad length:受制于padded标志控制是否顯示,8個比特表示填充的位元組數。 - e:一個比特表示流依賴是否專用,可選項,隻在流優先級priority被設定時有效 - stream dependency:31個比特表示流依賴,隻在流優先級priority被設定時有效 weight:8個比特(一個位元組)表示無符号的自然數流優先級,值範圍自然是(1~256),或稱之為權重。隻在流優先級priority被設定時有效 - header block fragment:報頭塊分片 - padding:填充的位元組,受制于padded标志控制是否顯示,長度由pad length字段決定

所需标志位: end_stream (0x1): 報頭塊為最後一個,意味着流的結束。後續可緊接着continuation幀在目前的流中,需要把continuation幀作為headers幀的一部分對待 end_headers (0x4): 此報頭幀不需分片,完整的一個幀。後續不再需要continuation幀幫忙湊齊。若沒有此标志的header幀,後續幀必須是以continuation幀傳遞在目前的流中,否則接收者需要響應protocol_error類型的連接配接錯誤。 padded (0x8): 需要填充的标志 priority (0x20): 優先級标志位,控制獨立标志位e,流依賴,和流權重。

資料幀 frame type=0

字段: pad length: 一個位元組表示填充的位元組長度。取決于padded标志是否被設定. data: 這裡是應用資料,真正大小需要減去其他字段(比如填充長度和填充内容)長度。 * padding: 填充内容為若幹個0x0位元組,受padded标志控制是否顯示。接收端處理時可忽略驗證填充内容。若驗證,可以對非0x0内容填充回應protocol_error類型連接配接異常。

标志位: end_stream (0x1): 标志此幀為對應标志流最後一個幀,流進入了半關閉/關閉狀态。 padded (0x8): 負載需要填充,padding length + data + padding組成。

服務端推送消息幀 frame type=5

字段清單: - promised stream id,31個比特表示無符号的自然數,為推送保留的流辨別符,後續适用于發送推送資料 - header block fragment,請求頭部字段值,可看做是伺服器端模拟用戶端發起一次資源請求

标志位: end_headers(0x4/00000010),此幀包含完整的報頭塊,不用後面跟随continuation幀了 padded(0x8/00000100),填充開關,決定了下面的pad length和padding是否要填充,具體和headers幀内容一緻

連接配接關閉幀 frame type=7,一端通知對端較為優雅的方式停止建立流。

與http/1比較:

HTTP/2深入學習

目前支援的容器與架構:netty、grpc、tomcat9、nginx

展望未來:http/2定稿不久,但是其發展速度驚人,因為有衆多優越的特性,還有google作為強有力的推手,相信未來網際網路應用http/2将會取代http/1

下面是官方給的一個http/1和http/2例子

<a href="https://http2.akamai.com/demo">https://http2.akamai.com/demo</a>

資料:

<a href="https://github.com/fex-team/http2-spec/blob/master/http2%e4%b8%ad%e8%8b%b1%e5%af%b9%e7%85%a7%e7%89%88%2806-29%29.md">https://github.com/fex-team/http2-spec/blob/master/http2%e4%b8%ad%e8%8b%b1%e5%af%b9%e7%85%a7%e7%89%88%2806-29%29.md</a>

<a href="http://www.cnblogs.com/ghj1976/p/4552583.html">http://www.cnblogs.com/ghj1976/p/4552583.html</a>

<a href="http://www.blogjava.net/yongboy/archive/2015/03/20/423655.html">http://www.blogjava.net/yongboy/archive/2015/03/20/423655.html</a>

繼續閱讀