前言
如果說(shuō) TCP/IP 協(xié)議是互聯(lián)網(wǎng)通信的根基,那么 HTTP 就是其中當(dāng)之無(wú)愧的王者,小到日常生活中的游戲,新聞,大到雙十一秒殺等都能看到它的身影,據(jù) NetCraft 統(tǒng)計(jì),目前全球至少有 16 億個(gè)網(wǎng)站、2 億多個(gè)獨(dú)立域名,而這個(gè)龐大網(wǎng)絡(luò)世界的底層運(yùn)轉(zhuǎn)機(jī)制就是 HTTP,可以毫不夸張的說(shuō),無(wú) HTTP 不通信!
畫外音: TCP/IP 協(xié)議群如下, IP 不是 IP 地址,是 Internet Protocol 的簡(jiǎn)稱
HTTP 應(yīng)用如此廣泛,我們確實(shí)必要好好學(xué)習(xí)下它,不僅有助于我們理解和解釋工作中的強(qiáng)制刷新,防盜鏈等現(xiàn)象和原理,還讓我們?cè)谠O(shè)計(jì)開源中間件時(shí)會(huì)有所啟發(fā),比如在設(shè)計(jì) MQ, Dubbo 這些組件時(shí),第一要?jiǎng)?wù)就是要設(shè)計(jì)協(xié)議,在其中你或多或少能看到 HTTP 協(xié)議的影子,學(xué)習(xí)了 HTTP 能讓你在設(shè)計(jì)中間件等組件協(xié)議時(shí),提供很好的思路
本文將會(huì)全面剖析 HTTP 的設(shè)計(jì)理念,助你徹底掌握 HTTP,相信看完以下問(wèn)題你會(huì)手到擒來(lái)!
什么是 HTTP,它有什么特點(diǎn),為什么說(shuō) HTTP 是萬(wàn)能的
為什么說(shuō)反爬是個(gè)偽命題
簡(jiǎn)要介紹一下 HTTP 0.9, 1.1, 2.0, 3.0 的特點(diǎn)
曾經(jīng)在我司有一位同事在大群里拋了一個(gè)問(wèn)題:在瀏覽器的地址欄輸入圖片 url,想預(yù)覽一下圖片,結(jié)果卻變成了下載圖片,你能替他解釋一下其中的原因嗎
DNS 協(xié)議了解多少,什么是 DNS 負(fù)載均衡
HTTP 1.1 唯一一個(gè)要求請(qǐng)求頭必傳的字段是哪個(gè),它有什么作用
no-cache 真的是不緩存的意思?你對(duì) HTTP 的緩存了解多少
為什么刷新了瀏覽器器卻抓不到請(qǐng)求,而強(qiáng)制刷新卻可以抓到,強(qiáng)制刷新到底做了什么事
301 和 302 的區(qū)別是啥
各種協(xié)議與 HTTP 的關(guān)系
老生常談很多人的誤解:GET 和 POST 的區(qū)別是啥
本文將會(huì)從以下幾點(diǎn)來(lái)展開闡述 HTTP
什么是 HTTP
與 HTTP 相關(guān)的各種協(xié)議
HTTP 內(nèi)容協(xié)商
HTTP 緩存
HTTP 連接與HTTP 2的改進(jìn)
什么是 HTTP
HTTP 全稱HyperTextTransferProtocol「超文本傳輸協(xié)議」,拆成三個(gè)部分來(lái)看,即「超文本」,「?jìng)鬏敗?「協(xié)議」
超文本:即「超越了普通文本的文本」,即音視頻,圖片,文件的混合體,大家常見的網(wǎng)頁(yè)很多就內(nèi)嵌了 img, video 這些標(biāo)簽解析展現(xiàn)而成的圖片,視頻等,除了這些超文本內(nèi)容外,最關(guān)鍵的是超文本中含有超鏈接,超鏈接意味著網(wǎng)頁(yè)等文件內(nèi)容的超文本上可以點(diǎn)擊鏈接到其他頁(yè)面上,互聯(lián)網(wǎng)就是通過(guò)這樣的超鏈接構(gòu)成的。
傳輸: 傳輸意味著至少有兩個(gè)參與者,比如 A,B,這意味著 HTTP 協(xié)議是個(gè)雙向協(xié)議,一般是將「超文本」按照約定的協(xié)議以二進(jìn)制數(shù)據(jù)包的形式從 A 傳到 B 或 B 傳到 A, A <===> B,我們把發(fā)起請(qǐng)求的叫請(qǐng)求方,接到請(qǐng)求后返回?cái)?shù)據(jù)的那一方稱為應(yīng)答方,但需要注意的是傳輸也不限于兩個(gè)參與者,允許中間有中轉(zhuǎn)或者接力,只要參與者間遵循約定的協(xié)議即可傳輸。
如圖示:傳輸可以有多個(gè)參與者,只要遵循相應(yīng)的協(xié)議即可
協(xié)議:HTTP 是一個(gè)協(xié)議,啥是協(xié)議?在日常生活中協(xié)議并不少見,比如我們租房時(shí)簽訂的租房協(xié)議,入職后和企業(yè)簽訂的勞動(dòng)合同協(xié)議,「協(xié)」意味著至少有兩人參與,「議」意味著雙方要就某項(xiàng)條款達(dá)成一致,比如租房協(xié)議規(guī)定月付 xx 元,勞動(dòng)合同協(xié)議規(guī)定月工資 xx 元,協(xié)議即對(duì)通信雙方的約束,雙方按照約定傳輸數(shù)據(jù)才能進(jìn)行明白對(duì)方的意思,否則便是雞同鴨講。
經(jīng)過(guò)以上解釋,我們可以給 HTTP 下一個(gè)比較準(zhǔn)確的定義了:
HTTP 是一個(gè)在計(jì)算機(jī)世界里專門在兩點(diǎn)之間傳輸文字、圖片、音頻、視頻等超文本數(shù)據(jù)的約定和規(guī)范。
與 HTTP 相關(guān)的各種協(xié)議
HTTP 雖然在當(dāng)今互聯(lián)網(wǎng)通信中占據(jù)統(tǒng)計(jì)地位,但要讓它生效還必須依賴其他協(xié)議或規(guī)范的支持
1. URI 和 URL
首先既然我們要在兩點(diǎn)之間傳輸超文本,那這個(gè)超文本該怎么表示?超文本即資源,互聯(lián)網(wǎng)上資源這么多,如何唯一標(biāo)記互聯(lián)網(wǎng)上的資源。
使用 URI(UniformResourceIdentifier) 即統(tǒng)一資源標(biāo)識(shí)符就可以唯一定位互聯(lián)網(wǎng)上的資源。URI 大家比較少聽過(guò),大家更熟悉的可能是 URL(UniformResourceLocator),即統(tǒng)一資源定位符,URL 其實(shí)是 URI 的一種子集,區(qū)別就是URI 定義資源,而 URL 不單定義這個(gè)資源,還定義了如何找到這個(gè)資源。
URL 主要由四個(gè)部分組成:協(xié)議、主機(jī)、端口、路徑
協(xié)議:即通信雙方指定的傳輸協(xié)議,應(yīng)用最廣的當(dāng)然是本文要介紹的 http 協(xié)議,除此之外還有 ftp, mailto,file 等協(xié)議。
主機(jī)名:存放資源的服務(wù)器主機(jī)名或 IP 地址,當(dāng)前有時(shí)候服務(wù)器由于安全原因還需要對(duì)用戶進(jìn)行認(rèn)證,需要提供用戶名和密碼,此時(shí)需要在 hostname前加 username:password。
端口:整數(shù),可選,省略時(shí)使用方案的默認(rèn)端口,各種傳輸協(xié)議都有默認(rèn)的端口,如 HTTP 默認(rèn)用的 80 端口,HTTPS 用的 443 端口,傳輸層協(xié)議正是利用這些端口號(hào)識(shí)別本機(jī)中正在進(jìn)行通信的應(yīng)用程序,并準(zhǔn)確地將數(shù)據(jù)傳輸。
路徑:即資源在主機(jī)上的存放路徑,一般表示主機(jī)的目錄或文件地址
parameter:用于指定特殊參數(shù)的可選項(xiàng)。
query:查詢字符串,可選,用于給動(dòng)態(tài)網(wǎng)頁(yè)或接口傳遞參數(shù),可有多個(gè)參數(shù),用“&”符號(hào)隔開,每個(gè)參數(shù)的名和值用“=”符號(hào)隔開。
fragment:瀏覽器專用,用于指定網(wǎng)絡(luò)資源中的片斷,指定后打開網(wǎng)頁(yè)可直接定位到 fragment 對(duì)應(yīng)的位置。
例子如下
2. TCP/IP 協(xié)議
在上述 URL 地址中,如果你指定了「www.example.com 」 這樣的主機(jī)名,最終會(huì)被 DNS 解析成 IP 地址然后才開始通信,為啥主機(jī)名最終要被解析成 IP 地址才能通信呢,因?yàn)?HTTP 協(xié)議使用的是 TCP/IP 協(xié)議棧,協(xié)議棧就是這樣規(guī)定的,一起來(lái)看看 TCP/IP 協(xié)議棧各層的功能。
TCP/IP協(xié)議棧
TCP/IP 協(xié)議??偣灿兴膶?/p>
link layer: 鏈接層,負(fù)責(zé)在以太網(wǎng),WIFI 這樣的底層網(wǎng)絡(luò)上發(fā)送原始數(shù)據(jù)包,工作在網(wǎng)卡這一層,使用 MAC 地址來(lái)標(biāo)記網(wǎng)絡(luò)上的設(shè)備,所以也叫 MAC 層
Internet layer: 網(wǎng)絡(luò)層,IP 協(xié)議即處于這一層,提供路由和尋址的功能,使兩終端系統(tǒng)能夠互連且決定最佳路徑,並具有一定的擁塞控制和流量控制的能力。相當(dāng)于傳送郵件時(shí)需要地址一般重要
transport layer: 傳輸層,該層的協(xié)議為應(yīng)用進(jìn)程提供端到端的通信服務(wù),這層主要有 TCP,UDP 兩個(gè)協(xié)議,TCP 提供面向連接的數(shù)據(jù)流支持、可靠性、流量控制、多路復(fù)用等服務(wù),UDP不提供復(fù)雜的控制機(jī)制,利用 IP 提供面向無(wú)連接的簡(jiǎn)單消息傳輸
application layer: 即應(yīng)用層,前面三層已經(jīng)為網(wǎng)絡(luò)通信打下了堅(jiān)實(shí)的基礎(chǔ),這層可發(fā)揮的空間就大很多了,應(yīng)用層協(xié)議可以想象為不同的服務(wù),每個(gè)應(yīng)用層協(xié)議都是為了解決某一類應(yīng)用問(wèn)題而生的,每一個(gè)服務(wù)需要用不同的協(xié)議,規(guī)定應(yīng)用進(jìn)程在通信時(shí)所遵循的協(xié)議。
我們可以把前面三層認(rèn)為是高速公路及其配套的基礎(chǔ)設(shè)施,至于要傳什么貨物,高速公路是否要關(guān)閉等則由應(yīng)用層決定。
利用 TCP/IP 協(xié)議族進(jìn)行網(wǎng)絡(luò)通信時(shí),會(huì)通過(guò)分層順序與對(duì)方進(jìn)行通信。每個(gè)分層中,都會(huì)對(duì)所發(fā)送的數(shù)據(jù)附加一個(gè)首部,在這個(gè)首部中包含了該層必要的信息,如發(fā)送的目標(biāo)地址以及協(xié)議相關(guān)信息。
接收方收到數(shù)據(jù)后,同樣的,每一層也會(huì)解析其首部字段,直到應(yīng)用層收到相應(yīng)的數(shù)據(jù)。
圖片來(lái)自文末參考鏈接
通過(guò)這樣分層的方式,每個(gè)層各司其職,只要管好自己的工作即可,可擴(kuò)展性很好,比如對(duì)于 HTTP 來(lái)說(shuō),它底層可以用 TCP,也可以用 UDP 來(lái)傳輸,哪天如果再出現(xiàn)了更牛逼的協(xié)議,也可以替換之,不影響上下層,這就是計(jì)算機(jī)中比較有名的分層理論:沒(méi)有什么是分層解決不了的,如果有那就再分一層。
IP 包的首部中定義了32 位的源 IP 地址和目的 IP 地址,如下圖所示址
所以應(yīng)用層在請(qǐng)求傳輸數(shù)據(jù)時(shí)必須事先要知道對(duì)方的 IP 地址,然后才能開始傳輸。
2. DNS 協(xié)議
由上一節(jié)可知請(qǐng)求時(shí)需要事先知道對(duì)方的 IP 地址,但 IP 地址是由「161.117.232.65」這樣的數(shù)字組成的,正常人根本記不住,想想看,如果我要上個(gè)百度,還要先知道它的 IP 地址,那豈不是要瘋掉,那怎么辦,聯(lián)系生活場(chǎng)景,想想看,如果我們要打某人電話,記不住他的電話號(hào)碼,是不是要先到電話本找某個(gè)人的名字,然后再打,電話本起的作用就是把姓名翻譯成電話號(hào)碼。
同樣的,正常人只會(huì)記住 baidu.com 這樣的網(wǎng)址,那就需要有類似電話本這樣的翻譯器把網(wǎng)址轉(zhuǎn)成 IP 地址,DNS(域名服務(wù)器)就是干這個(gè)事的
上面只是 DNS 的簡(jiǎn)化版本,實(shí)際上 DNS 解析無(wú)法一步到位,比較復(fù)雜,要理解 DNS 的工作機(jī)制,首先我們要看懂域名的層級(jí)結(jié)構(gòu),類似 www.baidu.com 這樣的網(wǎng)址也叫域名,是一個(gè)有層次的結(jié)構(gòu), 最右邊的被稱為頂級(jí)域名,然后是二級(jí)域名,層級(jí)關(guān)系向左降低,最左邊的是主機(jī)名,通常用來(lái)表示主機(jī)的用途,比如 「www」 表示提供萬(wàn)維網(wǎng)服務(wù)
當(dāng)然這也不是絕對(duì)的,起名的關(guān)鍵只是方便我們記憶而已。
域名有層次之分,DNS 也是有層次之分,DNS 核心系統(tǒng)是一個(gè)三層的樹狀,分布式結(jié)構(gòu),基本對(duì)應(yīng)域名結(jié)構(gòu)。
根域名服務(wù)器(Root DNS Server): 返回「com」,「cn」,「net」等頂級(jí)域名服務(wù)器的 IP 地址,
頂級(jí)域名服務(wù)器(Top-level DNS Server):管理各自域名下的權(quán)威域名服務(wù)器,比如 com 頂級(jí)域名服務(wù)器可以返回 apple.com 域名服務(wù)器的 IP 地址;
權(quán)威域名服務(wù)器(Authoritative DNS Server):管理自己域名下主機(jī)的 IP 地址,比如 apple.com 權(quán)威域名服務(wù)器可以返回 www.apple.com 的 IP 地址。
DNS 核心系統(tǒng)層級(jí)結(jié)構(gòu)
根域名服務(wù)器是關(guān)鍵,必須是眾所周知的,找到了它,下面的各級(jí)域名服務(wù)器才能找到,否則域名解析就無(wú)從談起了。既然知道了 DNS 的層次之分,那么不難猜出請(qǐng)求 www.apple.com 的 DNS 解析如下
首先訪問(wèn)根域名服務(wù)器,獲取「com」頂級(jí)域名服務(wù)器的地址
請(qǐng)求「com」頂級(jí)域名服務(wù)器,返回「apple.com」域名服務(wù)器的地址
然后返回「apple.com」域名服務(wù)器,返回 www.apple.com 的地址
以上三層解析我們稱為 DNS 核心解析系統(tǒng),那么大家想想,全世界的 PC,app 等設(shè)備多如牛毛,如果每發(fā)一次請(qǐng)求都要按上面的 DNS 解析來(lái)獲取 IP,那估計(jì) DNS 解析系統(tǒng)就要炸了,如何緩解這種壓力呢,答案是用緩存,事實(shí)上很多大公司,或網(wǎng)絡(luò)運(yùn)營(yíng)商都會(huì)自建自己的 DNS 服務(wù)器,作為用戶查詢的代理,代替用戶請(qǐng)求核心 DNS 系統(tǒng),這樣如果查到的話可以緩存查詢記錄,再次收到請(qǐng)求的號(hào)如果有緩存結(jié)果或者緩存未過(guò)期,則直接返回原來(lái)的緩存結(jié)果,大家可能聽過(guò) Google 的 8.8.8.8 DNS 解析服務(wù)器,這種就是 Google 自建的,我們一般稱這種自建的為「非權(quán)威域名服務(wù)器」。
配置 DNS Server
除了非權(quán)威域名服務(wù)器,還有瀏覽器緩存,操作系統(tǒng)緩存(大家熟知的 /etc/hosts 就是操作系統(tǒng) DSN 緩存的一種)
這樣的話如果請(qǐng)求 www.example.com,dns 的完整解析流程如下:
1、 瀏覽器中輸入 www.example.com 后,會(huì)先查看 瀏覽器的 DNS 緩存是否過(guò)期,未過(guò)期直接取緩存的,已過(guò)期會(huì)繼續(xù)請(qǐng)求操作系統(tǒng)的緩存(/etc/hosts 文件等),還未找到,進(jìn)入步驟 2
2、 請(qǐng)求本地地址配置的 DNS resolver(非權(quán)威域名服務(wù)器),一般由用戶的 Internet 服務(wù)提供商 (ISP) 進(jìn)行管理,例如有線 Internet 服務(wù)提供商、DSL 寬帶提供商或公司網(wǎng),MAC 的同學(xué)可以打開網(wǎng)絡(luò)配置中的 DNS Servers 來(lái)看下默認(rèn) ISP 提供的域名服務(wù)器(如果想用其他的非權(quán)威域名服務(wù)器,填入即可,這樣就會(huì)覆蓋 ISP 提供的默認(rèn)地址)
3、 DNS resolver 將 www.example.com 的請(qǐng)求轉(zhuǎn)發(fā)到 DNS 根名稱服務(wù)器, 根服務(wù)器返回「.com」頂級(jí)域名服務(wù)器地址
4、 DNS resolver 再次轉(zhuǎn)發(fā) www.example.com 的請(qǐng)求,這次轉(zhuǎn)發(fā)到步驟三獲取到的 .com 域的一個(gè) TLD 名稱服務(wù)器。.com 域的名稱服務(wù)器使用與 example.com 域相關(guān)的四個(gè) Amazon Route 53 名稱服務(wù)器的名稱來(lái)響應(yīng)該請(qǐng)求。
5、Amazon Route 53 名稱服務(wù)器在 example.com 托管區(qū)域中查找 www.example.com 記錄,獲得相關(guān)值,例如,Web 服務(wù)器的 IP 地址 (192.0.2.44),并將 IP 地址返回至 DNS 解析程序。
6、DNS resolver 最終獲得用戶需要的 IP 地址。解析程序?qū)⒋酥捣祷刂?Web 瀏覽器。DNS 解析程序還會(huì)將 example.com 的 IP 地址緩存 (存儲(chǔ)) 您指定的時(shí)長(zhǎng),以便它能夠在下次有人瀏覽 example.com 時(shí)更快地作出響應(yīng)。
我們可以用 dig 工具來(lái)驗(yàn)證一下上面的請(qǐng)求流程
可以看到請(qǐng)求流程確實(shí)與我們的流程圖一致!另外我們注意到 ip 地址返回了四個(gè),這樣的話 client 可以隨機(jī)選擇其中一個(gè)請(qǐng)求,這就是我們常說(shuō)的 DNS 負(fù)載均衡,可有效緩存 server 壓力。
HTTP 報(bào)文格式
接下來(lái)我們?cè)俳榻B下 HTTP 報(bào)文格式,通信雙方要能正常通信,就必須遵循協(xié)議才能理解對(duì)方的信息,協(xié)議規(guī)定了 HTTP 請(qǐng)求和響應(yīng)報(bào)文的格式。
請(qǐng)求和響應(yīng)報(bào)文都由「起始行」,「頭部」,「空行」,「實(shí)體」 四個(gè)部分組成,只不過(guò)起始行稍有不同。
請(qǐng)求報(bào)文格式
先來(lái)看下請(qǐng)求報(bào)文的格式
示例如下:
請(qǐng)求方法比較常見的有以下幾類
1、GET: 請(qǐng)求 URL 指定的資源,指定的資源經(jīng)服務(wù)器端解析后返回響應(yīng)內(nèi)容,GET 方法具有冪等性,即無(wú)論請(qǐng)求多次,都只會(huì)返回資源,而不會(huì)額外創(chuàng)建或改變資源, GET 請(qǐng)求只傳請(qǐng)求頭,不傳請(qǐng)求體。
2、HEAD: 語(yǔ)義上與 GET 類似,但 HEAD 的響應(yīng)只有請(qǐng)求頭,沒(méi)有請(qǐng)求體
3、POST: 主要用來(lái)創(chuàng)建,修改,上傳資源,不具有冪等性,一般將要請(qǐng)求的資源附在請(qǐng)求體上傳輸
4、PUT: 修改資源,基本上不用,因?yàn)?POST 也具有修改的語(yǔ)義,所以基本上線上大多用 POST 來(lái)代替。
5、OPTIONS: 列出可對(duì)資源實(shí)行的方法,這個(gè)方法很少見,但在跨域中會(huì)用到,也比較重要,后文結(jié)合 Cookie 談到安全問(wèn)題時(shí)我們會(huì)再提
協(xié)議版本指定了客戶端當(dāng)前支持的 HTTP 版本,HTTP 目前通用的有 1.1, 2.0 三個(gè)版本,如果請(qǐng)求方指定了 1.1,應(yīng)答方收到之后也會(huì)使用 HTTP 1.1 協(xié)議進(jìn)行回復(fù)。
響應(yīng)報(bào)文格式
響應(yīng)行的報(bào)文格式如下
示例如下
響應(yīng)報(bào)文主要有如下五類狀態(tài)碼:
1××:提示信息,表示目前是協(xié)議處理的中間狀態(tài),還需要后續(xù)的操作;
2××:成功,報(bào)文已經(jīng)收到并被正確處理;
3××:重定向,資源位置發(fā)生變動(dòng),需要客戶端重新發(fā)送請(qǐng)求;
4××:客戶端錯(cuò)誤,請(qǐng)求報(bào)文有誤,服務(wù)器無(wú)法處理;
5××:服務(wù)器錯(cuò)誤,服務(wù)器在處理請(qǐng)求時(shí)內(nèi)部發(fā)生了錯(cuò)誤。
常用的狀態(tài)碼及其釋義如下(更詳細(xì)的可以參考文末參考鏈接)
狀態(tài)碼狀態(tài)碼的原因短語(yǔ)釋義
100Continue這個(gè)臨時(shí)響應(yīng)表明,迄今為止的所有內(nèi)容都是可行的,客戶端應(yīng)該繼續(xù)請(qǐng)求,如果已經(jīng)完成,則忽略它。
200OK請(qǐng)求獲取/創(chuàng)建資源成功
201Created該請(qǐng)求已成功,并因此創(chuàng)建了一個(gè)新的資源,通過(guò)用到 POST 請(qǐng)求中
206Partial Content服務(wù)器已經(jīng)成功處理了部分 GET 請(qǐng)求。類似于 FlashGet 或者迅雷這類的 HTTP 下載工具都是使用此類響應(yīng)實(shí)現(xiàn)斷點(diǎn)續(xù)傳或者將一個(gè)大文檔分解為多個(gè)下載段同時(shí)下載。該請(qǐng)求必須包含 Range 頭信息來(lái)指示客戶端希望得到的內(nèi)容范圍,并且可能包含 If-Range 來(lái)作為請(qǐng)求條件。
301Moved Permanently永久重定向,說(shuō)明請(qǐng)求的資源已經(jīng)被移動(dòng)到了由 Location 頭部指定的url上,是固定的不會(huì)再改變
302Found臨時(shí)重定向,重定向狀態(tài)碼表明請(qǐng)求的資源被暫時(shí)的移動(dòng)到了由Location 頭部指定的 URL 上
400Bad Request1、語(yǔ)義有誤,當(dāng)前請(qǐng)求無(wú)法被服務(wù)器理解。除非進(jìn)行修改,否則客戶端不應(yīng)該重復(fù)提交這個(gè)請(qǐng)求。2、請(qǐng)求參數(shù)有誤
401Unauthorized當(dāng)前請(qǐng)求需要用戶驗(yàn)證。該響應(yīng)必須包含一個(gè)適用于被請(qǐng)求資源的 WWW-Authenticate 信息頭用以詢問(wèn)用戶信息。
403Forbidden服務(wù)器已經(jīng)理解請(qǐng)求,但是拒絕執(zhí)行它,可以認(rèn)為用戶沒(méi)有權(quán)限
404Not Found請(qǐng)求失敗,請(qǐng)求所希望得到的資源未被在服務(wù)器上發(fā)現(xiàn)
405Method Not Allowed請(qǐng)求行中指定的請(qǐng)求方法不能被用于請(qǐng)求相應(yīng)的資源。該響應(yīng)必須返回一個(gè)Allow 頭信息用以表示出當(dāng)前資源能夠接受的請(qǐng)求方法的列表
500Internal Server Error服務(wù)器遇到意外的情況并阻止其執(zhí)行請(qǐng)求。
502Bad Gateway此錯(cuò)誤響應(yīng)表明服務(wù)器作為網(wǎng)關(guān)需要得到一個(gè)處理這個(gè)請(qǐng)求的響應(yīng),但是得到一個(gè)錯(cuò)誤的響應(yīng)。
了解這些狀態(tài)碼,我們定位排查問(wèn)題就能成竹在胸,比如看到 5xx,就知道是 server 的邏輯有問(wèn)題,看到 400 就知道是客戶端的請(qǐng)求參數(shù)有問(wèn)題,另一端就不用傻傻去排查了。
請(qǐng)求和響應(yīng)頭
請(qǐng)求和響應(yīng)頭部報(bào)文的 header 格式基本都是一樣的,都是 key-value 的形式,key 和 value 都是用 「: 」分隔,此外 HTTP 頭字段非常靈活,除了使用標(biāo)準(zhǔn)的 Host,Connection 等頭字段外,也可以任意添加自定義頭,這就給 HTTP 協(xié)議帶來(lái)了無(wú)限的擴(kuò)展可能!
常用頭字段
HTTP 協(xié)議規(guī)定了非常多的頭字段,可以實(shí)現(xiàn)各種各樣的功能,但基本上可以分為以下四類
通用字段:在請(qǐng)求頭和響應(yīng)頭里都可以出現(xiàn);
請(qǐng)求字段:僅能出現(xiàn)在請(qǐng)求頭里,進(jìn)一步說(shuō)明請(qǐng)求信息或者額外的附加條件;
響應(yīng)字段:僅能出現(xiàn)在響應(yīng)頭里,補(bǔ)充說(shuō)明響應(yīng)報(bào)文的信息;
實(shí)體字段:它實(shí)際上屬于通用字段,但專門描述 body 的額外信息。
對(duì) HTTP 報(bào)文的解析和處理其實(shí)本質(zhì)上就是對(duì)頭字段的處理,HTTP 的連接管理,緩存控制,內(nèi)容協(xié)商等都是通過(guò)頭字段來(lái)處理的,理解了頭字段,基本上也就理解了 HTTP,所以理解頭字段非常重要。接下來(lái)我們就來(lái)看看這些頭部字段的具體含義
1、通用字段
首部字段名說(shuō)明
Cache-Control控制緩存的行為
Connection逐跳首部、連接的管理
Date創(chuàng)建報(bào)文的日期時(shí)間
Pragma報(bào)文指令
Trailer報(bào)文末端的首部一覽
Transfer-Encoding指定報(bào)文主體的傳輸編碼方式
Upgrade升級(jí)為其他協(xié)議
Via代理服務(wù)器的相關(guān)信息
Warning錯(cuò)誤通知
2、請(qǐng)求首部字段
首部字段名說(shuō)明
Accept用戶代理可處理的媒體類型
Accept-Charset優(yōu)先的字符集
Accept-Encoding優(yōu)先的內(nèi)容編碼
Accept-Language優(yōu)先的語(yǔ)言(自然語(yǔ)言)
AuthorizationWeb 認(rèn)證信息
Expect期待服務(wù)器的特定行為
From用戶的電子郵箱地址
Host請(qǐng)求資源所在服務(wù)器
If-Match比較實(shí)體標(biāo)記(ETag)
If-Modified-Since比較資源的更新時(shí)間
If-None-Match比較實(shí)體標(biāo)記(與 If-Match 相反)
If-Range資源未更新時(shí)發(fā)送實(shí)體 Byte 的范圍請(qǐng)求
If-Unmodified-Since比較資源的更新時(shí)間(與 If-Modified-Since 相反)
Max-Forwards最大傳輸逐跳數(shù)
Proxy-Authorization代理服務(wù)器要求客戶端的認(rèn)證信息
Range實(shí)體的字節(jié)范圍請(qǐng)求
Referer對(duì)請(qǐng)求中 URI 的原始獲取方 傳輸編碼的優(yōu)先級(jí)
TE傳輸編碼的優(yōu)先級(jí)
User-AgentHTTP 客戶端程序的信息
3、響應(yīng)首部字段
首部字段名說(shuō)明
Accept-Ranges是否接受字節(jié)范圍請(qǐng)求
Age推算資源創(chuàng)建經(jīng)過(guò)時(shí)間
ETag資源的匹配信息
Location令客戶端重定向至指定 URI
Retry-After對(duì)再次發(fā)起請(qǐng)求的時(shí)機(jī)要求
ServerHTTP 服務(wù)器的安裝信息
Vary代理服務(wù)器緩存的管理信息
WWW-Authenticate服務(wù)器對(duì)客戶端的認(rèn)證信息
If-Match比較實(shí)體標(biāo)記(ETag)
If-Modified-Since比較資源的更新時(shí)間
If-None-Match比較實(shí)體標(biāo)記(與 If-Match 相反)
If-Range資源未更新時(shí)發(fā)送實(shí)體 Byte 的范圍請(qǐng)求
If-Unmodified-Since比較資源的更新時(shí)間(與 If-Modified-Since 相反)
Max-Forwards最大傳輸逐跳數(shù)
Proxy-Authorization代理服務(wù)器要求客戶端的認(rèn)證信息
Range實(shí)體的字節(jié)范圍請(qǐng)求
Referer對(duì)請(qǐng)求中 URI 的原始獲取方 傳輸編碼的優(yōu)先級(jí)
TE傳輸編碼的優(yōu)先級(jí)
User-AgentHTTP 客戶端程序的信息
4、實(shí)體首部字段
首部字段名說(shuō)明
Allow資源可支持的 HTTP 方法
Content-Encoding實(shí)體主體適用的編碼方式
Content-Language實(shí)體主體的自然語(yǔ)言
Content-Length實(shí)體主體的大小(單位 :字節(jié))
Content-Location替代對(duì)應(yīng)資源的 URI
Content-MD5實(shí)體主體的報(bào)文摘要
Content-Range實(shí)體主體的位置范圍
Content-Type實(shí)體主體的媒體類型
Expires實(shí)體主體過(guò)期的日期時(shí)間
這么多字段該怎么記呢,死記硬背肯定不行,從功能上理解會(huì)更易懂,HTTP 主要為我們提供了如下功能
一、內(nèi)容協(xié)商
內(nèi)容協(xié)商機(jī)制是指客戶端和服務(wù)器端就響應(yīng)的資源內(nèi)容進(jìn)行交涉,然后提供給客戶端最為適合的資源。內(nèi)容協(xié)商會(huì)以響應(yīng)資源的語(yǔ)言、字符集、編碼方式等作為判斷的基準(zhǔn),舉一個(gè)常見的例子,大家在 Chrome 上設(shè)置不同的語(yǔ)言,主頁(yè)也就展示不同的內(nèi)容
內(nèi)容協(xié)商示例
請(qǐng)求報(bào)文的「Accept」,「Accept-Charset」,「Accept-Encoding」,「Accept-Language」,「Content-Language」,即為內(nèi)容協(xié)商的判定標(biāo)準(zhǔn)。
舉個(gè)例子
上圖表示的含義如下
客戶端:請(qǐng)給我類型為 text/*,語(yǔ)言為 en,編碼類型最好為 br(如果沒(méi)有 gzip 也可接受)的資源。
服務(wù)器:好的,我找到了編碼類型為 br(Content-Encoding: br),內(nèi)容為 en(Content-Language: en)的資源,它的 url 為 URLe(Content-Location: /URLe),你拿到后再去請(qǐng)求下就有了。
內(nèi)容協(xié)議請(qǐng)求頭和對(duì)應(yīng)的響應(yīng)頭對(duì)應(yīng)關(guān)系如下:
二、緩存管理
緩存管理也是 HTTP 協(xié)議非常重要的內(nèi)容,這部分也是務(wù)必要掌握的。
對(duì)于資源來(lái)說(shuō),由于有些挺長(zhǎng)時(shí)間內(nèi)都不會(huì)更新,所以沒(méi)必要每次請(qǐng)求都向 server 發(fā)起網(wǎng)絡(luò)請(qǐng)求,如果第一次請(qǐng)求后能保存在本地,下次請(qǐng)求直接在本地取,那無(wú)疑會(huì)快得多,對(duì)服務(wù)器的壓力也會(huì)減少。
涉及到緩存的請(qǐng)求頭為 Cache-Control。這個(gè)緩存指令是單向的,也就是說(shuō)請(qǐng)求中設(shè)置的指令,不一定包含在響應(yīng)中,請(qǐng)求中如果沒(méi)有傳 Cache-Control, server 也可以返回 Cache-Control。
如圖示:客戶端發(fā)起請(qǐng)求后,服務(wù)器返回 Cache-Control:max-age=30,代表資源在客戶端可以緩存 30 秒,30 秒內(nèi)客戶端的請(qǐng)求可以直接從緩存獲取,超過(guò) 30 秒后需要向服務(wù)器發(fā)起網(wǎng)絡(luò)請(qǐng)求。
max-age是 HTTP 緩存控制最常用的屬性,表示資源存儲(chǔ)的最長(zhǎng)時(shí)間,需要注意的是,時(shí)間的計(jì)算起點(diǎn)是響應(yīng)報(bào)文的創(chuàng)建時(shí)刻(即 Date 字段,也就是離開服務(wù)器的時(shí)刻),超過(guò)后客戶端需要重新發(fā)起請(qǐng)求。
除此之外,還有其它屬性值如下:
no-cache:這個(gè)是很容易造成誤解的字段,它的含義并不是不允許緩存,而是可以緩存,但在使用之前必須要去服務(wù)器驗(yàn)證是否過(guò)期,是否有最新的版本;
no-store:這才是真正的不允許緩存,比如秒殺頁(yè)面這樣變化非常頻繁的頁(yè)面就不適合緩存
no-cache
must-revalidate:一旦資源過(guò)期(比如已經(jīng)超過(guò)max-age),在成功向原始服務(wù)器驗(yàn)證之前,緩存不能用該資源響應(yīng)后續(xù)請(qǐng)求。
三者的區(qū)別如下:
Cache-Control 只能刷新數(shù)據(jù),但不能很好地利用緩存,又因?yàn)榫彺鏁?huì)失效,使用前還必須要去服務(wù)器驗(yàn)證是否是最新版,存在一定的性能穩(wěn)定,所以 HTTP 又引入了條件緩存。
條件請(qǐng)求以 If 開頭,有「If-Match」,「If-Modified-Since」,「If-None-Match」,「If-Range」,「If-Unmodified-Since」五個(gè)頭字段,我們最常用的是「if-Modified-Since」和「If-None-Match」這兩個(gè)頭字段,所以重點(diǎn)介紹一下。
if-Modified-Since:指的是文件最后修改時(shí)間,服務(wù)器只在所請(qǐng)求的資源在給定的日期時(shí)間之后對(duì)內(nèi)容進(jìn)行過(guò)修改的情況下才會(huì)將資源返回,如果請(qǐng)求的資源從那時(shí)起未經(jīng)修改,那么返回一個(gè)不帶有消息主體的 304 響應(yīng),需要第一次請(qǐng)求提供「Last-modified」,第二次請(qǐng)求就可以在 「if-Modified-Since」首部帶上此值了。
If-None-Match:條件請(qǐng)求首部,對(duì)于GET和HEAD請(qǐng)求方法來(lái)說(shuō),當(dāng)且僅當(dāng)服務(wù)器上沒(méi)有任何資源的 ETag 屬性值與這個(gè)首部中列出的相匹配的時(shí)候,服務(wù)器端會(huì)才返回所請(qǐng)求的資源,響應(yīng)碼為 200,
注意到上圖中有個(gè) ETag 返回,它是實(shí)體標(biāo)簽(Entity Tag)的縮寫,是資源的唯一標(biāo)識(shí),主要解決修改時(shí)間無(wú)法準(zhǔn)確區(qū)分文件變化的問(wèn)題,比如文件在一秒內(nèi)修改了多次,由于修改時(shí)間是秒級(jí)的,用 if-Modified-Since 就會(huì)誤認(rèn)為資源沒(méi)有變化,而每次文件修改了都會(huì)修改 ETag,也就是說(shuō) ETag 可以精確識(shí)別資源的變動(dòng), 所以如果對(duì)資源變化很敏感覺(jué)的話,應(yīng)該用 If-None-Match。
注:ETag 有強(qiáng),弱之分,強(qiáng) ETag 要求資源在字節(jié)級(jí)別必須完全相符,弱 ETag 在值前有 「W/」標(biāo)記,只要求資源在語(yǔ)義上沒(méi)啥變化,比如加了幾個(gè)空格等等。
需要注意的是不管是 if-Modified-Since 還是 If-None-Match,這兩者只會(huì)在資源過(guò)期(即存活時(shí)間超 max-age)后才會(huì)觸發(fā),但如果在開發(fā)環(huán)境下,緩存可能會(huì)影響我們聯(lián)調(diào),我們希望每次請(qǐng)求都從 server 拿,而不是緩存里,該怎么辦?這種情況下就要用到刷新或者強(qiáng)制刷新了。如果是刷新,請(qǐng)求頭里會(huì)加上一個(gè)Cache-Control: max-age=0,代表需要最新的資源,瀏覽器看到了后就不會(huì)使用本地資源,會(huì)向 server 請(qǐng)求,如果是強(qiáng)制刷新,請(qǐng)求頭會(huì)加上Cache-Control: no-cache,也會(huì)向 server 發(fā)送請(qǐng)求,通常刷新和強(qiáng)制刷新效果一下。
三、實(shí)體首部
由于實(shí)體部分可以傳文本,音視頻,文件等,所以一般要指定實(shí)體類型,內(nèi)容大小,編碼類型,實(shí)體采用的語(yǔ)言(英文,法語(yǔ))等,這樣應(yīng)答方才會(huì)理解其內(nèi)容。
先來(lái)看最重要的 Content-Type,通常有以下幾種數(shù)據(jù)類型
這些數(shù)據(jù)類型被稱為 MIME 類型,指示資源所屬類型,請(qǐng)求方如果要上傳資源(一般是 POST 請(qǐng)求),可以在用 Content-Type 指定資源所屬類型,如果請(qǐng)求方想要獲取資源(GET 請(qǐng)求),可以用 Accept 請(qǐng)求頭指定想要獲取什么資源,這樣 server 找到匹配的資源后就可以在 Content-Type 中指定返回的資源類型,瀏覽器等客戶端看到后就可以據(jù)此解析處理了
如圖示:客戶端使用 Accept: image/* 告訴服務(wù)器,我想請(qǐng)求 png,jpeg,svg 等屬于 image 類型的資源,服務(wù)端返回圖片的同時(shí)用 Content-Type: image/png 告訴客戶端資源類型為 png。
通過(guò) Content-Type 指定資源的方式瀏覽器等客戶端就可以據(jù)此作出相關(guān)解析處理了,再看下開頭的問(wèn)題,為啥請(qǐng)求圖片本來(lái)希望是能直接在瀏覽器上展示的,實(shí)際上卻直接下載了呢,抓包看下它的 Content-Type
可以看到返回的 Content-Type 是 application/octet-stream,這個(gè)類型是應(yīng)用程序文件的默認(rèn)值,意思是未知的應(yīng)用程序文件,瀏覽器一會(huì)不會(huì)自動(dòng)執(zhí)行或詢問(wèn)執(zhí)行,會(huì)直接下載。那如果希望圖片能自動(dòng)展示在瀏覽器而非下載呢,server 指定一下 Content-Type 的具體類型如 image/png 這樣的形式即可,瀏覽器識(shí)別到了具體類型就會(huì)自動(dòng)解析展示出來(lái)。
其他請(qǐng)求方,應(yīng)答方的實(shí)體首部對(duì)應(yīng)關(guān)系在上文內(nèi)容協(xié)商部分也給出了,再貼一下圖:
四、連接管理
連接管理也是 HTTP 非常重要的功能,只不過(guò)因?yàn)橛脩魺o(wú)感知,所以被很多人忽略了,實(shí)際它是幕后英雄,對(duì)提升傳輸性能起了巨大的作用,也促進(jìn) HTTP 不斷不斷改版衍進(jìn)的重要原因之一,我們可以從 HTTP 的各個(gè)版本來(lái)看下連接管理的功能改進(jìn)。
首先我們知道雙方要建立可靠連接要經(jīng)過(guò) TCP 的三次握手,然后才能開始傳輸 HTTP 的報(bào)文,報(bào)文傳輸之后要經(jīng)過(guò)四次揮手?jǐn)嚅_連接
HTTP 0.9,1.0 時(shí)期,發(fā)送完 HTTP 報(bào)文后, 連接立馬關(guān)閉,這種連接被稱為短鏈接
可以看到短鏈接效率非常低下,大量時(shí)間浪費(fèi)在無(wú)意義的三次握手和四次揮手上。
于是 HTTP 1.1 對(duì)此進(jìn)行了改進(jìn),每次報(bào)文發(fā)送后不立即關(guān)閉,可復(fù)用,我們稱這樣的鏈接為長(zhǎng)鏈接,對(duì)比下圖的長(zhǎng)短鏈接可以看到,長(zhǎng)鏈接由于減少了大量無(wú)意義的三次握手,四次揮手,效率大大提升了!
我們可以在請(qǐng)求頭里明確要求使用長(zhǎng)鏈接,指定Connection: keep-alive即可,在 HTTP 1.1 就算不指定也是默認(rèn)開啟的。如果服務(wù)器支持長(zhǎng)鏈接,不管客戶端是否顯式要求長(zhǎng)鏈接,它都會(huì)在返回頭里帶上Connection: keep-alive,這樣接下來(lái)雙方就會(huì)使用長(zhǎng)連接來(lái)收發(fā)報(bào)文,客戶端如果想顯式關(guān)閉關(guān)閉,只需要指定Connection: Close頭字段即可。
長(zhǎng)連接讓傳輸效率大大提升,但新的問(wèn)題又來(lái)了,因?yàn)?HTTP 規(guī)定報(bào)文必須一發(fā)一收,如果在要連接上發(fā)多個(gè) HTTP 報(bào)文,多個(gè)報(bào)文會(huì)被累積到隊(duì)列里依次處理(不能并行處理)只要隊(duì)首的請(qǐng)求被阻塞了,后續(xù) HTTP 的發(fā)送就受到影響,這就是有名的隊(duì)頭阻塞
隊(duì)頭阻塞
雖然 HTTP 1.1 提出了管線化機(jī)制,一次可以發(fā)送多個(gè)請(qǐng)求,但依然要等前一個(gè)請(qǐng)求的響應(yīng)返回后才能處理下一個(gè)請(qǐng)求,所以這種機(jī)制聊勝于無(wú)。
我們?cè)僮屑?xì)想想,為啥會(huì)有隊(duì)頭阻塞這樣的問(wèn)題,本質(zhì)上其實(shí)是因?yàn)槲覀儧](méi)法區(qū)分每一個(gè)請(qǐng)求,再回顧一下我們上文的分層模型
以上是每一層發(fā)出的包,每個(gè)數(shù)據(jù)鏈接層的包(準(zhǔn)確地說(shuō),鏈路層的包應(yīng)該叫幀)規(guī)定的 IP 數(shù)據(jù)報(bào)的大小是有限制的,一般把這個(gè)大小限制稱為最大傳輸單元(MTU,MaximumTransmissionUnit), TCP 數(shù)據(jù)報(bào)的大小也是有限制的,我們稱之為 MSS(MaximumTransmissionUnit)
也就是說(shuō)對(duì)于每一個(gè)最終發(fā)送的以太網(wǎng)包能傳輸?shù)膽?yīng)用層數(shù)據(jù)是有限的,如果上層的應(yīng)用層要發(fā)的數(shù)據(jù)大小超過(guò)了以太網(wǎng)包的大小,就需要對(duì)其進(jìn)行拆分,分成幾個(gè)以太網(wǎng)包再傳輸。
接收方拿到每個(gè)包的應(yīng)用層數(shù)據(jù)再組裝成應(yīng)用層數(shù)據(jù),然后一個(gè)請(qǐng)求才算接收完成,響應(yīng)也是類似的原理。
這也是實(shí)體首部字段 Content-Length 存在的意義,接收方通過(guò) Content-Length 就可以判斷幾個(gè)請(qǐng)求報(bào)文組合后大小是否達(dá)到這個(gè)設(shè)置值,如果是說(shuō)明報(bào)文接收完畢,就可以對(duì)請(qǐng)求進(jìn)行解析了,如果少于這個(gè)值,說(shuō)明還需要接收請(qǐng)求包直到達(dá)到這個(gè)設(shè)定的值。
畫外音,Content-Length 指的是實(shí)體消息首部,也就是在 POST,PUT 等方法中傳輸實(shí)體數(shù)據(jù)時(shí)才會(huì)出現(xiàn),GET 請(qǐng)求不會(huì)出現(xiàn)
在底層,每個(gè)請(qǐng)求是復(fù)用同一個(gè)連接的,也就是說(shuō)每個(gè)包發(fā)送都是串行的。
在 HTTP 1.1 中,沒(méi)法區(qū)分每個(gè)數(shù)據(jù)包所屬的請(qǐng)求,所以它規(guī)定每個(gè)請(qǐng)求只能串行處理,每個(gè)請(qǐng)求通過(guò) Content-Length 判斷接收完每個(gè)請(qǐng)求的數(shù)據(jù)包并處理后,才能再處理下一個(gè)請(qǐng)求,這樣的話如果某個(gè)請(qǐng)求處理太慢就會(huì)影響后續(xù)請(qǐng)求的處理。
那么 HTTP 2.0 又是如何處理隊(duì)頭阻塞的呢,接下來(lái)我們就來(lái)揭開一下 HTTP 2.0 的面紗。
HTTP 2.0 概覽
HTTP 2.0 在性能上實(shí)現(xiàn)了很大的飛躍,更難得的是它在改進(jìn)的同時(shí)保持了語(yǔ)義的不變,與 HTTP 1.1 的語(yǔ)義完全相同!比如請(qǐng)求方法、URI、狀態(tài)碼、頭字段等概念都保留不變,這樣就消除了再學(xué)習(xí)的成本,在我們的日常軟件升級(jí)中,向下兼容非常重要,也是促進(jìn)產(chǎn)品大規(guī)模使用的一個(gè)前提,不然你一升級(jí),各種接口之類的全換了,誰(shuí)還敢升。HTTP 2.0 只在語(yǔ)法上做了重要改進(jìn),完全變更了 HTTP 報(bào)文的傳輸格式
在語(yǔ)法上主要實(shí)現(xiàn)了以下改造
1、頭部壓縮
HTTP 1.1 考慮了 body 的壓縮,但沒(méi)有考慮 header 的壓縮, 經(jīng)常出現(xiàn)傳了頭部上百,上千字節(jié),但 Body 卻只有幾十字節(jié)的情況,浪費(fèi)了帶寬,而且我們知道從 1.1 開始默認(rèn)是長(zhǎng)連接,幾百上千個(gè)請(qǐng)求都用的這個(gè)連接,而請(qǐng)求的頭部很多都是重復(fù)的,造成了帶寬的極大浪費(fèi)!想象一下面的這個(gè)請(qǐng)求,為了傳輸區(qū)區(qū) 「name=michale 」這幾個(gè)字節(jié),卻要傳輸如此巨量的頭部,浪費(fèi)的帶寬確實(shí)驚人。
那么 HTTP 2.0 是如何解決的呢?它開發(fā)了專門的 「HPACK」算法,在客戶端和服務(wù)器兩端建立字典,用索引號(hào)表示重復(fù)的字符串,還采用哈夫曼編碼來(lái)壓縮數(shù)字和整數(shù),可以達(dá)到最高達(dá) 90% 的壓縮率
這里簡(jiǎn)單解釋下,頭部壓縮需要在支持 HTTP 2.0 的客戶端和服務(wù)器之間:
維護(hù)一份靜態(tài)的字典(Static table),包含常見的頭部名稱,以及特別常見的頭部名稱與值的組合。這樣的話如果請(qǐng)求響應(yīng)命中了靜態(tài)字典,直接發(fā)索引號(hào)即可
維護(hù)一份相同的動(dòng)態(tài)字典(Dynamic table),可以動(dòng)態(tài)地添加字典,這樣的話如果客戶端首次請(qǐng)求由于「User-Agent: xxx」,「host:xxx」,「Cookie」這些的動(dòng)態(tài)鍵值對(duì)沒(méi)有命中靜態(tài)字典,還是會(huì)傳給服務(wù)器,但服務(wù)器收到后會(huì)基于傳過(guò)來(lái)的鍵值對(duì)創(chuàng)建動(dòng)態(tài)字典條目,如上圖的「User-Agent: xxx」對(duì)應(yīng)數(shù)字 62,「host:xxx」對(duì)應(yīng)數(shù)字 63,這樣雙方都創(chuàng)建動(dòng)態(tài)條目后,之后就可以用只傳 62,63 這樣的索引號(hào)來(lái)通信了!顯而易見,傳輸數(shù)據(jù)急遽降低,極大地提升了傳輸效率!需要注意的是動(dòng)態(tài)字典是每個(gè)連接自己維護(hù)的,也就是對(duì)于每個(gè)連接而言,首次都必須發(fā)送動(dòng)態(tài)鍵值對(duì)
支持基于靜態(tài)哈夫曼碼表的哈夫曼編碼(Huffman Coding):對(duì)于靜態(tài)、動(dòng)態(tài)字典中不存在的內(nèi)容,可以使用哈夫曼編碼來(lái)減小體積。HTTP/2 使用了一份靜態(tài)哈夫曼碼表(詳見),也需要內(nèi)置在客戶端和服務(wù)端之中。
2、二進(jìn)制格式
HTTP 1.1 是純文本形式,而 2.0 是完全的二進(jìn)制形式,它把 TCP 協(xié)議的部分特性挪到了應(yīng)用層,把原來(lái)的 Header+Body 消息打散為了數(shù)個(gè)小版的二進(jìn)制"幀"(Frame),“HEADERS”幀存放頭數(shù)據(jù)、“DATA”幀存放實(shí)體數(shù)據(jù)
HTTP 2.0 二進(jìn)制幀
這些二進(jìn)制幀只認(rèn) 0,1,基于這種考慮 http 2.0 的協(xié)議解析決定采用二進(jìn)制格式,使用二進(jìn)制的形式雖然對(duì)人不友好,但大大方便了計(jì)算機(jī)的解析,原來(lái)使用純文本容易出現(xiàn)多義性,如大小寫,空白字符等,程序在處理時(shí)必須用復(fù)雜的狀態(tài)機(jī),效率低,還麻煩。而使用二進(jìn)制的話可以嚴(yán)格規(guī)定字段大小、順序、標(biāo)志位等格式,“對(duì)就是對(duì),錯(cuò)就是錯(cuò)”,解析起來(lái)沒(méi)有歧義,實(shí)現(xiàn)簡(jiǎn)單,而且體積小、速度快。
3. 流
HTTP 2 定義了「流」(stream)的的概念,它是二進(jìn)制幀的雙向傳輸序列,同一個(gè)消息往返的數(shù)據(jù)幀 (header 幀和 data 幀)會(huì)分配一個(gè)唯一的流 ID,這樣我們就能區(qū)分每一個(gè)請(qǐng)求。在這個(gè)虛擬的流里,數(shù)據(jù)幀按先后次序傳輸,到達(dá)應(yīng)答方后,將這些數(shù)據(jù)幀按它們的先后順序組裝起來(lái),最后解析 HTTP 1.1 的請(qǐng)求頭和實(shí)體。
在同一時(shí)間,請(qǐng)求方可以在流里發(fā)請(qǐng)求,應(yīng)答方也可以也流里發(fā)響應(yīng),對(duì)比 HTTP 1.1 一個(gè)連接一次只能處理一次請(qǐng)求-應(yīng)答,吞吐量大大提升
如圖示,一個(gè)連接里多個(gè)流可以同時(shí)收發(fā)請(qǐng)求-應(yīng)答數(shù)據(jù)幀,每個(gè)流中數(shù)據(jù)包按序傳輸組裝
所有的流都是在同一個(gè)連接中流動(dòng)的,這也是 HTTP 2.0 經(jīng)典的多路復(fù)用( Multiplexing),另外由于每個(gè)流都是獨(dú)立的,所以誰(shuí)先處理好請(qǐng)求,誰(shuí)就可以先將響應(yīng)通過(guò)連接發(fā)送給對(duì)方,也就解決了隊(duì)頭阻塞的問(wèn)題。
如圖示,在 HTTP 2 中,兩個(gè)請(qǐng)求同時(shí)發(fā)送,可以同時(shí)接收,而在 HTTP 1.1 中必須等上一個(gè)請(qǐng)求響應(yīng)后才能處理下一個(gè)請(qǐng)求
HTTP 2 的隊(duì)頭阻塞
HTTP 2 引入的流,幀等語(yǔ)法層面的改造確實(shí)讓其傳輸效率有了質(zhì)的飛躍,但是它依然存在著隊(duì)頭阻塞,這是咋回事?
其實(shí)主要是因?yàn)?HTTP 2 的分幀主要是在應(yīng)用層處理的,而分幀最終還是要傳給下層的 TCP 層經(jīng)由它封裝后再進(jìn)行傳輸,每個(gè)連接最終還是順序傳輸這些包,
如圖示:流只是我們虛擬出來(lái)的概念,最終在連接層面還是順序傳的
TCP 是可靠連接,為了保證這些包能順序傳給對(duì)方,會(huì)進(jìn)行丟包重傳機(jī)制,如果傳了三個(gè)包,后兩個(gè)包傳成功,但第一個(gè)包傳失敗了,TCP 協(xié)議棧會(huì)把已收到的包暫存到緩存區(qū)中,停下等待第一個(gè)包的重傳成功,這樣的話在網(wǎng)絡(luò)不佳的情況下只要一個(gè)包阻塞了,由于重傳機(jī)制,后面的包就被阻塞了,上層應(yīng)用由于拿不到包也只能干瞪眼了。
由于這是 TCP 協(xié)議層面的機(jī)制,無(wú)法改造,所以 HTTP 2 的隊(duì)頭阻塞是不可避免的。HTTP 3 對(duì)此進(jìn)行了改進(jìn),將 TCP 換成了 UDP 來(lái)進(jìn)行傳輸,由于 UDP 是無(wú)序的,不需要斷建連,包之間沒(méi)有依賴關(guān)系,所以從根本上解決了“隊(duì)頭阻塞”, 當(dāng)然由于 UDP 本身的這些特性不足以支撐可靠的通信,所以 Google 在 UDP 的基礎(chǔ)上也加了 TCP 的連接管理,擁塞窗口,流量控制等機(jī)制,這套協(xié)議我們稱之為 QUIC 協(xié)議。
HTTP 1,2,3 三者對(duì)比
可以看到不管是 HTTP 2 還是 3 它們底層都支持用 TLS,保留了 HTTPS 安全的特性,這也可以理解,在互聯(lián)網(wǎng)發(fā)展如此迅猛的今天,各大企業(yè)也越來(lái)越重視通信安全。
總結(jié):HTTP 的特點(diǎn)
說(shuō)了這么多 HTTP,接下來(lái)我們簡(jiǎn)單總結(jié)一下 HTTP 的特點(diǎn)
1、靈活可擴(kuò)展
這可以說(shuō)是 HTTP 最重要的特點(diǎn),也是 HTTP 能大行其道并碾壓其他協(xié)議稱霸于世的根本原因!它只規(guī)定了報(bào)文的基本格式,用空格分隔單詞,用換行分隔字段,「header+body」等基本語(yǔ)義,但在語(yǔ)法層面并不做限制,它并沒(méi)有強(qiáng)制規(guī)定 header 里應(yīng)該傳什么,也沒(méi)有限制它底層應(yīng)該用什么傳輸,這也為 HTTPS 添加 SSL/TLS 層來(lái)加密傳輸,HTTP2 使用幀,流來(lái)進(jìn)行多路復(fù)用,HTTP 3 使用 UDP 徹底解決解決隊(duì)頭阻塞問(wèn)題提供了可能!后續(xù)如果又出現(xiàn)了牛逼的協(xié)議,底層也隨時(shí)可以替換
2、可靠傳輸
不管底層是 TCP 還是 QUIC(底層使用 UDP),它們的傳輸都是可靠的,都能保證應(yīng)用層請(qǐng)求響應(yīng)的可靠傳輸,這一點(diǎn)很重要,不然傳輸過(guò)程中缺胳膊少腿,應(yīng)用層就無(wú)法解析了。
3、應(yīng)用層協(xié)議
HTTP 是一個(gè)協(xié)議,很多人把 HTTP 和 TCP 混在了一起, 就像前文所述,TCP 相當(dāng)于高速公路,為我們提供了可靠的傳輸通道,HTTP 規(guī)定貨物的表現(xiàn)形式(header + 空行+ body),貨物是否可從中間站運(yùn)回(緩存機(jī)制)高速公路是否應(yīng)該關(guān)閉(連接控制),至于貨物如何可靠傳輸?shù)侥康牡?,那?TCP 的事,與 HTTP 無(wú)關(guān),這一點(diǎn)也是不少人經(jīng)常搞混的。
4、請(qǐng)求應(yīng)答模式
HTTP 需要請(qǐng)求方發(fā)起請(qǐng)示,然后應(yīng)答方對(duì)此作出響應(yīng),應(yīng)答方不會(huì)無(wú)緣無(wú)故地發(fā)響應(yīng)給請(qǐng)求方,另外請(qǐng)求和應(yīng)答方的角色是可以互換的,比如 HTTP 2 中 server 是可以主動(dòng) push 給 client 的,這種情況下 server 即為請(qǐng)求方,cilent 即為應(yīng)答方
5、無(wú)狀態(tài)
HTTP 的每個(gè)請(qǐng)求-應(yīng)答都是無(wú)關(guān)的,即每次的收發(fā)報(bào)文都是完全獨(dú)立,沒(méi)有任何聯(lián)系的,服務(wù)器收到每個(gè)請(qǐng)求響應(yīng)后,不會(huì)記錄這個(gè)請(qǐng)求的任何信息,有人說(shuō)不對(duì)啊,為啥我添加多次購(gòu)物車,購(gòu)物車列表還能保留我之前加過(guò)的商品呢?這就要簡(jiǎn)單地了解一下 Session 和 Cookie 了,Session 可以認(rèn)為是 Server 用來(lái)追蹤每個(gè)用戶行為的一個(gè)會(huì)話,server 會(huì)給每個(gè)用戶分配一個(gè)這個(gè) Session 的 session,通過(guò) Cookie 這個(gè)頭字段返回給 client,之后 client 每次請(qǐng)求都會(huì)在 Cookie 里帶上這個(gè) sessionId,server 拿到 sessionId 之后就知道是哪個(gè)用戶發(fā)起的了
回答開篇問(wèn)題
大部分問(wèn)題已經(jīng)隱藏在本文的知識(shí)點(diǎn)講述中了,還有一些問(wèn)題,我們一起來(lái)看下
1、為什么說(shuō)反爬是個(gè)偽命題
因?yàn)椴还苁钦5目蛻舳苏?qǐng)求,還是爬蟲請(qǐng)求,都要遵循 HTTP 協(xié)議,爬蟲發(fā)的 HTTP 報(bào)文與正常用戶請(qǐng)求沒(méi)有本質(zhì)區(qū)別,服務(wù)器無(wú)法區(qū)分,服務(wù)器只能通過(guò)一些 trick,如短時(shí)間內(nèi)發(fā)現(xiàn)某個(gè) ip 的請(qǐng)求特別頻繁認(rèn)定其為爬蟲,直接拒絕服務(wù),或者通過(guò)驗(yàn)證碼的方式等提高爬蟲的難度,但無(wú)法徹底杜絕,當(dāng)然爬蟲也有應(yīng)對(duì)之道,它可以在請(qǐng)求的時(shí)候不停地更換自己的 ip 以達(dá)到欺騙 server 的目的,也可以破解驗(yàn)證碼,爬蟲和反爬也是在相愛相殺中不斷提高破解與被破解的手段了。
2、301 與 302 有啥區(qū)別
這一點(diǎn)其實(shí)我在之前的高性能短鏈設(shè)計(jì)有提過(guò),在短鏈設(shè)計(jì)中,重定向是一個(gè)必須要考慮的點(diǎn)
如圖示,輸入 A 網(wǎng)址后,會(huì)重定向到 B 網(wǎng)址,就需要考慮是用 301 還是 302,兩者的區(qū)別如下
301:代表永久重定向,也就是說(shuō)第一次請(qǐng)求拿到長(zhǎng)鏈接后,下次瀏覽器再去請(qǐng)求短鏈的話,不會(huì)向短網(wǎng)址服務(wù)器請(qǐng)求了,而是直接從瀏覽器的緩存里拿,這樣在 server 層面就無(wú)法獲取到短網(wǎng)址的點(diǎn)擊數(shù)了,如果這個(gè)鏈接剛好是某個(gè)活動(dòng)的鏈接,也就無(wú)法分析此活動(dòng)的效果。所以我們一般不采用 301。
302:代表臨時(shí)重定向,也就是說(shuō)每次去請(qǐng)求短鏈都會(huì)去請(qǐng)求短網(wǎng)址服務(wù)器(除非響應(yīng)中用 Cache-Control 或 Expired 暗示瀏覽器緩存),這樣就便于 server 統(tǒng)計(jì)點(diǎn)擊數(shù),所以雖然用 302 會(huì)給 server 增加一點(diǎn)壓力,但在數(shù)據(jù)異常重要的今天,這點(diǎn)代碼是值得的,所以推薦使用 302!
3、HTTP 1.1 唯一一個(gè)要求請(qǐng)求頭必傳的字段是哪個(gè),它有什么作用
是 Host, HTTP 1.1 允許一臺(tái)服務(wù)器搭建多個(gè) Web 站點(diǎn),也就是說(shuō)一臺(tái)服務(wù)器可以托管多個(gè)域名對(duì)應(yīng)的網(wǎng)站,這樣的話必須指定 Host,到達(dá)服務(wù)器后才能找到對(duì)應(yīng)的網(wǎng)址向其請(qǐng)求。
4. 老生常談很多人的誤解:GET 和 POST 的區(qū)別是啥
這個(gè)問(wèn)題很多人都有誤解,最常見的誤解比如 POST 請(qǐng)求安全,GET參數(shù)通過(guò) URL 傳遞,POST 放在請(qǐng)求體里等等,這些回答沒(méi)有 GET 到點(diǎn)上,其實(shí) GET,POST 都可以用來(lái)傳輸信息,GET 請(qǐng)求可以用 body 傳輸數(shù)據(jù),在POST 請(qǐng)求時(shí)你可以不用不用 body 而用 url 傳輸數(shù)據(jù),這都是可以實(shí)現(xiàn)的,這就好比你可以用救護(hù)車來(lái)運(yùn)貨,也可以用卡車來(lái)救人,都沒(méi)有問(wèn)題的,但這不符合人們的認(rèn)知, 不符合 HTTP 對(duì)其定義的語(yǔ)義,無(wú)規(guī)矩不成方圓,遵循語(yǔ)義大家溝通才能更高效,所以其實(shí)它們的區(qū)別只在語(yǔ)義上有區(qū)別,至于安全,那是 HTTPS 的事了。
巨人的肩膀
極客時(shí)間-透視 HTTP 協(xié)議
https://blog.konghy.cn/2019/08/06/dns-overview/
https://aws.amazon.com/cn/route53/what-is-dns/
https://developer.mozilla.org/en-US/docs/Web/HTTP
HTTP 狀態(tài)碼:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
http://entropymine.com/jason/testbed/mime/
https://httpwg.org/specs/rfc7541.html#static.table.definition
https://imququ.com/post/header-compression-in-http2.html
https://developers.google.com/web/fundamentals/performance/http2