1.5 万字 + 40 张图解 HTTP 常见面试题(值得收藏)
作者:小林coding
圖解計(jì)算機(jī)基礎(chǔ)網(wǎng)站:https://xiaolincoding.com
大家好,我是小林,我最開(kāi)始寫(xiě)的第一篇圖解文章就是這篇:
那時(shí)候我也就不到 100 讀者,如今這篇閱讀都快 2 萬(wàn)了。
當(dāng)時(shí)這篇有些地方?jīng)]有解釋到位,然后我周末抽時(shí)間把一些沒(méi)解釋清楚的地方重寫(xiě)了,而且還增加 HTTP 緩存技術(shù) 方面的面試題,文章的內(nèi)容相比以前多了 5000 +字和 10 +張圖。
不多說(shuō)了,發(fā)車(chē)發(fā)車(chē)!
HTTP 基本概念
HTTP 是什么?
HTTP 是超文本傳輸協(xié)議,也就是HyperText Transfer Protocol。
能否詳細(xì)解釋「超文本傳輸協(xié)議」?
HTTP的名字「超文本協(xié)議傳輸」,它可以拆成三個(gè)部分:
- 超文本
- 傳輸
- 協(xié)議
1. 「協(xié)議」
在生活中,我們也能隨處可見(jiàn)「協(xié)議」,例如:
- 剛畢業(yè)時(shí)會(huì)簽一個(gè)「三方協(xié)議」;
- 找房子時(shí)會(huì)簽一個(gè)「租房協(xié)議」;
生活中的協(xié)議,本質(zhì)上與計(jì)算機(jī)中的協(xié)議是相同的,協(xié)議的特點(diǎn):
- 「協(xié)」字,代表的意思是必須有兩個(gè)以上的參與者。例如三方協(xié)議里的參與者有三個(gè):你、公司、學(xué)校三個(gè);租房協(xié)議里的參與者有兩個(gè):你和房東。
- 「議」字,代表的意思是對(duì)參與者的一種行為約定和規(guī)范。例如三方協(xié)議里規(guī)定試用期期限、毀約金等;租房協(xié)議里規(guī)定租期期限、每月租金金額、違約如何處理等。
針對(duì) HTTP 協(xié)議,我們可以這么理解。
HTTP 是一個(gè)用在計(jì)算機(jī)世界里的協(xié)議。它使用計(jì)算機(jī)能夠理解的語(yǔ)言確立了一種計(jì)算機(jī)之間交流通信的規(guī)范(兩個(gè)以上的參與者),以及相關(guān)的各種控制和錯(cuò)誤處理方式(行為約定和規(guī)范)。
2. 「?jìng)鬏敗?/p>
所謂的「?jìng)鬏敗?#xff0c;很好理解,就是把一堆東西從 A 點(diǎn)搬到 B 點(diǎn),或者從 B 點(diǎn) 搬到 A 點(diǎn)。
別輕視了這個(gè)簡(jiǎn)單的動(dòng)作,它至少包含兩項(xiàng)重要的信息。
HTTP 協(xié)議是一個(gè)雙向協(xié)議。
我們?cè)谏暇W(wǎng)沖浪時(shí),瀏覽器是請(qǐng)求方 A ,百度網(wǎng)站就是應(yīng)答方 B。雙方約定用 HTTP 協(xié)議來(lái)通信,于是瀏覽器把請(qǐng)求數(shù)據(jù)發(fā)送給網(wǎng)站,網(wǎng)站再把一些數(shù)據(jù)返回給瀏覽器,最后由瀏覽器渲染在屏幕,就可以看到圖片、視頻了。
數(shù)據(jù)雖然是在 A 和 B 之間傳輸,但允許中間有中轉(zhuǎn)或接力。
就好像第一排的同學(xué)想傳遞紙條給最后一排的同學(xué),那么傳遞的過(guò)程中就需要經(jīng)過(guò)好多個(gè)同學(xué)(中間人),這樣的傳輸方式就從「A < — > B」,變成了「A <-> N <-> M <-> B」。
而在 HTTP 里,需要中間人遵從 HTTP 協(xié)議,只要不打擾基本的數(shù)據(jù)傳輸,就可以添加任意額外的東西。
針對(duì)傳輸,我們可以進(jìn)一步理解了 HTTP。
HTTP 是一個(gè)在計(jì)算機(jī)世界里專(zhuān)門(mén)用來(lái)在兩點(diǎn)之間傳輸數(shù)據(jù)的約定和規(guī)范。
3. 「超文本」
HTTP 傳輸?shù)膬?nèi)容是「超文本」。
我們先來(lái)理解「文本」,在互聯(lián)網(wǎng)早期的時(shí)候只是簡(jiǎn)單的字符文字,但現(xiàn)在「文本」的涵義已經(jīng)可以擴(kuò)展為圖片、視頻、壓縮包等,在 HTTP 眼里這些都算作「文本」。
再來(lái)理解「超文本」,它就是超越了普通文本的文本,它是文字、圖片、視頻等的混合體,最關(guān)鍵有超鏈接,能從一個(gè)超文本跳轉(zhuǎn)到另外一個(gè)超文本。
HTML 就是最常見(jiàn)的超文本了,它本身只是純文字文件,但內(nèi)部用很多標(biāo)簽定義了圖片、視頻等的鏈接,再經(jīng)過(guò)瀏覽器的解釋,呈現(xiàn)給我們的就是一個(gè)文字、有畫(huà)面的網(wǎng)頁(yè)了。
OK,經(jīng)過(guò)了對(duì) HTTP 里這三個(gè)名詞的詳細(xì)解釋,就可以給出比「超文本傳輸協(xié)議」這七個(gè)字更準(zhǔn)確更有技術(shù)含量的答案:
HTTP 是一個(gè)在計(jì)算機(jī)世界里專(zhuān)門(mén)在「兩點(diǎn)」之間「?jìng)鬏敗刮淖?、圖片、音頻、視頻等「超文本」數(shù)據(jù)的「約定和規(guī)范」。
那「HTTP 是用于從互聯(lián)網(wǎng)服務(wù)器傳輸超文本到本地瀏覽器的協(xié)議 ,這種說(shuō)法正確嗎?
這種說(shuō)法是不正確的。因?yàn)橐部梢允恰阜?wù)器< – >服務(wù)器」,所以采用兩點(diǎn)之間的描述會(huì)更準(zhǔn)確。
HTTP 常見(jiàn)的狀態(tài)碼有哪些?
1xx 類(lèi)狀態(tài)碼屬于提示信息,是協(xié)議處理中的一種中間狀態(tài),實(shí)際用到的比較少。
2xx 類(lèi)狀態(tài)碼表示服務(wù)器成功處理了客戶(hù)端的請(qǐng)求,也是我們最愿意看到的狀態(tài)。
-
「200 OK」是最常見(jiàn)的成功狀態(tài)碼,表示一切正常。如果是非 HEAD 請(qǐng)求,服務(wù)器返回的響應(yīng)頭都會(huì)有 body 數(shù)據(jù)。
-
「204 No Content」也是常見(jiàn)的成功狀態(tài)碼,與 200 OK 基本相同,但響應(yīng)頭沒(méi)有 body 數(shù)據(jù)。
-
「206 Partial Content」是應(yīng)用于 HTTP 分塊下載或斷點(diǎn)續(xù)傳,表示響應(yīng)返回的 body 數(shù)據(jù)并不是資源的全部,而是其中的一部分,也是服務(wù)器處理成功的狀態(tài)。
3xx 類(lèi)狀態(tài)碼表示客戶(hù)端請(qǐng)求的資源發(fā)送了變動(dòng),需要客戶(hù)端用新的 URL 重新發(fā)送請(qǐng)求獲取資源,也就是重定向。
-
「301 Moved Permanently」表示永久重定向,說(shuō)明請(qǐng)求的資源已經(jīng)不存在了,需改用新的 URL 再次訪問(wèn)。
-
「302 Found」表示臨時(shí)重定向,說(shuō)明請(qǐng)求的資源還在,但暫時(shí)需要用另一個(gè) URL 來(lái)訪問(wèn)。
301 和 302 都會(huì)在響應(yīng)頭里使用字段 Location,指明后續(xù)要跳轉(zhuǎn)的 URL,瀏覽器會(huì)自動(dòng)重定向新的 URL。
- 「304 Not Modified」不具有跳轉(zhuǎn)的含義,表示資源未修改,重定向已存在的緩沖文件,也稱(chēng)緩存重定向,也就是告訴客戶(hù)端可以繼續(xù)使用緩存資源,用于緩存控制。
4xx 類(lèi)狀態(tài)碼表示客戶(hù)端發(fā)送的報(bào)文有誤,服務(wù)器無(wú)法處理,也就是錯(cuò)誤碼的含義。
-
「400 Bad Request」表示客戶(hù)端請(qǐng)求的報(bào)文有錯(cuò)誤,但只是個(gè)籠統(tǒng)的錯(cuò)誤。
-
「403 Forbidden」表示服務(wù)器禁止訪問(wèn)資源,并不是客戶(hù)端的請(qǐng)求出錯(cuò)。
-
「404 Not Found」表示請(qǐng)求的資源在服務(wù)器上不存在或未找到,所以無(wú)法提供給客戶(hù)端。
5xx 類(lèi)狀態(tài)碼表示客戶(hù)端請(qǐng)求報(bào)文正確,但是服務(wù)器處理時(shí)內(nèi)部發(fā)生了錯(cuò)誤,屬于服務(wù)器端的錯(cuò)誤碼。
-
「500 Internal Server Error」與 400 類(lèi)型,是個(gè)籠統(tǒng)通用的錯(cuò)誤碼,服務(wù)器發(fā)生了什么錯(cuò)誤,我們并不知道。
-
「501 Not Implemented」表示客戶(hù)端請(qǐng)求的功能還不支持,類(lèi)似“即將開(kāi)業(yè),敬請(qǐng)期待”的意思。
-
「502 Bad Gateway」通常是服務(wù)器作為網(wǎng)關(guān)或代理時(shí)返回的錯(cuò)誤碼,表示服務(wù)器自身工作正常,訪問(wèn)后端服務(wù)器發(fā)生了錯(cuò)誤。
-
「503 Service Unavailable」表示服務(wù)器當(dāng)前很忙,暫時(shí)無(wú)法響應(yīng)服務(wù)器,類(lèi)似“網(wǎng)絡(luò)服務(wù)正忙,請(qǐng)稍后重試”的意思。
HTTP 常見(jiàn)字段有哪些?
Host 字段
客戶(hù)端發(fā)送請(qǐng)求時(shí),用來(lái)指定服務(wù)器的域名。
Host: www.A.com有了 Host 字段,就可以將請(qǐng)求發(fā)往「同一臺(tái)」服務(wù)器上的不同網(wǎng)站。
Content-Length 字段
服務(wù)器在返回?cái)?shù)據(jù)時(shí),會(huì)有 Content-Length 字段,表明本次回應(yīng)的數(shù)據(jù)長(zhǎng)度。
Content-Length: 1000如上面則是告訴瀏覽器,本次服務(wù)器回應(yīng)的數(shù)據(jù)長(zhǎng)度是 1000 個(gè)字節(jié),后面的字節(jié)就屬于下一個(gè)回應(yīng)了。
Connection 字段
Connection 字段最常用于客戶(hù)端要求服務(wù)器使用 TCP 持久連接,以便其他請(qǐng)求復(fù)用。
HTTP/1.1 版本的默認(rèn)連接都是持久連接,但為了兼容老版本的 HTTP,需要指定 Connection 首部字段的值為 Keep-Alive。
Connection: keep-alive一個(gè)可以復(fù)用的 TCP 連接就建立了,直到客戶(hù)端或服務(wù)器主動(dòng)關(guān)閉連接。但是,這不是標(biāo)準(zhǔn)字段。
Content-Type 字段
Content-Type 字段用于服務(wù)器回應(yīng)時(shí),告訴客戶(hù)端,本次數(shù)據(jù)是什么格式。
Content-Type: text/html; charset=utf-8上面的類(lèi)型表明,發(fā)送的是網(wǎng)頁(yè),而且編碼是UTF-8。
客戶(hù)端請(qǐng)求的時(shí)候,可以使用 Accept 字段聲明自己可以接受哪些數(shù)據(jù)格式。
Accept: */*上面代碼中,客戶(hù)端聲明自己可以接受任何格式的數(shù)據(jù)。
Content-Encoding 字段
Content-Encoding 字段說(shuō)明數(shù)據(jù)的壓縮方法。表示服務(wù)器返回的數(shù)據(jù)使用了什么壓縮格式
Content-Encoding: gzip上面表示服務(wù)器返回的數(shù)據(jù)采用了 gzip 方式壓縮,告知客戶(hù)端需要用此方式解壓。
客戶(hù)端在請(qǐng)求時(shí),用 Accept-Encoding 字段說(shuō)明自己可以接受哪些壓縮方法。
Accept-Encoding: gzip, deflateGET 與 POST
GET 和 POST 有什么區(qū)別?
根據(jù) RFC 規(guī)范,GET 的語(yǔ)義是從服務(wù)器獲取指定的資源,這個(gè)資源可以是靜態(tài)的文本、頁(yè)面、圖片視頻等。GET 請(qǐng)求的參數(shù)位置一般是寫(xiě)在 URL 中,URL 規(guī)定只能支持 ASCII,所以 GET 請(qǐng)求的參數(shù)只允許 ASCII 字符 ,而且瀏覽器會(huì)對(duì) URL 的長(zhǎng)度有限制(HTTP協(xié)議本身對(duì) URL長(zhǎng)度并沒(méi)有做任何規(guī)定)。
比如,你打開(kāi)我的文章,瀏覽器就會(huì)發(fā)送 GET 請(qǐng)求給服務(wù)器,服務(wù)器就會(huì)返回文章的所有文字及資源。
根據(jù) RFC 規(guī)范,POST 的語(yǔ)義是根據(jù)請(qǐng)求負(fù)荷(報(bào)文body)對(duì)指定的資源做出處理,具體的處理方式視資源類(lèi)型而不同。POST 請(qǐng)求攜帶數(shù)據(jù)的位置一般是寫(xiě)在報(bào)文 body 中, body 中的數(shù)據(jù)可以是任意格式的數(shù)據(jù),只要客戶(hù)端與服務(wù)端協(xié)商好即可,而且瀏覽器不會(huì)對(duì) body 大小做限制。
比如,你在我文章底部,敲入了留言后點(diǎn)擊「提交」(暗示你們留言),瀏覽器就會(huì)執(zhí)行一次 POST 請(qǐng)求,把你的留言文字放進(jìn)了報(bào)文 body 里,然后拼接好 POST 請(qǐng)求頭,通過(guò) TCP 協(xié)議發(fā)送給服務(wù)器。
GET 和 POST 方法都是安全和冪等的嗎?
先說(shuō)明下安全和冪等的概念:
- 在 HTTP 協(xié)議里,所謂的「安全」是指請(qǐng)求方法不會(huì)「破壞」服務(wù)器上的資源。
- 所謂的「冪等」,意思是多次執(zhí)行相同的操作,結(jié)果都是「相同」的。
如果從 RFC 規(guī)范定義的語(yǔ)義來(lái)看:
- GET 方法就是安全且冪等的,因?yàn)樗恰钢蛔x」操作,無(wú)論操作多少次,服務(wù)器上的數(shù)據(jù)都是安全的,且每次的結(jié)果都是相同的。所以,可以對(duì) GET 請(qǐng)求的數(shù)據(jù)做緩存,這個(gè)緩存可以做到瀏覽器本身上(徹底避免瀏覽器發(fā)請(qǐng)求),也可以做到代理上(如nginx),而且在瀏覽器中 GET 請(qǐng)求可以保存位書(shū)簽。
- POST 因?yàn)槭恰感略龌蛱峤粩?shù)據(jù)」的操作,會(huì)修改服務(wù)器上的資源,所以是不安全的,且多次提交數(shù)據(jù)就會(huì)創(chuàng)建多個(gè)資源,所以不是冪等的。所以,瀏覽器一般不會(huì)緩存 POST 請(qǐng)求,也不能把 POST 請(qǐng)求保存為書(shū)簽。
做個(gè)簡(jiǎn)要的小結(jié)。
GET 的語(yǔ)義是請(qǐng)求獲取指定的資源。GET 方法是安全、冪等、可被緩存的。
POST 的語(yǔ)義是根據(jù)請(qǐng)求負(fù)荷(報(bào)文主體)對(duì)指定的資源做出處理,具體的處理方式視資源類(lèi)型而不同。POST 不安全,不冪等,(大部分實(shí)現(xiàn))不可緩存。
注意, 上面是從 RFC 規(guī)范定義的語(yǔ)義來(lái)分析的。
但是實(shí)際過(guò)程中,開(kāi)發(fā)者不一定會(huì)按照 RFC 規(guī)范定義的語(yǔ)義來(lái)實(shí)現(xiàn) GET 和 POST 方法。比如:
- 可以用 GET 方法實(shí)現(xiàn)新增或刪除數(shù)據(jù)的請(qǐng)求,這樣實(shí)現(xiàn)的 GET 方法自然就不是安全和冪等。
- 可以用 POST 方法實(shí)現(xiàn)查詢(xún)數(shù)據(jù)的請(qǐng)求,這樣實(shí)現(xiàn)的 POST 方法自然就是安全和冪等。
曾經(jīng)有個(gè)笑話,有人寫(xiě)了個(gè)博客,刪除博客用的是GET請(qǐng)求,他覺(jué)得沒(méi)人訪問(wèn)就連鑒權(quán)都沒(méi)做。然后Google服務(wù)器爬蟲(chóng)爬了一遍,他所有博文就沒(méi)了。。。
如果「安全」放入概念是指信息是否會(huì)被泄漏的話,雖然 POST 用 body 傳輸數(shù)據(jù),而 GET 用 URL 傳輸,這樣數(shù)據(jù)會(huì)在瀏覽器地址攔容易看到,但是并不能說(shuō) GET 不如 POST 安全的。
因?yàn)?HTTP 傳輸?shù)膬?nèi)容都是明文的,雖然在瀏覽器地址攔看不到 POST 提交的 body 數(shù)據(jù),但是只要抓個(gè)包就都能看到了。
所以,要避免傳輸過(guò)程中數(shù)據(jù)被竊取,就要使用 HTTPS 協(xié)議,這樣所有 HTTP 的數(shù)據(jù)都會(huì)被加密傳輸。
GET 請(qǐng)求可以帶 body 嗎?
RFC 規(guī)范并沒(méi)有規(guī)定 GET 請(qǐng)求不能帶 body 的。理論上,任何請(qǐng)求都可以帶 body 的。只是因?yàn)?RFC 規(guī)范定義的 GET 請(qǐng)求是獲取資源,所以根據(jù)這個(gè)語(yǔ)義不需要用到 body。
另外,URL 中的查詢(xún)參數(shù)也不是 GET 所獨(dú)有的,POST 請(qǐng)求的 URL 中也可以有參數(shù)的。
HTTP 緩存技術(shù)
HTTP 緩存有哪些實(shí)現(xiàn)方式?
對(duì)于一些具有重復(fù)性的 HTTP 請(qǐng)求,比如每次請(qǐng)求得到的數(shù)據(jù)都一樣的,我們可以把這對(duì)「請(qǐng)求-響應(yīng)」的數(shù)據(jù)都緩存在本地,那么下次就直接讀取本地的數(shù)據(jù),不必在通過(guò)網(wǎng)絡(luò)獲取服務(wù)器的響應(yīng)了,這樣的話 HTTP/1.1 的性能肯定肉眼可見(jiàn)的提升。
所以,避免發(fā)送 HTTP 請(qǐng)求的方法就是通過(guò)緩存技術(shù),HTTP 設(shè)計(jì)者早在之前就考慮到了這點(diǎn),因此 HTTP 協(xié)議的頭部有不少是針對(duì)緩存的字段。
HTTP 緩存有兩種實(shí)現(xiàn)方式,分別是強(qiáng)制緩存和協(xié)商緩存。
什么是強(qiáng)制緩存?
強(qiáng)緩存指的是只要瀏覽器判斷緩存沒(méi)有過(guò)期,則直接使用瀏覽器的本地緩存,決定是否使用緩存的主動(dòng)性在于瀏覽器這邊。
如下圖中,返回的是 200 狀態(tài)碼,但在 size 項(xiàng)中標(biāo)識(shí)的是 from disk cache,就是使用了強(qiáng)制緩存。
強(qiáng)緩存是利用下面這兩個(gè) HTTP 響應(yīng)頭部(Response Header)字段實(shí)現(xiàn)的,它們都用來(lái)表示資源在客戶(hù)端緩存的有效期:
- Cache-Control, 是一個(gè)相對(duì)時(shí)間;
- Expires,是一個(gè)絕對(duì)時(shí)間;
如果 HTTP 響應(yīng)頭部同時(shí)有 Cache-Control 和 Expires 字段的話,Cache-Control的優(yōu)先級(jí)高于 Expires 。
Cache-control 選項(xiàng)更多一些,設(shè)置更加精細(xì),所以建議使用 Cache-Control 來(lái)實(shí)現(xiàn)強(qiáng)緩存。具體的實(shí)現(xiàn)流程如下:
- 當(dāng)瀏覽器第一次請(qǐng)求訪問(wèn)服務(wù)器資源時(shí),服務(wù)器會(huì)在返回這個(gè)資源的同時(shí),在 Response 頭部加上 Cache-Control,Cache-Control 中設(shè)置了過(guò)期時(shí)間大小;
- 瀏覽器再次請(qǐng)求訪問(wèn)服務(wù)器中的該資源時(shí),會(huì)先通過(guò)請(qǐng)求資源的時(shí)間與 Cache-Control 中設(shè)置的過(guò)期時(shí)間大小,來(lái)計(jì)算出該資源是否過(guò)期,如果沒(méi)有,則使用該緩存,否則重新請(qǐng)求服務(wù)器;
- 服務(wù)器再次收到請(qǐng)求后,會(huì)再次更新 Response 頭部的 Cache-Control。
什么是協(xié)商緩存?
當(dāng)我們?cè)跒g覽器使用開(kāi)發(fā)者工具的時(shí)候,你可能會(huì)看到過(guò)某些請(qǐng)求的響應(yīng)碼是 304,這個(gè)是告訴瀏覽器可以使用本地緩存的資源,通常這種通過(guò)服務(wù)端告知客戶(hù)端是否可以使用緩存的方式被稱(chēng)為協(xié)商緩存。
上圖就是一個(gè)協(xié)商緩存的過(guò)程,所以協(xié)商緩存就是與服務(wù)端協(xié)商之后,通過(guò)協(xié)商結(jié)果來(lái)判斷是否使用本地緩存。
協(xié)商緩存可以基于兩種頭部來(lái)實(shí)現(xiàn)。
第一種:請(qǐng)求頭部中的 If-Modified-Since 字段與響應(yīng)頭部中的 Last-Modified 字段實(shí)現(xiàn),這兩個(gè)字段的意思是:
- 響應(yīng)頭部中的 Last-Modified:標(biāo)示這個(gè)響應(yīng)資源的最后修改時(shí)間;
- 請(qǐng)求頭部中的 If-Modified-Since:當(dāng)資源過(guò)期了,發(fā)現(xiàn)響應(yīng)頭中具有 Last-Modified 聲明,則再次發(fā)起請(qǐng)求的時(shí)候帶上 Last-Modified 的時(shí)間,服務(wù)器收到請(qǐng)求后發(fā)現(xiàn)有 If-Modified-Since 則與被請(qǐng)求資源的最后修改時(shí)間進(jìn)行對(duì)比(Last-Modified),如果最后修改時(shí)間較新(大),說(shuō)明資源又被改過(guò),則返回最新資源,HTTP 200 OK;如果最后修改時(shí)間較舊(小),說(shuō)明資源無(wú)新修改,響應(yīng) HTTP 304 走緩存。
第二種:請(qǐng)求頭部中的 If-None-Match 字段與響應(yīng)頭部中的 ETag 字段,這兩個(gè)字段的意思是:
- 響應(yīng)頭部中 Etag:唯一標(biāo)識(shí)響應(yīng)資源;
- 請(qǐng)求頭部中的 If-None-Match:當(dāng)資源過(guò)期時(shí),瀏覽器發(fā)現(xiàn)響應(yīng)頭里有 Etag,則再次向服務(wù)器發(fā)起請(qǐng)求時(shí),會(huì)將請(qǐng)求頭If-None-Match 值設(shè)置為 Etag 的值。服務(wù)器收到請(qǐng)求后進(jìn)行比對(duì),如果資源沒(méi)有變化返回 304,如果資源變化了返回 200。
第一種實(shí)現(xiàn)方式是基于時(shí)間實(shí)現(xiàn)的,第二種實(shí)現(xiàn)方式是基于一個(gè)唯一標(biāo)識(shí)實(shí)現(xiàn)的,相對(duì)來(lái)說(shuō)后者可以更加準(zhǔn)確地判斷文件內(nèi)容是否被修改,避免由于時(shí)間篡改導(dǎo)致的不可靠問(wèn)題。
如果 HTTP 響應(yīng)頭部同時(shí)有 Etag 和 Last-Modified 字段的時(shí)候, Etag 的優(yōu)先級(jí)更高,也就是先會(huì)判斷 Etag 是否變化了,如果 Etag 沒(méi)有變化,然后再看 Last-Modified。
注意,協(xié)商緩存這兩個(gè)字段都需要配合強(qiáng)制緩存中 Cache-control 字段來(lái)使用,只有在未能命中強(qiáng)制緩存的時(shí)候,才能發(fā)起帶有協(xié)商緩存字段的請(qǐng)求。
使用 ETag 字段實(shí)現(xiàn)的協(xié)商緩存的過(guò)程如下;
- 當(dāng)瀏覽器第一次請(qǐng)求訪問(wèn)服務(wù)器資源時(shí),服務(wù)器會(huì)在返回這個(gè)資源的同時(shí),在 Response 頭部加上 ETag 唯一標(biāo)識(shí),這個(gè)唯一標(biāo)識(shí)的值是根據(jù)當(dāng)前請(qǐng)求的資源生成的;
- 當(dāng)瀏覽器再次請(qǐng)求訪問(wèn)服務(wù)器中的該資源時(shí),首先會(huì)先檢查強(qiáng)制緩存是否過(guò)期,如果沒(méi)有過(guò)期,則直接使用本地緩存;如果緩存過(guò)期了,會(huì)在 Request 頭部加上 If-None-Match 字段,該字段的值就是 ETag 唯一標(biāo)識(shí);
- 服務(wù)器再次收到請(qǐng)求后,會(huì)根據(jù)請(qǐng)求中的 If-None-Match 值與當(dāng)前請(qǐng)求的資源生成的唯一標(biāo)識(shí)進(jìn)行比較:
- 如果值相等,則返回 304 Not Modified,不會(huì)返回資源;
- 如果不相等,則返回 200 狀態(tài)碼和返回資源,并在 Response 頭部加上新的 ETag 唯一標(biāo)識(shí);
- 如果瀏覽器收到 304 的請(qǐng)求響應(yīng)狀態(tài)碼,則會(huì)從本地緩存中加載資源,否則更新資源。
HTTP 特性
HTTP(1.1) 的優(yōu)點(diǎn)有哪些?
HTTP 最凸出的優(yōu)點(diǎn)是「簡(jiǎn)單、靈活和易于擴(kuò)展、應(yīng)用廣泛和跨平臺(tái)」。
1. 簡(jiǎn)單
HTTP 基本的報(bào)文格式就是 header + body,頭部信息也是 key-value 簡(jiǎn)單文本的形式,易于理解,降低了學(xué)習(xí)和使用的門(mén)檻。
2. 靈活和易于擴(kuò)展
HTTP協(xié)議里的各類(lèi)請(qǐng)求方法、URI/URL、狀態(tài)碼、頭字段等每個(gè)組成要求都沒(méi)有被固定死,都允許開(kāi)發(fā)人員自定義和擴(kuò)充。
同時(shí) HTTP 由于是工作在應(yīng)用層( OSI 第七層),則它下層可以隨意變化。
HTTPS 也就是在 HTTP 與 TCP 層之間增加了 SSL/TLS 安全傳輸層,HTTP/3 甚至把 TCP 層換成了基于 UDP 的 QUIC。
3. 應(yīng)用廣泛和跨平臺(tái)
互聯(lián)網(wǎng)發(fā)展至今,HTTP 的應(yīng)用范圍非常的廣泛,從臺(tái)式機(jī)的瀏覽器到手機(jī)上的各種 APP,從看新聞、刷貼吧到購(gòu)物、理財(cái)、吃雞,HTTP 的應(yīng)用遍地開(kāi)花,同時(shí)天然具有跨平臺(tái)的優(yōu)越性。
HTTP(1.1) 的缺點(diǎn)有哪些?
HTTP 協(xié)議里有優(yōu)缺點(diǎn)一體的雙刃劍,分別是「無(wú)狀態(tài)、明文傳輸」,同時(shí)還有一大缺點(diǎn)「不安全」。
1. 無(wú)狀態(tài)雙刃劍
無(wú)狀態(tài)的好處,因?yàn)榉?wù)器不會(huì)去記憶 HTTP 的狀態(tài),所以不需要額外的資源來(lái)記錄狀態(tài)信息,這能減輕服務(wù)器的負(fù)擔(dān),能夠把更多的 CPU 和內(nèi)存用來(lái)對(duì)外提供服務(wù)。
無(wú)狀態(tài)的壞處,既然服務(wù)器沒(méi)有記憶能力,它在完成有關(guān)聯(lián)性的操作時(shí)會(huì)非常麻煩。
例如登錄->添加購(gòu)物車(chē)->下單->結(jié)算->支付,這系列操作都要知道用戶(hù)的身份才行。但服務(wù)器不知道這些請(qǐng)求是有關(guān)聯(lián)的,每次都要問(wèn)一遍身份信息。
這樣每操作一次,都要驗(yàn)證信息,這樣的購(gòu)物體驗(yàn)還能愉快嗎?別問(wèn),問(wèn)就是酸爽!
對(duì)于無(wú)狀態(tài)的問(wèn)題,解法方案有很多種,其中比較簡(jiǎn)單的方式用 Cookie 技術(shù)。
Cookie 通過(guò)在請(qǐng)求和響應(yīng)報(bào)文中寫(xiě)入 Cookie 信息來(lái)控制客戶(hù)端的狀態(tài)。
相當(dāng)于,在客戶(hù)端第一次請(qǐng)求后,服務(wù)器會(huì)下發(fā)一個(gè)裝有客戶(hù)信息的「小貼紙」,后續(xù)客戶(hù)端請(qǐng)求服務(wù)器的時(shí)候,帶上「小貼紙」,服務(wù)器就能認(rèn)得了了,
2. 明文傳輸雙刃劍
明文意味著在傳輸過(guò)程中的信息,是可方便閱讀的,通過(guò)瀏覽器的 F12 控制臺(tái)或 Wireshark 抓包都可以直接肉眼查看,為我們調(diào)試工作帶了極大的便利性。
但是這正是這樣,HTTP 的所有信息都暴露在了光天化日下,相當(dāng)于信息裸奔。在傳輸?shù)穆L(zhǎng)的過(guò)程中,信息的內(nèi)容都毫無(wú)隱私可言,很容易就能被竊取,如果里面有你的賬號(hào)密碼信息,那你號(hào)沒(méi)了。
3. 不安全
HTTP 比較嚴(yán)重的缺點(diǎn)就是不安全:
- 通信使用明文(不加密),內(nèi)容可能會(huì)被竊聽(tīng)。比如,賬號(hào)信息容易泄漏,那你號(hào)沒(méi)了。
- 不驗(yàn)證通信方的身份,因此有可能遭遇偽裝。比如,訪問(wèn)假的淘寶、拼多多,那你錢(qián)沒(méi)了。
- 無(wú)法證明報(bào)文的完整性,所以有可能已遭篡改。比如,網(wǎng)頁(yè)上植入垃圾廣告,視覺(jué)污染,眼沒(méi)了。
HTTP 的安全問(wèn)題,可以用 HTTPS 的方式解決,也就是通過(guò)引入 SSL/TLS 層,使得在安全上達(dá)到了極致。
HTTP/1.1 的性能如何?
HTTP 協(xié)議是基于 TCP/IP,并且使用了「請(qǐng)求 - 應(yīng)答」的通信模式,所以性能的關(guān)鍵就在這兩點(diǎn)里。
1. 長(zhǎng)連接
早期 HTTP/1.0 性能上的一個(gè)很大的問(wèn)題,那就是每發(fā)起一個(gè)請(qǐng)求,都要新建一次 TCP 連接(三次握手),而且是串行請(qǐng)求,做了無(wú)謂的 TCP 連接建立和斷開(kāi),增加了通信開(kāi)銷(xiāo)。
為了解決上述 TCP 連接問(wèn)題,HTTP/1.1 提出了長(zhǎng)連接的通信方式,也叫持久連接。這種方式的好處在于減少了 TCP 連接的重復(fù)建立和斷開(kāi)所造成的額外開(kāi)銷(xiāo),減輕了服務(wù)器端的負(fù)載。
持久連接的特點(diǎn)是,只要任意一端沒(méi)有明確提出斷開(kāi)連接,則保持 TCP 連接狀態(tài)。
當(dāng)然,如果某個(gè) HTTP 長(zhǎng)連接超過(guò)一定時(shí)間沒(méi)有任何數(shù)據(jù)交互,服務(wù)端就會(huì)主動(dòng)斷開(kāi)這個(gè)連接。
2. 管道網(wǎng)絡(luò)傳輸
HTTP/1.1 采用了長(zhǎng)連接的方式,這使得管道(pipeline)網(wǎng)絡(luò)傳輸成為了可能。
即可在同一個(gè) TCP 連接里面,客戶(hù)端可以發(fā)起多個(gè)請(qǐng)求,只要第一個(gè)請(qǐng)求發(fā)出去了,不必等其回來(lái),就可以發(fā)第二個(gè)請(qǐng)求出去,可以減少整體的響應(yīng)時(shí)間。
舉例來(lái)說(shuō),客戶(hù)端需要請(qǐng)求兩個(gè)資源。以前的做法是,在同一個(gè) TCP 連接里面,先發(fā)送 A 請(qǐng)求,然后等待服務(wù)器做出回應(yīng),收到后再發(fā)出 B 請(qǐng)求。那么,管道機(jī)制則是允許瀏覽器同時(shí)發(fā)出 A 請(qǐng)求和 B 請(qǐng)求,如下圖:
但是服務(wù)器必須按照接收請(qǐng)求的順序發(fā)送對(duì)這些管道化請(qǐng)求的響應(yīng)。
注意,是按照服務(wù)端收到的請(qǐng)求順序響應(yīng),并不管哪個(gè)請(qǐng)求是先發(fā)送的,假設(shè)客戶(hù)端先發(fā)送 A 請(qǐng)求,后發(fā)送 B 請(qǐng)求,如果服務(wù)端先收到 B 請(qǐng)求,就先響應(yīng) B 請(qǐng)求,然后再響應(yīng) A 請(qǐng)求,但是假設(shè)處理 B 請(qǐng)求的時(shí)候,耗時(shí)比較長(zhǎng),那么請(qǐng)求 A 的響應(yīng)就會(huì)被阻塞,這稱(chēng)為「隊(duì)頭堵塞」。
所以,HTTP/1.1 管道解決了請(qǐng)求的隊(duì)頭阻塞,但是沒(méi)有解決響應(yīng)的隊(duì)頭阻塞。
3. 隊(duì)頭阻塞
「請(qǐng)求 - 應(yīng)答」的模式加劇了 HTTP 的性能問(wèn)題。
因?yàn)楫?dāng)順序發(fā)送的請(qǐng)求序列中的一個(gè)請(qǐng)求因?yàn)槟撤N原因被阻塞時(shí),在后面排隊(duì)的所有請(qǐng)求也一同被阻塞了,會(huì)招致客戶(hù)端一直請(qǐng)求不到數(shù)據(jù),這也就是「隊(duì)頭阻塞」,好比上班的路上塞車(chē)。
總之 HTTP/1.1 的性能一般般,后續(xù)的 HTTP/2 和 HTTP/3 就是在優(yōu)化 HTTP 的性能。
HTTP 與 HTTPS
HTTP 與 HTTPS 有哪些區(qū)別?
HTTPS 解決了 HTTP 的哪些問(wèn)題?
HTTP 由于是明文傳輸,所以安全上存在以下三個(gè)風(fēng)險(xiǎn):
- 竊聽(tīng)風(fēng)險(xiǎn),比如通信鏈路上可以獲取通信內(nèi)容,用戶(hù)號(hào)容易沒(méi)。
- 篡改風(fēng)險(xiǎn),比如強(qiáng)制植入垃圾廣告,視覺(jué)污染,用戶(hù)眼容易瞎。
- 冒充風(fēng)險(xiǎn),比如冒充淘寶網(wǎng)站,用戶(hù)錢(qián)容易沒(méi)。
HTTPS 在 HTTP 與 TCP 層之間加入了 SSL/TLS 協(xié)議,可以很好的解決了上述的風(fēng)險(xiǎn):
- 信息加密:交互信息無(wú)法被竊取,但你的號(hào)會(huì)因?yàn)椤缸陨硗洝官~號(hào)而沒(méi)。
- 校驗(yàn)機(jī)制:無(wú)法篡改通信內(nèi)容,篡改了就不能正常顯示,但百度「競(jìng)價(jià)排名」依然可以搜索垃圾廣告。
- 身份證書(shū):證明淘寶是真的淘寶網(wǎng),但你的錢(qián)還是會(huì)因?yàn)椤付缡帧苟鴽](méi)。
可見(jiàn),只要自身不做「惡」,SSL/TLS 協(xié)議是能保證通信是安全的。
HTTPS 是如何解決上面的三個(gè)風(fēng)險(xiǎn)的?
- 混合加密的方式實(shí)現(xiàn)信息的機(jī)密性,解決了竊聽(tīng)的風(fēng)險(xiǎn)。
- 摘要算法的方式來(lái)實(shí)現(xiàn)完整性,它能夠?yàn)閿?shù)據(jù)生成獨(dú)一無(wú)二的「指紋」,指紋用于校驗(yàn)數(shù)據(jù)的完整性,解決了篡改的風(fēng)險(xiǎn)。
- 將服務(wù)器公鑰放入到數(shù)字證書(shū)中,解決了冒充的風(fēng)險(xiǎn)。
1. 混合加密
通過(guò)混合加密的方式可以保證信息的機(jī)密性,解決了竊聽(tīng)的風(fēng)險(xiǎn)。
HTTPS 采用的是對(duì)稱(chēng)加密和非對(duì)稱(chēng)加密結(jié)合的「混合加密」方式:
- 在通信建立前采用非對(duì)稱(chēng)加密的方式交換「會(huì)話秘鑰」,后續(xù)就不再使用非對(duì)稱(chēng)加密。
- 在通信過(guò)程中全部使用對(duì)稱(chēng)加密的「會(huì)話秘鑰」的方式加密明文數(shù)據(jù)。
采用「混合加密」的方式的原因:
- 對(duì)稱(chēng)加密只使用一個(gè)密鑰,運(yùn)算速度快,密鑰必須保密,無(wú)法做到安全的密鑰交換。
- 非對(duì)稱(chēng)加密使用兩個(gè)密鑰:公鑰和私鑰,公鑰可以任意分發(fā)而私鑰保密,解決了密鑰交換問(wèn)題但速度慢。
2. 摘要算法
摘要算法用來(lái)實(shí)現(xiàn)完整性,能夠?yàn)閿?shù)據(jù)生成獨(dú)一無(wú)二的「指紋」,用于校驗(yàn)數(shù)據(jù)的完整性,解決了篡改的風(fēng)險(xiǎn)。
客戶(hù)端在發(fā)送明文之前會(huì)通過(guò)摘要算法算出明文的「指紋」,發(fā)送的時(shí)候把「指紋 + 明文」一同加密成密文后,發(fā)送給服務(wù)器,服務(wù)器解密后,用相同的摘要算法算出發(fā)送過(guò)來(lái)的明文,通過(guò)比較客戶(hù)端攜帶的「指紋」和當(dāng)前算出的「指紋」做比較,若「指紋」相同,說(shuō)明數(shù)據(jù)是完整的。
3. 數(shù)字證書(shū)
客戶(hù)端先向服務(wù)器端索要公鑰,然后用公鑰加密信息,服務(wù)器收到密文后,用自己的私鑰解密。
這就存在些問(wèn)題,如何保證公鑰不被篡改和信任度?
所以這里就需要借助第三方權(quán)威機(jī)構(gòu) CA (數(shù)字證書(shū)認(rèn)證機(jī)構(gòu)),將服務(wù)器公鑰放在數(shù)字證書(shū)(由數(shù)字證書(shū)認(rèn)證機(jī)構(gòu)頒發(fā))中,只要證書(shū)是可信的,公鑰就是可信的。
通過(guò)數(shù)字證書(shū)的方式保證服務(wù)器公鑰的身份,解決冒充的風(fēng)險(xiǎn)。
HTTPS 是如何建立連接的?其間交互了什么?
SSL/TLS 協(xié)議基本流程:
- 客戶(hù)端向服務(wù)器索要并驗(yàn)證服務(wù)器的公鑰。
- 雙方協(xié)商生產(chǎn)「會(huì)話秘鑰」。
- 雙方采用「會(huì)話秘鑰」進(jìn)行加密通信。
前兩步也就是 SSL/TLS 的建立過(guò)程,也就是 TLS 握手階段。
SSL/TLS 的「握手階段」涉及四次通信,可見(jiàn)下圖:
SSL/TLS 協(xié)議建立的詳細(xì)流程:
1. ClientHello
首先,由客戶(hù)端向服務(wù)器發(fā)起加密通信請(qǐng)求,也就是 ClientHello 請(qǐng)求。
在這一步,客戶(hù)端主要向服務(wù)器發(fā)送以下信息:
(1)客戶(hù)端支持的 SSL/TLS 協(xié)議版本,如 TLS 1.2 版本。
(2)客戶(hù)端生產(chǎn)的隨機(jī)數(shù)(Client Random),后面用于生成「會(huì)話秘鑰」條件之一。
(3)客戶(hù)端支持的密碼套件列表,如 RSA 加密算法。
2. SeverHello
服務(wù)器收到客戶(hù)端請(qǐng)求后,向客戶(hù)端發(fā)出響應(yīng),也就是 SeverHello。服務(wù)器回應(yīng)的內(nèi)容有如下內(nèi)容:
(1)確認(rèn) SSL/ TLS 協(xié)議版本,如果瀏覽器不支持,則關(guān)閉加密通信。
(2)服務(wù)器生產(chǎn)的隨機(jī)數(shù)(Server Random),也是后面用于生產(chǎn)「會(huì)話秘鑰」條件之一。
(3)確認(rèn)的密碼套件列表,如 RSA 加密算法。
(4)服務(wù)器的數(shù)字證書(shū)。
3.客戶(hù)端回應(yīng)
客戶(hù)端收到服務(wù)器的回應(yīng)之后,首先通過(guò)瀏覽器或者操作系統(tǒng)中的 CA 公鑰,確認(rèn)服務(wù)器的數(shù)字證書(shū)的真實(shí)性。
如果證書(shū)沒(méi)有問(wèn)題,客戶(hù)端會(huì)從數(shù)字證書(shū)中取出服務(wù)器的公鑰,然后使用它加密報(bào)文,向服務(wù)器發(fā)送如下信息:
(1)一個(gè)隨機(jī)數(shù)(pre-master key)。該隨機(jī)數(shù)會(huì)被服務(wù)器公鑰加密。
(2)加密通信算法改變通知,表示隨后的信息都將用「會(huì)話秘鑰」加密通信。
(3)客戶(hù)端握手結(jié)束通知,表示客戶(hù)端的握手階段已經(jīng)結(jié)束。這一項(xiàng)同時(shí)把之前所有內(nèi)容的發(fā)生的數(shù)據(jù)做個(gè)摘要,用來(lái)供服務(wù)端校驗(yàn)。
上面第一項(xiàng)的隨機(jī)數(shù)是整個(gè)握手階段的第三個(gè)隨機(jī)數(shù),會(huì)發(fā)給服務(wù)端,所以這個(gè)隨機(jī)數(shù)客戶(hù)端和服務(wù)端都是一樣的。
服務(wù)器和客戶(hù)端有了這三個(gè)隨機(jī)數(shù)(Client Random、Server Random、pre-master key),接著就用雙方協(xié)商的加密算法,各自生成本次通信的「會(huì)話秘鑰」。
4. 服務(wù)器的最后回應(yīng)
服務(wù)器收到客戶(hù)端的第三個(gè)隨機(jī)數(shù)(pre-master key)之后,通過(guò)協(xié)商的加密算法,計(jì)算出本次通信的「會(huì)話秘鑰」。
然后,向客戶(hù)端發(fā)送最后的信息:
(1)加密通信算法改變通知,表示隨后的信息都將用「會(huì)話秘鑰」加密通信。
(2)服務(wù)器握手結(jié)束通知,表示服務(wù)器的握手階段已經(jīng)結(jié)束。這一項(xiàng)同時(shí)把之前所有內(nèi)容的發(fā)生的數(shù)據(jù)做個(gè)摘要,用來(lái)供客戶(hù)端校驗(yàn)。
至此,整個(gè) SSL/TLS 的握手階段全部結(jié)束。接下來(lái),客戶(hù)端與服務(wù)器進(jìn)入加密通信,就完全是使用普通的 HTTP 協(xié)議,只不過(guò)用「會(huì)話秘鑰」加密內(nèi)容。
HTTP/1.1、HTTP/2、HTTP/3 演變
HTTP/1.1 相比 HTTP/1.0 提高了什么性能?
HTTP/1.1 相比 HTTP/1.0 性能上的改進(jìn):
- 使用 TCP 長(zhǎng)連接的方式改善了 HTTP/1.0 短連接造成的性能開(kāi)銷(xiāo)。
- 支持管道(pipeline)網(wǎng)絡(luò)傳輸,只要第一個(gè)請(qǐng)求發(fā)出去了,不必等其回來(lái),就可以發(fā)第二個(gè)請(qǐng)求出去,可以減少整體的響應(yīng)時(shí)間。
但 HTTP/1.1 還是有性能瓶頸:
- 請(qǐng)求 / 響應(yīng)頭部(Header)未經(jīng)壓縮就發(fā)送,首部信息越多延遲越大。只能壓縮 Body 的部分;
- 發(fā)送冗長(zhǎng)的首部。每次互相發(fā)送相同的首部造成的浪費(fèi)較多;
- 服務(wù)器是按請(qǐng)求的順序響應(yīng)的,如果服務(wù)器響應(yīng)慢,會(huì)招致客戶(hù)端一直請(qǐng)求不到數(shù)據(jù),也就是隊(duì)頭阻塞;
- 沒(méi)有請(qǐng)求優(yōu)先級(jí)控制;
- 請(qǐng)求只能從客戶(hù)端開(kāi)始,服務(wù)器只能被動(dòng)響應(yīng)。
HTTP/2 做了什么優(yōu)化?
HTTP/2 協(xié)議是基于 HTTPS 的,所以 HTTP/2 的安全性也是有保障的。
那 HTTP/2 相比 HTTP/1.1 性能上的改進(jìn):
1. 頭部壓縮
HTTP/2 會(huì)壓縮頭(Header)如果你同時(shí)發(fā)出多個(gè)請(qǐng)求,他們的頭是一樣的或是相似的,那么,協(xié)議會(huì)幫你消除重復(fù)的部分。
這就是所謂的 HPACK 算法:在客戶(hù)端和服務(wù)器同時(shí)維護(hù)一張頭信息表,所有字段都會(huì)存入這個(gè)表,生成一個(gè)索引號(hào),以后就不發(fā)送同樣字段了,只發(fā)送索引號(hào),這樣就提高速度了。
2. 二進(jìn)制格式
HTTP/2 不再像 HTTP/1.1 里的純文本形式的報(bào)文,而是全面采用了二進(jìn)制格式,頭信息和數(shù)據(jù)體都是二進(jìn)制,并且統(tǒng)稱(chēng)為幀(frame):頭信息幀(Headers Frame)和數(shù)據(jù)幀(Data Frame)。
這樣雖然對(duì)人不友好,但是對(duì)計(jì)算機(jī)非常友好,因?yàn)橛?jì)算機(jī)只懂二進(jìn)制,那么收到報(bào)文后,無(wú)需再將明文的報(bào)文轉(zhuǎn)成二進(jìn)制,而是直接解析二進(jìn)制報(bào)文,這增加了數(shù)據(jù)傳輸?shù)男?/strong>。
比如狀態(tài)碼 200 ,在 HTTP/1.1 是用 ‘2’‘0’‘0’ 三個(gè)字符來(lái)表示(二進(jìn)制:110010 110000 110000),如圖:
在 HTTP/2 是用數(shù)字 200 表示(二進(jìn)制:11001000),如圖:
3. 數(shù)據(jù)流
HTTP/2 的數(shù)據(jù)包不是按順序發(fā)送的,同一個(gè)連接里面連續(xù)的數(shù)據(jù)包,可能屬于不同的回應(yīng)。因此,必須要對(duì)數(shù)據(jù)包做標(biāo)記,指出它屬于哪個(gè)回應(yīng)。
在 HTTP/2 中每個(gè)請(qǐng)求或相應(yīng)的所有數(shù)據(jù)包,稱(chēng)為一個(gè)數(shù)據(jù)流(Stream)。每個(gè)數(shù)據(jù)流都標(biāo)記著一個(gè)獨(dú)一無(wú)二的編號(hào)(Stream ID),不同 Stream 的幀是可以亂序發(fā)送的(因此可以并發(fā)不同的 Stream ),因?yàn)槊總€(gè)幀的頭部會(huì)攜帶 Stream ID 信息,所以接收端可以通過(guò) Stream ID 有序組裝成 HTTP 消息
客戶(hù)端和服務(wù)器雙方都可以建立 Stream, Stream ID 也是有區(qū)別的,客戶(hù)端建立的 Stream 必須是奇數(shù)號(hào),而服務(wù)器建立的 Stream 必須是偶數(shù)號(hào)。
客戶(hù)端還可以指定數(shù)據(jù)流的優(yōu)先級(jí)。優(yōu)先級(jí)高的請(qǐng)求,服務(wù)器就先響應(yīng)該請(qǐng)求。
4. 多路復(fù)用
HTTP/2 是可以在一個(gè)連接中并發(fā)多個(gè)請(qǐng)求或回應(yīng),而不用按照順序一一對(duì)應(yīng)。
移除了 HTTP/1.1 中的串行請(qǐng)求,不需要排隊(duì)等待,也就不會(huì)再出現(xiàn)「隊(duì)頭阻塞」問(wèn)題,降低了延遲,大幅度提高了連接的利用率。
舉例來(lái)說(shuō),在一個(gè) TCP 連接里,服務(wù)器收到了客戶(hù)端 A 和 B 的兩個(gè)請(qǐng)求,如果發(fā)現(xiàn) A 處理過(guò)程非常耗時(shí),于是就回應(yīng) A 請(qǐng)求已經(jīng)處理好的部分,接著回應(yīng) B 請(qǐng)求,完成后,再回應(yīng) A 請(qǐng)求剩下的部分。
5. 服務(wù)器推送
HTTP/2 還在一定程度上改善了傳統(tǒng)的「請(qǐng)求 - 應(yīng)答」工作模式,服務(wù)不再是被動(dòng)地響應(yīng),也可以主動(dòng)向客戶(hù)端發(fā)送消息。
比如,客戶(hù)端通過(guò) HTTP/1.1 請(qǐng)求從服務(wù)器那獲取到了 HTML 文件,而 HTML 可能還需要依賴(lài) CSS 來(lái)渲染頁(yè)面,這時(shí)客戶(hù)端還要再發(fā)起獲取 CSS 文件的請(qǐng)求,需要兩次消息往返,如下圖左邊部分:
如上圖右邊部分,在 HTTP/2 中,客戶(hù)端在訪問(wèn) HTML 時(shí),服務(wù)器可以直接主動(dòng)推送 CSS 文件,減少了消息傳遞的次數(shù)。
HTTP/2 有什么缺陷?
HTTP/2 通過(guò) Stream 的并發(fā)能力,解決了 HTTP/1 隊(duì)頭阻塞的問(wèn)題,看似很完美了,但是 HTTP/2 還是存在“隊(duì)頭阻塞”的問(wèn)題,只不過(guò)問(wèn)題不是在 HTTP 這一層面,而是在 TCP 這一層。
HTTP/2 是基于 TCP 協(xié)議來(lái)傳輸數(shù)據(jù)的,TCP 是字節(jié)流協(xié)議,TCP 層必須保證收到的字節(jié)數(shù)據(jù)是完整且連續(xù)的,這樣內(nèi)核才會(huì)將緩沖區(qū)里的數(shù)據(jù)返回給 HTTP 應(yīng)用,那么當(dāng)「前 1 個(gè)字節(jié)數(shù)據(jù)」沒(méi)有到達(dá)時(shí),后收到的字節(jié)數(shù)據(jù)只能存放在內(nèi)核緩沖區(qū)里,只有等到這 1 個(gè)字節(jié)數(shù)據(jù)到達(dá)時(shí),HTTP/2 應(yīng)用層才能從內(nèi)核中拿到數(shù)據(jù),這就是 HTTP/2 隊(duì)頭阻塞問(wèn)題。
舉個(gè)例子,如下圖:
圖中發(fā)送方發(fā)送了很多個(gè) packet,每個(gè) packet 都有自己的序號(hào),你可以認(rèn)為是 TCP 的序列號(hào),其中 packet 3 在網(wǎng)絡(luò)中丟失了,即使 packet 4-6 被接收方收到后,由于內(nèi)核中的 TCP 數(shù)據(jù)不是連續(xù)的,于是接收方的應(yīng)用層就無(wú)法從內(nèi)核中讀取到,只有等到 packet 3 重傳后,接收方的應(yīng)用層才可以從內(nèi)核中讀取到數(shù)據(jù),這就是 HTTP/2 的隊(duì)頭阻塞問(wèn)題,是在 TCP 層面發(fā)生的。
所以,一旦發(fā)生了丟包現(xiàn)象,就會(huì)觸發(fā) TCP 的重傳機(jī)制,這樣在一個(gè) TCP 連接中的所有的 HTTP 請(qǐng)求都必須等待這個(gè)丟了的包被重傳回來(lái)。
HTTP/3 做了哪些優(yōu)化?
前面我們知道了 HTTP/1.1 和 HTTP/2 都有隊(duì)頭阻塞的問(wèn)題:
- HTTP/1.1 中的管道( pipeline)雖然解決了請(qǐng)求的隊(duì)頭阻塞,但是沒(méi)有解決響應(yīng)的隊(duì)頭阻塞,因?yàn)榉?wù)端需要按順序響應(yīng)收到的請(qǐng)求,如果服務(wù)端處理某個(gè)請(qǐng)求消耗的時(shí)間比較長(zhǎng),那么只能等相應(yīng)完這個(gè)請(qǐng)求后, 才能處理下一個(gè)請(qǐng)求,這屬于 HTTP 層隊(duì)頭阻塞。
- HTTP/2 雖然通過(guò)多個(gè)請(qǐng)求復(fù)用一個(gè) TCP 連接解決了 HTTP 的隊(duì)頭阻塞 ,但是一旦發(fā)生丟包,就會(huì)阻塞住所有的 HTTP 請(qǐng)求,這屬于 TCP 層隊(duì)頭阻塞。
HTTP/2 隊(duì)頭阻塞的問(wèn)題是因?yàn)?TCP,所以 HTTP/3 把 HTTP 下層的 TCP 協(xié)議改成了 UDP!
UDP 發(fā)生是不管順序,也不管丟包的,所以不會(huì)出現(xiàn)像 HTTP/2 隊(duì)頭阻塞的問(wèn)題
大家都知道 UDP 是不可靠傳輸?shù)?#xff0c;但基于 UDP 的 QUIC 協(xié)議 可以實(shí)現(xiàn)類(lèi)似 TCP 的可靠性傳輸。
QUIC 有以下 3 個(gè)特點(diǎn)。
1、無(wú)隊(duì)頭阻塞
QUIC 協(xié)議也有類(lèi)似 HTTP/2 Stream 與多路復(fù)用的概念,也是可以在同一條連接上并發(fā)傳輸多個(gè) Stream,Stream 可以認(rèn)為就是一條 HTTP 請(qǐng)求。
QUIC 有自己的一套機(jī)制可以保證傳輸?shù)目煽啃缘摹?strong>當(dāng)某個(gè)流發(fā)生丟包時(shí),只會(huì)阻塞這個(gè)流,其他流不會(huì)受到影響,因此不存在隊(duì)頭阻塞問(wèn)題。這與 HTTP/2 不同,HTTP/2 只要某個(gè)流中的數(shù)據(jù)包丟失了,其他流也會(huì)因此受影響。
所以,QUIC 連接上的多個(gè) Stream 之間并沒(méi)有依賴(lài),都是獨(dú)立的,某個(gè)流發(fā)生丟包了,只會(huì)影響該流,其他流不受影響。
2、更快的連接建立
對(duì)于 HTTP/1 和 HTTP/2 協(xié)議,TCP 和 TLS 是分層的,分別屬于內(nèi)核實(shí)現(xiàn)的傳輸層、openssl 庫(kù)實(shí)現(xiàn)的表示層,因此它們難以合并在一起,需要分批次來(lái)握手,先 TCP 握手,再 TLS 握手。
HTTP/3 在傳輸數(shù)據(jù)前雖然需要 QUIC 協(xié)議握手,這個(gè)握手過(guò)程只需要 1 RTT,握手的目的是為確認(rèn)雙方的「連接 ID」,連接遷移就是基于連接 ID 實(shí)現(xiàn)的。
但是 HTTP/3 的 QUIC 協(xié)議并不是與 TLS 分層,而是QUIC 內(nèi)部包含了 TLS,它在自己的幀會(huì)攜帶 TLS 里的“記錄”,再加上 QUIC 使用的是 TLS/1.3,因此僅需 1 個(gè) RTT 就可以「同時(shí)」完成建立連接與密鑰協(xié)商,如下圖:
甚至,在第二次連接的時(shí)候,應(yīng)用數(shù)據(jù)包可以和 QUIC 握手信息(連接信息 + TLS 信息)一起發(fā)送,達(dá)到 0-RTT 的效果。
3、連接遷移
基于 TCP 傳輸協(xié)議的 HTTP 協(xié)議,由于是通過(guò)四元組(源 IP、源端口、目的 IP、目的端口)確定一條 TCP 連接,那么當(dāng)移動(dòng)設(shè)備的網(wǎng)絡(luò)從 4G 切換到 WIFI 時(shí),意味著 IP 地址變化了,那么就必須要斷開(kāi)連接,然后重新建立連接。而建立連接的過(guò)程包含 TCP 三次握手和 TLS 四次握手的時(shí)延,以及 TCP 慢啟動(dòng)的減速過(guò)程,給用戶(hù)的感覺(jué)就是網(wǎng)絡(luò)突然卡頓了一下,因此連接的遷移成本是很高的。
而 QUIC 協(xié)議沒(méi)有用四元組的方式來(lái)“綁定”連接,而是通過(guò)連接 ID來(lái)標(biāo)記通信的兩個(gè)端點(diǎn),客戶(hù)端和服務(wù)器可以各自選擇一組 ID 來(lái)標(biāo)記自己,因此即使移動(dòng)設(shè)備的網(wǎng)絡(luò)變化后,導(dǎo)致 IP 地址變化了,只要仍保有上下文信息(比如連接 ID、TLS 密鑰等),就可以“無(wú)縫”地復(fù)用原連接,消除重連的成本,沒(méi)有絲毫卡頓感,達(dá)到了連接遷移的功能。
所以, QUIC 是一個(gè)在 UDP 之上的偽 TCP + TLS + HTTP/2 的多路復(fù)用的協(xié)議。
QUIC 是新協(xié)議,對(duì)于很多網(wǎng)絡(luò)設(shè)備,根本不知道什么是 QUIC,只會(huì)當(dāng)做 UDP,這樣會(huì)出現(xiàn)新的問(wèn)題,因?yàn)橛械木W(wǎng)絡(luò)設(shè)備是會(huì)丟掉 UDP 包的,而 QUIC 是基于UDP 實(shí)現(xiàn)的,那么如果網(wǎng)絡(luò)設(shè)備無(wú)法識(shí)別這個(gè)是 QUIC 包,那么就會(huì)當(dāng)作 UDP包,然后被丟棄。
所以,HTTP/3 現(xiàn)在普及的進(jìn)度非常的緩慢,不知道未來(lái) UDP 是否能夠逆襲 TCP。
參考資料:
[1] 上野 宣.圖解HTTP.人民郵電出版社.
[2] 羅劍鋒.透視HTTP協(xié)議.極客時(shí)間.
[3] 陳皓.HTTP的前世今.酷殼CoolShell.https://coolshell.cn/articles/19840.html
[4] 阮一峰.HTTP 協(xié)議入門(mén).阮一峰的網(wǎng)絡(luò)日志.http://www.ruanyifeng.com/blog/2016/08/http.html
讀者問(wèn)答
讀者問(wèn):“https和http相比,就是傳輸?shù)膬?nèi)容多了對(duì)稱(chēng)加密,可以這么理解嗎?”
建立連接時(shí)候:https 比 http多了 TLS 的握手過(guò)程;
傳輸內(nèi)容的時(shí)候:https 會(huì)把數(shù)據(jù)進(jìn)行加密,通常是對(duì)稱(chēng)加密數(shù)據(jù);
讀者問(wèn):“ 我看文中 TLS 和 SSL 沒(méi)有做區(qū)分,這兩個(gè)需要區(qū)分嗎?”
這兩實(shí)際上是一個(gè)東西。
SSL 是洋文 “Secure Sockets Layer 的縮寫(xiě),中文叫做「安全套接層」。它是在上世紀(jì) 90 年代中期,由網(wǎng)景公司設(shè)計(jì)的。
到了1999年,SSL 因?yàn)閼?yīng)用廣泛,已經(jīng)成為互聯(lián)網(wǎng)上的事實(shí)標(biāo)準(zhǔn)。IETF 就在那年把 SSL 標(biāo)準(zhǔn)化。標(biāo)準(zhǔn)化之后的名稱(chēng)改為 TLS(是 “Transport Layer Security” 的縮寫(xiě)),中文叫做 「?jìng)鬏攲影踩珔f(xié)議」。
很多相關(guān)的文章都把這兩者并列稱(chēng)呼(SSL/TLS),因?yàn)檫@兩者可以視作同一個(gè)東西的不同階段。
讀者問(wèn):“為啥 ssl 的握手是 4 次?”
SSL/TLS 1.2 需要 4 握手,需要 2 個(gè) RTT 的時(shí)延,我文中的圖是把每個(gè)交互分開(kāi)畫(huà)了,實(shí)際上把他們合在一起發(fā)送,就是 4 次握手:
另外, SSL/TLS 1.3 優(yōu)化了過(guò)程,只需要 1 個(gè) RTT 往返時(shí)延,也就是只需要 3 次握手:
小林是專(zhuān)為大家圖解的工具人,Goodbye,我們下次見(jiàn)!
總結(jié)
以上是生活随笔為你收集整理的1.5 万字 + 40 张图解 HTTP 常见面试题(值得收藏)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 解析一个C语言俄罗斯方块游戏,包你看了就
- 下一篇: Linux同步原语系列-spinlock