網站Cache全分析

本文經過我對大多數網站的http頭分析、自己做網站中的一些緩存設置、服務器的一些配置優化,後又閱讀了RFC2616HTTP協議相關內容,以做此文。


1、爲什麼要cache?

這是個經常提到但很多時候又無法突然準確回答的問題。粗略講,就是要加速和減壓。
加速:試想中國的網絡,網通電信(就不提鐵通之類)開創了互聯互通新障礙,我們的用戶在不多交錢的時候就得忍受奇慢的跨網速度。而作爲互聯網運營者,我們希望用戶獲得最好的用戶體驗,因此我們就要打破這個障礙。
很多互聯網服務商已經用分佈cache解決了這個問題,比如sina,把中心服務器的內容分發到各個省市的cache服務器,根據用戶所在的網絡(ip段)來確定用戶拉去最近服務器上的內容,達到訪問速度最快。
對於小型網站,頁面靜態化也是常用的cache方法,因爲web服務器處理靜態頁面速度優於程序腳本,所以也起到了加速的作用,當然,也爲了下一個原因-服務器減壓。
減壓:顯而易見,動態腳本的輸出速度比一個靜態的頁面要慢的多(解釋、執行、數據庫數據讀取)。因此,如果我們讓用戶只是讀取一個靜態文件,那硬件成本就會很低。大量用戶訪問靜態文件而很少訪問動態腳本,如何以節約昂貴的硬件資源。
可以看出,cache還是有二利而無一害的。

2、目前流行的cache模型

lamp cache

可以看到,在數據庫、web上,都做了相應的cache。至於生成靜態文件,這個技術應該不是一個問題,所以,我在這裏主要說一下通用的前端cache。

3、前端cache工作原理(apache mod_cache、squid)

Squid是一個linux下非常流行的代理服務器,當然,這裏我們更多的是使用其緩衝(cache)的功能。
Apache mod_cache是從2.0版本開始加入的一個緩存模塊,可以作爲類似squid的緩存服務,我只嘗試過在使用代理模塊(mod_proxy)的時候使用,組合作爲一個簡易版squid,至於其他應用還沒有深挖。
這裏,首先提幾個http協議裏的頭標誌:
Last-Modified 文檔最後修改時間,類似於php中的filemtime
Date 文檔從服務器端發出的服務器端時間
Expires 文檔過期時間
Cache-Control 這個指令我暫時不能用人類的語言表達清楚,大概就是,如果其他域緩存控制有衝突,一切聽這個,優先級別比較高。
Pragma 一些特定指令,一般不用,但用的也很多。因爲用其他幾個參數就能實現目的了。

突然發現語言邏輯出了問題,居然不能表述cache的原理,故底下作圖,簡單說明。

http

這樣就清楚多了,廢話全省了。看不懂者請我吃飯,我給你們當面講解。

4、實例

現在訪問
http://www.sunboyu.cn/wp-content/themes/zen-in-grey-10/images/headerimage.jpg
第一次訪問,我們用firebug網絡標籤查看:200 OK
然後刷新一下再看:304 Not Modified
綜合一下其他域,分析爲啥會這樣
第一次訪問:
請求信息

  1. Host www.sunboyu.cn
  2. User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)
  3. Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  4. Accept-Language zh-cn,zh;q=0.5
  5. Accept-Encoding gzip,deflate
  6. Accept-Charset GB2312,utf-8;q=0.7,*;q=0.7
  7. Keep-Alive 300
  8. Connection keep-alive
  9. Referer http://www.sunboyu.cn/
  10. Cookie __utma=100971139.746333267.1249527244.1253502591.1253523523.65; __utmz=100971139.1249527244.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); wp-settings-1=editor%3Dhtml%26align%3Dcenter%26m0%3Do%26m1%3Dc%26m2%3Dc%26m3%3Dc%26m4%3Do%26m5%3Do%26m6%3Do%26m7%3Dc%26m8%3Do%26hidetb%3D1%26urlbutton%3Durlfile%26imgsize%3Dlarge%26m9%3Do%26m10%3Do; wp-settings-time-1=1252910178; PHPSESSID=a9af3fbfa19442fe549227e1a355ac52; __utmc=100971139

第一次訪問,發送基本的請求信息。

反饋信息:

  1. Date Mon, 21 Sep 2009 09:09:01 GMT
  2. Server Apache/2.2.11 (FreeBSD) mod_ssl/2.2.11 OpenSSL/0.9.8e DAV/2
  3. Last-Modified Wed, 22 Jul 2009 08:13:25 GMT
  4. Etag "797fe8-45a3-46f46f1fb1340"
  5. Accept-Ranges bytes
  6. Content-Length 17827
  7. Keep-Alive timeout=5, max=100
  8. Connection Keep-Alive
  9. Content-Type image/jpeg

服務器反饋了該文件的信息,其中包括date:反饋的時間;last-modified:最後修改時間;etag:頁面的姆印信息。

第二次訪問:
請求信息:

  1. Host www.sunboyu.cn
  2. User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)
  3. Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  4. Accept-Language zh-cn,zh;q=0.5
  5. Accept-Encoding gzip,deflate
  6. Accept-Charset GB2312,utf-8;q=0.7,*;q=0.7
  7. Keep-Alive 300
  8. Connection keep-alive
  9. Referer http://www.sunboyu.cn/
  10. Cookie __utma=100971139.746333267.1249527244.1253502591.1253523523.65; __utmz=100971139.1249527244.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); wp-settings-1=editor%3Dhtml%26align%3Dcenter%26m0%3Do%26m1%3Dc%26m2%3Dc%26m3%3Dc%26m4%3Do%26m5%3Do%26m6%3Do%26m7%3Dc%26m8%3Do%26hidetb%3D1%26urlbutton%3Durlfile%26imgsize%3Dlarge%26m9%3Do%26m10%3Do; wp-settings-time-1=1252910178; PHPSESSID=a9af3fbfa19442fe549227e1a355ac52; __utmc=100971139
  11. If-Modified-Since Wed, 22 Jul 2009 08:13:25 GMT
  12. If-None-Match "797fe8-45a3-46f46f1fb1340"
  13. Cache-Control max-age=0

由於信息瀏覽被緩存在瀏覽器cache中,所以向服務器請求的時候順便帶上了if-modified-sine信息,if-none-match的信息。

反饋信息:

  1. Date Mon, 21 Sep 2009 09:09:33 GMT
  2. Server Apache/2.2.11 (FreeBSD) mod_ssl/2.2.11 OpenSSL/0.9.8e DAV/2
  3. Last-Modified Wed, 22 Jul 2009 08:13:25 GMT
  4. Etag "797fe8-45a3-46f46f1fb1340"
  5. Accept-Ranges bytes
  6. Content-Length 17827
  7. Content-Type image/jpeg

此次訪問,因爲etag信息的比較,服務器確定瀏覽器本地緩存了此文件。並且在瀏覽器發送的信息 if-modified-since時間後,服務器的文件並沒有發生變化,因此,服務器發送304指令使瀏覽器讀取本地緩存,以減少http信息傳輸量,提高響應速度。

5、進階(對緩存服務器的控制)

http關於緩存的控制不僅可以控制瀏覽器,針對代理服務器依然有效。比如我們使用squid進行反向代理服務,我們可以通過調整http頭信息而確定用戶是否要訪問squid的緩存信息,或者不緩存而直接轉發服務器的反饋。
假如現在,一臺apache的server跑php,主要是cms,由於工期的問題,沒工夫做生成靜態了,但我們還希望負載不要太大,因此我們在前邊加了一臺squid服務器,希望能緩存html結果,這樣實現項目快速推進。
當然,服務器部署好之後,還需要程序這邊做一些配置,就是發送合適的http的頭信息,程序做好修改即可。
首先,我們做一個規劃:
網站首頁希望緩存5分鐘,因爲不斷有新網站發佈。
列表頁面緩存1小時,因爲不斷有新的文章發佈,但比首頁頻度還是下降了一些。
文章最終顯示頁面緩存一天,因爲發佈要經過多人審覈,一般不會修改,但也難免修改一兩個字眼。
爲了便於演示,我本地架設了apache和squid。當然鄙視一下自己,是用的windows版本,否則就不能玩劍三了。
言歸正傳,結構如下:

demo

當訪問http://127.0.0.1:8888的時候,squid轉發請求至http://127.0.0.1:80獲取內容,然後反饋給用戶,抑或把緩存的內容直接發送給用戶。
現在做首頁的發佈。
首頁緩存5分鐘,也就是300秒。
從squid訪問apache時間算起,過期時間則爲 time()+300
用php發送http頭 header(”Expires: ” .gmdate(”D, d M Y H:i:s”,time()+300). ” GMT”);
驗證:在仿真環境中模擬,我們用httpwatch來查看squid反饋,根據apache和squid的日誌來驗證緩存是否生效。
寫一個文件

  1. < ?php
  2. header("Expires: " .gmdate("D, d M Y H:i:s",time()+300). " GMT");
  3. echo "Content test!!";
  4.  ?>

部署完畢後訪問 http://127.0.0.1 看是否順利輸出。然後訪問 http://127.0.0.1:8888 看是否可以看到預期內容。
如果能順利輸出,則證明squid apache工作皆正常。
現在用httpwatch查看頭信息
第一次訪問:

  1. HTTP/1.0 200 OK
  2. Date: Wed, 23 Sep 2009 07:55:42 GMT
  3. Server: Apache/2.2.13 (Win32) PHP/5.2.6
  4. Accept-Ranges: bytes
  5. X-Powered-By: PHP/5.2.6
  6. Expires: Wed, 23 Sep 2009 08:00:42 GMT
  7. Content-Type: text/html
  8. X-Cache: MISS from squid.sunboyu.cn
  9. X-Cache-Lookup: MISS from squid.sunboyu.cn:8888
  10. Via: 1.0 squid.sunboyu.cn (squid/3.0.STABLE13-BZR)
  11. Connection: close

第二次訪問:

  1. HTTP/1.0 200 OK
  2. Date: Wed, 23 Sep 2009 07:55:42 GMT
  3. Server: Apache/2.2.13 (Win32) PHP/5.2.6
  4. Accept-Ranges: bytes
  5. X-Powered-By: PHP/5.2.6
  6. Expires: Wed, 23 Sep 2009 08:00:42 GMT
  7. Content-Type: text/html
  8. Age: 28
  9. X-Cache: HIT from squid.sunboyu.cn
  10. X-Cache-Lookup: HIT from squid.sunboyu.cn:8888
  11. Via: 1.0 squid.sunboyu.cn (squid/3.0.STABLE13-BZR)
  12. Connection: close

比較兩次訪問,兩次訪問時間 07:55:42 相同,過期時間相同 08:00:42,跟之前設置的5分鐘相符。第一次X-Cache顯示爲MISS,因爲一次訪問,squid緩存中並沒有相關信息,所以從apache中獲得,第二次緩存中存在緩存信息,直接讀取緩存輸出,爲之“命中”。

根據這個實驗,很容易得知第二和第三條要求的做法:

  1. < ?php
  2. header("Expires: " .gmdate("D, d M Y H:i:s",time()+3600). " GMT");
  3. ……
  4. ?>
  5. < ?php
  6. header("Expires: " .gmdate("D, d M Y H:i:s",time()+86400). " GMT");
  7. ……
  8. ?>

只需要給頁面加上一個過期的http頭,即可實現我們的緩存目的。這樣做,比生成頁面方便多了。當然,如果求seo效果,可以用重寫來實現,緩存效果不變。
如何來顯示一下緩存後的結果呢?我們可以寫一個一般複雜的php腳本,其中運算數據庫都使用,直接在apache運行,壓力測試一下;然後使用squid訪問,訪問一次後,關閉apache,然後再壓力測試,比較成績。^_^,因爲有了squid,apache跟mysql都不工作了,當然要非常節約資源。
Squid的合理使用,達到了快速網站開發,減輕服務器壓力,提高用戶影響的目的。
底下分析下squid在分佈式加速方面的應用。
分析新浪的一些圖片,發現http頭中有這樣的信息:

  1. http://i1.sinaimg.cn/blog/temp/1/2008/0306/U2725P503T1D169F1DT20090701101305.jpg
  2. HTTP/1.0 304 Not Modified
  3. Date: Fri, 28 Aug 2009 22:09:32 GMT
  4. Content-Type: image/jpeg
  5. Expires: Sat, 28 Aug 2010 22:09:32 GMT
  6. Last-Modified: Wed, 01 Jul 2009 02:13:05 GMT
  7. Age: 81943
  8. X-Cache: HIT from zjm-209.sina.com.cn
  9. Connection: keep-alive

可以發現是命中了zjm-209.sina.com.cn。
Sina在全國各地有好多服務器,其DNS根據用戶訪問的ip確定用戶需要訪問哪臺服務器最快,則把請求轉發到相應服務器,來提高用戶訪問速度。
大概結構如下:

總結
Cache的確是個好東西,使用好了,做事事半功倍。不過很多時候我們得cache和”非cache”共用。有些內容需要cache,有些頁面卻要避免cache。當然squid也給出了接口方面控制每個緩存元素。過度的cache也是不好的,在cache之前,對項目進行嚴謹的分析和大量的測試是我們創建緩存機制的一個前提。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章