在前端性能優(yōu)化中應(yīng)用HTTP緩存的三部曲
Spike先生是Best Experience公司的IT運(yùn)營部門主管,他的團(tuán)隊(duì)成功地利用Http Cache優(yōu)化了前端工程。
Spike將通過三個(gè)Scenario來展示他的團(tuán)隊(duì)是如何做到這一點(diǎn)的:
- 通過配置Http Cache Expire來消減訪問壓力,提高用戶體驗(yàn)
- 通過版本化來強(qiáng)制失效本地的過期緩存
- 通過內(nèi)容摘要命名文件來更精確的控制緩存以及實(shí)現(xiàn)非覆蓋式的發(fā)布
***個(gè)故事:我不想要那么多服務(wù)器和帶寬
Best Experience面臨的資源訪問壓力和用戶體驗(yàn)方面的問題
隨著Best Experience提供的前端應(yīng)用越來越強(qiáng)大,Spike的壓力也越來越大:
- IT部門為了應(yīng)對(duì)來自靜態(tài)資源的訪問壓力,不斷購置服務(wù)器和帶寬。
- 糟糕的用戶體驗(yàn)使得用戶轉(zhuǎn)投到競(jìng)爭(zhēng)對(duì)手的網(wǎng)站。
工程師們剛剛通過應(yīng)用Minify、AMD、打包、Gzip等手段優(yōu)化了前端頁面的體驗(yàn), 最終得到如下圖所示的一個(gè)資源引用關(guān)系:
“還是很多東西要下載啊,該拿什么來拯救該死的延遲呢?”——Spike看著圖想到。
他突然想起來:在早年間,Yahoo曾發(fā)布了《關(guān)于優(yōu)化前端體驗(yàn)的35條建議和指導(dǎo)》,其中第三條是:“Add an Expires or a Cache-Control Header”。
Yahoo是這樣描述這條建議的:
Web page designs are getting richer and richer, which means more scripts, stylesheets, images, and Flash in the page. A first-time visitor to your page may have to make several HTTP requests, but by using the Expires header you make those components cacheable. This avoids unnecessary HTTP requests on subsequent page views. Expires headers are most often used with images, but they should be used on all components including scripts, stylesheets, and Flash components.
Browsers (and proxies) use a cache to reduce the number and size of HTTP requests, making web pages load faster. |
“這個(gè)正是我尋找的銀彈”——Spike得意的笑了。
于是,Spike寫下了***個(gè)Technology Story
作為IT 部門的老大:
作為IT 部門的老大:
我希望通過應(yīng)用HTTP緩存技術(shù),重用已經(jīng)下載過的資源, 用于消減用戶在瀏覽頁面時(shí)產(chǎn)生的不必要的Http Request。 以此,來提升用戶在瀏覽頁面時(shí)候的體驗(yàn), 以及降低對(duì)于公司服務(wù)器資源的訪問壓力。 |
并找來了工程師Tom。
Expire帶來的美好生活
Tom剛剛參與了前一輪的優(yōu)化工作,雖然成果顯著,但是他并不滿足。
當(dāng)Tom看到Jim寫下的Story時(shí)眼前一亮:“這個(gè)方法太贊了!我甚至可以在登錄頁面底部放置對(duì)其他頁面資源的引用。提升用戶在整個(gè)網(wǎng)站的瀏覽體驗(yàn)。”——Tom的小宇宙瞬間爆發(fā),很快就完成了新的優(yōu)化方案。
Best-Experience的用戶在接下來的時(shí)間里瀏覽頁面,會(huì)這樣下載資源,以圖片bgimage.png為例:
- 用戶***次獲取圖片的時(shí)候,Http Request 如圖:
- 之后用戶再次獲取圖片的時(shí)候,則完全可以從瀏覽器的緩存中讀取數(shù)據(jù)了。
因?yàn)椴捎昧薍ttp緩存方案,
- 用戶的feedback越來越好,訪問量提高了;
- IT部門也不用那么多服務(wù)器和帶寬了。
財(cái)務(wù)總監(jiān)邀請(qǐng)Spike共進(jìn)晚餐,并談起了自己在希臘的度假。
“我想我也應(yīng)該去圣托里尼度個(gè)假,犒勞下自己”——Spike美滋滋的想到。
第二個(gè)故事:失效緩存是個(gè)技術(shù)活
這個(gè)BUG我們明明修了啊!
一天,QA Tyke發(fā)現(xiàn)最近一輪發(fā)布的前端應(yīng)用中沒有包含很多新的feature。Jerry承諾說已經(jīng)跟著這個(gè)月的release上線了,還測(cè)試過了。經(jīng)過一番折騰,Jerry發(fā)現(xiàn)瀏覽器一直在使用舊的緩存,而不是***的版本。Spike找來了Jerry 和Tom,三個(gè)人一起手動(dòng)對(duì)引用的資源做了重命名、做了緊急修復(fù)。
“真是沒有銀彈啊,我的圣托里尼啊!”——Spike頭疼的想到。
Spike、Jerry、Tom和Tyke坐在了一起,得出了新的結(jié)論:
- 緩存前端工程中的資源時(shí),需要考慮緩存有效期的問題
- 雖然35條建議和指導(dǎo)中建議“Configure ETags”,但是很難確定靜態(tài)資源緩存的有效期
- 雖然Http緩存可以支持No-Cache或者max-age =0的方式,保證瀏覽器每次都向服務(wù)器驗(yàn)證緩存有效性,但是這樣會(huì)大大增加服務(wù)器的壓力
- 可以通過在資源引用上增加形如:<.... src="###.js?v=$version$">的版本化方式,來強(qiáng)制瀏覽器更新緩存。
Spike寫下了新的Technology Story
作為IT部門的老大:
我希望在前端系統(tǒng)中,對(duì)引用的靜態(tài)資源進(jìn)行版本化管理。 使之既可以通過Http緩存來提升用戶體驗(yàn),降低服務(wù)器壓力; 也可以方便用戶即時(shí)獲得更新后的資源。 |
“這都10月了,看來是去不成圣托里尼了,總覺得這個(gè)方案哪里有問題”——Spike忐忑不安。
用版本機(jī)制來保證瀏覽器更新資源
Jerry和Tom(很難想象他們兩怎么配合的)終于在前端工程中實(shí)現(xiàn)了自動(dòng)化的資源版本化管理:用戶在最初訪問頁面的時(shí)候,會(huì)得到這樣一個(gè)資源引用:
而當(dāng)新的版本上線后,用戶會(huì)得到這樣一個(gè)資源引用:
第三個(gè)故事:更精確的緩存管理和平滑升級(jí)
(這個(gè)案例來自于知乎的大公司里怎樣開發(fā)和部署前端代碼? 張?jiān)讫埖幕卮?,前一個(gè) story的內(nèi)容有涉及)
每次更新后的尖峰時(shí)刻
11月的Release后,運(yùn)維人員Nibbles找到Spike,“這次上線以后,服務(wù)器壓力突然劇增,從GA上看到用戶花了很多時(shí)間在資源下載上”,Spike找來了Tom、Jerry、Tyke和Nibbles,幾個(gè)人坐在一起分析原因:
“這是因?yàn)?1月的部署完成后,前端應(yīng)用引用的資源版本升級(jí),所有緩存失效導(dǎo)致的”——Tom 想了想說
“所有的資源引用?我還以為我們能精確到每一個(gè)文件的更新呢”——Nibbles驚訝道 “如果單獨(dú)標(biāo)明每一個(gè)資源的版本,那么按照我們的實(shí)際情況來看,每次上線后訪問壓力就沒那么大了”——Tyke “我之前看WebPack做到了”——Jerry興致勃勃的談了起來。 “他們采用的是文件摘要的方式,就是用MD5對(duì)文件求值,如果兩個(gè)文件是相同的,那么就求得同一個(gè)hash值;如果文件是不同的,就求得不同的hash值”——Jerry “我們可以用這些文件的hash值作為版本號(hào),就像這樣”——Jerry “能不能通過文件名做版本管理,我希望知道哪些文件是這次部署要移除的,哪些是新增的”——Nibbles “這有什么問題么?”——Spike很疑惑 “明年不是要做CDN么?靜態(tài)資源和頁面文件會(huì)放置到不同的服務(wù)器上,很難做到頁面文件和靜態(tài)資源同批次更新,而且CDN的資源生效是有延遲的”——Nibbles |
(關(guān)于 CDN 和非覆蓋部署式部署,請(qǐng)參考張?jiān)讫埖拇蠊纠镌鯓娱_發(fā)和部署前端代碼?和前端工程之CDN部署)
"恩,那么就這樣吧,我回去寫Story。"——Spike 一錘定音。
"還好,我們之前用了WebPack,這就簡(jiǎn)單了"——Jerry |
Spike寫下了第三個(gè)Story
作為IT 部門的老大:
我希望能用文件hash來命名靜態(tài)資源文件, 使之可以按照文件來控制緩存和部署 "我覺得這回是***一個(gè)Story了"——Spike越來越樂觀。 |
過渡到非覆蓋式部署——大圓滿?
如何應(yīng)用WebPack的具體過程不再概述。
圖片來源大公司里怎樣開發(fā)和部署前端代碼?
這樣,Nibbles就可以很愉快的通過文件名比對(duì),來分析每次部署變更的內(nèi)容;而Best Experience未來上線的流程也會(huì)變?yōu)?
- 先將新增的靜態(tài)資源文件發(fā)布到靜態(tài)資源服務(wù)器上
- 驗(yàn)證新的靜態(tài)資源是否正確發(fā)布
- 服務(wù)器暫時(shí)離線,替換 html 文件等
- 刪除無用的靜態(tài)資源文件
“終于可以踏踏實(shí)實(shí)過圣誕節(jié)了”——Spike看著日歷。
總結(jié)
Spike的總結(jié)
年底了,Spike在年終總結(jié)中寫到:
以后在實(shí)施前端工程中,我們可以通過:
- 配置永不過期的本地緩存——節(jié)約帶寬,提升用戶體驗(yàn)
- 采用文件摘要作為緩存依據(jù)——更精確的緩存控制
- 采用CDN——降低用戶請(qǐng)求資源時(shí)解析DNS的延遲
- 利用文件摘要作為文件名——實(shí)現(xiàn)非覆蓋式的部署,降低down time
我的總結(jié)
我引用前端工程之CDN部署一文中對(duì)非覆蓋式、緩存設(shè)計(jì)、CDN這些解決方案間的前因后果做的總結(jié):
如果考慮到項(xiàng)目開發(fā)階段,那么這將是更為復(fù)雜的軟件工程問題。在這個(gè)問題域中,還需要囊括文件壓縮、合并、打包、重命名、目錄設(shè)置等問題。還好Gulp、Webpack、FIS、AMD、RequireJS這些工具及對(duì)應(yīng)的插件能幫助到我們。WebPack提供了Hash、ChunkHash、ContentHash,與此同時(shí),社區(qū)提供了MD5-Hash。
當(dāng)然這些都是關(guān)于工具的話題了,這次我們主要談的是工程。淺談前端集成解決方案里提到了前端領(lǐng)域的8個(gè)技術(shù)元素與分類,挺有意思的。
【本文是51CTO專欄作者“ThoughtWorks”的原創(chuàng)稿件,微信公眾號(hào):思特沃克,轉(zhuǎn)載請(qǐng)聯(lián)系原作者】