?一、背景
今天測試反饋,前端更新了H5的內(nèi)容,但是客戶端通過webview的方式打開后,發(fā)現(xiàn)內(nèi)容沒有更新,使用charles抓包,發(fā)現(xiàn)客戶端訪問訪問時,連請求都沒法發(fā)出。于是測試的妹子在企業(yè)微信中@我,看到消息有點(diǎn)懵,記得去年就排查過webview的緩存方式,怪沒有記錄,今天決定把問題重新記錄一下。
二、webview的緩存方式
先上代碼:
WebSettings settings = webView.getSettings();// 開啟 DOM storage API 功能settings.setDomStorageEnabled(true);// 開啟 DB storage API 功能settings.setDatabaseEnabled(true);// 開啟 AppCacheEnablesettings.setAppCacheEnabled(true);
//?設(shè)置緩存模式,非常重要,決定了webview緩存資源的方式settings.setCacheMode(WebSettings.LOAD_DEFAULT);
?我們重點(diǎn)關(guān)注setCacheMode方法緩存模式,系統(tǒng)提供了5種緩存模式,其中一種已經(jīng)LOAD_CACHE_NORMAL在新版本中廢棄:
LOAD_CACHE_ONLY: 不發(fā)網(wǎng)絡(luò)請求資源,只讀取緩存。
LOAD_DEFAULT:?根據(jù)cache-control或者Last-Modified決定是否從網(wǎng)絡(luò)上取數(shù)據(jù)。默認(rèn)采用該方案
LOAD_CACHE_NORMAL:?新版本已經(jīng)廢棄,同LOAD_DEFAULT
LOAD_NO_CACHE: 不使用緩存,只從網(wǎng)絡(luò)獲取數(shù)據(jù)。
LOAD_CACHE_ELSE_NETWORK:只要本地有,無論是否過期,或者no-cache,都使用緩存中的數(shù)據(jù)。本地沒有緩存時才從網(wǎng)絡(luò)上獲取。
大家都知道,webview是一個瀏覽器,且在4.4以下版本,都是采用WebKit作為內(nèi)核,4.4以上采用Chrome作為內(nèi)核。既然是瀏覽器,且又是緩存,那我們就有必要引入Http協(xié)議中的緩存,因為瀏覽器是對Http協(xié)議最基本的執(zhí)行者,且瀏覽器的緩存原理,都是對Http協(xié)議的緩存做了基本且標(biāo)準(zhǔn)的支持(可能瀏覽器有擴(kuò)展,但是都兼容Http基礎(chǔ)協(xié)議)
三、HTTP協(xié)議緩存
我們這里只介紹幾個常用的緩存header,詳細(xì)的大家可以度娘Http協(xié)議相關(guān)。
Http協(xié)議常用的緩存header有:
Cache-Control:Cache-Control是非常最重要的規(guī)則,主要用于控制網(wǎng)頁緩存。比如當(dāng)Cache-Control:max-age=30時,在表示資源正確加載后,30s內(nèi)重新請求,不在重新發(fā)網(wǎng)絡(luò)請求,直接使用本地資源。cache-control可以配置很多規(guī)則,這里介紹幾種常用的:
【max-age = xx】:在客戶端緩存xx秒,過了xx秒后,重新請求資源,但是這個另外一個Etag或last-modified有關(guān)系,如果服務(wù)端跟進(jìn)Etag判斷文件沒有修改,則返回304,這是還是使用緩存,如果返回200,則說明資源跟新了,瀏覽器正常從網(wǎng)絡(luò)加載資源
【s-maxage = xx】:作用同max-age,但是會覆蓋max-age,不過他只有在代理服務(wù)器中生效
【no-store】:不緩存任何資源
【no-cache】:資源會緩存,表示必須先與服務(wù)器確認(rèn)返回的響應(yīng)是否發(fā)生了變化,然后才能使用該響應(yīng)來滿足后續(xù)對同一網(wǎng)址的請求
Expires:具體時間,例如Expires:Tue,25 Sep 2018 07:17:34 GMT, 這表示這個文件的過期時間是格林尼治時間2018年9月25日7點(diǎn)17分。因為我是北京時間2018年8月26日15點(diǎn)請求的, 所以可以看出也是差不多一個月有效期。在這個時間之前瀏覽器都不會再次發(fā)出請求去獲取這個文件。Expires是HTTP/1.0中的字段,如果客戶端和服務(wù)器時間不同步會導(dǎo)致緩存出現(xiàn)問題,因此才有了上面的Cache-Control。當(dāng)它們同時出現(xiàn)時,Cache-Control優(yōu)先級更高。
Etag:文件的一個標(biāo)識,可以理解為MD5
Last-Modified:文件最后修改時間。
總結(jié):瀏覽器就是跟進(jìn)上述緩存邏輯,對資源進(jìn)行緩存,當(dāng)然了,緩存業(yè)務(wù)不僅僅這么簡單,還是非常復(fù)雜的,有興趣的可以查看HTTP協(xié)議和瀏覽器內(nèi)核。
四、解決我遇到的問題
【問題現(xiàn)象】:服務(wù)器的H5內(nèi)容更新了,但是客戶端卻更新不到,且沒有發(fā)請求。
【問題原因猜想】:猜想android 系統(tǒng)的Webview做了緩存策略,導(dǎo)致在緩存有效期,不發(fā)請求。
既然我們已經(jīng)猜想了問題原因,那就需要順著這個思路排查該H5的緩存策略配置是什么:
這是抓包看到請求Http response的Header內(nèi)置,發(fā)現(xiàn)只有ETag和Last-Modified,并沒有配置Cache-Control,那問題來了,如果沒有配置Cache-Control,那么瀏覽器的緩存策略是什么呢?通過一番折騰,終于找到了瀏覽器默認(rèn)的緩存策略,這個緩存策略叫做:啟發(fā)式算法,原理是:
通過采用請求響應(yīng)頭中的Date減輕Last-Modified的值的10%作為緩存時間,即在這10%的時間內(nèi)不發(fā)請求。直接使用緩存資源。
通過驗證,發(fā)現(xiàn)webview的默認(rèn)緩存策略,確實(shí)如此。我們的問題頁定位到了。既然定位了,就好解決。
五、解決方案
1、服務(wù)端添加cache-control 的方式,這里我們配置的是max-age = 24*60*60(一天),需跟進(jìn)自己的業(yè)務(wù)處理。
2、也可以配置為no-cache,但是可能增加服務(wù)端的請求量,需要跟進(jìn)自己的業(yè)務(wù)更新頻率配置。