您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么理解緩存”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
瀏覽器緩存是指當我們去訪問一個網站或者Http服務的時候,服務器可以設置Http的響應頭信息,其中如果設置緩存相關的頭信息,那么瀏覽器就會緩存這些數據,下次再訪問這些數據的時候就直接從瀏覽器緩存中獲取或者是只需要去服務器中校驗下緩存時候有效,可以減少瀏覽器與服務器之間的網絡時間的開銷以及節省帶寬。
> Htpp相關的知識,歡迎去參觀 《面試篇》Http協議
該命令是通用首部字段(請求首部和響應首部都可以使用),用于控制緩存的工作機制,該命令參數稍多,常用的參數:
no-cache: 表示不需要緩存該資源
max-age(秒): 緩存的最大有效時間,當max-age=0時,表示不需要緩存
控制資源失效的日期,當瀏覽器接受到Expires
之后,瀏覽器都會使用本地的緩存,在過期日期之后才會向務器發送請求;如果服務器同時在響應頭中也指定了Cache-Control
的max-age
指令時,瀏覽器會優先處理max-age
。 如果服務器不想要讓瀏覽器對資源緩存,可以把Expires
和首部字段Date
設置相同的值
Last-Modified
用于指明資源最終被修改的時間。配合If-Modified-Since
一起使用可以通過時間對緩存是否有效進行校驗;后面實戰會使用到這種方式。
如果請求頭中If-Modified-Since
的日期早于請求資源的更新日期,那么服務會進行處理,返回最新的資源;如果If-Modified-Since
指定的日期之后請求的資源都未更新過,那么服務不會處理請求并返回304 Mot Modified
的響應,表示緩存的文件有效可以繼續使用。
使用SpringMVC做緩存的測試代碼:
@ResponseBody @RequestMapping("/http/cache") public ResponseEntity<string> cache(@RequestHeader(value = "If-Modified-Since", required = false) String ifModifiedSinceStr) throws ParseException { DateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); Date ifModifiedSince = dateFormat.parse(ifModifiedSinceStr); long lastModifiedDate = getLastModifiedDate(ifModifiedSince);//獲取文檔最后更新時間 long now = System.currentTimeMillis(); int maxAge = 30; //數據在瀏覽器端緩存30秒 //判斷文檔是否被修改過 if (Objects.nonNull(ifModifiedSince) && ifModifiedSince.getTime() == lastModifiedDate) { HttpHeaders headers = new HttpHeaders(); headers.add("Date", dateFormat.format(new Date(now))); //設置當前時間 headers.add("Expires", dateFormat.format(new Date(now + maxAge * 1000))); //設置過期時間 headers.add("Cache-Control", "max-age=" + maxAge); return new ResponseEntity<>(headers, HttpStatus.NOT_MODIFIED); } //文檔已經被修改過 HttpHeaders headers = new HttpHeaders(); headers.add("Date", dateFormat.format(new Date(now))); //設置當前時間 headers.add("Last-Modified", dateFormat.format(new Date(lastModifiedDate))); //設置最近被修改的日期 headers.add("Expires", dateFormat.format(new Date(now + maxAge * 1000))); //設置過期時間 headers.add("Cache-Control", "max-age=" + maxAge); String responseBody = JSON.toJSONString(ImmutableMap.of("website", "https://silently9527.cn")); return new ResponseEntity<>(responseBody, headers, HttpStatus.OK); } //獲取文檔的最后更新時間,方便測試,每15秒換一次;去掉毫秒值 private long getLastModifiedDate(Date ifModifiedSince) { long now = System.currentTimeMillis(); if (Objects.isNull(ifModifiedSince)) { return now; } long seconds = (now - ifModifiedSince.getTime()) / 1000; if (seconds > 15) { return now; } return ifModifiedSince.getTime(); }
當第一次訪問http://localhost:8080/http/cache
的時候,我們可以看到如下的響應頭信息:
前面我們已提到了Cache-Control
的優先級高于Expires
,實際的項目中我們可以同時使用,或者只使用Cache-Control
。Expires
的值通常情況下都是系統當前時間+緩存過期時間
當我們在15秒之內再次訪問http://localhost:8080/http/cache
會看到如下的請求頭:
此時發送到服務器端的頭信息If-Modified-Since
就是上次請求服務器返回的Last-Modified
,瀏覽器會拿這個時間去和服務器校驗內容是否發送了變化,由于我們后臺程序在15秒之內都表示沒有修改過內容,所以得到了如下的響應頭信息
響應的狀態碼304,表示服務器告訴瀏覽器,你的緩存是有效的可以繼續使用。
請求首部字段If-None-Match
傳輸給服務器的值是服務器返回的ETag值,只有當服務器上請求資源的ETag
值與If-None-Match
不一致時,服務器才去處理該請求。
響應首部字段ETag
能夠告知客服端響應實體的標識,它是一種可將資源以字符串的形式做唯一標識的方式。服務器可以為每份資源指定一個ETag
值。當資源被更新時,ETag
的值也會被更新。通常生成ETag
值的算法使用的是md5。
強ETag值:不論實體發生了多么細微的變化都會改變其值
弱ETag值:只用于提示資源是否相同,只有當資源發送了根本上的變化,ETag才會被改變。使用弱ETag值需要在前面添加W/
ETag: W/"etag-xxxx"
通常建議選擇弱ETag值,因為大多數時候我們都會在代理層開啟gzip壓縮,弱ETag可以驗證壓縮和不壓縮的實體,而強ETag值要求響應實體字節必須完全一致。
@ResponseBody @RequestMapping("/http/etag") public ResponseEntity<string> etag(@RequestHeader(value = "If-None-Match", required = false) String ifNoneMatch) throws ParseException { long now = System.currentTimeMillis(); int maxAge = 30; //數據在瀏覽器端緩存30秒 String responseBody = JSON.toJSONString(ImmutableMap.of("website", "https://silently9527.cn")); String etag = "W/\"" + MD5Encoder.encode(responseBody.getBytes()) + "\""; //弱ETag值 if (etag.equals(ifNoneMatch)) { return new ResponseEntity<>(HttpStatus.NOT_MODIFIED); } DateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); HttpHeaders headers = new HttpHeaders(); headers.add("ETag", etag); headers.add("Date", dateFormat.format(new Date(now))); //設置當前時間 headers.add("Cache-Control", "max-age=" + maxAge); return new ResponseEntity<>(responseBody, headers, HttpStatus.OK); }
ETag是用于發送到服務器端進行內容變更驗證的,第一次請求http://localhost:8080/http/etag
,獲取到的響應頭信息:
在30秒之內,我們再次刷新頁面,可以看到如下的請求頭信息:
這里的If-None-Match
就是上一次請求服務返回的ETag
值,服務器校驗If-None-Match
值與ETag
值相等,所以返回了304告訴瀏覽器緩存可以用。
通過上面的兩個事例我們可以看出ETag
需要服務器先查詢出需要響應的內容,然后計算出ETag值,再與瀏覽器請求頭中If-None-Match
來比較覺得是否需要返回數據,對于服務器來說僅僅是節省了帶寬,原本應該服務器調用后端服務查詢的信息依然沒有被省掉;而Last-Modified
通過時間的比較,如果內容沒有更新,服務器不需要調用后端服務查詢出響應數據,不僅節省了服務器的帶寬也降低了后端服務的壓力;
對比之后得出結論:通常來說為了降低后端服務的壓力ETag
適用于圖片/js/css等靜態資源,而類似用戶詳情信息需要調用后端服務的數據適合使用Last-Modified
來處理。
通常情況下我們都會使用到Nginx來做反向代理服務器,我們可以通過緩沖、緩存來對Nginx進行調優,本篇我們就從這兩個方面來聊聊Nginx調優
默認情況下,Nginx在返回響應給客戶端之前會盡可能快的從上游服務器獲取數據,Nginx會盡可能的將上有服務器返回的數據緩沖到本地,然后一次性的全部返回給客戶端,如果每次從上游服務器返回的數據都需要寫入到磁盤中,那么Nginx的性能肯定會降低;所以我們需要根據實際情況對Nginx的緩存做優化。
proxy_buffer_size
: 設置Nginx緩沖區的大小,用來存儲upstream端響應的header。
proxy_buffering
: 啟用代理內容緩沖,當該功能禁用時,代理一接收到上游服務器的返回就立即同步的發送給客戶端,proxy_max_temp_file_size
被設置為0;通過設置proxy_buffering
為on,proxy_max_temp_file_size
為0 可以確保代理的過程中不適用磁盤,只是用緩沖區; 開啟后proxy_buffers
和proxy_busy_buffers_size
參數才會起作用
proxy_buffers
: 設置響應上游服務器的緩存數量和大小,當一個緩沖區占滿后會申請開啟下一個緩沖區,直到緩沖區數量到達設置的最大值
proxy_busy_buffers_size
: proxy_busy_buffers_size
不是獨立的空間,他是proxy_buffers
和proxy_buffer_size
的一部分。nginx會在沒有完全讀完后端響應就開始向客戶端傳送數據,所以它會劃出一部分busy狀態的buffer來專門向客戶端傳送數據(建議為proxy_buffers
中單個緩沖區的2倍),然后它繼續從后端取數據。 proxy_busy_buffer_size
參數用來設置處于busy狀態的buffer有多大。
1)如果完整數據大小小于busy_buffer大小,當數據傳輸完成后,馬上傳給客戶端;
2)如果完整數據大小不小于busy_buffer大小,則裝滿busy_buffer后,馬上傳給客戶端;
典型是設置成proxy_buffers
的兩倍。
Nginx代理緩沖的設置都是作用到每一個請求的,想要設置緩沖區的大小到最佳狀態,需要測量出經過反向代理服務器器的平均請求數和響應的大小;proxy_buffers
指令的默認值 8個 4KB 或者 8個 8KB(具體依賴于操作系統),假如我們的服務器是1G,這臺服務器只運行了Nginx服務,那么排除到操作系統的內存使用,保守估計Nginx能夠使用的內存是768M
每個活動的連接使用緩沖內存:8個4KB = 8 * 4 * 1024 = 32768字節
系統可使用的內存大小768M: 768 * 1024 * 1024 = 805306368字節
所以Nginx能夠同時處理的連接數:805306368 / 32768 = 24576
經過我們的粗略估計,1G的服務器只運行Nginx大概可以同時處理24576個連接。
假如我們測量和發現經過反向代理服務器響應的平均數據大小是 900KB , 而默認的 8個4KB的緩沖區是無法滿足的,所以我們可以調整大小
http { proxy_buffers 30 32k; }
這樣設置之后每次請求可以達到最快的響應,但是同時處理的連接數減少了,(768 * 1024 * 1024) / (30 * 32 * 1024)
=819個活動連接;
如果我們系統的并發數不是太高,我們可以將proxy_buffers
緩沖區的個數下調,設置稍大的proxy_busy_buffers_size
加大往客戶端發送的緩沖區,以確保Nginx在傳輸的過程中能夠把從上游服務器讀取到的數據全部寫入到緩沖區中。
http { proxy_buffers 10 32k; proxy_busy_buffers_size 64k; }
Nignx除了可以緩沖上游服務器的響應達到快速返回給客戶端,它還可以是實現響應的緩存,通過上圖我們可以看到
1A: 一個請求到達Nginx,先從緩存中嘗試獲取
1B: 緩存不存在直接去上游服務器獲取數據
1C: 上游服務器返回響應,Nginx把響應放入到緩存
1D: 把響應返回到客戶端
2A: 另一個請求達到Nginx, 到緩存中查找
2B: 緩存中有對應的數據,直接返回,不去上游服務器獲取數據
Nginx的緩存常用配置:
proxy_cache_path
: 放置緩存響應和共享的目錄。levels
設置緩存文件目錄層次, levels=1:2 表示兩級目錄,最多三層,其中 1 表示一級目錄使用一位16進制作為目錄名,2 表示二級目錄使用兩位16進制作為目錄名,如果文件都存放在一個目錄中,文件量大了會導致文件訪問變慢。keys_zone
設置緩存名字和共享內存大小,inactive
當被放入到緩存后如果不被訪問的最大存活時間,max_size
設置緩存的最大空間
proxy_cache
: 定義響應應該存放到哪個緩存區中(keys_zone
設置的名字)
proxy_cache_key
: 設置緩存使用的Key, 默認是完整的訪問URL,可以自己根據實際情況設置
proxy_cache_lock
: 當多個客戶端同時訪問一下URL時,如果開啟了這個配置,那么只會有一個客戶端會去上游服務器獲取響應,獲取完成后放入到緩存中,其他的客戶端會等待從緩存中獲取。
proxy_cache_lock_timeout
: 啟用了proxy_cache_lock
之后,如果第一個請求超過了proxy_cache_lock_timeout
設置的時間默認是5s,那么所有等待的請求會同時到上游服務器去獲取數據,可能會導致后端壓力增大。
proxy_cache_min_uses
: 設置資源被請求多少次后才會被緩存
proxy_cache_use_stale
: 在訪問上游服務器發生錯誤時,返回已經過期的數據給客戶端;當緩存內容對于過期時間不敏感,可以選擇采用這種方式
proxy_cache_valid
: 為不同響應狀態碼設置緩存時間。如果設置proxy_cache_valid 5s
,那么所有的狀態碼都會被緩存。
設置所有的響應被緩存后最大不被訪問的存活時間6小時,緩存的大小設置為1g,緩存的有效期是1天,配置如下:
http { proxy_cache_path /export/cache/proxy_cache keys_zone=CACHE:10m levels=1:2 inactive=6h max_size=1g; server { location / { proxy_cache CACHE; //指定存放響應到CACHE這個緩存中 proxy_cache_valid 1d; //所有的響應狀態碼都被緩存1d proxy_pass: http://upstream; } } }
如果當前響應中設置了Set-Cookie頭信息,那么當前的響應不會被緩存,可以通過使用proxy_ignore_headers
來忽略頭信息以達到緩存
proxy_ignore_headers Set-Cookie
如果這樣做了,我們需要把cookie中的值作為proxy_cache_key
的一部分,防止同一個URL響應的數據不同導致緩存數據被覆蓋,返回到客戶端錯誤的數據
proxy_cache_key "$host$request_uri$cookie_user"
注意,這種情況還是有問題,因為在緩存的key中添加cookie信息,那么可能導致公共資源被緩存多份導致浪費空間;要解決這個問題我們可以把不同的資源分開配置,比如:
server { proxy_ignore_headers Set-Cookie; location /img { proxy_cache_key "$host$request_uri"; proxy_pass http://upstream; } location / { proxy_cache_key "$host$request_uri$cookie_user"; proxy_pass http://upstream; } }
雖然我們設置了緩存加快了響應,但是有時候會遇到緩存錯誤的請求,通常我們需要為自己開一個后面,方便發現問題之后通過手動的方式及時的清理掉緩存。Nginx可以考慮使用ngx_cache_purge
模塊進行緩存清理。
location ~ /purge/.* { allow 127.0.0.1; deny all; proxy_cache_purge cache_one $host$1$is_args$args }
該方法要限制訪問權限; proxy_cache_purge
緩存清理的模塊,cache_one
指定的key_zone,$host$1$is_args$args
指定的生成緩存key的參數
如果有大的靜態文件,這些靜態文件基本不會別修改,那么我們就可以不用給它設置緩存的有效期,讓Nginx直接存儲這些文件直接。如果上游服務器修改了這些文件,那么可以單獨提供一個程序把對應的靜態文件刪除。
http { proxy_temp_path /var/www/tmp; server { root /var/www/data; location /img { error_page 404 = @store } location @store { internal; proxy_store on; proxy_store_access group:r all:r; proxy_pass http://upstream; } } }
請求首先會去/img
中查找文件,如果不存在再去上游服務器查找;internal
指令用于指定只允許來自本地 Nginx 的內部調用,來自外部的訪問會直接返回 404 not found 狀態。proxy_store
表示需要把從上游服務器返回的文件存儲到 /var/www/data
; proxy_store_access
設置訪問權限
“怎么理解緩存”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。