頁面片段緩存(二)
在上一篇文章中,我介紹了我們用土法煉鋼的方法,使用Velocity提供的自定義標簽實現片段緩存。這樣的方式雖然也解決了我們的問題,但還是引出了一些bug。而且還有點hack的味道(雖然我喜歡hack)。實際上對于片段緩存,業界有成熟的解決方案,還有一個所謂的W3C標準:ESI(Edge Side Include) 。
ESI本身沒有什么,只是一個XML的標簽集合。ESI和SSI(Server Side Include)很相似,做過ASP開發的都熟悉這么一個標簽:
IIS碰到這么一個標簽后,會把header.inc里面的東西合并到當前的頁面,這樣做的好處是header.inc本身可以復用了,你可以在多個頁面include它。
ESI的功能也是類似的,只不過解析和合并它的任務一般落到緩存服務器或代理上:
<ul>
#foreach($book in $books)
<li>$book.name</li>
#end
</ul>
比如對于上面這個片段,esi:include標簽那里本來是應該顯示特定于每個用戶的歡迎信息的,就因為這一點整個頁面不能緩存太可惜了。這個時候ESI粉墨登場。緩存服務器會將整個頁面cache,然后它發現這兒有個ESI標簽,它會根據src指定的地址去源服務器請求內容,然后將其合并到緩存的頁面中,然后將完整的內容發送到客戶端。整個過程類似下圖所示:
這樣我們就不需要每次都去數據庫查詢那個圖書列表了,因為圖書列表可能更新的很緩慢。這樣大大的降低了Web服務器的壓力。
既然ESI是一個規范,那么肯定有遵循這個規范的實現。比如大名鼎鼎的Squid就有支持ESI的模塊。不過本文要介紹的是Varnish。Varnish是一個反向代理,使用緩存對HTTP加速,可以把它放到你的Web服務器的前面,對內容緩存,跟Squid的目的相似。不過因為Squid想干的事兒實在是太多,它已經不再是一個純粹的用戶緩存加速的方向代理了,所以顯得太過于臃腫,而比較起來Varnish更專注,更輕便。根據網站上的數據顯示,Varnish表現出來的性能比Squid更好,而且占用的資源更少。
下面我就簡單的描述一下如何安裝Varnish以及配置使用ESI。
安裝
我是在Mac上安裝Varnish的,從源代碼編譯過去的。基本上是一路命令下去,在configure的時候碰到缺少libpcre,用port裝上pcre就可以了:
$tar zxvf varnish-2.1.5.tar.gz
$cd varnish-2.1.5
$sh autogen.sh
$sh configure
$make
$make install
根據環境不同,可能缺少一些依賴,我在安裝的過程中就缺少libpcre。
Varnish默認是安裝在/usr/local/sbin目錄下:varnishd。是daemo。
配置
根據上面那幅圖可以看出,Varnish并不自己提供服務,它只是作為一個緩存服務器。那么就必須有一個后端的服務器提供應用程序的服務。那我們就需要讓Varnish知道到哪兒去請求后端服務器。這個是通過配置文件設置的,Varnish的默認配置文件在/usr/local/etc/varish/default.vcl。
打開后我們會發現一段被注釋的代碼,取消注釋將其指向你的后端服務器(比如IIS):
.host = "127.0.0.1";
.port = "8080";
}
配置好后,啟動Varnish,然后去看看效果,啟動的命令是:
我簡單的介紹下這幾個選項,更詳細的可以看man。
-a指定Varnish鑒定的地址。如果你的配置是上圖所示,那么端口應該是80。如果你沒有指定-a,那么默認配置使用/etc/services中的配置,一般情況下就是80。而-f指定配置文件的地址,默認情況下就是default.vcl,除非你想使用別的配置文件。
最有趣的是-s選項,它用來指定存儲方式,以及緩存的大小。比如這里是malloc,那就是使用內存作為緩存,你還可以使用file,使用磁盤作為緩存。后面是緩存大小,500兆。還可以指定K,G,T等,大小寫都可以。
運行后我們去瀏覽器打開http://127.0.0.1看看。為了測試你可以在頁面輸出一個DateTime.Now,然后刷新頁面看看是不是每次都變化。
Varnish還有個不錯的工具:varnishlog,打開后可以看到用戶訪問的一些情況。
好了,安裝和簡單配置就介紹這么多,那如何使用ESI呢?其實很簡單,分為兩個步驟,首先將你的頁面上需要施加不同緩存策略的片段切分,放到不同的連接中。
比如原來你的首頁混沌一片:有熱點新聞板塊、通知板塊、還有廣告以及顯示用戶登錄的板塊。而它們原來很可能都是由index.aspx處理的。如果你想有針對性的對這些板塊施加緩存策略,那么就應該將其獨立開,使用不同的連接(控制器)來處理:
<head>
<title>index</title>
</head>
<body>
<p>Welcome yuyijq</p>
<div>
<h3>Hot News</h3>
<ul>
<li>慶祝奧特曼45歲生日</li>
<li>拉登掛了</li>
</ul>
</div>
<div>
<h3>通知</3>
<ul>
<li>機房維護通知</li>
</ul>
</div>
</body>
<html>
現在我們要拆分:
hotnews->hotnews.aspx
welcome->welcome.aspx
然后使用ESI標簽分離:
<head>
<title>index</title>
</head>
<body>
<esi:include src="/welcome.aspx" />
<esi:include src="hotnews.aspx" />
<div>
<h3>通知</3>
<ul>
<li>機房維護通知</li>
</ul>
</div>
</body>
<html>
注意的是welcome.aspx和hotnews.aspx現在輸出的是兩個片段,不是完整的頁面。
這些都準備好后,就是啟用Varnish對ESI的處理了。
配置
還是編輯default.vcl文件。Varnish的配置能力非常強大,這個vcl是Varnish Configuration Language。剛才我們僅僅是配置一個后端服務器,現在讓我們打開default.vcl仔細欣賞一下:
}
sub vcl_pipe {
}
sub vcl_pass {
}
sub vcl_hash {
}
sub vcl_hit {
}
sub vcl_miss {
}
sub vcl_fetch {
}
sub vcl_deliver {
}
sub vcl_error {
}
你會看到有vcl_recv,vcl_pipe,vcl_pass,vcl_hash,vcl_hit等函數。這些函數就代表一個請求進入Varnish所通過的階段(參見這里)。就像一個管道一樣,你可以在這些管道上做些自定義的處理。所以配置性非常強大。現在我們只對vcl_fetch函數做一下簡單修改:
if(req.url == "/welcome.aspx"){
return (pass);
}
if (req.url == "/index.aspx") {
esi;
}
}
如果請求的url是/welcome.aspx就直接pass,也就是不做任何處理,請求直接遞交給后端應用服務器(IIS)。如果請求是/index.aspx,也就是我們上面列出的那個包含esi標簽的頁面,就對其進行ESI處理,這樣Varnish就會解析這個頁面,看看緩存中有沒有,如果有的話則從緩存中取出頁面,并將其與從后端服務器獲取的welcome.aspx片段合并,然后直接發送回請求。
用Varnish處理ESI就這么簡單,不過使用Varnish之前我們還得對其某些參數進行配置,比如緩存多長時間啊等等。默認情況下Varnish會緩存120秒,你可以通過管理功能對其進行配置,還可以通過在vcl里對某些模塊進行精細的控制。
管理
還記得我們是怎樣啟動Varnish的么,如果在啟動參數里加上-T 127.0.0.1:5000,那么我們就可以使用telnet對Varnish進行管理了。我們只需要telnet上這個端口,然后可以使用很多豐富的命令了,更多細節留給你嘗試吧。
兩種方式的比較
前一篇文章介紹的使用Velocity自定義標簽來實現片段緩存和這一篇介紹的使用ESI都有自己的優缺點。
使用Velocity自定義標簽的方案工作在應用程序這一層,這樣開發人員有最大的控制權力,而且實現起來也比較簡單,所使用的也都是大家都熟悉的技術,但問題是它還是由應用程序服務器來處理得,可以說它減輕了一部分應用程序服務器和數據庫服務器的壓力,但還有一部分壓力還是需要它來承擔,而且在應用程序中解決所使用的緩存必定是和應用程序所采用的緩存機制一樣(當然你也可以為此獨立使用一個緩存),對緩存服務器也有部分壓力。
而使用ESI的方案,它需要運維團隊的配置,甚至需要修改服務器配置的架構(添加了前端服務器),如果在多部門協調比較困難的項目中,這種方案還會遇到一些阻力。
但是它帶來的好處確實顯而易見的。首先ESI是一個W3C標準,我更傾向于采用標準的做法。而且Varnish這樣的方向代理,它本來就擅長這個,它可以完全把這部分壓力從應用程序服務器和緩存服務器上接管過來,而且會處理的更出色。
由此可見什么方案都是需要權衡各方利弊,得到一個平衡的效果啊。