51 張圖助你完全掌握 HTTP 協議

前言

若是說 TCP/IP 協議是互聯網通訊的根基,那麼 HTTP 就是其中當之無愧的王者,小到平常生活中的遊戲,新聞,大到雙十一秒殺等都能看到它的身影,據 NetCraft 統計,目前全球至少有 16 億個網站、2 億多個獨立域名,而這個龐大網絡世界的底層運起色制就是 HTTP,能夠絕不誇張的說,無 HTTP 不通訊!程序員

畫外音: TCP/IP 協議羣以下, IP 不是 IP 地址,是 Internet Protocol 的簡稱算法

HTTP 應用如此普遍,咱們確實必要好好學習下它,不只有助於咱們理解和解釋工做中的強制刷新,防盜鏈等現象和原理,還讓咱們在設計開源中間件時會有所啓發,好比在設計 MQ, Dubbo 這些組件時,第一要務就是要設計協議,在其中你或多或少能看到 HTTP 協議的影子,學習了 HTTP 能讓你在設計中間件等組件協議時,提供很好的思路跨域

本文將會全面剖析 HTTP 的設計理念,助你完全掌握 HTTP,相信看完如下問題你會手到擒來!瀏覽器

  1. 什麼是 HTTP,它有什麼特色,爲何說 HTTP 是萬能的
  2. 爲何說反爬是個僞命題
  3. 簡要介紹一下 HTTP 0.9, 1.1, 2.0, 3.0 的特色
  4. 曾經在我司有一位同事在大羣裏拋了一個問題:在瀏覽器的地址欄輸入圖片 url,想預覽一下圖片,結果卻變成了下載圖片,你能替他解釋一下其中的緣由嗎
  5. DNS 協議瞭解多少,什麼是 DNS 負載均衡
  6. HTTP 1.1 惟一一個要求請求頭必傳的字段是哪一個,它有什麼做用
  7. no-cache 真的是不緩存的意思?你對 HTTP 的緩存瞭解多少
  8. 爲何刷新了瀏覽器器卻抓不到請求,而強制刷新卻能夠抓到,強制刷新到底作了什麼事
  9. 301 和 302 的區別是啥
  10. 各類協議與 HTTP 的關係
  11. 老生常談不少人的誤解:GET 和 POST 的區別是啥

本文將會從如下幾點來展開闡述 HTTP緩存

  1. 什麼是 HTTP
  2. 與 HTTP 相關的各類協議
  3. HTTP 內容協商
  4. HTTP 緩存
  5. HTTP 鏈接與HTTP 2的改進

什麼是 HTTP

HTTP 全稱 HyperText Transfer Protocol「超文本傳輸協議」,拆成三個部分來看,即「超文本」,「傳輸」,「協議」安全

51 張圖助你完全掌握 HTTP 協議

 

超文本:即「超越了普通文本的文本」,即音視頻,圖片,文件的混合體,你們常見的網頁不少就內嵌了 img, video 這些標籤解析展示而成的圖片,視頻等,除了這些超文本內容外,最關鍵的是超文本中含有超連接,超連接意味着網頁等文件內容的超文本上能夠點擊連接到其餘頁面上,互聯網就是經過這樣的超連接構成的。服務器

51 張圖助你完全掌握 HTTP 協議

 

傳輸: 傳輸意味着至少有兩個參與者,好比 A,B,這意味着 HTTP 協議是個雙向協議,通常是將「超文本」按照約定的協議以二進制數據包的形式從 A 傳到 B 或 B 傳到 A, A <===> B,咱們把發起請求的叫請求方,接到請求後返回數據的那一方稱爲應答方,但須要注意的是傳輸也不限於兩個參與者,容許中間有中轉或者接力,只要參與者間遵循約定的協議便可傳輸。網絡

51 張圖助你完全掌握 HTTP 協議

 

如圖示:傳輸能夠有多個參與者,只要遵循相應的協議便可session

協議:HTTP 是一個協議,啥是協議?在平常生活中協議並很多見,好比咱們租房時簽定的租房協議,入職後和企業簽定的勞動合同協議,「協」意味着至少有兩人蔘與,「議」意味着雙方要就某項條款達成一致,好比租房協議規定月付 xx 元,勞動合同協議規定月工資 xx 元,協議即對通訊雙方的約束,雙方按照約定傳輸數據才能進行明白對方的意思,不然即是雞同鴨講。app

通過以上解釋,咱們能夠給 HTTP 下一個比較準確的定義了:

HTTP 是一個在計算機世界裏專門在兩點之間傳輸文字、圖片、音頻、視頻等超文本數據的約定和規範。

與 HTTP 相關的各類協議

HTTP 雖然在當今互聯網通訊中佔據統計地位,但要讓它生效還必須依賴其餘協議或規範的支持

1. URI 和 URL

首先既然咱們要在兩點之間傳輸超文本,那這個超文本該怎麼表示?超文本即資源,互聯網上資源這麼多,如何惟一標記互聯網上的資源。

使用 URI(Uniform Resource Identifier) 即統一資源標識符就能夠惟必定位互聯網上的資源。URI 你們比較少聽過,你們更熟悉的多是 URL(Uniform Resource Locator),即統一資源定位符,URL 實際上是 URI 的一種子集,區別就是URI 定義資源,而 URL 不單定義這個資源,還定義瞭如何找到這個資源。

URL 主要由四個部分組成:協議、主機、端口、路徑

51 張圖助你完全掌握 HTTP 協議

 

協議:即通訊雙方指定的傳輸協議,應用最廣的固然是本文要介紹的 http 協議,除此以外還有 ftp, mailto,file 等協議。

主機名:存放資源的服務器主機名或 IP 地址,當前有時候服務器因爲安全緣由還須要對用戶進行認證,須要提供用戶名和密碼,此時須要在 hostname前加 username:password。

端口:整數,可選,省略時使用方案的默認端口,各類傳輸協議都有默認的端口,如 HTTP 默認用的 80 端口,HTTPS 用的 443 端口,傳輸層協議正是利用這些端口號識別本機中正在進行通訊的應用程序,並準確地將數據傳輸。

路徑:即資源在主機上的存放路徑,通常表示主機的目錄或文件地址

parameter:用於指定特殊參數的可選項。

query:查詢字符串,可選,用於給動態網頁或接口傳遞參數,可有多個參數,用「&」符號隔開,每一個參數的名和值用「=」符號隔開。

fragment:瀏覽器專用,用於指定網絡資源中的片段,指定後打開網頁可直接定位到 fragment 對應的位置。

例子以下

51 張圖助你完全掌握 HTTP 協議

 

2. TCP/IP 協議

在上述 URL 地址中,若是你指定了「www.example.com 」 這樣的主機名,最終會被 DNS 解析成 IP 地址而後纔開始通訊,爲啥主機名最終要被解析成 IP 地址才能通訊呢,由於 HTTP 協議使用的是 TCP/IP 協議棧,協議棧就是這樣規定的,一塊兒來看看 TCP/IP 協議棧各層的功能。

51 張圖助你完全掌握 HTTP 協議

 

TCP/IP協議棧

TCP/IP 協議棧總共有四層

  1. link layer: 連接層,負責在以太網,WIFI 這樣的底層網絡上發送原始數據包,工做在網卡這一層,使用 MAC 地址來標記網絡上的設備,因此也叫 MAC 層
  2. Internet layer: 網絡層,IP 協議即處於這一層,提供路由和尋址的功能,使兩終端系統可以互連且決定最佳路徑,並具備必定的擁塞控制和流量控制的能力。至關於傳送郵件時須要地址通常重要
  3. transport layer: 傳輸層,該層的協議爲應用進程提供端到端的通訊服務,這層主要有 TCP,UDP 兩個協議,TCP 提供面向鏈接的數據流支持、可靠性、流量控制、多路複用等服務,UDP不提供複雜的控制機制,利用 IP 提供面向無鏈接的簡單消息傳輸
  4. application layer: 即應用層,前面三層已經爲網絡通訊打下了堅實的基礎,這層可發揮的空間就大不少了,應用層協議能夠想象爲不一樣的服務,每一個應用層協議都是爲了解決某一類應用問題而生的,每個服務須要用不一樣的協議,規定應用進程在通訊時所遵循的協議。

咱們能夠把前面三層認爲是高速公路及其配套的基礎設施,至於要傳什麼貨物,高速公路是否要關閉等則由應用層決定。

利用 TCP/IP 協議族進行網絡通訊時,會經過分層順序與對方進行通訊。每一個分層中,都會對所發送的數據附加一個首部,在這個首部中包含了該層必要的信息,如發送的目標地址以及協議相關信息。

51 張圖助你完全掌握 HTTP 協議

 

接收方收到數據後,一樣的,每一層也會解析其首部字段,直到應用層收到相應的數據。

51 張圖助你完全掌握 HTTP 協議

 

圖片來自文末參考連接

經過這樣分層的方式,每一個層各司其職,只要管好本身的工做便可,可擴展性很好,好比對於 HTTP 來講,它底層能夠用 TCP,也能夠用 UDP 來傳輸,哪天若是再出現了更牛逼的協議,也能夠替換之,不影響上下層,這就是計算機中比較有名的分層理論:沒有什麼是分層解決不了的,若是有那就再分一層。

IP 包的首部中定義了32 位的源 IP 地址和目的 IP 地址,以下圖所示址

51 張圖助你完全掌握 HTTP 協議

 

因此應用層在請求傳輸數據時必須事先要知道對方的 IP 地址,而後才能開始傳輸。

2. DNS 協議

由上一節可知請求時須要事先知道對方的 IP 地址,但 IP 地址是由「161.117.232.65」這樣的數字組成的,正常人根本記不住,想一想看,若是我要上個百度,還要先知道它的 IP 地址,那豈不是要瘋掉,那怎麼辦,聯繫生活場景,想一想看,若是咱們要打某人電話,記不住他的電話號碼,是否是要先到電話本找某我的的名字,而後再打,電話本起的做用就是把姓名翻譯成電話號碼。

51 張圖助你完全掌握 HTTP 協議

 

一樣的,正常人只會記住 baidu.com 這樣的網址,那就須要有相似電話本這樣的翻譯器把網址轉成 IP 地址,DNS(域名服務器)就是幹這個事的

51 張圖助你完全掌握 HTTP 協議

 

上面只是 DNS 的簡化版本,實際上 DNS 解析沒法一步到位,比較複雜,要理解 DNS 的工做機制,首先咱們要看懂域名的層級結構,相似 www.baidu.com 這樣的網址也叫域名,是一個有層次的結構, 最右邊的被稱爲頂級域名,而後是二級域名,層級關係向左下降,最左邊的是主機名,一般用來表示主機的用途,好比 「www」 表示提供萬維網服務

51 張圖助你完全掌握 HTTP 協議

 

固然這也不是絕對的,起名的關鍵只是方便咱們記憶而已。

域名有層次之分,DNS 也是有層次之分,DNS 核心系統是一個三層的樹狀,分佈式結構,基本對應域名結構。

  1. 根域名服務器(Root DNS Server): 返回「com」,「cn」,「net」等頂級域名服務器的 IP 地址,
  2. 頂級域名服務器(Top-level DNS Server):管理各自域名下的權威域名服務器,好比 com 頂級域名服務器能夠返回 apple.com 域名服務器的 IP 地址;
  3. 權威域名服務器(Authoritative DNS Server):管理本身域名下主機的 IP 地址,好比 apple.com 權威域名服務器能夠返回 www.apple.com 的 IP 地址。

51 張圖助你完全掌握 HTTP 協議

 

DNS 核心系統層級結構

根域名服務器是關鍵,必須是衆所周知的,找到了它,下面的各級域名服務器才能找到,不然域名解析就無從談起了。既然知道了 DNS 的層次之分,那麼不難猜出請求 www.apple.com 的 DNS 解析以下

  1. 首先訪問根域名服務器,獲取「com」頂級域名服務器的地址
  2. 請求「com」頂級域名服務器,返回「apple.com」域名服務器的地址
  3. 而後返回「apple.com」域名服務器,返回 www.apple.com 的地址

以上三層解析咱們稱爲 DNS 核心解析系統,那麼你們想一想,全世界的 PC,app 等設備多如牛毛,若是每發一次請求都要按上面的 DNS 解析來獲取 IP,那估計 DNS 解析系統就要炸了,如何緩解這種壓力呢,答案是用緩存,事實上不少大公司,或網絡運營商都會自建本身的 DNS 服務器,做爲用戶查詢的代理,代替用戶請求核心 DNS 系統,這樣若是查到的話能夠緩存查詢記錄,再次收到請求的號若是有緩存結果或者緩存未過時,則直接返回原來的緩存結果,你們可能聽過 Google 的 8.8.8.8 DNS 解析服務器,這種就是 Google 自建的,咱們通常稱這種自建的爲「非權威域名服務器」。

51 張圖助你完全掌握 HTTP 協議

 

配置 DNS Server

除了非權威域名服務器,還有瀏覽器緩存,操做系統緩存(你們熟知的 /etc/hosts 就是操做系統 DSN 緩存的一種)

這樣的話若是請求 www.example.com,dns 的完整解析流程以下:

51 張圖助你完全掌握 HTTP 協議

 

一、 瀏覽器中輸入 www.example.com 後,會先查看 瀏覽器的 DNS 緩存是否過時,未過時直接取緩存的,已過時會繼續請求操做系統的緩存(/etc/hosts 文件等),還未找到,進入步驟 2

二、 請求本地地址配置的 DNS resolver(非權威域名服務器),通常由用戶的 Internet 服務提供商 (ISP) 進行管理,例若有線 Internet 服務提供商、DSL 寬帶提供商或公司網,MAC 的同窗能夠打開網絡配置中的 DNS Servers 來看下默認 ISP 提供的域名服務器(若是想用其餘的非權威域名服務器,填入便可,這樣就會覆蓋 ISP 提供的默認地址)

51 張圖助你完全掌握 HTTP 協議

 

三、 DNS resolver 將 www.example.com 的請求轉發到 DNS 根名稱服務器, 根服務器返回「.com」頂級域名服務器地址

四、 DNS resolver 再次轉發 www.example.com 的請求,此次轉發到步驟三獲取到的 .com 域的一個 TLD 名稱服務器。.com 域的名稱服務器使用與 example.com 域相關的四個 Amazon Route 53 名稱服務器的名稱來響應該請求。

五、Amazon Route 53 名稱服務器在 example.com 託管區域中查找 www.example.com 記錄,得到相關值,例如,Web 服務器的 IP 地址 (192.0.2.44),並將 IP 地址返回至 DNS 解析程序。

六、DNS resolver 最終得到用戶須要的 IP 地址。解析程序將此值返回至 Web 瀏覽器。DNS 解析程序還會將 example.com 的 IP 地址緩存 (存儲) 您指定的時長,以便它可以在下次有人瀏覽 example.com 時更快地做出響應。

咱們能夠用 dig 工具來驗證一下上面的請求流程

51 張圖助你完全掌握 HTTP 協議

 

能夠看到請求流程確實與咱們的流程圖一致!另外咱們注意到 ip 地址返回了四個,這樣的話 client 能夠隨機選擇其中一個請求,這就是咱們常說的 DNS 負載均衡,可有效緩存 server 壓力。

HTTP 報文格式

接下來咱們再介紹下 HTTP 報文格式,通訊雙方要能正常通訊,就必須遵循協議才能理解對方的信息,協議規定了 HTTP 請求和響應報文的格式。

51 張圖助你完全掌握 HTTP 協議

 

請求和響應報文都由「起始行」,「頭部」,「空行」,「實體」 四個部分組成,只不過起始行稍有不一樣。

請求報文格式

先來看下請求報文的格式

51 張圖助你完全掌握 HTTP 協議

 

示例以下:

51 張圖助你完全掌握 HTTP 協議

 

請求方法比較常見的有如下幾類

一、 GET: 請求 URL 指定的資源,指定的資源經服務器端解析後返回響應內容,GET 方法具備冪等性,即不管請求屢次,都只會返回資源,而不會額外建立或改變資源, GET 請求只傳請求頭,不傳請求體。

二、 HEAD: 語義上與 GET 相似,但 HEAD 的響應只有請求頭,沒有請求體

三、 POST: 主要用來建立,修改,上傳資源,不具備冪等性,通常將要請求的資源附在請求體上傳輸

四、 PUT: 修改資源,基本上不用,由於 POST 也具備修改的語義,因此基本上線上大多用 POST 來代替。

五、 OPTIONS: 列出可對資源實行的方法,這個方法不多見,但在跨域中會用到,也比較重要,後文結合 Cookie 談到安全問題時咱們會再提

協議版本指定了客戶端當前支持的 HTTP 版本,HTTP 目前通用的有 1.1, 2.0 三個版本,若是請求方指定了 1.1,應答方收到以後也會使用 HTTP 1.1 協議進行回覆。

響應報文格式

響應行的報文格式以下

51 張圖助你完全掌握 HTTP 協議

 

示例以下

51 張圖助你完全掌握 HTTP 協議

 

響應報文主要有以下五類狀態碼:

  • 1××:提示信息,表示目前是協議處理的中間狀態,還須要後續的操做;
  • 2××:成功,報文已經收到並被正確處理;
  • 3××:重定向,資源位置發生變更,須要客戶端從新發送請求;
  • 4××:客戶端錯誤,請求報文有誤,服務器沒法處理;
  • 5××:服務器錯誤,服務器在處理請求時內部發生了錯誤。

經常使用的狀態碼及其釋義以下(更詳細的能夠參考文末參考連接)

51 張圖助你完全掌握 HTTP 協議

 

請求和響應頭

請求和響應頭部報文的 header 格式基本都是同樣的,都是 key-value 的形式,key 和 value 都是用 「: 」分隔,此外 HTTP 頭字段很是靈活,除了使用標準的 Host,Connection 等頭字段外,也能夠任意添加自定義頭,這就給 HTTP 協議帶來了無限的擴展可能!

經常使用頭字段

HTTP 協議規定了很是多的頭字段,能夠實現各類各樣的功能,但基本上能夠分爲如下四類

  1. 通用字段:在請求頭和響應頭裏均可以出現;
  2. 請求字段:僅能出如今請求頭裏,進一步說明請求信息或者額外的附加條件;
  3. 響應字段:僅能出如今響應頭裏,補充說明響應報文的信息;
  4. 實體字段:它實際上屬於通用字段,但專門描述 body 的額外信息。

對 HTTP 報文的解析和處理其實本質上就是對頭字段的處理,HTTP 的鏈接管理,緩存控制,內容協商等都是經過頭字段來處理的,理解了頭字段,基本上也就理解了 HTTP,因此理解頭字段很是重要。接下來咱們就來看看這些頭部字段的具體含義

一、通用字段

51 張圖助你完全掌握 HTTP 協議

 

二、請求首部字段

51 張圖助你完全掌握 HTTP 協議

 

三、響應首部字段

51 張圖助你完全掌握 HTTP 協議

 

四、實體首部字段

51 張圖助你完全掌握 HTTP 協議

 

這麼多字段該怎麼記呢,死記硬背確定不行,從功能上理解會更易懂,HTTP 主要爲咱們提供了以下功能

1、內容協商

內容協商機制是指客戶端和服務器端就響應的資源內容進行交涉,而後提供給客戶端最爲適合的資源。內容協商會以響應資源的語言、字符集、編碼方式等做爲判斷的基準,舉一個常見的例子,你們在 Chrome 上設置不一樣的語言,主頁也就展現不一樣的內容

51 張圖助你完全掌握 HTTP 協議

 

內容協商示例

請求報文的「Accept」,「Accept-Charset」,「Accept-Encoding」,「Accept-Language」,「Content-Language」,即爲內容協商的斷定標準。

舉個例子

51 張圖助你完全掌握 HTTP 協議

 

上圖表示的含義以下

  1. 客戶端:請給我類型爲 text/*,語言爲 en,編碼類型最好爲 br(若是沒有 gzip 也可接受)的資源。
  2. 服務器:好的,我找到了編碼類型爲 br(Content-Encoding: br),內容爲 en(Content-Language: en)的資源,它的 url 爲 URLe(Content-Location: /URLe),你拿到後再去請求下就有了。

內容協議請求頭和對應的響應頭對應關係以下:

51 張圖助你完全掌握 HTTP 協議

 

2、緩存管理

緩存管理也是 HTTP 協議很是重要的內容,這部分也是務必要掌握的。

對於資源來講,因爲有些挺長時間內都不會更新,因此不必每次請求都向 server 發起網絡請求,若是第一次請求後能保存在本地,下次請求直接在本地取,那無疑會快得多,對服務器的壓力也會減小。

涉及到緩存的請求頭爲 Cache-Control。這個緩存指令是單向的,也就是說請求中設置的指令,不必定包含在響應中,請求中若是沒有傳 Cache-Control, server 也能夠返回 Cache-Control。

51 張圖助你完全掌握 HTTP 協議

 

如圖示:客戶端發起請求後,服務器返回 Cache-Control:max-age=30,表明資源在客戶端能夠緩存 30 秒,30 秒內客戶端的請求能夠直接從緩存獲取,超過 30 秒後須要向服務器發起網絡請求。

max-age 是 HTTP 緩存控制最經常使用的屬性,表示資源存儲的最長時間,須要注意的是,時間的計算起點是響應報文的建立時刻(即 Date 字段,也就是離開服務器的時刻),超事後客戶端須要從新發起請求。

除此以外,還有其它屬性值以下:

no-cache這個是很容易形成誤解的字段,它的含義並非不容許緩存,而是能夠緩存,但在使用以前必需要去服務器驗證是否過時,是否有最新的版本;

no-store這纔是真正的不容許緩存,好比秒殺頁面這樣變化很是頻繁的頁面就不適合緩存

51 張圖助你完全掌握 HTTP 協議

 

no-cache

must-revalidate:一旦資源過時(好比已經超過max-age),在成功向原始服務器驗證以前,緩存不能用該資源響應後續請求。

三者的區別以下:

51 張圖助你完全掌握 HTTP 協議

 

Cache-Control 只能刷新數據,但不能很好地利用緩存,又由於緩存會失效,使用前還必需要去服務器驗證是不是最新版,存在必定的性能穩定,因此 HTTP 又引入了條件緩存。

條件請求以 If 開頭,有「If-Match」,「If-Modified-Since」,「If-None-Match」,「If-Range」,「If-Unmodified-Since」五個頭字段,咱們最經常使用的是「if-Modified-Since」和「If-None-Match」這兩個頭字段,因此重點介紹一下。

if-Modified-Since:指的是文件最後修改時間,服務器只在所請求的資源在給定的日期時間以後對內容進行過修改的狀況下才會將資源返回,若是請求的資源從那時起未經修改,那麼返回一個不帶有消息主體的 304 響應,須要第一次請求提供「Last-modified」,第二次請求就能夠在 「if-Modified-Since」首部帶上此值了。

51 張圖助你完全掌握 HTTP 協議

 

If-None-Match:條件請求首部,對於 GET 和 HEAD 請求方法來講,當且僅當服務器上沒有任何資源的 ETag 屬性值與這個首部中列出的相匹配的時候,服務器端會才返回所請求的資源,響應碼爲 200,

注意到上圖中有個 ETag 返回,它是實體標籤(Entity Tag)的縮寫,是資源的惟一標識,主要解決修改時間沒法準確區分文件變化的問題,好比文件在一秒內修改了屢次,因爲修改時間是秒級的,用 if-Modified-Since 就會誤認爲資源沒有變化,而每次文件修改了都會修改 ETag,也就是說 ETag 能夠精確識別資源的變更, 因此若是對資源變化很敏感受的話,應該用 If-None-Match。

注:ETag 有強,弱之分,強 ETag 要求資源在字節級別必須徹底相符,若 ETag 在以前有 「W/」標記,只要求資源在語義上沒啥變化,好比加了幾個空格等等。

須要注意的是無論是 if-Modified-Since 仍是 If-None-Match,這二者只會在資源過時(即存活時間超 max-age)後纔會觸發,但若是在開發環境下,緩存可能會影響咱們聯調,咱們但願每次請求都從 server 拿,而不是緩存裏,該怎麼辦?這種狀況下就要用到刷新或者強制刷新了。若是是刷新,請求頭裏會加上一個 Cache-Control: max-age=0,表明須要最新的資源,瀏覽器看到了後就不會使用本地資源,會向 server 請求,若是是強制刷新,請求頭會加上Cache-Control: no-cache,也會向 server 發送請求,一般刷新和強制刷新效果一下。

3、實體首部

因爲實體部分能夠傳文本,音視頻,文件等,因此通常要指定實體類型,內容大小,編碼類型,實體採用的語言(英文,法語)等,這樣應答方纔會理解其內容。

先來看最重要的 Content-Type,一般有如下幾種數據類型

51 張圖助你完全掌握 HTTP 協議

 

這些數據類型被稱爲 MIME 類型,指示資源所屬類型,請求方若是要上傳資源(通常是 POST 請求),能夠在用 Content-Type 指定資源所屬類型,若是請求方想要獲取資源(GET 請求),能夠用 Accept 請求頭指定想要獲取什麼資源,這樣 server 找到匹配的資源後就能夠在 Content-Type 中指定返回的資源類型,瀏覽器等客戶端看到後就能夠據此解析處理了

51 張圖助你完全掌握 HTTP 協議

 

如圖示:客戶端使用 Accept: image/* 告訴服務器,我想請求 png,jpeg,svg 等屬於 image 類型的資源,服務端返回圖片的同時用 Content-Type: image/png 告訴客戶端資源類型爲 png。

經過 Content-Type 指定資源的方式瀏覽器等客戶端就能夠據此做出相關解析處理了,再看下開頭的問題,爲啥請求圖片原本但願是能直接在瀏覽器上展現的,實際上卻直接下載了呢,抓包看下它的 Content-Type

51 張圖助你完全掌握 HTTP 協議

 

能夠看到返回的 Content-Type 是 application/octet-stream,這個類型是應用程序文件的默認值,意思是未知的應用程序文件,瀏覽器一會不會自動執行或詢問執行,會直接下載。那若是但願圖片能自動展現在瀏覽器而非下載呢,server 指定一下 Content-Type 的具體類型如 image/png 這樣的形式便可,瀏覽器識別到了具體類型就會自動解析展現出來。

其餘請求方,應答方的實體首部對應關係在上文內容協商部分也給出了,再貼一下圖:

51 張圖助你完全掌握 HTTP 協議

 

4、鏈接管理

鏈接管理也是 HTTP 很是重要的功能,只不過由於用戶無感知,因此被不少人忽略了,實際它是幕後英雄,對提高傳輸性能起了巨大的做用,也促進 HTTP 不斷不斷改版演進的重要緣由之一,咱們能夠從 HTTP 的各個版原本看下鏈接管理的功能改進。

首先咱們知道雙方要創建可靠鏈接要通過 TCP 的三次握手,而後才能開始傳輸 HTTP 的報文,報文傳輸以後要通過四次揮手斷開鏈接

HTTP 0.9,1.0 時期,發送完 HTTP 報文後, 鏈接立馬關閉,這種鏈接被稱爲短連接

51 張圖助你完全掌握 HTTP 協議

 

能夠看到短連接效率很是低下,大量時間浪費在無心義的三次握手和四次揮手上。

因而 HTTP 1.1 對此進行了改進,每次報文發送後不當即關閉,可複用,咱們稱這樣的連接爲長連接,對比下圖的長短連接能夠看到,長連接因爲減小了大量無心義的三次握手,四次揮手,效率大大提高了!

51 張圖助你完全掌握 HTTP 協議

 

咱們能夠在請求頭裏明確要求使用長連接,指定 Connection: keep-alive 便可,在 HTTP 1.1 就算不指定也是默認開啓的。若是服務器支持長連接,無論客戶端是否顯式要求長連接,它都會在返回頭裏帶上 Connection: keep-alive,這樣接下來雙方就會使用長鏈接來收發報文,客戶端若是想顯式關閉關閉,只須要指定 Connection: Close 頭字段便可。

長鏈接讓傳輸效率大大提高,但新的問題又來了,由於 HTTP 規定報文必須一發一收,若是在要鏈接上發多個 HTTP 報文,多個報文會被累積到隊列裏依次處理(不能並行處理)只要隊首的請求被阻塞了,後續 HTTP 的發送就受到影響,這就是有名的隊頭阻塞

51 張圖助你完全掌握 HTTP 協議

 

隊頭阻塞

雖然 HTTP 1.1 提出了管線化機制,一次能夠發送多個請求,但依然要等前一個請求的響應返回後才能處理下一個請求,因此這種機制聊勝於無。

咱們再仔細想一想,爲啥會有隊頭阻塞這樣的問題,本質上實際上是由於咱們無法區分每個請求,再回顧一下咱們上文的分層模型

51 張圖助你完全掌握 HTTP 協議

 

以上是每一層發出的包,每一個數據連接層的包(準確地說,鏈路層的包應該叫幀)規定的 IP 數據報的大小是有限制的,通常把這個大小限制稱爲最大傳輸單元(MTU, Maximum Transmission Unit), TCP 數據報的大小也是有限制的,咱們稱之爲 MSS(Maximum Transmission Unit)

51 張圖助你完全掌握 HTTP 協議

 

也就是說對於每個最終發送的以太網包能傳輸的應用層數據是有限的,若是上層的應用層要發的數據大小超過了以太網包的大小,就須要對其進行拆分,分紅幾個以太網包再傳輸。

51 張圖助你完全掌握 HTTP 協議

 

接收方拿到每一個包的應用層數據再組裝成應用層數據,而後一個請求才算接收完成,響應也是相似的原理。

這也是實體首部字段 Content-Length 存在的意義,接收方經過 Content-Length 就能夠判斷幾個請求報文組合後大小是否達到這個設置值,若是是說明報文接收完畢,就能夠對請求進行解析了,若是少於這個值,說明還須要接收請求包直到達到這個設定的值。

畫外音,Content-Length 指的是實體消息首部,也就是在 POST,PUT 等方法中傳輸實體數據時纔會出現,GET 請求不會出現

在底層,每一個請求是複用同一個鏈接的,也就是說每一個包發送都是串行的。

51 張圖助你完全掌握 HTTP 協議

 

在 HTTP 1.1 中,無法區分每一個數據包所屬的請求,因此它規定每一個請求只能串行處理,每一個請求經過 Content-Length 判斷接收完每一個請求的數據包並處理後,才能再處理下一個請求,這樣的話若是某個請求處理太慢就會影響後續請求的處理。

那麼 HTTP 2.0 又是如何處理隊頭阻塞的呢,接下來咱們就來揭開一下 HTTP 2.0 的面紗。

HTTP 2.0 概覽

HTTP 2.0 在性能上實現了很大的飛躍,更可貴的是它在改進的同時保持了語義的不變,與 HTTP 1.1 的語義徹底相同!好比請求方法、URI、狀態碼、頭字段等概念都保留不變,這樣就消除了再學習的成本,在咱們的平常軟件升級中,向下兼容很是重要,也是促進產品大規模使用的一個前提,否則你一升級,各類接口之類的全換了,誰還敢升。 HTTP 2.0 只在語法上作了重要改進,徹底變動了 HTTP 報文的傳輸格式

在語法上主要實現瞭如下改造

一、頭部壓縮

HTTP 1.1 考慮了 body 的壓縮,但沒有考慮 header 的壓縮, 常常出現傳了頭部上百,上千字節,但 Body 卻只有幾十字節的狀況,浪費了帶寬,並且咱們知道從 1.1 開始默認是長鏈接,幾百上千個請求都用的這個鏈接,而請求的頭部不少都是重複的,形成了帶寬的極大浪費!想象一下面的這個請求,爲了傳輸區區 「name=michale 」這幾個字節,卻要傳輸如此巨量的頭部,浪費的帶寬確實驚人。

51 張圖助你完全掌握 HTTP 協議

 

那麼 HTTP 2.0 是如何解決的呢?它開發了專門的 「HPACK」算法,在客戶端和服務器兩端創建字典,用索引號表示重複的字符串,還採用哈夫曼編碼來壓縮數字和整數,能夠達到最高達 90% 的壓縮率

51 張圖助你完全掌握 HTTP 協議

 

這裏簡單解釋下,頭部壓縮須要在支持 HTTP 2.0 的客戶端和服務器之間:

  1. 維護一份靜態的字典(Static table),包含常見的頭部名稱,以及特別常見的頭部名稱與值的組合。這樣的話若是請求響應命中了靜態字典,直接發索引號便可
  2. 維護一份相同的動態字典(Dynamic table),能夠動態地添加字典,這樣的話若是客戶端首次請求因爲「User-Agent: xxx」,「host:xxx」,「Cookie」這些的動態鍵值對沒有命中靜態字典,仍是會傳給服務器,但服務器收到後會基於傳過來的鍵值對建立動態字典條目,如上圖的「User-Agent: xxx」對應數字 62,「host:xxx」對應數字 63,這樣雙方都建立動態條目後,以後就能夠用只傳 62,63 這樣的索引號來通訊了!顯而易見,傳輸數據急遽下降,極大地提高了傳輸效率!須要注意的是動態字典是每一個鏈接本身維護的,也就是對於每一個鏈接而言,首次都必須發送動態鍵值對
  3. 支持基於靜態哈夫曼碼錶的哈夫曼編碼(Huffman Coding):對於靜態、動態字典中不存在的內容,能夠使用哈夫曼編碼來減少體積。HTTP/2 使用了一份靜態哈夫曼碼錶(詳見),也須要內置在客戶端和服務端之中。

二、二進制格式

HTTP 1.1 是純文本形式,而 2.0 是徹底的二進制形式,它把 TCP 協議的部分特性挪到了應用層,把原來的 Header+Body 消息打散爲了數個小版的二進制"幀"(Frame),「HEADERS」幀存放頭數據、「DATA」幀存放實體數據

51 張圖助你完全掌握 HTTP 協議

 

HTTP 2.0 二進制幀

這些二進制幀只認 0,1,基於這種考慮 http 2.0 的協議解析決定採用二進制格式,使用二進制的形式雖然對人不友好,但大大方便了計算機的解析,原來使用純文本容易出現多義性,如大小寫,空白字符等,程序在處理時必須用複雜的狀態機,效率低,還麻煩。而使用二進制的話能夠嚴格規定字段大小、順序、標誌位等格式,「對就是對,錯就是錯」,解析起來沒有歧義,實現簡單,並且體積小、速度快。

3. 流

HTTP 2 定義了「流」(stream)的的概念,它是二進制幀的雙向傳輸序列,同一個消息往返的數據幀 (header 幀和 data 幀)會分配一個惟一的流 ID,這樣咱們就能區分每個請求。在這個虛擬的流裏,數據幀按前後次序傳輸,到達應答方後,將這些數據幀按它們的前後順序組裝起來,最後解析 HTTP 1.1 的請求頭和實體。

同一時間,請求方能夠在流裏發請求,應答方也能夠也流裏發響應,對比 HTTP 1.1 一個鏈接一次只能處理一次請求-應答,吞吐量大大提高

51 張圖助你完全掌握 HTTP 協議

 

如圖示,一個鏈接裏多個流能夠同時收發請求-應答數據幀,每一個流中數據包按序傳輸組裝

全部的流都是在同一個鏈接中流動的,這也是 HTTP 2.0 經典的多路複用( Multiplexing),另外因爲每一個流都是獨立的,因此誰先處理好請求,誰就能夠先將響應經過鏈接發送給對方,也就解決了隊頭阻塞的問題。

51 張圖助你完全掌握 HTTP 協議

 

如圖示,在 HTTP 2 中,兩個請求同時發送,能夠同時接收,而在 HTTP 1.1 中必須等上一個請求響應後才能處理下一個請求

HTTP 2 的隊頭阻塞

HTTP 2 引入的流,幀等語法層面的改造確實讓其傳輸效率有了質的飛躍,可是它依然存在着隊頭阻塞,這是咋回事?

其實主要是由於 HTTP 2 的分幀主要是在應用層處理的,而分幀最終仍是要傳給下層的 TCP 層經由它封裝後再進行傳輸,每一個鏈接最終仍是順序傳輸這些包,

51 張圖助你完全掌握 HTTP 協議

 

如圖示:流只是咱們虛擬出來的概念,最終在鏈接層面仍是順序傳的

TCP 是可靠鏈接,爲了保證這些包能順序傳給對方,會進行丟包重傳機制,若是傳了三個包,後兩個包傳成功,但第一個包傳失敗了,TCP 協議棧會把已收到的包暫存到緩存區中,停下等待第一個包的重傳成功,這樣的話在網絡不佳的狀況下只要一個包阻塞了,因爲重傳機制,後面的包就被阻塞了,上層應用因爲拿不到包也只能乾瞪眼了。

因爲這是 TCP 協議層面的機制,沒法改造,因此 HTTP 2 的隊頭阻塞是不可避免的。HTTP 3 對此進行了改進,將 TCP 換成了 UDP 來進行傳輸,因爲 UDP 是無序的,不須要斷建連,包之間沒有依賴關係,因此從根本上解決了「隊頭阻塞」, 固然因爲 UDP 自己的這些特性不足以支撐可靠的通訊,因此 Google 在 UDP 的基礎上也加了 TCP 的鏈接管理,擁塞窗口,流量控制等機制,這套協議咱們稱之爲 QUIC 協議。

51 張圖助你完全掌握 HTTP 協議

 

HTTP 1,2,3 三者對比

能夠看到無論是 HTTP 2 仍是 3 它們底層都支持用 TLS,保留了 HTTPS 安全的特性,這也能夠理解,在互聯網發展如此迅猛的今天,各大企業也愈來愈重視通訊安全。

總結:HTTP 的特色

說了這麼多 HTTP,接下來咱們簡單總結一下 HTTP 的特色

一、靈活可擴展

這能夠說是 HTTP 最重要的特色,也是 HTTP 能大行其道並碾壓其餘協議稱霸於世的根本緣由!它只規定了報文的基本格式,用空格分隔單詞,用換行分隔字段,「header+body」等基本語義,但在語法層面並不作限制,它並無強制規定 header 裏應該傳什麼,也沒有限制它底層應該用什麼傳輸,這也爲 HTTPS 添加 SSL/TLS 層來加密傳輸,HTTP2 使用幀,流來進行多路複用,HTTP 3 使用 UDP 完全解決解決隊頭阻塞問題提供了可能!後續若是又出現了牛逼的協議,底層也隨時能夠替換

二、可靠傳輸

無論底層是 TCP 仍是 QUIC(底層使用 UDP),它們的傳輸都是可靠的,都能保證應用層請求響應的可靠傳輸,這一點很重要,否則傳輸過程當中缺胳膊少腿,應用層就沒法解析了。

三、應用層協議

HTTP 是一個協議,不少人把 HTTP 和 TCP 混在了一塊兒, 就像前文所述,TCP 至關於高速公路,爲咱們提供了可靠的傳輸通道,HTTP 規訂貨物的表現形式(header + 空行+ body),貨物是否可從中間站運回(緩存機制)高速公路是否應該關閉(鏈接控制),至於貨物如何可靠傳輸到目的地,那是 TCP 的事,與 HTTP 無關,這一點也是很多人常常搞混的。

四、請求應答模式

HTTP 須要請求方發起請示,而後應答方對此做出響應,應答方不會平白無故地發響應給請求方,另外請求和應答方的角色是能夠互換的,好比 HTTP 2 中 server 是能夠主動 push 給 client 的,這種狀況下 server 即爲請求方,cilent 即爲應答方

五、無狀態

HTTP 的每一個請求-應答都是無關的,即每次的收發報文都是徹底獨立,沒有任何聯繫的,服務器收到每一個請求響應後,不會記錄這個請求的任何信息,有人說不對啊,爲啥我添加屢次購物車,購物車列表還能保留我以前加過的商品呢?這就要簡單地瞭解一下 Session 和 Cookie 了,Session 能夠認爲是 Server 用來追蹤每一個用戶行爲的一個會話,server 會給每一個用戶分配一個這個 Session 的 session,經過 Cookie 這個頭字段返回給 client,以後 client 每次請求都會在 Cookie 裏帶上這個 sessionId,server 拿到 sessionId 以後就知道是哪一個用戶發起的了

51 張圖助你完全掌握 HTTP 協議

 

回答開篇問題

大部分問題已經隱藏在本文的知識點講述中了,還有一些問題,咱們一塊兒來看下

一、爲何說反爬是個僞命題

由於無論是正常的客戶端請求,仍是爬蟲請求,都要遵循 HTTP 協議,爬蟲發的 HTTP 報文與正經常使用戶請求沒有本質區別,服務器沒法區分,服務器只能經過一些 trick,如短期內發現某個 ip 的請求特別頻繁認定其爲爬蟲,直接拒絕服務,或者經過驗證碼的方式等提升爬蟲的難度,但沒法完全杜絕,固然爬蟲也有應對之道,它能夠在請求的時候不停地更換本身的 ip 以達到欺騙 server 的目的,也能夠破解驗證碼,爬蟲和反爬也是在相愛相殺中不斷提升破解與被破解的手段了。

二、301 與 302 有啥區別

這一點其實我在以前的高性能短鏈設計有提過,在短鏈設計中,重定向是一個必需要考慮的點

51 張圖助你完全掌握 HTTP 協議

 

如圖示,輸入 A 網址後,會重定向到 B 網址,就須要考慮使用 301 仍是 302,二者的區別以下

  • 301:表明永久重定向,也就是說第一次請求拿到長連接後,下次瀏覽器再去請求短鏈的話,不會向短網址服務器請求了,而是直接從瀏覽器的緩存裏拿,這樣在 server 層面就沒法獲取到短網址的點擊數了,若是這個連接恰好是某個活動的連接,也就沒法分析此活動的效果。因此咱們通常不採用 301。
  • 302:表明臨時重定向,也就是說每次去請求短鏈都會去請求短網址服務器(除非響應中用 Cache-Control 或 Expired 暗示瀏覽器緩存),這樣就便於 server 統計點擊數,因此雖然用 302 會給 server 增長一點壓力,但在數據異常重要的今天,這點代碼是值得的,因此推薦使用 302!

三、HTTP 1.1 惟一一個要求請求頭必傳的字段是哪一個,它有什麼做用

是 Host, HTTP 1.1 容許一臺服務器搭建多個 Web 站點,也就是說一臺服務器能夠託管多個域名對應的網站,這樣的話必須指定 Host,到達服務器後才能找到對應的網址向其請求。

51 張圖助你完全掌握 HTTP 協議

 

4. 老生常談不少人的誤解:GET 和 POST 的區別是啥

這個問題不少人都有誤解,最多見的誤解好比 POST 請求安全,GET參數經過 URL 傳遞,POST 放在請求體裏等等,這些回答沒有 GET 到點上,其實 GET,POST 均可以用來傳輸信息,GET 請求能夠用 body 傳輸數據,在POST 請求時你能夠不用不用 body 而用 url 傳輸數據,這都是能夠實現的,這就比如你能夠用救護車來運貨,也能夠用卡車來救人,都沒有問題的,但這不符合人們的認知, 不符合 HTTP 對其定義的語義,無規矩不成方圓,遵循語義你們溝通才能更高效,因此其實它們的區別只在語義上有區別,至於安全,那是 HTTPS 的事了。

 

推薦閱讀

程序員年薪百萬的飛馬計劃你據說過嗎?

從事開發一年的程序員能拿到多少錢?

程序員50W年薪的知識體系與成長路線。

關於【暴力遞歸算法】你所不知道的思路

開闢鴻蒙,誰作系統,聊聊華爲微內核

 

看完三件事❤️

若是你以爲這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:

點贊,轉發,有大家的 『點贊和評論』,纔是我創造的動力。

關注公衆號 『 Java鬥帝 』,不按期分享原創知識。

同時能夠期待後續文章ing🚀

相關文章
相關標籤/搜索