1. 程式人生 > >HTML5離線應用無法更新的定位與解決

HTML5離線應用無法更新的定位與解決

 一、些許前提

最近在製作一個Web應用, 其中用到了HTML5的離線應用功能(offline application), 離線應用的概念就不再闡述, 可以檢視這兩篇文章:

http://www.ibm.com/developerworks/cn/web/1011_guozb_html5off/

http://www.mhtml5.com/2011/02/583.html

這裡主要討論它的更新問題. 首先瀏覽器是有兩部分cache的, browser cache 和app cache, browser cache就是常說的瀏覽器快取, app cache是離線應用的快取. 他們各自的更新機制如下:

 

Browser cache

App cache

其中browser cache的機制大家都很清楚了, 其中離線應用的更新是: 除了第一次訪問是直接拉取server的, 然後後臺更新app cache之外, 其餘的情況都是直接訪問app cache. 因此, 要如果離線應用的程式碼更新了, 只有下次開啟或者重新整理才會生效.

二、找出凶手

OK, 鋪墊完畢. 我的應用主要在webkit核心的瀏覽器使用的, 為了方便起見, 下面的文字都是在chrome的環境下產生的.

在測試機測試離線功能時, 我們發現, 如果更改了js檔案且更新了manifest, 重新整理兩次(嗯, 你沒看錯,是兩次, 第一次於後臺更新app cache, 第二次應用新cache)就會應用上新的程式碼. 但是, 釋出到正式環境之後, 就不能更新了, 把F5按爛了, 也沒什麼變化. 當然這是刪除掉app cache是沒問題的, 但是我們沒辦法要求使用者這樣做.

通過抓包發現, 無論哪個環境, manifest更新了, 瀏覽器端都能抓取新的, 在chrome的控制檯也能看到更新app cache的log, 因此不是manifest本身被快取了的原因. 但是在正式環境裡面, 拉取了新的manifest之後, 就沒有任何更新的請求出去, 太詭異了.

繼續對比http的響應頭, 發現了不同之處, 如下:

測試環境 正式環境

HTTP/1.1 200 OK

Date: Thu, 05 Jan 2012 05:56:38 GMT

Server: NWS_HY_P91

Last-Modified: Thu, 05 Jan 2012 04:29:52 GMT

Expires: Thu, 05 Jan 2012 05:56:38 GMT

Connection: close

Content-Type: application/javascript

Vary: Accept-Encoding

HTTP/1.1 200 OK

Date: Thu, 05 Jan 2012 05:56:38 GMT

Server: nginx

Last-Modified: Thu, 05 Jan 2012 04:29:52 GMT

Expires: Thu, 05 Jan 2012 05:56:38 GMT

Connection: keep-alive

Content-Type: application/javascript

Cache-Control: max-age=10368000

可以看到, 兩個環境裡面有3個不同, connection, vary, cache-control. 第一眼望去, 感覺就可能是cache-control的問題. 於是用fildder把響應卡住, 把max-age改成0, 結果呢, 它正常更新了! 因此猜測app cache的更新應該是先去browser cache找, 找到了該檔案, 並且沒過期, 就不再訪問server了, 因此抓包也看不到任何請求. 它的流程應該是這樣的:

App cache 2

於是我本地搭了一個apache驗證, 把js的max-age設定為30秒, 果然在30秒內, 無論怎麼修改manifest和js, 都不會有對js的新請求, 它一直在向browser cache拉取, 而30秒之後, 就能去server拉去新的js了.

三、誰是真凶?

理論上這件事就應該到此為止了, 只要把正式環境的cdn都去掉cache-control就大功告成啦. 但是,去掉cache-control將大大浪費公司的頻寬! 而且deewii童鞋發現, 有一臺放置vm(應用用到的一個介面層, 是一個頁面)的機器, 也設定了cache-control, 但是卻能正常更新, 這下又變得撲朔迷離了.

剛才我們對比響應頭髮現了三個不同, 繼續看connection這東西, keep-alive是用來保持長連線的, 莫非是它的影響? 但是抓了幾個包, 卻發現vm所在機器返回的響應頭裡面是Connection: keep-alive, 因此排除了這個影響.

最後只能把希望放在Vary: Accept-Encoding 裡面了, 還是剛剛搭apache, 加上max-age=10368000, 加上keep-alive, 加上Vary: Accept-Encoding, 修改manifest, 重新整理… 天, 竟然發起更新請求了! 原來你(Accept-Encoding)才是真正的凶手! 有沒有可能是本地才會這樣呢, 繼續用fiddler卡住正式環境的響應, 加上Vary: Accept-Encoding, 果然重新整理之後也能正常更新了.

雖然找到原因, 但是本人對這個Accept-Encoding不是很瞭解, 查了些資料(參考這裡: http://www.falconhan.com/webanalytics/vary-accept-encoding-header.htm ), 猜測Accept-Encoding是用來告訴瀏覽器只快取它自己宣告的型別(在發起的http請求頭裡面指定, 例如: Accept-Encoding: gzip,deflate,sdch)的檔案, 而存在於browser cache裡面的內容則是瀏覽器解壓後的, 因此app cache去browser cache更新的時候發現格式不對, 就拋棄掉, 繼續去server請求. 不知道想的對不對, 歡迎拍磚指正.

四、寫在後面

花了一個晚上+一個上午, 總算把這個無法更新的問題解決了. 雖然最後得到的結論很簡單, 只要在伺服器配個返回頭就行了, 但是找問題的時候相當痛苦. 歸根到底還是對http協議不夠了解, 學藝不精還得繼續努力.

PS: 在用firefox測試的時候, 發現它只有第一次開啟(或者刪掉離線資料之後)的時候會去請求manifest和其他離線資源, 之後它竟然完全不訪問manifest, 導致沒辦法更新, 網上也沒找到什麼好資料(網上也有遇到相同狀況的童鞋: http://hi.baidu.com/erik168/blog/item/aadff9547720d8013b293559.html ), 不知道有沒有童鞋瞭解的.

參考資料

Offline Application

http://www.ibm.com/developerworks/cn/web/1011_guozb_html5off/

http://www.mhtml5.com/2011/02/583.html

http://www.mhtml5.com/resources/html5-js-api-%E6%95%99%E7%A8%8B%EF%BC%88%E5%9B%9B%EF%BC%89-%E7%A6%BB%E7%BA%BF%E5%BA%94%E7%94%A8

Vary: Accept-Encoding

http://www.falconhan.com/webanalytics/vary-accept-encoding-header.htm

http://hi.baidu.com/%B9%E3%D6%DD_it%C4%D0/blog/item/c2dd76c96d1eb4009c163d2a.html

http://mark.koli.ch/2010/09/understanding-the-http-vary-header-and-caching-proxies-squid-etc.html

Firefox的ApplicationCache

http://hi.baidu.com/erik168/blog/item/aadff9547720d8013b293559.html