相比於 HTTP 1.0,HTTP 1.1引入了新的特性:javascript
Host
頭,可以使不一樣域名配置在同一個IP地址的服務器上。在 HTTP 1.1 中, Connection: Keep-Alive
字段默認開啓,這個字段容許服務端和客戶端保持一個長鏈接,當客戶端發送另外一個請求時,它會複用同一個鏈接。這個鏈接會一直持續到客戶端或服務器端認爲會話已經結束,其中一方中斷鏈接。中斷鏈接的取決與否取決於 Keep-Alive
字段中的:html
s
,超過設置的時間後斷開鏈接專業的名詞解釋能夠查看中文WIKI:連接java
HTTP管線化(英語:HTTP pipelining)是將多個 HTTP 請求(request)整批提交的技術,而在發送過程當中不需先等待服務器的迴應。 流水線操做創建在長鏈接之上,能夠將全部的 HTTP 請求一次性發出,而無需關心上一次發送請求的狀態,雖說客戶端一次性可以發出全部的請求,可是在服務端接收到的請求仍是一一進行處理的,若是當服務端返回的其中一個響應阻塞後,接下來的響應也會被阻塞。 node
在長鏈接請求中,沒法繼續使用以前的方法來判斷數據是否徹底發送完畢,可是咱們能夠根據 Content-Length
的長度是否爲 0 來判斷數據是否傳輸完畢,請求傳輸的內容與長度必須保持一致,不然內容將會被截斷。而且這種方法有一個弊端,那就是當發送的內容足夠大時,服務器都須要去計算 content
的長度,致使性能下降和速度變慢,因此咱們須要一個更好的機制來檢測長鏈接的請求開始與結束。 下面一段 node
代碼演示了當 Content-Lenth
長度與實際內容長度不一致致使內容被截斷的例子web
const http = require('http');
const fs = require('fs');
function handle(req, res) {
res.setHeader('Content-Length', '12');
res.end('1234567890123456');
}
const server = http.createServer(handle);
server.listen(3000);
console.log('Server start at port 3000...');
複製代碼
因爲 Content-Length
存在的弊端,咱們可使用 Transfer-Encoding
配合 Content-Encoding
來告訴瀏覽器傳輸的結果, Transfer-Encoding
的經常使用值爲 chunked
,而 Content-Encoding
的做用是告知瀏覽器採用何種編碼(壓縮),一般使用的是 gzip
。對資源進行壓縮後,再對內容進行分塊傳輸。當最後一個分塊長度爲0時,則表明數據傳輸完成。 經過下面使用 Node
建立一個服務器的例子能夠了解到什麼是分塊傳輸算法
const http = require('http');
function handle(req, res) {
res.setHeader('Content-type', 'text/html; charset=UTF-8');
res.setHeader('Transfer-Encoding', 'chunked');
let i = 0;
const timer = setInterval(()=>{
if(i < 5) {
res.write(`<p>this is ${i} chunk</p>`);
i ++;
}
else {
res.end('<p>this is last chunk</p>');
clearInterval(timer);
}
}, 500);
}
const server = http.createServer(handle);
server.listen(3000);
console.log('Server start at port 3000...');
複製代碼
注: HTTP2 再也不支持使用 chunked
分塊機制傳輸數據,有了更好的流傳輸機制,詳細的會在以後的文章說起json
相比 HTTP 1.0,HTTP 1.1 新增了若干項緩存機制:瀏覽器
詳細的緩存機制將會在下面的章節詳解緩存
強緩存,是瀏覽器優先命中的緩存,速度最快。當咱們在狀態碼後面看到 (from memory disk) 時,就表示瀏覽器從內存種讀取了緩存,當進程結束後,也就是 tab 關閉之後,內存裏的數據也將不復存在。只有當強緩存不被命中的時候,纔會進行協商緩存的查找。bash
在 HTTP 1.0 時代,使用的是 Pragma: no-cache
字段來進行緩存的判斷,因爲如今已經步入 1.1 時代,更多的由 Cache-Control
控制,因此建議只在須要兼容 HTTP/1.0 客戶端的場合下應用 Pragma 首部。
該字段表示的是,設定的時間爲緩存的有效時間,當發生請求時,瀏覽器將會把 Expires
的值與本地時間進行對比,若是本地時間小於設置的時間,則讀取緩存。 Expires
的值爲標準的 GMT 格式:
Expires: Wed, 21 Oct 2015 07:28:00 GMT
複製代碼
由於對比的是本地時間,因此也存在着弊端:當本地時間與服務端時間不一致時,沒法達到預期的資源讀取結果。 注 :
Expires
的字段設置爲 0 時,表明該資源已通過期Cache-Control
響應頭設置了 "max-age" 或者 "s-max-age" 指令,那麼 Expires
頭會被忽略。因爲 Expires
的侷限性, Cache-Control
登場了 (具體的參數能夠點擊查看MDN) ,下面說明幾個經常使用的字段
補充說明: 一、關於這no-store 和 no-cache 的區別,能夠查看 stackoverflow 的這個提問:連接
Exactly checking
Last-Modified
orETag
. Client would ask server if it has new version of data using those headers and if the answer is no it will serve cached data.
也就是說,若是將 Cache-Control
設置爲 no-cache
後,那麼服務器將去驗證 Last-Modeified
、 ETag
等字段,而 no-store
的做用則是不進行資源的緩存 二、max-age 指的是從當前時間開始計算的秒數,好比客戶端初次請求某個資源文件的時間是18:00,假設 max-age
爲 600秒,那麼就表明着這個資源將在 18:10 過時 下面是一個使用 node 建立具備緩存 js 文件服務器的例子 文件結構以下:
favicon.ico
index.html
test.js
package.json
複製代碼
const http = require('http');
const fs = require('fs');
function handle(req, res) {
if(req.url === '/test.js') {
const js = fs.readFileSync('./test.js');
res.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control':'max-age=600'
});
res.end(js);
} else {
res.setHeader('Content-type', 'text/html; charset=UTF-8');
res.setHeader('Transfer-Encoding', 'chunked');
res.setHeader('Cache-Control', 'max-age=600');
const html = fs.readFileSync('./index.html');
res.end(html);
}
}
const server = http.createServer(handle);
server.listen(3000);
console.log('Server start at port 3000...');
複製代碼
咱們將 max-age
設置爲 600秒,那麼這個 js 文件將在10分鐘以內都使用緩存
當瀏覽器沒有命中強緩存後,便會命中協商緩存,協商緩存由如下幾個 HTTP 字段控制
服務端將資源傳送給客戶端的時候,會將資源最後的修改時間以 Last-Modified: GMT
的形式加在實體首部上返回
Last-Modified: Fri, 22 Jul 2019 01:47:00 GMT
複製代碼
客戶端接收到後會爲此資源信息作上標記,等下次從新請求該資源的時候將會帶上時間信息給服務器作檢查,若傳遞的值與服務器上的值一致,則返回 304
,表示文件沒有被修改過,若時間不一致,則從新進行資源請求並返回 200
。 那麼在從新請求的時候,客戶端要用什麼方式去傳遞時間呢?答案是使用 If-Modified-Since
。
在客戶端從新請求資源的時候,將會在請求頭添加 If-Modifed-Since: GMT
字段傳遞給服務端,服務端接收後會與當前該文件的最後修改時間對比,若是時間一致則返回 304
,若是不一致則傳遞最新的資源並返回 200
狀態碼。
該值告訴服務器,若Last-Modified沒有匹配上(資源在服務端的最後更新時間改變了),則應當返回412
(Precondition Failed) 狀態碼給客戶端。 Last-Modified 存在必定問題,若是在服務器上,一個資源被修改了,但其實際內容根本沒發生改變,會由於Last-Modified時間匹配不上而返回了整個實體給客戶端(即便客戶端緩存裏有個如出一轍的資源)。 這個字段的一個典型應用場景就是斷點續傳:當下載的資源內容被改變時,不該該繼續返回以前的資源內容。
爲了解決上面資源修改可是內容卻沒被修改,卻依舊返回最新的資源的問題,ETag是一種比較好的解決方案, 服務端經過某種算法(好比 MD5)計算出該資源的惟一標緻符,在響應資源的時候,將會添加在實體首部字段連同資源一併返回給客戶端
ETag: "5b6d63d2-61afa"
複製代碼
客戶端接收到後將會保存該信息,而且在下一次請求中附上該信息給服務端,服務器只須要比較客戶端傳來的ETag跟本身服務器上該資源的ETag是否一致,就能很好地判斷資源相對客戶端而言是否被修改過了。 若是服務端檢測到傳遞過來的值與服務器端的不一致,則返回最新的資源和 200
狀態碼,不然返回 304
告知客戶端使用緩存文件 那麼客戶端如何告知服務端 ETag 的相關信息呢?能夠經過請求首部字段 If-Match
和 If-Node-Match
來進行傳遞
示例爲 If-None-Match: "5d8c72a5edda8d6a:3239"
該字段告知服務端若是 ETag 沒匹配上須要重發資源數據,不然直接回送304
和響應報頭便可。 當前各瀏覽器均是使用的該請求首部來向服務器傳遞保存的 ETag 值。
告訴服務器若是沒有匹配到ETag,或者收到了 "*" 值而當前並無該資源實體,則應當返回412
(Precondition Failed) 狀態碼給客戶端。不然服務器直接忽略該字段。 須要注意的是,若是資源是走分佈式服務器(好比CDN)存儲的狀況,須要這些服務器上計算ETag惟一值的算法保持一致,纔不會致使明明同一個文件,在服務器A和服務器B上生成的ETag卻不同。
注:If-None-Math 的 優先級比 If-Modified-Since 的優先級更高,有了 If-None-Match 字段後 If-Modified-Since 字段將會被忽略
字段名稱 | 說明 |
---|---|
Cache-Control | 控制緩存的行爲 |
Pragma | HTTP 1.0 遺留字段,使用 "no-cache" 做爲是否緩存依據 |
字段名稱 | 說明 |
---|---|
If-Match | 比較 ETag 是否一致 |
If-None-Match | 比較 ETag 是否不一致 |
If-Modified-Since | 比較資源最後的更新時間是否一致 |
If-Unmodified-Since | 比較資源最後的更新時間是否不一致 |
字段名稱 | 說明 |
---|---|
ETag | 資源的匹配信息(經過強比較算法生成值) |
字段名稱 | 說明 |
---|---|
Expires | HTTP 1.0 遺留字段,資源的過時時間(取決於客戶端本地時間) |
Last-Modified | 資源的最後一次修改時間 |
強緩存 --> 協商緩存 Cache-Control
-> Expires
-> ETag
-> Last-Modified