HTTP協議學習---(三)進階篇

一 HTTP協議body數據格式

http協議是明文傳輸的,爲了具體描述body中的數據需要在header中加入一些描述字段。

1 MIME type
MIME 把數據分成了八大類,每個大類下再細分出多個子類,形式是「type/subtype」的字符串(常用的四大類以下)
text:即文本格式的可讀數據,我們最熟悉的應該就是 text/html 了,表示超文本文檔,此外還有純文本 text/plain、樣式表 text/css 等。
image:即圖像文件,有 image/gif、image/jpeg、image/png 等。
audio/video:音頻和視頻數據,例如 audio/mpeg、video/mp4 等。
application:數據格式不固定,可能是文本也可能是二進制,必須由上層應用程序來解釋。常見的有 application/json,application/javascript、application/pdf 等,另外,如果實在是不知道數據是什麼類型,就會是 application/octet-stream,即不透明的二進制數據。

2 Encoding type
gzip:GNU zip 壓縮格式,也是互聯網上最流行的壓縮格式;
deflate:zlib(deflate)壓縮格式,流行程度僅次於 gzip;
br:一種專門爲 HTTP 優化的新壓縮算法(Brotli)

3 請求與響應的協商
HTTP 協議也使用 Accept 請求頭字段和 Content 實體頭字段,用於客戶端和服務器就語言與編碼進行「內容協商」
例如訪問https://www.baidu.com/
在這裏插入圖片描述
在這裏插入圖片描述

其中q指的是權重,與對應的類型中間用’;’隔開,最大值也是默認值爲1,最小值爲0表示不接受,*/*指的是除前面之外的其他類型。
Accept表示客戶端希望得到的MIME type,中間用’,’隔開。
Content-Type表示實際返回的MIME type。
Accept-Encoding表示客戶端支持的壓縮算法(省略表示不支持壓縮)
Content-Encoding表示服務端返回數據採用的壓縮格式(省略表示不壓縮)。
Accept-Language表示希望返回的語言
Content-Language表示實際使用的語言類型
Accept-Charset表示支持的字符集(基本不用,字符集基本都支持)
沒有Content-Charset,會在Content-Type最後直接加上返回的字符集。

不同服務器協商的結果是不一定的,有的時候,服務器會在響應頭裏多加一個 Vary 字段,記錄服務器在內容協商時參考的請求頭字段,給出一點信息,例如:
Vary: Accept-Encoding,User-Agent,Accept
這個 Vary 字段表示服務器依據了 Accept-Encoding、User-Agent 和 Accept 這三個頭字段,然後決定了發回的響應報文。

二 HTTP協議大文件傳輸方式

1 數據壓縮
Accept-Encoding中用到的基本只對純文本數據有很好的壓縮效率,但是不適用於音頻視頻類的大型二進制數據,因爲本身壓縮率已經很高了,用gzip等可能會起到反作用。

2 chunked
Transfer-Encoding: chunked,把大文件拆分成小文件分塊傳輸,然後再由接收端拼接。注意,因爲http是一問一答的形式,所以在接收端未完全獲取所有的數據塊完成拼接之前,這個請求就一直不會完成。這裏的分塊與TCP的分塊沒有任何關係,TCP本身就會對內容進行分塊傳輸,但是HTTP不關注這些內容,相當於HTTP本身先對數據進行分塊,下層的TCP在對分塊之後的數據在分塊。
Transfer-Encoding: chunked」和「Content-Length」這兩個字段是互斥的,也就是說響應報文裏這兩個字段不能同時出現。

3 數據塊的格式
①每個分塊包含兩個部分,長度頭和數據塊;
②長度頭是以 CRLF(回車換行,即\r\n)結尾的一行明文,用 16 進制數字表示長度;
③數據塊緊跟在長度頭後,最後也用 CRLF 結尾,但數據不包含 CRLF;
④最後用一個長度爲 0 的塊表示結束,即「0\r\n\r\n」。
在這裏插入圖片描述

4 範圍請求
在處理完大數據傳輸之後,對於音頻視頻可能存在獲取大數據塊中的某些數據塊,所以需要用到範圍請求。範圍請求不是 Web 服務器必備的功能,可以實現也可以不實現,所以服務器必須在響應頭裏使用字段「Accept-Ranges: bytes」明確告知客戶端:「我是支持範圍請求的」。如果不支持的話該怎麼辦呢?服務器可以發送「Accept-Ranges: none」,或者乾脆不發送「Accept-Ranges」字段
請求頭 Range 是 HTTP 範圍請求的專用字段,格式是「bytes=x-y」,其中的 x 和 y 是以字節爲單位的數據範圍。
注意 x、y 表示的是「偏移量」,範圍必須從 0 計數,例如前 10 個字節表示爲「0-9」,第二個 10 字節表示爲「10-19」,而「0-10」實際上是前 11 個字節。以下爲多種表達方式
①「0-」表示從文檔起點到文檔終點,相當於「0-99」,即整個文件;
②「10-」是從第 10 個字節開始到文檔末尾,相當於「10-99」;
③「-1」是文檔的最後一個字節,相當於「99-99」;
④「-10」是從文檔末尾倒數 10 個字節,相當於「90-99」。
服務器收到請求之後會做四件事:
①它必須檢查範圍是否合法,比如文件只有 100 個字節,但請求「200-300」,這就是範圍越界了。服務器就會返回狀態碼 416,意思是「你的範圍請求有誤,我無法處理,請再檢查一下」。
②如果範圍正確,服務器就可以根據 Range 頭計算偏移量,讀取文件的片段了,返回狀態碼「206 Partial Content」,和 200 的意思差不多,但表示 body 只是原數據的一部分。
③服務器要添加一個響應頭字段 Content-Range,告訴片段的實際偏移量和資源的總大小,格式是「bytes x-y/length」,與 Range 頭區別在沒有「=」,範圍後多了總長度。例如,對於「0-10」的範圍請求,值就是「bytes 0-10/100」。
④剩下的就是發送數據了,直接把片段用 TCP 發給客戶端,一個範圍請求就算是處理完了。
例:
請求
在這裏插入圖片描述

響應
在這裏插入圖片描述

5 補充
範圍請求與壓縮算法同時使用的時候,裏面的range表示的是壓縮之前的數據塊,因爲壓縮一般都是一邊壓縮一邊發送數據,所以並不知道壓縮後總數據塊的大小。
斷點續傳的實現原理:
先發送一個HEAD請求看服務器是否支持chunked,如果支持,則開N個線程,每個線程的range填寫本線程請求資源的大小。完了之後再進行合併即可,即使中斷,也只需要再請求本線程的range即可。
一次請求多段數據:
需額外加入一個MIME type,multipart/byteranges」,表示報文的 body 是由多段字節序列組成的,並且還要用一個參數「boundary=xxx」給出段之間的分隔標記。
在這裏插入圖片描述

例:
請求
在這裏插入圖片描述

響應
在這裏插入圖片描述

三 HTTP協議連接管理

1 早期的 HTTP 協議使用短連接,收到響應後就立即關閉連接,效率很低;

2 HTTP/1.1 默認啓用長連接,在一個連接上收發多個請求響應,提高了傳輸效率;

同時因爲TCP有慢啓動擁塞窗口等特性,新建立的鏈接發送數據一般比較慢,所以長連接會更快一些。

3 服務器會發送「Connection: keep-alive」字段表示啓用了長連接;

4 報文頭裏如果有「Connection: close」就意味着長連接即將關閉;

5 過多的長連接會佔用服務器資源,所以服務器會用一些策略有選擇地關閉長連接;
nginx的連接處理策略:
①使用「keepalive_timeout」指令,設置長連接的超時時間,如果在一段時間內連接上沒有任何數據收發就主動斷開連接,避免空閒連接佔用系統資源。
②使用「keepalive_requests」指令,設置長連接上可發送的最大請求次數。比如設置成 1000,那麼當 Nginx 在這個連接上處理了 1000 個請求後,也會主動斷開連接。

6 「隊頭阻塞」問題會導致性能下降,可以用「併發連接」和「域名分片」技術緩解。
隊頭阻塞是指因爲http是應答模式,所以所有的請求都會排隊等待發送,當有請求佔用時間較長時會阻塞剩餘請求,導致整體變慢。
併發連接指的是瀏覽器與一個域名建立多個連接,發送請求時就有多個隊列分攤。
域名分片指的是如果瀏覽器限制了一個域名只能建立N個鏈接,那麼可以對於域名進行分片,請求時客戶端與多個域名建立連接即可。例如www.baidu.com可以被分片爲www1.baidu.com www2.baidu.com,再瀏覽器層面www1與www2都指向www的IP即可

7 補充
DDOS攻擊:
利用長連接特性對服務器建立大量的連接請求導致服務器資源用盡從而拒絕提供服務
Connection:
請求頭中Connection除了keep-alive以及close之外,還有一個Upgrade,配合101狀態碼錶示切換協議。

四 HTTP協議重定向跳轉

1重定向是服務器發起的跳轉,要求客戶端改用新的 URI 重新發送請求,通常會自動進行,用戶是無感知的;

2 301/302 是最常用的重定向狀態碼,分別是「永久重定向」和「臨時重定向」;

3響應頭字段 Location 指示了要跳轉的 URI,可以用絕對或相對的形式,一定要與3xx狀態碼連用纔可以;

4重定向可以把一個 URI 指向另一個 URI,也可以把多個 URI 指向同一個 URI,用途很多;

5使用重定向時需要當心性能損耗,還要避免出現循環跳轉。

6 補充
300狀態碼少用,一般指返回多個可跳轉地址,讓用戶自己選擇。
重定向報文裏也可以使用Refresh字段實現延遲重定向。
例:Refresh:5;url=xxx 表示延遲5秒後重定向到XXX
與跳轉有關的還有一個Referer和Referrer-Policy。表示瀏覽器跳轉的來源,可以用於後續統計或者防盜鏈等。

五 HTTP的COOKIE機制

1 cookie的傳遞
響應頭字段Set-Cookie以及請求頭字段Cookie

2 cookie的屬性
①生命週期
Expires過期時間 具體的時間
Max-age(優先使用) 相對時間 單位是秒,計算方法爲瀏覽器收到報文的時
間加上過期時間計算出過期的具體時間,設置爲0表示立即失效。
如果不指定這兩個屬性則表示只在瀏覽器運行期間有限,關閉瀏覽器則失效。
②作用域
Domain 所屬的域名
Path 路徑 全路徑表示/
③安全性
XSS(跨站腳本)攻擊指的是通過JS執行腳本document.cookie等方式獲取數據進行攻擊
HttpOnly
此 Cookie 只能通過瀏覽器 HTTP 協議傳輸,禁止其他方式訪問
SameSite
「SameSite=Strict」可以嚴格限定 Cookie 不能隨着跳轉鏈接跨站發送, 而「SameSite=Lax」則略寬鬆一點,允許 GET/HEAD 等安全方法,但禁止 POST 跨 站發送。
Secure
表示這個 Cookie 僅能用 HTTPS 協議加密傳輸,明文的 HTTP 協議會禁止發送

六 HTTP的緩存機制

1 響應頭中的Cache-Control
max-age
緩存最大存活時間,單位秒。告知瀏覽器緩存的持續時間
no-store
不允許瀏覽器緩存該資源
no-cache
允許緩存資源,但是使用前需要訪問服務器看是否有最新的資源,如果有就用最新的,如果沒有就用瀏覽器本地的緩存
must-revalidate
允許緩存資源,在有效期內可以直接使用,但過了有效期就要訪問服務器看是否允許使用。
在這裏插入圖片描述

2 請求頭中的Cache-Control
與響應頭中類似,當請求頭中如果有Cache-Control: max-age=0表示獲取最新的資源,當請求頭中有Cache-Control: no-cache含義與max-age一樣,表示請求最新的資源。

3 條件請求
與普通請求不同,成功返回的是304而不是200,200表示有新的資源
瀏覽器用「Cache-Control」做緩存控制只能是刷新數據,不能很好地利用緩存數據,又因爲緩存會失效,使用前還必須要去服務器驗證是否是最新版。瀏覽器可以用兩個連續的請求組成「驗證動作」:先是一個 HEAD,獲取資源的修改時間等元信息,然後與緩存數據比較,如果沒有改動就使用緩存,節省網絡流量,否則就再發一個 GET 請求,獲取最新的版本。但是這樣成本太高,因此 HTTP 協議就定義了一系列「If」開頭的「條件請求」字段,專門用來檢查驗證資源是否過期,也就是校驗過期的責任交給瀏覽器。
首先請求資源第一次的響應報文需要提供兩個字段:
Last-modified
最近一次的更新時間
ETag
資源的唯一標識,因爲Last-modified只能精確到秒,在秒之內的改變無法區分。ETag有強弱之分,強表示資源在字節級別上必須完全相同,弱表示資源整體的語義不變。
條件請求一共有 5 個頭字段:
if-Modified-Since
If-None-Match
If-Unmodified-Since
If-Match
If-Range
注意:請求頭中同時有If-None-Match、If-Modified-Since、Cache-Control,對於服務器來說,If-None-Match、If-Modified-Since的優先級高,也就是即使請求頭有Cache-Control: no-cache,走的也是條件請求,而不是直接返回最新完整數據

七 HTTP的代理服務

1 代理的作用
①負載均衡 分散入口流量
②健康檢查 使用心跳機制連接後端服務器,發現故障阻斷服務
③安全防護 限制IP或者流量
④加密卸載 SSL/TSL加密解密,外網加密,內網解密
⑤數據過濾 攔截數據修改字段
⑥內容緩存 緩存資源減少後端壓力

2 代理信息記錄
代理服務器使用Via字段表名代理身份,在請求頭中沒經過一個代理,就需要在Via字段中追加代理的信息。非官方通常採用「X-Forwarded-For」(只記錄IP信息,第一個是客戶端的IP,之後是代理/服務器的IP)和「X-Real-IP」(只記錄一個客戶端IP)

3 代理協議
因爲X-Forwarded-For以及X-Real-IP這些字段在請求頭中,如果要獲取真實客戶端IP需要解析請求頭,但在有些場景下是不允許的或者被加密了的,所以由代理軟件公司HAProxy定義了一個事實標準的代理協議。
代理協議分爲兩版,V1,V2。V1是明文,V2是二進制格式。
它在 HTTP 報文前增加了一行 ASCII 碼文本,相當於又多了一個頭。這一行文本其實非常簡單,開頭必須是「PROXY」五個大寫字母,然後是「TCP4」或者「TCP6」,表示客戶端的 IP 地址類型,再後面是請求方地址、應答方地址、請求方端口號、應答方端口號,最後用一個回車換行(\r\n)結束。這樣代理服務器只需要解析報文前的之一行數據即可,不用理會報文內的實際數據。
例:
在這裏插入圖片描述

八 HTTP的代理緩存服務

1 Cache-Control緩存屬性區分
除了基本的max-age、no-store、no-cache 和 must-revalidate之外,由於加入了代理,對於一些資源代理有沒有權限緩存的權限做的限制,private表示資源只能在客戶端緩存,public表示資源可以在代理與客戶端都緩存。

2 代理端緩存失效後的重新驗證以及其他字段
在這裏插入圖片描述

3 客戶端緩存失效後的重新驗證
在這裏插入圖片描述

4 緩存清理
比較常用的是服務端向代理服務器發送一個PURGE(自定義的)請求方式的請求,來刪除資源
九 HTTP2
1 http2概覽
HTTP 協議取消了小版本號,所以 HTTP/2 的正式名字不是 2.0;
HTTP/2 在「語義」上兼容 HTTP/1,保留了請求方法、URI 等傳統概念;
HTTP/2 使用「HPACK」算法壓縮頭部信息,消除冗餘數據節約帶寬;
HTTP/2 的消息不再是「Header+Body」的形式,而是分散爲多個二進制「幀」; HTTP/2 使用虛擬的「流」傳輸消息,解決了困擾多年的「隊頭阻塞」問題,同時實現了「多路複用」,提高連接的利用率;
HTTP/2 也增強了安全性,要求至少是 TLS1.2,而且禁用了很多不安全的密碼套件。
2 具體實現
HTTP/2 必須先發送一個「連接前言」字符串,然後才能建立正式連接;
HTTP/2 廢除了起始行,統一使用頭字段,在兩端維護字段「Key-Value」的索引表,使用「HPACK」算法壓縮頭部;
HTTP/2 把報文切分爲多種類型的二進制幀,報頭裏最重要的字段是流標識符,標記幀屬於哪個流;
流是 HTTP/2 虛擬的概念,是幀的雙向傳輸序列,相當於 HTTP/1 裏的一次「請求 - 應答」;
在一個 HTTP/2 連接上可以併發多個流,也就是多個「請求 - 響應」報文,這就是「多路複用」。

十 HTTP的優化

1 整體方向
性能優化是一個複雜的概念,在 HTTP 裏可以分解爲服務器性能優化、客戶端性能優化和傳輸鏈路優化;
服務器有三個主要的性能指標:吞吐量、併發數和響應時間,此外還需要考慮資源利用率;
客戶端的基本性能指標是延遲,影響因素有地理距離、帶寬、DNS 查詢、TCP 握手等;
從服務器到客戶端的傳輸鏈路可以分爲三個部分,我們能夠優化的是前兩個部分,也就是「第一公里」和「中間一公里」;
有很多工具可以測量這些指標,服務器端有 ab、top、sar 等,客戶端可以使用測試網站,瀏覽器的開發者工具。

2 細節 花錢購買硬件、軟件或者服務可以直接提升網站的服務能力,其中最有價值的是 CDN; 不花錢也可以優化 HTTP,三個關鍵詞是「開源」「節流」和「緩存」; 後端應該選用高性能的 Web 服務器,開啓長連接,提升 TCP 的傳輸效率; 前端應該啓用 gzip、br 壓縮,減小文本、圖片的體積,儘量少傳不必要的頭字段; 緩存是無論何時都不能忘記的性能優化利器,應該總使用 Etag 或 Last-modified 字段標記資源; 升級到 HTTP/2 能夠直接獲得許多方面的性能提升,但要留意一些 HTTP/1 的「反模式」。