曾經(jīng)寫過是否要放棄使用varnish/squid, 經(jīng)過幾天的實驗,終于找到一種比較理想的解決方案:
直接使用proxy模塊的proxy_store來實現(xiàn)分布mirror.
首先說說我的需求:
1. 我需要將一些靜態(tài)文件從應(yīng)用服務(wù)器剝離, 負(fù)載到其他的節(jié)點.
2. 這些文件主要是靜態(tài)Html和圖片,包括縮略圖. 這些文件一旦創(chuàng)建,更新的頻率很少.
3. 在某些時候需要手動立即從各個分布節(jié)點刪除或更新某些文件
4. 盡可能減少應(yīng)用服務(wù)器的請求, 進而減少內(nèi)網(wǎng)的流量
之前,我分別使用了squid和varnish.
最初用的squid,還湊合.不過,squid在高負(fù)載下會出現(xiàn)停滯甚至crash或者是空白頁.
于是換成varnish.
varnish也是老毛病,偶爾也會crash.
二者的共同點,就是當(dāng)cache快滿的時候,效率會急劇下降, 同時,對主服務(wù)器的請求甚至都
阻塞了整個內(nèi)網(wǎng).
要解決這個情況,varnish需要手動重啟, squid則需要清除整個緩存目錄.
對于varnish, 由于是純內(nèi)存的加速,因此,無法將cache設(shè)置太大,否則用上swap, 基本上是幾倍的速度下降,
而且很容易就段違例了. 于是,當(dāng)bots訪問網(wǎng)站的時段, 就是噩夢產(chǎn)生的時候, 由于爬蟲遍歷太多的文件,
造成緩存很快溢出,于是頻繁的invalid,此時,內(nèi)網(wǎng)的帶寬占用能達到100m以上….
可能有人說,為什么不用NFS. NFS的問題主要是鎖的問題. 很容易造成死鎖, 只有硬件重啟才能解決.
為了脫離這個噩夢,我決定試驗nginx的proxy_store. 如果使用Lighty,倒是非常簡單,因為有mod_cache,配合lua,
會很靈活. 不過nginx的proxy_store并非是一個cache,因為它不具備expires, 新的cache模塊仍在開發(fā)中.
不過經(jīng)過仔細(xì)考量, 我驚喜的發(fā)現(xiàn),其實這正是我想要的, 因為在我的需求中,絕大多數(shù)的文件都是不過期的,因而也無必要
去和后端服務(wù)器驗證是否過期.
配置其實并不太復(fù)雜,但是過程有些曲折, 基本的思路是:
nginx首先檢查本地是否有請求的文件,如果有直接送出,沒有則從后端請求,并將結(jié)果存儲在本地.
第一個方案,是基于error_page來實現(xiàn)的:
upstream backend{
server 192.168.8.10:80;
}
server {
listen 80;
access_log /logs/cache.log main;
server_name blog.night9.cn www.night9.cn night9.cn;
proxy_temp_path /cache/temp;
root /cache/$host;
location / {
index index.shtml;
error_page 404 = /fetch$uri;
}
ssi on;
location /fetch {
internal;
proxy_pass http://backend;
proxy_store on;
proxy_store_access user:rw group:rw all:rw;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Via "s9/nginx";
alias /cache/$host;
}
#對于請求目錄的情況下要特殊對待
location ~ /$ {
index index.shtml;
error_page 403 404 = @fetch;
}
location @fetch {
internal;
proxy_pass http://backend;
proxy_store /cache/$host${uri}index.shtml;
proxy_store_access user:rw group:rw all:rw;
proxy_set_header Host $host;
proxy_set_header Via "s9/nginx";
proxy_set_header X-Real-IP $remote_addr;
}
}
這個方案對于普通的情況下,基本滿足.
緩存是做到了,但是如何實現(xiàn)更新呢?
其實很簡單,只要將指定url的從本地cache目錄刪除即可.
因為proxy_store會按照實際請求的url地址建立相應(yīng)的目錄結(jié)構(gòu).
于是,我寫了一個fastcgi, 只要將需要清楚的url傳遞給它,從cache目錄中刪除.
其實可以用perl_module實現(xiàn),但是考慮到獨立fastcgi服務(wù)更為穩(wěn)定,還是和以前的統(tǒng)計一樣,
用perl的CGI::Fast模塊實現(xiàn), 替換了10幾行代碼就搞定了.
事情本來就該告一段,不過,由于主服務(wù)器上使用了SSI, 新的問題就來了:
我們希望SSI的解析是在子節(jié)點上進行,而不是在主服務(wù)器上進行, 這樣我們可以獨立更新相應(yīng)
區(qū)塊的文件即可, 否則就需要清除所有的shtml文件,這是比較可怕的.
但是,Nginx對于SSI的subrequest無法使用error_page來重定向.(不確定是否是bug,不過如果允許
的確容易造成死循環(huán)).
于是,一個更為簡單的方案就誕生了:
set $index 'index.shtml';
set $store_file $request_filename;
if ($uri ~ /$ ){
set $store_file $request_filename$index;
rewrite (.*) $1index.shtml last;
}
location / {
index index.shtml;
proxy_store on;
proxy_temp_path /cache/temp;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Via "s9/nginx";
proxy_store_access user:rw group:rw all:rw;
if ( !-e $store_file ) {
proxy_pass http://backend;
}
}
Wow! 更為簡單.
應(yīng)該感謝Nginx的Rewrite模塊, 這點也是我用Nginx替換Lighttpd的一個主要原因.
好了,我可以忘掉varnish,squid了.
如果有興趣的人想使用, 請一定注意:
這個方案對靜態(tài)文件更為有效,如果要加速動態(tài)請求,還是要用varnish
說道加速, 利用Memcached和nginx配合可以迅速提升訪問動態(tài)頁面的速度,
有時間再說