|
開發(fā)出高性能的網(wǎng)站,第二部分 — 最佳緩沖控制 - 2005-04-02 作者 Thomas A. Powell 和 Joe Lima 第二部分 — 最佳緩沖控制本文的第一部分 (二月份)介紹了如何通過優(yōu)化代碼來盡可能少的傳輸數(shù)據(jù),在本文的第二部分中,我們將著重介紹如何利用Web端的緩沖技術(shù)(caching)來盡可能降低傳輸?shù)念l繁度。一旦您開始注意進行有效的緩沖設(shè)置,您便可以極大地減少網(wǎng)頁加載的次數(shù),尤其對于經(jīng)常訪問您網(wǎng)站的??秃椭艺\的訪問者來說更是如此,而且還可以降低您整體帶寬的消耗,并減少您有限的服務(wù)器資源的占用。 Web緩沖的種類 緩沖技術(shù)的原理很簡單。為防止每次都重復(fù)地加載同一內(nèi)容而帶來的各種資源消耗和浪費,我們保留一份內(nèi)容的本地副本,并且只要它還有效就可以反復(fù)使用它。最常見的網(wǎng)頁緩沖是瀏覽器緩沖,瀏覽器緩沖在最終用戶的本地硬盤上儲存了圖像和其他網(wǎng)頁對象的副本,以供重復(fù)使用。除此之外,還有別的緩沖,比如web服務(wù)器上的,還有網(wǎng)絡(luò)路徑上的,甚至最終用戶本地的網(wǎng)絡(luò)上都有—不過,這些不同的緩沖其目的是基本一致的。從本地瀏覽器的緩沖上開始,往外有本地網(wǎng)絡(luò)的代理(proxy)緩沖,這樣如果代理緩沖中有相同的內(nèi)容,本地網(wǎng)絡(luò)中其它的用戶便不必跑到遠端的web服務(wù)器上去索取內(nèi)容了,他們直接使用代理緩沖里這些相同的東西就可以了。再往外,你的ISP和再往外的各個ISP們可能都有一個類似的代理緩沖。最后,在web服務(wù)器上還會使用一個叫做‘reverse proxy cache’的緩沖來保持住最后生成的網(wǎng)頁,準備提交,這樣可以減輕服務(wù)器重復(fù)生成和提交被請求的內(nèi)容的負擔。 我們可以把上述各種網(wǎng)頁緩存分成兩類:私有的(private)和公共的(public)。私有緩沖,基本上就是瀏覽器緩沖,對于一個單個的用戶代理(user agent)來說是獨一無二的,也就是說只有擁有它的某個最終用戶才可以使用。其他的緩沖,比如proxy和reverse proxy緩沖都是公共緩沖。它們是共享資源,其中存儲的東西可以被不止一個最終用戶使用。圖一展示了網(wǎng)頁主要常見的緩沖類型:
圖一:網(wǎng)頁中的緩沖 上述圖示說明了我們討論的一個關(guān)鍵問題:網(wǎng)頁緩沖存在于網(wǎng)站和網(wǎng)頁服務(wù)的各個地方,它們盡可能的保存您要訪問的網(wǎng)站內(nèi)容。一方面,從網(wǎng)站性能的角度來講,我們要讓緩沖能夠自行發(fā)揮作用;另一方面對其能夠進行有目的的控制也很關(guān)鍵,比如我們必須可以規(guī)定哪些對象可以放入緩沖中、哪些不可以,以及對象保留多長時間等。 新鮮程度和有效性 為了更好的使用緩沖,包括更有效的使用瀏覽器緩沖,我們需要對某一個資源的有效性提供指示,以表明這個資源是否還可以放在緩沖中。更具體的說,我們需要對網(wǎng)頁對象提供一系列的緩沖規(guī)則,比如設(shè)置失效期,達到失效期的對象就不能再保存在緩沖中了。幸運的是,對于依據(jù)緩沖控制規(guī)則失效的東西,我們有一些工具來處理它們。 有兩個概念控制著緩沖如何工作:新鮮程度(freshness)和驗證(validation)。新鮮程度指的是某個放在緩沖的對象是否是新的,換成更技術(shù)化的術(shù)語來說,就是這個在緩沖中的對象的狀態(tài)是否和其相對應(yīng)的在web服務(wù)器上的資源的狀態(tài)一致。如果瀏覽器緩沖或者其他的緩沖沒有足夠的信息表明其是新鮮的,則系統(tǒng)就會產(chǎn)生警告錯誤并把這個對象當作過期的或者是不新鮮的(stale)。驗證則是某個緩沖檢查原始服務(wù)器來確認是否有潛在不新鮮的對象的過程。如果服務(wù)器確認某個緩沖對象仍是新鮮的,則瀏覽器繼續(xù)使用本地的資源,否則服務(wù)器就會送出一份新鮮的拷貝。 一個簡單的緩沖例子 我們舉一個例子來說新鮮和驗證的概念。在此我們使用瀏覽器緩沖為例,別的公共緩沖的核心原理和瀏覽器緩沖是一樣的。 第一步:遠程站點有一個網(wǎng)頁叫做page1.html。這個網(wǎng)頁引用了image1.gif、image2.gif和image3.gif,并且有一個到page2.html的鏈接。當我們第一次訪問這個網(wǎng)頁的時候,HTML和相關(guān)的GIF圖像就會被一個接一個的被下載到本地的瀏覽器緩沖中
圖二:第一次緩沖加載 一旦數(shù)據(jù)被下載到緩沖中,這些數(shù)據(jù)就會被加上‘郵戳’,這個郵戳就標示了它們從哪里來、何時被訪問過。郵戳中也有可能包含有第三條信息:何時需要被重新下載。但是,大多數(shù)的網(wǎng)站都不會給它們的數(shù)據(jù)加上這第三條信息,所以我們也假設(shè)我們的例子中也不涉及到這第三條信息。 第二步:用戶點擊了page2.html的鏈接,這個網(wǎng)頁以往從未被訪問過,這個網(wǎng)頁引用了image1.gif、image3.gif和image4.gif。此時,瀏覽器下載了這個網(wǎng)頁的標示代碼,但問題來了:還需要重新下載image1.gif和image3.gif嗎,既然它們已經(jīng)存在于緩沖中了?這個問題的答案顯然是不必重新下載。不過,我們?nèi)绾伪WC這兩個圖像自打上次存到緩沖中后,從來沒有修改過呢?如果沒有緩沖控制信息,可能我們沒法保證。所以,瀏覽器會給服務(wù)器發(fā)送請求來能夠重新驗證這些圖像是否被修改過。如果沒有被修改過,服務(wù)器就會快速返回一個304
圖三:檢查緩沖 從這個簡單的例子中可以很明顯地看出來,,即使當CSS、圖像、和JavaScript是新鮮的,我們也不一定能夠享受到緩沖帶來的好處,因為瀏覽器訪問一個對象前都要先到服務(wù)器那里兜個圈子。 IE 瀏覽器中缺省的設(shè)置是‘自動’,這個設(shè)置可以在單次瀏覽器會話中跳過對緩沖對象再驗證的過程,這在一定程度上減少了瀏覽器和服務(wù)器之間的持續(xù)的來回通信。您可能注意過在同一次的瀏覽會話中,一般來說重復(fù)訪問同一個網(wǎng)頁的加載速度都比較快。您如果在瀏覽器設(shè)置中選擇‘每次訪問網(wǎng)頁’這個選項,則您就會發(fā)現(xiàn)性能上的下降,這是很多個304
圖四:IE的緩沖控制對話框 注意:盡管IE的‘智能緩沖’可以有效地減少不必要的驗證請求,它也是IE不斷提醒用戶若要看到新內(nèi)容請清除緩沖的罪魁禍首。所以,使用緩沖,必須折中考慮很多事情。 設(shè)置緩沖控制策略 通過最小化來回通信的次數(shù)可以對瀏覽器加載次數(shù)產(chǎn)生巨大的變化。這種變化最顯著的情況出現(xiàn)在下述情況下:經(jīng)過了第一次訪問后,用戶再次光顧這個網(wǎng)頁。這種情況下,所有的網(wǎng)頁對象都得再驗證,每次都會消耗寶貴的時間,更不用說消耗的帶寬和服務(wù)器資源。另一方面,恰當?shù)氖褂镁彌_控制可以讓瀏覽器直接從緩沖中加載原來加載過的對象,而不必‘長途跋涉’再次造訪服務(wù)器。添加緩沖控制規(guī)則的效果,可以在網(wǎng)頁的加載時間上看得出來,即使用戶使用的是寬帶,也可以感覺得到網(wǎng)頁渲染的速度變快了。除了用戶確實感覺得到的變化外,web服務(wù)器也可以從處理大量的緩沖再驗證請求的負擔重稍事解脫一些,這樣進而可以更好的提供服務(wù)。 不過,為了更充分的享受到緩沖帶來的好處,程序員需要花些時間精心制作一些緩沖控制策略,來按照網(wǎng)站對象可能的生命周期對其進行分類。這里有一個例子,說的是某簡單的電子商務(wù)網(wǎng)站的一整套緩沖控制策略:
在這個表中,從緩沖控制的角度來看,共有六類不同類型的對象。既然和公司標識及其他品牌標示有關(guān)的東西不太可能變化,這些導(dǎo)航和公司標識的圖片就可以看作是基本上永久不變的。CSS和JavaScript文件一般來說也是至少半年才會變化。因為網(wǎng)站內(nèi)容對于搜索引擎優(yōu)化和用戶體驗來說至關(guān)重要,因此主要的頁眉圖像可以設(shè)置成變化稍微頻繁一些。每月的‘特價商品’圖像,當然它的保鮮期也就設(shè)置成一個月了。還有一些針對個人的特價商品,它們的圖象保存在用戶的緩沖中,其保鮮期設(shè)置成兩周,也就是從首次訪問開始算起的過期時限是兩周。注意,這個類別被標注成‘私有’,以表示這種緩沖不能存在于共享或代理緩沖中。最后,除上述之外其他所有別的內(nèi)容,它們的缺省都是不能作為緩沖內(nèi)容的,依次來保證這些文本和動態(tài)的內(nèi)容每次訪問請求都是新鮮送出的。 對于緩沖HTML網(wǎng)頁,你必須得小心謹慎,不管它們是不是靜態(tài)生成的,除非你很清楚其中的玄機,如果你不清楚的話,就最好保證這些網(wǎng)頁不要緩沖。如果某用戶緩沖了你的HTML網(wǎng)頁,你設(shè)置了一定長度的過期時間,則在過期之前,無論你的網(wǎng)頁做了多少更新,該用戶都不會看到這些變化。另一方面,如果你只是對于獨立的對象進行緩沖的話,比如圖像、Flash文件、JavaScript、和樣式表單等,簡單的進行重命名就可以替換掉這些緩沖內(nèi)容。打個比方,您設(shè)定了一個策略,想要每年更新您站點的標識文件,但只過了半年的時候,您的公司要做品牌形象改變,并且需要對網(wǎng)站進行相應(yīng)的修改。幸運的是,如果您沒有設(shè)定HTML網(wǎng)頁要做緩沖,您只消改變標識文件的名稱(比如logo.gif變成newlogo.gif)并更改相應(yīng)HTML文件中的 在慎重的考慮過哪些對象可以或者不可以被存入緩沖中,下一步就是要部署這些策略。 管理緩沖 有三種方法來設(shè)置緩沖控制規(guī)則:
上述三種方法各有其優(yōu)缺點,我們簡要的來總結(jié)一下。 通過 最簡單設(shè)定緩沖控制的方法就是使用 <meta HTTP-EQUIV="Expires" content="Sun, 31 Oct 2004 23:59:00 GMT" />
在上述例子中,瀏覽器解析到這個HTML時,會認為這個網(wǎng)頁到了2004年10月就會過期,并把這個規(guī)則加到瀏覽器的緩沖中。因為這個網(wǎng)頁會通過 當然了,緩沖網(wǎng)頁雖然可以帶來好處,但如上面所述有時我們并不想緩沖它們,這時您可以把 您可能會想用戶電腦時鐘可能會和服務(wù)器的不一樣,您可能因為這個會把過期時間設(shè)置成很久很久以前,但在實際中,這基本上不是什么問題,因為緩沖控制參考的就是服務(wù)器的日期。 和HTTP1.0和HTTP1.1兼容的瀏覽器都可以使用帶有以前時間的 <meta HTTP-EQUIV="Pragma" content="no-cache" /> <meta HTTP-EQUIV="Cache-Control" content="no-cache" />
這兩個標簽很簡單,它們有一個重大問題—中間的代理緩沖讀不到它們,因為中間代理不會去解析HTML數(shù)據(jù),所以就要依賴HTTP的header來進行緩沖控制了。因為如此,反正瀏覽器也要讀HTTP headers, 編程的緩沖控制 多數(shù)的服務(wù)器端的編程環(huán)境,如:PHP,ASP,和ColdFusion都可以讓你修改HTTP Headers來實現(xiàn)一些特殊的功能。比如,在ASP中,你可以在網(wǎng)頁的上面加上一些代碼,來調(diào)用一些現(xiàn)成的Response對象屬性: <%
在此你通過ASP來生成適合HTTP1.0的Expires header和適合HTTP1.1的 Expires: Sat, 14 Feb 2004 20:46:04 GMT
這個機制或類似的其他主流服務(wù)器端編程環(huán)境下的機制比單單使用 不過,服務(wù)器端編程環(huán)境也面臨著一個無法解決的問題。假設(shè),若生成的ASP文件中調(diào)用了幾個圖像,并且根據(jù)你設(shè)定的緩沖策略,它們的保鮮期是一年。那么,你將如何通過部署HTTP headers來告訴緩沖,這些圖像要保存那么長時間呢?你可以使用服務(wù)器端的腳本來返回這些圖像,但是這樣既復(fù)雜又浪費資源。在服務(wù)器端本身設(shè)置緩沖控制信息,這是較理想的為靜態(tài)的外部的代碼(如CSS、JavaScript和二進制對象等)設(shè)置緩沖控制信息的方法。 編程緩沖控制 Apache和Microsoft IIS都提供一系列的緩沖控制機制。麻煩的是,這兩種常用的web服務(wù)器使用不同的方法來緩沖,并且緩沖控制策略的授權(quán)也不完全掌握在對網(wǎng)站資源非常熟悉的開發(fā)者手中。 Apache上的緩沖控制 就設(shè)定緩沖策略而言,在Apache模塊正常安裝的情況下,Apache比IIS要容易一些。 Apache的服務(wù)器管理員,可以通過在服務(wù)器的配置文件中(一般來說是httpd.conf)設(shè)定mod_expires值來給不同的對象設(shè)定保鮮期。在Apache中, mod_expires的 若您的Apache服務(wù)器起初沒有mod_expires這一項,我們可以生成它,最好方法就是把它制作成一個共享對象(最簡單的方法是使用aspx),之后再在httpd.conf文件中加上這樣一行: LoadModule expires_module modules/mod_expires.so
如上述,您可以把您配置的指示直接放在httpd.conf文件中。不過,不少管理員更傾向于在外部配置文件中來使用這些東西,這樣可以讓配置文件干凈利落。在我們的例子中,我們也遵循這樣的方法,在httpd.conf中使用Apache的 <IfModule mod_expires.c≶ Include conf/expires.conf
|
|
|