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