亚洲精品中文免费|亚洲日韩中文字幕制服|久久精品亚洲免费|一本之道久久免费

      
      

            <dl id="hur0q"><div id="hur0q"></div></dl>

                HTTP 2.0 為什么這么設(shè)計(jì)

                HTTP 1.0 是 1996 年發(fā)布的,奠定了 web 的基礎(chǔ)。時(shí)隔三年,1999 年又發(fā)布了 HTTP 1.1,對(duì)功能上做了擴(kuò)充。之后又時(shí)隔十六年,2015 年發(fā)布了 HTTP 2.0。

                同學(xué)們肯定會(huì)覺得,隔了這么長時(shí)間,而且還從版本號(hào)還從 1 到了 2,那肯定有很多的新功能。其實(shí)不是的,HTTP 2.0 沒有沒有功能上的新增,只是優(yōu)化了性能。

                為什么要這么大的版本升級(jí)來優(yōu)化性能,HTTP 1.1 的性能很差么?

                那我們就來看下 HTTP 1.1 有什么問題:

                HTTP 1.1 的問題

                我們知道,HTTP 的下層協(xié)議是 TCP,需要經(jīng)歷三次握手才能建立連接。而 HTTP 1.0 的時(shí)候一次請(qǐng)求和響應(yīng)結(jié)束就會(huì)斷開鏈接,這樣下次請(qǐng)求又要重新三次握手來建立連接。

                圖片

                為了減少這種建立 TCP 鏈接的消耗,HTTP 1.1 支持了 keep-alive,只要請(qǐng)求或響應(yīng)頭帶上 Connection: keep-alive,就可以告訴對(duì)方先不要斷開鏈接,我之后還要用這個(gè)鏈接發(fā)消息。當(dāng)需要斷開的時(shí)候,再指定 Connection: close 的 header。

                這樣就可以用同一個(gè) TCP 鏈接進(jìn)行多次 HTTP 請(qǐng)求響應(yīng)了:

                圖片

                但這樣雖然減少了鏈接的建立,在性能上卻有問題,下次請(qǐng)求得等上一個(gè)請(qǐng)求返回響應(yīng)才能發(fā)出。

                這個(gè)問題有個(gè)名字,叫做隊(duì)頭阻塞,很容易理解,因?yàn)槎鄠€(gè)請(qǐng)求要排隊(duì)嘛,隊(duì)前面的卡住了,那后面的也就執(zhí)行不了了。

                怎么解決這個(gè)問題呢?

                HTTP 1.1 提出了管道的概念,就是多個(gè)請(qǐng)求可以并行發(fā)送,返回響應(yīng)后再依次處理。

                也就是這樣:

                圖片

                其實(shí)這樣能部分解決問題,但是返回的響應(yīng)依然要依次處理,解決不了隊(duì)頭阻塞的問題。

                所以說管道化是比較雞肋的一個(gè)功能,現(xiàn)在絕大多數(shù)瀏覽器都默認(rèn)關(guān)閉了,甚至都不支持。

                那還能怎么解決這個(gè)隊(duì)頭阻塞的問題呢?

                開多個(gè)隊(duì)不就行了。

                瀏覽器一般會(huì)同一個(gè)域名建立 6-8 個(gè) TCP 鏈接,也就是 6-8 個(gè)隊(duì),如果一個(gè)隊(duì)發(fā)生隊(duì)頭阻塞了,那就放到其他的隊(duì)里。

                這樣就緩解了隊(duì)頭阻塞問題。

                我們寫的網(wǎng)頁想盡快的打開就要利用這一點(diǎn),比如把靜態(tài)資源部署在不同的域名下。這樣每個(gè)域名都能并發(fā) 6-8 個(gè)下載請(qǐng)求,網(wǎng)頁打開的速度自然就會(huì)快很多。

                這種優(yōu)化手段叫做“域名分片”,CDN 一般都支持這個(gè)。

                除了隊(duì)頭阻塞的問題,HTTP 1.1 還有沒有別的問題?

                有,比如 header 部分太大了。

                不知道大家有沒有感覺,就算你內(nèi)容只傳輸幾個(gè)字符,也得帶上一大堆 header:

                圖片

                而且這些 header 還都是文本的,這樣占據(jù)的空間就格外的大。

                比如,如果是二進(jìn)制,表示 true 和 false 直接 1 位就行了,而文本的那就得經(jīng)過編碼,“true” 就占了 4 個(gè)字節(jié),也就是 32 位。那就是 32 倍的差距呀!

                所以呢,HTTP 1.1 的時(shí)候,我們就要盡量避免一些小請(qǐng)求,因?yàn)榫退阏?qǐng)求的內(nèi)容很少,也會(huì)帶上一大段 header。特別是有 cookie 的情況,問題格外明顯。

                因此,我們的網(wǎng)頁就要做打包,也就是需要打包工具把模塊合并成多個(gè) chunk 來加載。需要把小圖片合并成大圖片,通過調(diào)整 background:position 來使用。需要把一些 css、圖片等內(nèi)聯(lián)。而且靜態(tài)資源的域名也要禁止攜帶 cookie。

                這些都是為了減少請(qǐng)求次數(shù)來達(dá)到提高加載性能的目的。

                而且 HTTP 的底層是 TCP,其實(shí)是可以雙向傳輸數(shù)據(jù)的,現(xiàn)在卻只能通過請(qǐng)求—響應(yīng)這種一問一答的方式,并沒有充分利用起 TCP 的能力。

                聊了這么多,不知道大家是否有優(yōu)化它的沖動(dòng)了。

                也就是因?yàn)檫@些問題,HTTP 2.0 出現(xiàn)了,做了很多性能優(yōu)化,基本解決了上面那些問題。

                那 HTTP2 都做了哪些優(yōu)化呢?

                HTTP 2.0 的優(yōu)化

                先不著急看 HTTP 2.0 是怎么優(yōu)化的,就上面那些問題來說,如果讓我們解決,我們會(huì)怎么解決?

                比如隊(duì)頭阻塞的問題,也就是第二個(gè)響應(yīng)要等第一個(gè)響應(yīng)處理完之后才能處理。怎么解決?

                這個(gè)很容易解決呀,每個(gè)請(qǐng)求、響應(yīng)都加上一個(gè) ID,然后每個(gè)響應(yīng)和通過 ID 來找到它對(duì)應(yīng)的請(qǐng)求。各回各家,自然就不用阻塞的等待了。

                再比如說 header 過大這個(gè)問題,怎么解決?

                文本傳輸太占空間,換成二進(jìn)制的是不是會(huì)好很多。

                還有,每次傳輸都有很多相同的 header,能不能建立一張表,傳的時(shí)候只傳輸下標(biāo)就行了。

                還有,body 可以壓縮,那 header 是不是可以壓縮。

                這樣處理之后,應(yīng)該會(huì)好很多。

                那沒有充分利用 TCP 的能力,只支持請(qǐng)求–響應(yīng)的方式呢?

                那就支持服務(wù)端主動(dòng)推送呀,但是客戶端可以選擇接收或者不接收。

                上面是我們對(duì)這些問題的解決方案的思考,我們?cè)賮砜纯?HTTP2 是怎么解決這些問題的:

                HTTP2 確實(shí)是通過 ID 把請(qǐng)求和響應(yīng)關(guān)聯(lián)起來了,它把這個(gè)概念叫做流 stream。

                而且我們之前說了 header 需要單獨(dú)的優(yōu)化嘛,所以把 header 和 body 部分分開來傳送,叫做不同的幀 frame。

                每個(gè)幀都是這樣的格式:

                圖片

                payload 部分是傳輸?shù)膬?nèi)容這沒啥可說的。

                header 部分最開始是長度,然后是這個(gè)幀的類型,有這樣幾種類型:

                SETTINGS 幀:配置信息,比如最大幀 size,是否支持 server push 等。

                HEADERS 幀:請(qǐng)求或響應(yīng)的 header

                DATA 幀:請(qǐng)求或響應(yīng)的 body

                CONTINUATION 幀:一個(gè)幀不夠裝的時(shí)候,可以分幀,用這個(gè)可以引用上一個(gè)幀。

                PUSH_PROMISE 幀:服務(wù)端推送數(shù)據(jù)的幀

                END_STREAM 幀:表示流傳輸結(jié)束

                RST_STREAM 幀,用來終止當(dāng)前流

                這幾種幀里面 HEADERS 和 DATA 幀沒啥可說的。

                SETTING 幀是配置信息,先告訴對(duì)方我這里支持什么,幀大小設(shè)置為多大等。

                幀大小是有個(gè)上限的,如果幀太大了,可以分成多個(gè),這時(shí)候幀類型就是 CONTINUATION(繼續(xù))。也很容易理解。

                HTTP2 確實(shí)是支持服務(wù)端推送的,這時(shí)候幀類型也是單獨(dú)的,叫做 PUSH_PROMISE。

                流是用來傳輸請(qǐng)求響應(yīng)或者服務(wù)端推送的,那傳輸完畢的時(shí)候就可以發(fā)送 END_STREAM 幀來表示傳輸完了,然后再傳輸 RST_STREAM 來結(jié)束當(dāng)前流。

                幀的類型講完了,我們繼續(xù)往后看,后面還有個(gè) flags 標(biāo)志位,這個(gè)在不同的幀類型里會(huì)放不同的內(nèi)容:

                圖片

                比如 header 幀會(huì)在 flags 中設(shè)置優(yōu)先級(jí),這樣高優(yōu)先級(jí)的流就可以更早的被處理。

                HTTP 1.1 的時(shí)候都是排隊(duì)處理的,沒什么優(yōu)先級(jí)可言,而 HTTP 2.0 通過流的方式實(shí)現(xiàn)了請(qǐng)求的并發(fā),那自然就可以控制優(yōu)先級(jí)了。

                后面還有個(gè) R,這個(gè)現(xiàn)在還沒啥用,是一個(gè)保留的位。

                再后面的流標(biāo)識(shí)符就是 stream id 了,關(guān)聯(lián)同一個(gè)流的多個(gè)幀用的。

                幀的格式講完了,大家是不是有點(diǎn)暈暈的。確實(shí),幀還是有很多種的。這些幀之間發(fā)送順序也不同,不同的幀會(huì)在不同狀態(tài)下發(fā)送,也會(huì)改變流的狀態(tài)。

                我們來看下流的狀態(tài)機(jī),也就是流收到什么幀會(huì)進(jìn)入什么狀態(tài),并且在什么狀態(tài)下會(huì)發(fā)送什么幀:

                (看不明白可以先往后看)圖片

                剛開始,流是 idle 狀態(tài),也就是空閑。

                收到或發(fā)送 HEADERS 幀以后會(huì)進(jìn)入 open 狀態(tài)。

                oepn 狀態(tài)下可以發(fā)送或接收多次 DATA 幀。

                之后發(fā)送或接收 END_STREAM 幀進(jìn)入 half_closed 狀態(tài)。

                half_closed 狀態(tài)下收到或者發(fā)送 RST_STREAM 幀就關(guān)閉流。

                這個(gè)流程很容易理解,就是先發(fā)送 HEADER,再發(fā)送 DATA,之后告訴對(duì)方結(jié)束,也就是 END_STREAM,然后關(guān)閉 RST_STREAM。

                圖片

                但是 HTTP2 還可以服務(wù)端推送呀,所以還有另一條狀態(tài)轉(zhuǎn)換流程。

                流剛開始是 idle 狀態(tài)。

                接收到 PUSH_PROMISE 幀,也就是服務(wù)端推送過來的數(shù)據(jù),變?yōu)?reserved 狀態(tài)。

                reserved 狀態(tài)可以再發(fā)送或接收 header,之后進(jìn)入 half_closed 狀態(tài)。

                后面的流程是一樣的,也是 END_STREAM 和 RST_STREAM。

                這個(gè)流程是 HTTP2 特有的,也就是先推送數(shù)據(jù),再發(fā)送 headers,然后結(jié)束流。

                圖片

                這就是 http2 發(fā)送一次請(qǐng)求、響應(yīng),或者一次服務(wù)端推送的流程,都是封裝在一個(gè)個(gè)流里面的。

                流和流之間可以并發(fā),還可以設(shè)置優(yōu)先級(jí),這樣自然就沒有了隊(duì)頭阻塞的問題,這個(gè)特性叫做多路復(fù)用。也就是復(fù)用同一個(gè)鏈接,建立起多條通路(流)的意思。

                而且傳輸?shù)?header 幀也是經(jīng)過處理的,就像我們前面說的,會(huì)用二進(jìn)制的方式表示,用做壓縮,而且壓縮算法是專門設(shè)計(jì)的,叫做 HPACK:

                兩端會(huì)維護(hù)一個(gè)索引表,通過下標(biāo)來標(biāo)識(shí) header,這樣傳輸量就少了不少:

                圖片

                首先,header 里其實(shí)不止有 header,還有一行 GET xxx/xxx 的請(qǐng)求行,和 200 xxx 的響應(yīng)行,為了統(tǒng)一處理,就換成了 :host :path 等 header 來表示。

                這樣發(fā)送的時(shí)候只需要發(fā)送下標(biāo)就行:

                圖片

                比如 :method: get 就只需要發(fā)送個(gè) 2: get。

                這個(gè)編碼也是根據(jù)頻率高低來設(shè)置的,頻率高的用小編碼,這種方式叫做哈夫曼編碼。

                這樣就實(shí)現(xiàn)了 header 的壓縮。

                至此, HTTP2.0 的主要特性就講完了,也就是多路復(fù)用,服務(wù)端推送,頭部壓縮,二進(jìn)制傳輸。

                最主要的特性是多路復(fù)用,也就是流和幀,流在什么狀態(tài)下發(fā)送什么幀。其他的特性是圍繞這個(gè)來設(shè)計(jì)的。

                回過頭來看一下 HTTP1.1 的問題是否都得到了解決:

                隊(duì)頭阻塞:通過流的來標(biāo)識(shí)請(qǐng)求、響應(yīng),同一個(gè)流的分為多個(gè)幀來傳輸,多個(gè)流之間可以并發(fā),不會(huì)相互阻塞。

                header 太大:通過二進(jìn)制的形式,加上 HPACK的壓縮算法,使得 header 減小了很多。

                沒有充分利用 TCP 的特性:支持了服務(wù)端推送。

                這樣看來,HTTP2.0 確實(shí)解決了 HTTP 1.1 的問題。

                看起來,HTTP 2.0 已經(jīng)很完美了?

                其實(shí)不是的,雖然 HTTP 層面沒有了隊(duì)頭阻塞問題,多個(gè)請(qǐng)求響應(yīng)可以并行處理。但是同一個(gè)流的多個(gè)幀還是有隊(duì)頭阻塞問題,以為你 TCP 層面會(huì)保證順序處理,丟失了會(huì)重傳,這就導(dǎo)致了上一個(gè)幀沒收到的話,下一個(gè)幀是處理不了的。

                這個(gè)問題是 TCP 的可靠傳輸?shù)奶匦詭淼?,所以想徹底解決隊(duì)頭阻塞問題,只能把 HTTP 的底層傳輸協(xié)議換掉了。

                這就是 HTTP3 做的事情了,它的傳輸層協(xié)議換成了 UDP。當(dāng)然,現(xiàn)在 HTTP3 還不是很成熟,我們先重點(diǎn)關(guān)注 HTTP2 即可。

                總結(jié)

                1996 年發(fā)布 HTTP 1.0,1999 年 HTTP 1.1,2015 年 HTTP 2.0。

                1.1 和 2 之間間隔了 16 年,確實(shí)改變了很多,但只是性能方面的。

                1.1 的問題是第二個(gè)請(qǐng)求要等第一個(gè)響應(yīng)之后才能發(fā)出,就算用了管道化,多個(gè)響應(yīng)之間依然也會(huì)阻塞,這就是“隊(duì)頭阻塞”問題。

                而且 header 部分太大了,還是純文本的,可能比 body 部分傳的都多。

                針對(duì) 1.1 的隊(duì)頭阻塞問題,我們會(huì)做域名分片,針對(duì) header 過大的問題,我們會(huì)減少請(qǐng)求次數(shù),也就是打包分 chunk、資源內(nèi)聯(lián)、雪碧圖、靜態(tài)資源請(qǐng)求禁止 cookie 等優(yōu)化策略。

                HTTP 2.0 解決了 1.1 的這些問題,通過多路復(fù)用,也就是請(qǐng)求和響應(yīng)在一個(gè)流里,通過同一個(gè)流 id 來關(guān)聯(lián)多個(gè)幀的方式來傳輸數(shù)據(jù)。多個(gè)流可以并發(fā)。

                我們看了幀的格式,有長度、類型、stream id、falgs 還有 payload 等部分。

                幀的類型還是挺多的,有 HEADRS、DATA、SETTINGS、PUSH_PROMISE、END_STREAM、EST_STREAM、等。

                這些幀類型之間也不是毫無關(guān)聯(lián)的,流在不同的狀態(tài)下會(huì)發(fā)送、接收不同的幀,而且發(fā)送、接收不同的幀也會(huì)進(jìn)入不同的狀態(tài)。

                理解 HTTP2.0 的 stream 就要理解這樣的一個(gè)狀態(tài)流轉(zhuǎn)流程。

                此外,HTTP 2.0 通過單獨(dú)設(shè)計(jì)的 HPACK 算法對(duì) header 做了壓縮,也支持服務(wù)端推送。而且內(nèi)容是通過二進(jìn)制傳輸?shù)模鉀Q了 HTTP 1.1 的問題。

                但是 HTTP 2.0 的底層是 TCP,它的可靠傳輸?shù)奶匦允沟猛粋€(gè)流內(nèi)的多個(gè)幀依然是順序傳輸?shù)?,依然有?duì)頭阻塞問題。也是因?yàn)?HTP 3把底層協(xié)議換成 UDP。

                雖然還是有一些問題,但 HTTP 2.0 已經(jīng)基本上把 HTTP 1.1 的各方面性能不好的點(diǎn)都優(yōu)化到了極致,是很有意義的一次版本升級(jí)。

                鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
                用戶投稿
                上一篇 2022年6月29日 11:13
                下一篇 2022年6月29日 11:13

                相關(guān)推薦

                聯(lián)系我們

                聯(lián)系郵箱:admin#wlmqw.com
                工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息