java http2_探索HTTP/2: HTTP 2協議簡述(原)

探索HTTP/2: HTTP/2協議簡述

HTTP/2的協議包含著兩個RFC:Hypertext Transfer Protocol Version 2 (RFC7540),即HTTP/2;HPACK: Header Compression for HTTP/2 (RFC7541),即HPACK。RFC7540描述了HTTP/2的語義,RFC7541則描述了用于HTTP/2的頭部壓縮的格式。本文只涉及HTTP/2協議,本系列的后續文章將會涉及HPACK協議。(2016.10.13最后更新)

1. HTTP/2要解決的問題

HTTP/1.0只允許在一個TCP連接中出現一個請求。后來的HTTP/1.1雖然引入了請求流水線,以允許在一個連接中發送多個請求,但這只是部分地解決了請求并發的問題。服務器端在返回響應時,還是必須要按照它接收到的請求的順序進行返回。如果排在前面的響應要消耗較長的時間,那依然會對后面的響應的造成阻塞,亦即線頭阻塞(Head-of-line blocking)。所以,客戶端必須要使用多條連接去發起多個的請求以實現并發,并進而減小延遲。更大的并發會增大服務器的負載,也會占用更大的網絡帶寬。另外,頭部通常會包含有大量的信息,如cookie,而這也會增加網絡傳輸的開銷。

HTTP/2允許在同一個TCP連接中交錯地出現多個請求與響應,亦即多工(Multiplex)。同時,它使用了一個高效的編碼方法對頭部進行壓縮。HTTP/2還允許對請求進行優先級排序,以便讓更為重要的請求得以更快的完成,這會進一步提高性能。HTTP/2還改變了服務器端只能被動地向客戶端返回響應的定式,允許服務器端主動地向客戶端推送數據,這就可以減少客戶端發起請求的數量。

總之,HTTP/2主要是解決性能問題。

2. 發起HTTP/2

HTTP/2會使用與HTTP/1相同的URI scheme,即http和https。而且實現HTTP/2的服務器端也不會使用不同的端口去分別支持HTTP/1和HTTP/2。這樣有利于平滑地從HTTP/1升級到HTTP/2。畢竟目前已部署的絕大部分網絡服務都只支持HTTP/1,當未來它們升級到HTTP/2時,如果換用了不同URI scheme或端口,那么肯定會對客戶端產生極大的影響。但是HTTP/2協議為運行在http和https上的HTTP/2分別定義了兩個不同的標識符:h2c和h2。h2c中的"c"指的是cleartext,即明文。本文后面會使用h2c指代運行在http2上(直接使用TCP)的HTTP/2,而用h2指代運行在https上(使用TLS)的HTTP/2。

那么,支持HTTP/2的客戶端如何知道它所連接的服務器端是否也支持HTTP/2呢?

對于h2c,支持HTTP/2的客戶端可以在發起的請求中使用HTTP/1.1的Upgrade頭部去嘗試要求服務器升級到HTTP/2。該請求的格式如下:

GET / HTTP/1.1

Host: server.example.com

Connection: Upgrade, HTTP2-Settings

Upgrade: h2c

HTTP2-Settings:

HTTP2-Settings是一個經由BASE64編碼過的字符串,其原始內容是客戶端將要發送的SETTINGS幀的載荷,即一些配置參數。

如果服務器端支持HTTP/2,它就響應"101 Switching Protocols",表示可以進行升級。該響應的格式如下:

HTTP/1.1 101 Switching Protocols

Connection: Upgrade

Upgrade: h2c

如果服務器端不支持HTTP/2,則會忽略Upgrade請求頭部,后續依然使用HTTP/1.1。

對于h2,會使用到協議Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension (RFC7301),即TLS-ALPN。該協議允許客戶端和服務器端就使用何種版本的HTTP進行協商。如果TLS-ALPN在現實中運行良好的話,也許某天還會使用該方法去協商使用別的協議。

當客戶端與服務器端都同意使用HTTP/2時,雙方都需要各自發出一個連接序言(Connection Preface)以進行最后的確認。

客戶端在接收到服務器端的"101 Switching Protocols"響應(針對h2c)或TLS連接的第一個應用數據字節(針對h2)之后會立即發出連接序言。該序言的開頭是"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"(其十六進制形式為"0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a")(1),后面必須再跟一個SETTINGS幀,哪怕這個幀是空的。

服務器端的連接序言則由一個SETTINGS幀構成,該幀必須是服務器端在HTTP/2連接中發送的第一個幀。這個SETTINGS幀可以為空,也可以包含一些希望客戶端如何與自己進行通信的必要配置信息。

3. 幀(Frame)

HTTP/2消息使用二進制格式(實際編碼時使用十六進制書寫),相比于文本格式,這樣可以提高消息處理的效率。HTTP/2消息的最小單元為幀,它由頭部與載荷(Payload)組成。每個幀的長度必須是一個或多個8比特位字節(octet,下文將其簡寫為"字節")。

幀頭部依次包含有如下的5個字段:

長度(Length):該字段占用24個比特位,代表幀載荷的長度。該長度是一個24位的無符號整數。

類型(Type):該字段占用8個比特位,代表幀的類型。

標志(Flags):該字段占用8個比特位,代表幀所定義的一個或多個標志。并不是所有的幀都定義了標志。

保留位(R):該字段占用1個比特位,其語義尚未被定義。在讀取幀時,該位需要被忽略;但在發送幀時,該位需要保持為0(0x0)。

流標識符(Stream Identifier):該字段占用31個比特位,代表該幀所在流的標識符。

在頭部之后,緊接著的就是載荷。載荷的結構與內容完全由幀的類型決定,它的長度也是不定的。???? HTTP/2定義了如下10種不同類型的幀。

DATA:用于攜帶一組長度不定的字節。一個或多個DATA可作為請求或響應的載荷。

HEADERS:用于開啟一個流,并可攜帶一個頭部塊片斷。頭部塊指由一個HEADERS/PUSH_PROMISE幀和緊隨它的零到多個CONTINUATION幀組成的集合,因為只有它們才可能攜帶頭部信息。這個集合可被分割成一個或一組字節,這樣的字節被稱為頭部塊片斷。頭部塊中各個特定類型的幀必須緊緊相鄰,不能出現其它類型的幀。

PRIORITY:用于指定發送端建議的流優先級。

RST_STREAM:用于立即終止流。當希望取消一個流或發生錯誤時,就可發送RST_STREAM幀。

SETTINGS:用于攜帶可以影響兩端之間通信方式的配置參數。SETTINGS幀定義了一個ACK標志,用于指示該幀所設置的參數是否已被接收端獲知。當收到一個SETTINGS且其中的ACK標志為0時,接收端必須盡可能快的應用其中已被更新的參數。

PUSH_PROMISE:用于向接收端通知發送端將要創建的流。當接收端接收到該幀時,新的流尚未被發送端創建,但發送端承諾會創建該流。該幀用于實現HTTP/2的重要特性"服務器端推送(Server Push)"。

PING:用于測量發送端與接收端之間的最小往返時間。這與使用眾所周知的ping命令的目的相似,是為了測試某個空閑的連接是否還可用。

GOAWAY:用于發起對連接的關閉,或觸發嚴重的錯誤條件。該幀允許一端,在完成對之前已創建的流的處理的同時,優雅地停止接收新的流。一端在創建新的流,另一端在發送GOAWAY,這兩者之間天然存在著競爭關系。為了就對這種情況,發送端在發送GOAWAY時會讓它攜帶上(該發送端所知曉的)接收端最后創建的流的標識符,當該GOAWAY被發送之后,發送端將會忽視由接收端創建的任何一個標識符比該標識符大的流。

WINDOW_UPDATE:用于流量控制。該幀的載荷由一個單比特保留位和一個31比特位的無符號整數組成。該整數向該幀的接收端指示了其向當前流量控制窗口所能增加傳輸量的值。

CONTINUATION:用于繼續發送頭部塊片斷。只要同一個流中前面的幀是HEADERS,PUSH_PROMISE或CONTINUATION,并且該幀沒有設置END_HEADERS標志,那么可無限量地發送CONTINUATION幀。

部分幀,DATA,HEADERS和PUSH_PROMISE,的載荷中可能包含填白(Padding)。填白在業務上沒有實際的用處,它的出現是基于安全目的。比如,可以用它來擾亂實際數據的長度,以減輕特定的HTTP攻擊。發送端發送的幀的最大長度要尊重接收端設定的SETTINGS_MAX_FRAME_SIZE的值。但該值的范圍要介于2^14至2^24-1個字節之間。

4. 流(Stream)

流是用于在客戶端與服務器端之間進行幀傳送的通道,同一個TCP連接中可以同時有多個流,如下圖所示,

┌────────┐????????? Connection?????????? ┌────────┐

│??????? │ ============================= │??????? │

│??????? │??? ---------------------

│??????? │??? ┌─────┐┌─────────┐┌─┐????? │??????? │

│??????? │??? └─────┘└─────────┘└─┘

│??????? │??? ---------------------????? │??????? │

│ Client │?????????????????????????????? │ Server │

│??????? │??? ----------???????????????? │??????? │

│??????? │??? ┌──┐┌────┐???????????????? │??????? │

│??????? │??? └──┘└────┘???????????????? │??????? │

│??????? │??? ----------???????????????? │??????? │

│??????? │ ============================= │??????? │

└────────┘?????????????????????????????? └────────┘

服務器端和客戶端可以交錯地向同一個連接中的不同流中傳送幀。可以把一個流看作HTTP/1中的一個連接。客戶端與服務器端在同一個流中的交互依然遵循發送請求-等待響應模式。兩端都可以創建新的流,共享對方創建的流,也可以關閉對方創建的流。幀在流中的順序是有意義的,接收端會以接收到的順序去處理幀。

每個流都有一個標識符,是一個31比特位的無符合整數。在同一個連接中,流標識符是唯一的。由客戶端創建的流的標識符為奇數,由服務器創建的流的標識符為偶數。但標識符為0的流可看作連接,用于連接控制信息,創建新的流時不可使用該標識符。同一個連接中的任何一個流的標識符都不可重用,即便這個流已被關閉了。對于長時間沒有中斷的連接,可能會出現標識符不夠用的情況,那時就必須強制創建一個新的連接。???? HTTP/2協議為流的生命周期定義了7種狀態(2):idle,reserved(local),reserved(remote),open,half closed(local),half closed(remote)和closed。當一端接收或發送頭部塊或(幀DATA和HEADERS的)標志RST_STREAM后可使流的狀態發生轉變。

使用流來實現多工就會引起對TCP連接使用的競爭,這會造成流的阻塞。基于幀WINDOW_UPDATE的流量控制方案可以確保相同連接中的流相互之間不會產生破壞性干擾。流量控制可以作用于兩個層面,即單個流或整個連接。只有幀DATA需要遵守流量控制,所有其它的幀所有消耗的空間均不會占用流量控制窗口。HTTP/2協議只是定義了WINDOW_UPDATE幀的結構和語義,協議的實現可以選擇任何適用自己的流量控制算法。

流可以有優先級。客戶端在創建一個新的流時,可在HEADERS中指定優先級權重。在后續任何時間,通過PRIORITY可以改變流的優先級權重。在并發能力有限的情況下,高權重流的幀會被優先傳送。權重的值必須介于1至256之間,默認權重為16。流與流之間還可以有依賴關系,這種關系會組成一棵依賴關系樹。一個流能夠指定自己成為另一個流的子流。這一過程,可以是非排他的,也可以是排他的。非排他性依賴,是指一個流在將自己變成另一個流的子流的過程中,允許另一個流還有別的子流,即允許有自己的兄弟流存在。排他性依賴,指在前述過程中,不允許另一個流還有別的子流。如果另一個流已經有子流了,那么該流會把所有潛在的兄弟流先變成自己的子流,然后再使自己成為另一個流的唯一子流。其實,排他性依賴的作用就是為了能夠打破已有的關系樹,在既成的父子節點中插入新的節點。否則,只能為已有節點添加子節點,那么關系樹將不可能進行重構。所有的流在被創建時,默認成為標識符為0x0的流的子流。在"服務器端推送"中生成的"推送"流將自動地成為生成該推送流的流的子流,其默認權重也為16。

5. 消息交換

5.1 請求/響應交換

HTTP/2沿襲了HTTP/1的語義,即所有的請求與響應語義均得到了保留,盡管傳遞這些語義的語法已經改變了。

一個HTTP/2消息由如下幾個部分組成:

[1]僅對于響應消息,可以包含一個攜帶有1xx響應頭部的頭部塊。該頭部塊由一個HEADERS幀和緊隨它的零到多個CONTINUATION幀組成。

[2]一個頭部塊。該頭部塊由一個HEADERS幀和緊隨它的零到多個CONTINUATION幀組成。

[3]零到多個攜帶有體部(Body)消息的DATA幀。HTTP/1中使用的"分塊(chunked)"體部將不適用于HTTP/2。因為一個體部可由多個DATA幀組成,所以HTTP/2的體部天然就是可分塊的。

[4]一個可能存在的包含著尾部消息的頭部塊。該頭部塊由一個HEADERS幀和緊隨它的零到多個CONTINUATION幀組成。

HTTP/2仍然沿用HTTP/1中的頭部字段,但字段名稱中的字母必須全部為小寫。另外,還將HTTP/1消息開始行(請求中的請求行與響應中的狀態行)中的消息,分解成了若干偽頭部字段,此類字段均以冒號(:)開頭。

HTTP/1請求行格式為"method request-target HTTP-version",對應的HTTP/2偽頭部字段有:method=method和:path=request-target,但HTTP-version無對應字段,默認為HTTP/2。

HTTP/1狀態行格式為"HTTP-version status-code reason-phrase",對應的HTTP/2偽頭部字段有:status=status-code。但HTTP-version無對應字段,默認為HTTP/2;reason-phrase也無對應字段,因為可以通過狀態代碼查找到其對應的reason-phrase。HTTP/2協議是在盡量減少冗余消息。

HTTP/2協議還為請求頭部定義了另外兩個偽字段:

:scheme:URI中的scheme部分。它可以不僅僅是http或https,因為有時候可能會與非HTTP服務進行交互。

:authority:URI中的授權部分。即,scheme://user:password@host:port/path?query#fragment中的"user:password@host:port"。

HTTP/2協議8.1.3節中給出一些簡單示例,展示了如何將HTTP/1消息對應到HTTP/2消息。

5.2 服務器端推送

HTTP/2的服務器端推送是傳統的請求/響應模式的一種特殊形式。服務器端在收到客戶端的請求(主請求)之后,為了主動向客戶端推送更多的內容,會自動地生成若干新的請求(推送請求)。服務器向客戶端發送的響應中,不僅包含對主請求的響應(主響應),還包含對推送請求的響應(推送響應)。

客戶端可以通過發送包含有SETTINGS_MAX_CONCURRENT_STREAMS參數的SETTINGS幀去禁用服務器端推送,也可以通過發送RST_STREAM幀去取消已經發起的服務器端推送,但不能發送包含有END_STREAM標志的幀。

(1)"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"中的"PRI"與"SM"合起來就是"RRISM(棱鏡)"。呵呵,HTTPbis工作組這是想表達什么意思呢 ;-)

(2)本系列的后續文章解讀了流的狀態。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/388812.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/388812.shtml
英文地址,請注明出處:http://en.pswp.cn/news/388812.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

錯誤處理

錯誤處理: 許多系統調用和函數在失敗后,會在失敗時設置外部變量errno的值來指明失敗原因。許多不同的函數庫都把這個變量作為報告錯誤的標準方法。程序必須在函數報告出錯后立刻檢查errno變量,因為它可能被下一個函數調用所覆蓋&#xff…

Android類庫介紹

Android類庫介紹 GPhone開發包Android SDK含了很多豐富的類庫: android.util 涉及系統底層的輔助類庫 android.os 提供了系統服務、消息傳輸、IPC管道 android.graphics GPhone圖形庫,包含了文本顯示、輸入輸出、文字樣式 android.database 包含底層的AP…

遞歸函數基例和鏈條_鏈條和叉子

遞歸函數基例和鏈條因果推論 (Causal Inference) This is the fifth post on the series we work our way through “Causal Inference In Statistics” a nice Primer co-authored by Judea Pearl himself.這是本系列的第五篇文章,我們通過“因果統計推斷”一書進行…

前端技能拾遺

本文主要是對自己前端知識遺漏點的總結和歸納,希望對大家有用,會持續更新的~ 解釋語言和編譯型語言 解釋型語言與編譯型語言的區別翻譯時間的不同。 編譯型語言在程序執行之前,有一個單獨的編譯過程,將程序翻譯成機器語言&#xf…

java lock 信號_java各種鎖(ReentrantLock,Semaphore,CountDownLatch)的實現原理

先放結論:主要是實現AbstractQueuedSynchronizer中進入和退出函數,控制不同的進入和退出條件,實現適用于各種場景下的鎖。JAVA中對于線程的同步提供了多種鎖機制,比較著名的有可重入鎖ReentrantLock,信號量機制Semapho…

Intent.ACTION_MAIN

1 Intent.ACTION_MAIN String: android.intent.action.MAIN 標識Activity為一個程序的開始。比較常用。 Input:nothing Output:nothing 例如&#xff1a; 1 <activity android:name".Main"android:label"string/app_name">2 <intent-filter…

足球預測_預測足球熱

足球預測By Aditya Pethe通過阿蒂亞皮特(Aditya Pethe) From September to January every year, football takes over America. Games dominate TV Sunday and Monday nights, and my brother tears his hair out each week over his consistently underperforming fantasy te…

C#的特性Attribute

一、什么是特性 特性是用于在運行時傳遞程序中各種元素&#xff08;比如類、方法、結構、枚舉、組件等&#xff09;的行為信息的聲明性標簽&#xff0c;這個標簽可以有多個。您可以通過使用特性向程序添加聲明性信息。一個聲明性標簽是通過放置在它所應用的元素前面的方括號&am…

java 技能鑒定_JAVA試題-技能鑒定

一、單選題1.以下創建了幾個對象( B)String A,B,CA"a";B"b":AAB;StringBuffer Dnew StringBuffer("abc");DD.append("567");A.6B.4C.3D.52.關于以下程序段&#xff0c;正確的說法是( C )1&#xff0e;String s1“a”“b”;2&#xff0…

ADD_SHORTCUT_ACTION

String ADD_SHORTCUT_ACTION 動作&#xff1a;在系統中添加一個快捷方式。. “android.intent.action.ADD_SHORTCUT”   String ALL_APPS_ACTION 動作&#xff1a;列舉所有可用的應用。   輸入&#xff1a;無。 “android.intent.action.ALL_APPS”   String ALTERNATIVE…

python3中樸素貝葉斯_貝葉斯統計:Python中從零開始的都會都市

python3中樸素貝葉斯你在這里 (You are here) If you’re reading this, odds are: (1) you’re interested in bayesian statistics but (2) you have no idea how Markov Chain Monte Carlo (MCMC) sampling methods work, and (3) you realize that all but the simplest, t…

java映射的概念_Java 反射 概念理解

文章來源:http://hollischuang.gitee.io/tobetopjavaer/#/basics/java-basic/reflection反射反射機制指的是程序在運行時能夠獲取自身的信息。在java中&#xff0c;只要給定類的名字&#xff0c;那么就可以通過反射機制來獲得類的所有屬性和方法。反射有什么作用在運行時判斷任…

【轉載】移動端布局概念總結

布局準備工作及布局思想及概念: 一個顯示器&#xff08;pc端顯示器 及 手機屏顯示器&#xff09;&#xff0c;既有物理像素&#xff0c;又有獨立像素&#xff08;獨立像素也叫作css像素&#xff0c;用于前端人員使用&#xff09;&#xff1b; -->重要 首先確定設計稿的尺寸…

深入淺出:HTTP/2

上篇文章深入淺出&#xff1a;5G和HTTP里給自己挖了一根深坑&#xff0c;說是要寫一篇關于HTTP/2的文章&#xff0c;今天來還賬了。 本文分為以下幾個部分&#xff1a; HTTP/2的背景HTTP/2的特點HTTP/2的協議分析HTTP/2的支持 HTTP/2簡介 HTTP/2主要是為了解決現HTTP 1.1性能不…

畫了個Android

畫了個Android 今晚瞎折騰&#xff0c;閑著沒事畫了個機器人——android&#xff0c;浪費了一個晚上的時間。畫這丫還真不容易&#xff0c;為那些坐標&#xff0c;差點砸了鍵盤&#xff0c;好在最后畫出個有模有樣的&#xff0c;心稍安。 下面來看看畫這么個機器人需要些什么東…

數據治理 主數據 元數據_我們對數據治理的誤解

數據治理 主數據 元數據Data governance is top of mind for many of my customers, particularly in light of GDPR, CCPA, COVID-19, and any number of other acronyms that speak to the increasing importance of data management when it comes to protecting user data.…

mysql 選擇前4個_mysql從4個表中選擇

不要認為GROUP BY是必需的 . 雖然如果一個孩子有2個父記錄&#xff0c;你可能想用它來將2個父母分組到一行 - 但不確定這是否是你的要求 . 因為如果一個孩子有2個父母&#xff0c;那么將為該孩子返回的父母是未定義的 .假設所有孩子都有父母&#xff0c;所有父母都會有姓&#…

提高機器學習質量的想法_如何提高機器學習的數據質量?

提高機器學習質量的想法The ultimate goal of every data scientist or Machine Learning evangelist is to create a better model with higher predictive accuracy. However, in the pursuit of fine-tuning hyperparameters or improving modeling algorithms, data might …

mysql 集群實踐_MySQL Cluster集群探索與實踐

MySQL集群是一種在無共享架構(SNA&#xff0c;Share Nothing Architecture)系統里應用內存數據庫集群的技術。這種無共享的架構可以使得系統使用低廉的硬件獲取高的可擴展性。MySQL集群是一種分布式設計&#xff0c;目標是要達到沒有任何單點故障點。因此&#xff0c;任何組成部…

Python基礎:搭建開發環境(1)

1.Python語言簡介 2.Python環境 Python環境產品存在多個。 2.1 CPython CPython是Python官方提供的。一般情況下提到的Python就是指CPython&#xff0c;CPython是基于C語言編寫的。 CPython實現的解釋器將源代碼編譯為字節碼&#xff08;ByteCode&#xff09;&#xff0c;再由虛…