摘要:用探究下緩存用搞服務(wù)和直接用做服務(wù)器不太一樣,很多工作都需要自己做。另外,緩存作為一個(gè)前端優(yōu)化的一個(gè)要點(diǎn),也應(yīng)該有所了解。什么是緩存指出緩存是響應(yīng)消息的本地存儲(chǔ),并且是控制其中消息的存儲(chǔ)檢索和刪除的子系統(tǒng)。
用node探究下http緩存
用node搞web服務(wù)和直接用tomcat、Apache做服務(wù)器不太一樣, 很多工作都需要自己做。緩存策略也要自己選擇,雖然有像koa-static,express.static這些東西可以用來管理靜態(tài)資源,但是為了開發(fā)或配置時(shí)更加得心應(yīng)手,知其所以然,有了解http緩存的必要。另外,http緩存作為一個(gè)前端優(yōu)化的一個(gè)要點(diǎn),也應(yīng)該有所了解。什么是http緩存
RFC 7234 (https://tools.ietf.org/pdf/rfc7234.pdf)指出HTTP緩存是響應(yīng)消息的本地存儲(chǔ),并且是控制其中消息的存儲(chǔ)、檢索和刪除的子系統(tǒng)。
通俗講: http協(xié)議規(guī)定了一些指令, 實(shí)現(xiàn)http協(xié)議的服務(wù)器和瀏覽器根據(jù)這些指令決定要不要以及如何把響應(yīng)存儲(chǔ)起來以備后續(xù)使用.
http緩存的意義提高響應(yīng)速度
減少帶寬占用, 省流量
減小服務(wù)器壓力
不指定任何與緩存有關(guān)的指令這種情況下瀏覽器不做緩存, 每次都會(huì)想服務(wù)器請(qǐng)求. 但是比較奇怪的是在nginx的實(shí)現(xiàn)中, 這種情況下還是被代理服務(wù)器做了緩存.也就是說, 當(dāng)多次請(qǐng)求同一個(gè)資源時(shí), 代理服務(wù)器只向源服務(wù)器請(qǐng)求一次.
演示第1個(gè)例子nothing_1
強(qiáng)制緩存所謂強(qiáng)制緩存就是給出資源的到期時(shí)間expires或者有效時(shí)間max-age, 在這個(gè)時(shí)間之內(nèi)該資源應(yīng)該被緩存.
如何讓一個(gè)資源被強(qiáng)緩存
1.expires
這個(gè)字段定義了一個(gè)資源到期的時(shí)間. 看一個(gè)實(shí)際的例子:
可以看到這個(gè)expires是個(gè)GMT時(shí)間, 它的工作機(jī)制是, 首次請(qǐng)求時(shí), 服務(wù)器在響應(yīng)中加上expires標(biāo)識(shí)資源的到期時(shí)間, 瀏覽器緩存這個(gè)資源, 再次請(qǐng)求時(shí), 瀏覽器將上一次請(qǐng)求到這個(gè)資源的過期時(shí)間與自己的系統(tǒng)時(shí)間對(duì)比, 若系統(tǒng)時(shí)間小于過期時(shí)間, 則證明資源沒有過期, 直接用上次緩存的資源, 不必請(qǐng)求; 否則重新請(qǐng)求, 服務(wù)器在響應(yīng)中給出新的過期時(shí)間.
演示第9個(gè)例子expires_9
const d = new Date(Date.now() + 5000); res.writeHead(200, { "Content-Type": "image/png", "expires": d.toGMTString() }); res.end(img);
2.Cache-Control:[public | private,] max-age=${n}, s-maxage=${m}
expires 存在的問題是他依賴于客戶端的系統(tǒng)時(shí)間, 客戶端系統(tǒng)時(shí)間錯(cuò)誤可能會(huì)引起判斷錯(cuò)誤. HTTP1.1增加了Cache-Control解決此問題, 這個(gè)指令值比較豐富, 常見的如下:
public/private: 標(biāo)識(shí)資源能不能被代理服務(wù)器緩存, public 標(biāo)識(shí)資源既能被代理服務(wù)器緩存也能被瀏覽器緩存, private標(biāo)識(shí)資源只能被瀏覽器緩存, 不能被代理服務(wù)器緩存.
max-age: 用于指定在客戶端緩存的有效時(shí)間, 單位s, 超過n秒需要重新請(qǐng)求, 不超過則可以使用緩存
s-maxage: 這個(gè)是針對(duì)代理服務(wù)器的, 表示資源在代理服務(wù)器緩存時(shí)間沒有超過這個(gè)時(shí)間不必向源服務(wù)器請(qǐng)求, 否則需要.
no-cache: 有這個(gè)指令表示不走瀏覽器緩存了, 協(xié)商緩存還可以走
no-store: 強(qiáng)制無緩存, 協(xié)商緩存也不走了, 測試發(fā)下即使響應(yīng)中有Last-Modified, 瀏覽器請(qǐng)求時(shí)頁不會(huì)帶If-Modified-Since
一個(gè)實(shí)例
演示第2,3,4,5,7
協(xié)商緩存所謂協(xié)商緩存就是客戶端想用緩存資源時(shí)先向服務(wù)器詢問, 如果服務(wù)器如果認(rèn)為這個(gè)資源沒有過期, 可以繼續(xù)用則給出304響應(yīng), 客戶端繼續(xù)使用原來的資源; 否則給出200, 并在響應(yīng)body加上資源, 客戶端使新的資源.
1.Last-Modified與If-Modified-Since
這個(gè)機(jī)制是, 服務(wù)器在響應(yīng)頭中加上Last-Modified, 一般是一個(gè)資源的最后修改時(shí)間, 瀏覽器首次請(qǐng)求時(shí)獲得這個(gè)時(shí)間, 下一次請(qǐng)求時(shí)將這個(gè)時(shí)間放在請(qǐng)求頭的If-Modified-Since, 服務(wù)器收到這個(gè)If-Modified-Since時(shí)間n后查詢資源的最后修改時(shí)間m與之對(duì)比, 若m>n, 給出200響應(yīng), 更新Last-Modified為新的值, body中為這個(gè)資源, 瀏覽器收到后使用新的資源; 否則給出304響應(yīng), body無數(shù)據(jù), 瀏覽器使用上一次緩存的資源.
2.Etag與If-None-Match
Last-Modified模式存兩個(gè)問題, 一是它是秒級(jí)別的比對(duì), 所以當(dāng)資源的變化小于一秒時(shí)瀏覽器可能使用錯(cuò)誤的資源; 二是資源的最新修改時(shí)間變了可能內(nèi)容并沒有變, 但是還是會(huì)給出完整響應(yīng), 造成浪費(fèi). 基于此在HTTP1.1引入了Etag模式.
這個(gè)與上面的Last-Modified機(jī)制基本相同, 不過不再是比對(duì)最后修改時(shí)間而是比對(duì)資源的標(biāo)識(shí), 這個(gè)Etag一般是基于資源內(nèi)容生成的標(biāo)識(shí). 由于Etag是基于內(nèi)容生成的, 所以當(dāng)且僅當(dāng)內(nèi)容變化才會(huì)給出完整響應(yīng), 無浪費(fèi)和錯(cuò)誤的問題.
演示第8, 10
如何選擇緩存策略https://developers.google.cn/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn
附錄1.演示代碼
const http = require("http"); const fs = require("fs"); let etag = 0; let tpl = fs.readFileSync("./index.html"); let img = fs.readFileSync("./test.png"); http.createServer((req, res) => { etag++; // 我是個(gè)假的eTag console.log("--->", req.url); switch (req.url) { // 模板 case "/index": res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-store" }); res.end(tpl); break; // 1. 不給任何與緩存相關(guān)的頭, 任何情況下, 既不會(huì)被瀏覽器緩存, 也不會(huì)被代理服務(wù)緩存 case "/img/nothing_1": res.writeHead(200, { "Content-Type": "image/png" }); res.end(img); break; // 2. 設(shè)置了no-cache表明每次要使用緩存資源前需要向服務(wù)器確認(rèn) case "/img/cache-control=no-cache_2": res.writeHead(200, { "Content-Type": "image/png", "cache-control": "no-cache" }); res.end(img); break; // 3. 設(shè)置max-age表示在瀏覽器最多緩存的時(shí)間 case "/img/cache-control=max-age_3": res.writeHead(200, { "Content-Type": "image/png", "cache-control": "max-age=10" }); res.end(img); break; // 4. 設(shè)置了max-age s-maxage public: public 是說這個(gè)資源可以被服務(wù)器緩存, 也可以被瀏覽器緩存, // max-age意思是瀏覽器的最長緩存時(shí)間為n秒, s-maxage表明代理服務(wù)器的最長緩存時(shí)間為那么多秒 case "/img/cache-control=max-age_s-maxage_public_4": res.writeHead(200, { "Content-Type": "image/png", "cache-control": "public, max-age=10, s-maxage=40" }); res.end(img); break; // 設(shè)置了max-age s-maxage private: private 是說這個(gè)資源只能被瀏覽器緩存, 不能被代理服務(wù)器緩存 // max-age說明了在瀏覽器最長緩存時(shí)間, 這里的s-maxage實(shí)際是無效的, 因?yàn)椴荒鼙淮矸?wù)緩存 case "/img/cache-control=max-age_s-maxage_private_5": res.writeHead(200, { "Content-Type": "image/png", "cache-control": "private, max-age=10, s-maxage=40" }); res.end(img); break; // 7. 可以被代理服務(wù)器緩存, 確不能被瀏覽器緩存 case "/img/cache-control=private_max-age_7": res.writeHead(200, { "Content-Type": "image/png", "cache-control": "public, s-maxage=40" }); res.end(img); break; // 8. 協(xié)商緩存 case "/img/talk_8": let stats = fs.statSync("./test.png"); let mtimeMs = stats.mtimeMs; let If_Modified_Since = req.headers["if-modified-since"]; let oldTime = 0; if(If_Modified_Since) { const If_Modified_Since_Date = new Date(If_Modified_Since); oldTime = If_Modified_Since_Date.getTime(); } mtimeMs = Math.floor(mtimeMs / 1000) * 1000; // 這種方式的精度是秒, 所以毫秒的部分忽略掉 console.log("mtimeMs", mtimeMs); console.log("oldTime", oldTime); if(oldTime < mtimeMs) { res.writeHead(200, { "Cache-Control": "no-cache", // 測試發(fā)現(xiàn), 必須要有max-age=0 或者no-cache,或者expires為當(dāng)前, 才會(huì)協(xié)商, 否則沒有協(xié)商的過程 "Last-Modified": new Date(mtimeMs).toGMTString() }); res.end(fs.readFileSync("./test.png")); }else { res.writeHead(304); res.end(); } // 9. 設(shè)置了expires, 表示資源到期時(shí)間 case "/img/expires_9": const d = new Date(Date.now() + 5000); res.writeHead(200, { "Content-Type": "image/png", "expires": d.toGMTString() }); res.end(img); break; // 10. 設(shè)置了expires, 表示資源到期時(shí)間 case "/img/etag_10": const If_None_Match = req.headers["if-none-match"]; console.log("If_None_Match,",If_None_Match); if(If_None_Match != etag) { res.writeHead(200, { "Content-Type": "image/png", "Etag": String(etag) }); res.end(img); }else { res.statusCode = 304; res.end(); } break; // 11. no-store 能協(xié)商緩存嗎? 不能, 請(qǐng)求不會(huì)帶if-modified-since case "/img/no-store_11": const stats2 = fs.statSync("./test.png"); let mtimeMs2 = stats2.mtimeMs; let If_Modified_Since2 = req.headers["if-modified-since"]; let oldTime2 = 0; if(If_Modified_Since2) { const If_Modified_Since_Date = new Date(If_Modified_Since2); oldTime2 = If_Modified_Since_Date.getTime(); } mtimeMs2 = Math.floor(mtimeMs2 / 1000) * 1000; // 這種方式的精度是秒, 所以毫秒的部分忽略掉 console.log("mtimeMs", mtimeMs2); console.log("oldTime", oldTime2); if(oldTime2 < mtimeMs2) { res.writeHead(200, { "Cache-Control": "no-store", // 測試發(fā)現(xiàn), 必須要有max-age=0 或者no-cache,或者expires為當(dāng)前, 才會(huì)協(xié)商, 否則沒有協(xié)商的過程 "Last-Modified": new Date(mtimeMs2).toGMTString() }); res.end(fs.readFileSync("./test.png")); }else { res.writeHead(304); res.end(); } default: res.statusCode = 404; res.statusMessage = "Not found", res.end(); } }).listen(1234);
2.測試用代理服務(wù)器nginx配置
不要問我這是個(gè)啥, 我是copy的
worker_processes 8; events { worker_connections 65535; } http { include mime.types; default_type application/octet-stream; charset utf-8; log_format main "$http_x_forwarded_for $remote_addr $remote_user [$time_local] "$request" " "$status $body_bytes_sent "$http_referer" " ""$http_user_agent" "$http_cookie" $host $request_time"; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; proxy_connect_timeout 500; #跟后端服務(wù)器連接的超時(shí)時(shí)間_發(fā)起握手等候響應(yīng)超時(shí)時(shí)間 proxy_read_timeout 600; #連接成功后_等候后端服務(wù)器響應(yīng)的時(shí)間_其實(shí)已經(jīng)進(jìn)入后端的排隊(duì)之中等候處理 proxy_send_timeout 500; #后端服務(wù)器數(shù)據(jù)回傳時(shí)間_就是在規(guī)定時(shí)間內(nèi)后端服務(wù)器必須傳完所有數(shù)據(jù) proxy_buffer_size 128k; #代理請(qǐng)求緩存區(qū)_這個(gè)緩存區(qū)間會(huì)保存用戶的頭信息以供Nginx進(jìn)行規(guī)則處理_一般只要能保存下頭信息即可 proxy_buffers 4 128k; #同上 告訴Nginx保存單個(gè)用的幾個(gè)Buffer最大用多大空間 proxy_busy_buffers_size 256k; #如果系統(tǒng)很忙的時(shí)候可以申請(qǐng)更大的proxy_buffers 官方推薦*2 proxy_temp_file_write_size 128k; #設(shè)置web緩存區(qū)名為cache_one,內(nèi)存緩存空間大小為12000M,自動(dòng)清除超過15天沒有被訪問過的緩存數(shù)據(jù),硬盤緩存空間大小200g #要想開啟nginx的緩存功能,需要添加此處的兩行內(nèi)容! #設(shè)置Web緩存區(qū)名稱為cache_one,內(nèi)存緩存空間大小為500M,緩存的數(shù)據(jù)超過1天沒有被訪問就自動(dòng)清除;訪問的緩存數(shù)據(jù),硬盤緩存空間大小為30G proxy_cache_path /usr/local/nginx/proxy_cache_path levels=1:2 keys_zone=cache_one:500m inactive=1d max_size=30g; #創(chuàng)建緩存的時(shí)候可能生成一些臨時(shí)文件存放的位置 proxy_temp_path /usr/local/nginx/proxy_temp_path; fastcgi_connect_timeout 3000; fastcgi_send_timeout 3000; fastcgi_read_timeout 3000; fastcgi_buffer_size 256k; fastcgi_buffers 8 256k; fastcgi_busy_buffers_size 256k; fastcgi_temp_file_write_size 256k; fastcgi_intercept_errors on; client_header_timeout 600s; client_body_timeout 600s; client_max_body_size 100m; client_body_buffer_size 256k; gzip off; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 9; gzip_types text/plain application/x-javascript text/css application/xml text/javascript; gzip_vary on; include vhosts/*.conf; server { listen 80; server_name localhost; location / { proxy_pass http://127.0.0.1:1234; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_redirect off; proxy_cache cache_one; #此處的cache_one必須于上一步配置的緩存區(qū)域名稱相同 proxy_cache_valid 200 304 12h; proxy_cache_valid 301 302 1d; proxy_cache_valid any 1h; #不同的請(qǐng)求設(shè)置不同的緩存時(shí)效 proxy_cache_key $uri$is_args$args; #生產(chǎn)緩存文件的key,通過4個(gè)string變量結(jié)合生成 expires off; #加了這個(gè)的話會(huì)自己修改cache-control, 寫成off則不會(huì) proxy_set_header X-Forwarded-Proto $scheme; } } }參考文獻(xiàn)
https://juejin.im/book/5b936540f265da0a9624b04b/section/5b9ba651f265da0ac726e5de
這是一個(gè)付費(fèi)的冊(cè)子,可能沒法訪問
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/98776.html
摘要:要想讓模塊再次運(yùn)行,必須清除緩存。用戶自己編寫的模塊,稱為文件模塊。并且和指向了同一個(gè)模塊對(duì)象。模塊路徑這是在定位文件模塊的具體文件時(shí)指定的查找策略,具體表現(xiàn)為一個(gè)路徑組成的數(shù)組。 前言 Node應(yīng)用是由模塊組成的,Node遵循了CommonJS的模塊規(guī)范,來隔離每個(gè)模塊的作用域,使每個(gè)模塊在它自身的命名空間中執(zhí)行。 CommonJS規(guī)范的主要內(nèi)容: 模塊必須通過 module.exp...
摘要:前言緩存探究第一彈中我們講了一些緩存的基礎(chǔ)知識(shí)和策略。第二彈我們來講講如何實(shí)際在項(xiàng)目中配置。在緩存探究第一彈定制緩存策略中已經(jīng)提到對(duì)于最好標(biāo)記為不緩存,以便及時(shí)獲取最新的靜態(tài)資源版本。 前言 WEB緩存探究第一彈中我們講了一些WEB緩存的基礎(chǔ)知識(shí)和策略。第二彈我們來講講如何實(shí)際在項(xiàng)目中配置。 實(shí)戰(zhàn) 鑒于叉燒包本包是個(gè)前端,所以我們就以HTML和Node為例開始showImg(https...
摘要:前言緩存探究第一彈中我們講了一些緩存的基礎(chǔ)知識(shí)和策略。第二彈我們來講講如何實(shí)際在項(xiàng)目中配置。在緩存探究第一彈定制緩存策略中已經(jīng)提到對(duì)于最好標(biāo)記為不緩存,以便及時(shí)獲取最新的靜態(tài)資源版本。 前言 WEB緩存探究第一彈中我們講了一些WEB緩存的基礎(chǔ)知識(shí)和策略。第二彈我們來講講如何實(shí)際在項(xiàng)目中配置。 實(shí)戰(zhàn) 鑒于叉燒包本包是個(gè)前端,所以我們就以HTML和Node為例開始showImg(https...
摘要:前言緩存探究第一彈中我們講了一些緩存的基礎(chǔ)知識(shí)和策略。第二彈我們來講講如何實(shí)際在項(xiàng)目中配置。在緩存探究第一彈定制緩存策略中已經(jīng)提到對(duì)于最好標(biāo)記為不緩存,以便及時(shí)獲取最新的靜態(tài)資源版本。 前言 WEB緩存探究第一彈中我們講了一些WEB緩存的基礎(chǔ)知識(shí)和策略。第二彈我們來講講如何實(shí)際在項(xiàng)目中配置。 實(shí)戰(zhàn) 鑒于叉燒包本包是個(gè)前端,所以我們就以HTML和Node為例開始showImg(https...
摘要:前言緩存探究第一彈中我們講了一些緩存的基礎(chǔ)知識(shí)和策略。第二彈我們來講講如何實(shí)際在項(xiàng)目中配置。在緩存探究第一彈定制緩存策略中已經(jīng)提到對(duì)于最好標(biāo)記為不緩存,以便及時(shí)獲取最新的靜態(tài)資源版本。 前言 WEB緩存探究第一彈中我們講了一些WEB緩存的基礎(chǔ)知識(shí)和策略。第二彈我們來講講如何實(shí)際在項(xiàng)目中配置。 實(shí)戰(zhàn) 鑒于叉燒包本包是個(gè)前端,所以我們就以HTML和Node為例開始showImg(https...
閱讀 898·2019-08-30 15:54
閱讀 1466·2019-08-30 15:54
閱讀 2400·2019-08-29 16:25
閱讀 1292·2019-08-29 15:24
閱讀 749·2019-08-29 12:11
閱讀 2506·2019-08-26 10:43
閱讀 1227·2019-08-26 10:40
閱讀 466·2019-08-23 16:24