主流瀏覽器都有緩存機制,主要基於HTTP協議定義的緩存策略。對於一定時間內不發生變動的文檔緩存起來,對於下次請求,就可以直接返回緩存的結果。使用緩存有以下好處:
1、減少冗余的數據傳輸,節省網絡流量成本
2、減少加載時間,客戶能夠快速加載頁面
3、減少對服務端的壓力,避免過載
我們一般會為靜態文件,如圖片,腳本,樣式表等設置緩存,這樣客戶端在下次請求時可以減少不必要的網絡請求。實際上,我們可以把它用在所有組件上,包括Ajax響應,只要可以確認結果不經常變動,就可以使用緩存策略。
AJAX
AJAX (Asynchronous JavaScript and XML)表示異步的javascript和XML,實際上,現在最著名的是JSON。Ajax可分為主動請求和被動請求。被動請求指為了將來使用而預先發起的,提前加載一些用戶可能用到的數據,來提高用戶體驗。主動請求,指基於用戶當前的操作而發起的請求。
雖然AJAX是異步請求,但是對於主動請求,用戶任然需要等待響應。即便是被動請求可以讓用戶感受不到等待,但是仍然會有請求,產生流量,對服務端產生壓力。
HTTP緩存策略
HTTP協議通過Header來控制緩存的,包括請求和響應,這里只講響應的緩存機制。響應緩存主要通過Expires頭和Cache-control頭來控制。下面按優先級由高到列出一些常見的緩存策略,:
- Cache-Control:no-store 不要緩存這個文檔
- Cache-Control:no-cache 保存文檔,但是需要與服務端再驗證才能使用
- Cache-control:max-age 緩存最長保存時間,單位是秒
- Expires:日期 根據日期判斷過期,由於客戶端和服務器日期可能不同步,不推薦使用
緩存過期檢測
為了防止使用了過期的數據,必須要為緩存設置過期時間,當緩存過期時,需要向服務器請求新的數據。但實際上,雖然緩存過期了,但是服務器的數文檔並沒有變動,並不需要返回整個文檔。因此客戶端可以讓服務端在改變文檔的情況下才發送整個文檔。這種特殊請求就是有條件的請求。條件請求一般使用Last-Modified/If-Modified-Since 和 Etag/If-None-Match。
Last-Modified表示文檔的最后修改日期,當緩存過期了,客戶端向服務器發送If-Modified-Since:Last-Modified的值進行條件請求,當服務端在這段時間沒有修改文檔,那么就返回不包含主體的304響應,否則就返回新的文檔。
ETag 則是一個唯一文件標識符,每次文件修改后都會生成一個新的 ETag,因此也可以根據Etag的值來判斷文檔有沒有更新。和最后修改時間檢測類似。
如果響應首部僅包含Last-Modified,就使用If-Modified-Since驗證,如果僅包含ETag,那么使用If-None-Match來驗證。如果兩者都提供了,那么客戶端就應該同時發送兩種驗證。
基於Flask的緩存響應
def make_cache_control_response(json_data): #json_data可能來自數據庫或者服務端緩存(如redis等),也可能是新生成的json數據 resp = make_response() resp.headers['Content-Type'] = 'application/json' resp.cache_control.max_age = 600 #最長緩存600秒 if json_data.get('from_cache'): #來自服務端緩存 del json_data['from_cache'] last_modified = json_data.get('cache_time', 0) if last_modified: del json_data['cache_time'] resp.last_modified = datetime.fromtimestamp(last_modified) #添加Last-Moditified頭 if not resp.last_modified: resp.last_modified = datetime.now() if request.if_modified_since and request.if_modified_since >= resp.last_modified: #如果是條件請求,並且緩存未過期,返回304響應 return resp, 304 resp.data = json.dumps(json_data) return resp, 200