大綱
二、Varnish 特點
三、Varnish 與 Squid 對比
四、Varnish 設計結構
五、Varnish 工作流程
六、Varnish 狀態引擎(state engine)
七、安裝與配置 Varnish
一、Varnish 簡介
Varnish是一款高性能的開源HTTP加速器,挪威最大的在線報紙 Verdens Gang 使用3臺Varnish代替了原來的12臺Squid,性能比以前更好。
Varnish 的作者Poul-Henning Kamp是FreeBSD的內核開發者之一,他認爲現在的計算機比起1975年已經複雜許多。在1975年時,儲存媒介只有兩種:內存與硬盤。但現在計算機系統的內存除了主存外,還包括了CPU內的L1、L2,甚至有L3快取。硬盤上也有自己的快取裝置,因此Squid Cache自行處理物件替換的架構不可能得知這些情況而做到最佳化,但操作系統可以得知這些情況,所以這部份的工作應該交給操作系統處理,這就是 Varnish cache設計架構。
Varnish項目是2006年發佈的第一個版本0.9.距今已經八年多了,此文檔之前也提過varnish還不穩定,那是2007年時候編寫的,經過varnish開發團隊和網友們的辛苦耕耘,現在的varnish已經很健壯。很多門戶網站已經部署了varnish,並且反應都很好,甚至反應比squid還穩定,且效率更高,資源佔用更少。相信在反向代理,web加速方面,varnish已經有足夠能力代替squid。
二、Varnish 特點
Varnish是一個輕量級的Cache和反向代理軟件。先進的設計理念和成熟的設計框架是Varnish的主要特點。現在的Varnish總共代碼量不大,雖然功能在不斷改進,但是還需要繼續豐富和加強。下面總結了Varnish的一些特點。
基於內存進行緩存,重啓後數據將消失。
利用虛擬內存方式,I/O性能好。
支持設置0~60秒內的精確緩存時間。
VCL配置管理比較靈活。
32位機器上緩存文件大小爲最大2GB。
具有強大的管理功能,例如top,stat,admin,list等。
狀態機設計巧妙,結構清晰。
利用二叉堆管理緩存文件,達到積極刪除目的。
三、Varnish 與 Squid 對比
說到Varnish,不能不提Squid,Squid是一個高性能的代理緩存服務器,它和Varnish之間有諸多的異同點,下面進行分析。
下面是Varnish與Squid之間的相同點。
都是一個反向代理服務器。
都是開源軟件。
下面是它們的不同點,也是Varnish的優點。
Varnish的穩定性很高。兩者在完成相同負荷的工作時,Squid服務器發生故障的機率要高於Varnish,因爲Squid需要經常重啓
Varnish訪問速度更快。Varnish採用了“Visual Page Cache”技術,所有緩存數據都直接從內存讀取,而Squid是從硬盤讀取緩存數據,因此Varnish在訪問速度方面會更快。
Varnish可以支持更多的併發連接。因爲Varnish的TCP連接釋放要比Squid快,所以在高併發連接情況下可以支持更多TCP連接。
Varnish可以通過管理端口,使用正則表達式批量清除部分緩存,而Squid做不到。 當然,與傳統的Squid相比,
Varnish也有缺點。
Varnish在高併發狀態下CPU、I/O和內存等資源開銷都高於Squid。
Varnish進程一旦掛起、崩潰或者重啓,緩存數據都會從內存中完全釋放,此時所有請求都會被髮送到後端服務器,在高併發情況下,這會給後端服務器造成很大壓力。
四、Varnish 設計結構
Varnish主要運行兩個進程:Management進程和Child進程(也叫Cache進程)。如上圖,
1.Management進程
Management進程主要實現應用新的配置、編譯VCL、監控varnish、初始化varnish以及提供一個命令行接口等。Management進程會每隔幾秒鐘探測一下Child進程以判斷其是否正常運行,如果在指定的時長內未得到Child進程的迴應,Management將會重啓此Child進程。
Management 管理接口:
CLI interface 命令行接口
Telnet interface telnet接口
Web interface Web管理接口
2.Child/Cache 進程
Child/Cache 進程包含多種類型的線程,常見的如:
Accept 線程:接收新的連接請求並響應;
Worker 線程:child進程會爲每個會話啓動一個worker線程,因此,在高併發的場景中可能會出現數百個worker線程甚至更多;
Object Expiry 線程:從緩存中清理過期內容;
Commad line 線程 : 管理接口
Storage/hashing 線程 :緩存存儲
Log/stats 線程:日誌管理線程
Backend Communication 線程:管理後端主機線程
Varnish依賴“工作區(workspace)”以降低線程在申請或修改內存時出現競爭的可能性。在varnish內部有多種不同的工作區,其中最關鍵的當屬用於管理會話數據的session工作區。
3.Varnish日誌
爲了與系統的其它部分進行交互,Child進程使用了可以通過文件系統接口進行訪問的共享內存日誌(shared memory log),因此,如果某線程需要記錄信息,其僅需要持有一個鎖,而後向共享內存中的某內存區域寫入數據,再釋放持有的鎖即可。而爲了減少競爭,每個worker線程都使用了日誌數據緩存。共享內存日誌大小一般爲90M,其分爲兩部分,前一部分爲計數器,後半部分爲客戶端請求的數據。varnish提供了多個不同的工具如varnishlog、varnishncsa或varnishstat等來分析共享內存日誌中的信息並能夠以指定的方式進行顯示。
4.VCL(Varnish Configuation Language)簡介
Varnish Configuration Language (VCL)是varnish配置緩存策略的工具,它是一種基於“域”(domain specific)的簡單編程語言,它支持有限的算術運算和邏輯運算操作、允許使用正則表達式進行字符串匹配、允許用戶使用set自定義變量、支持if判斷語句,也有內置的函數和變量等。使用VCL編寫的緩存策略通常保存至.vcl文件中,其需要編譯成二進制的格式後才能由varnish調用。事實上,整個緩存策略就是由幾個特定的子例程如vcl_recv、vcl_fetch等組成,它們分別在不同的位置(或時間)執行,如果沒有事先爲某個位置自定義子例程,varnish將會執行默認的定義。
VCL策略在啓用前,會由management進程將其轉換爲C代碼,而後再由gcc編譯器將C代碼編譯成二進制程序。編譯完成後,management負責將其連接至varnish實例,即child進程。正是由於編譯工作在child進程之外完成,它避免了裝載錯誤格式VCL的風險。因此,varnish修改配置的開銷非常小,其可以同時保有幾份尚在引用的舊版本配置,也能夠讓新的配置即刻生效。編譯後的舊版本配置通常在varnish重啓時纔會被丟棄,如果需要手動清理,則可以使用varnishadm的vcl.discard命令完成。
5.Varnish 的後端存儲
Varnish支持多種不同類型的後端存儲,這可以在varnishd啓動時使用-s選項指定。後端存儲的類型包括:
file:使用特定的文件存儲全部的緩存數據,並通過操作系統的mmap()系統調用將整個緩存文件映射至內存區域(如果條件允許);
malloc:使用malloc()庫調用在varnish啓動時向操作系統申請指定大小的內存空間以存儲緩存對象;
persistent(experimental):與file的功能相同,但可以持久存儲數據(即重啓varnish數據時不會被清除);仍處於測試期;
Varnish無法追蹤某緩存對象是否存入了緩存文件,從而也就無從得知磁盤上的緩存文件是否可用,因此,file存儲方法在varnish停止或重啓時會清除數據。而persistent方法的出現對此有了一個彌補,但persistent仍處於測試階段,例如目前尚無法有效處理要緩存對象總體大小超出緩存空間的情況,所以,其僅適用於有着巨大緩存空間的場景。
選擇使用合適的存儲方式有助於提升系統性,從經驗的角度來看,建議在內存空間足以存儲所有的緩存對象時使用malloc的方法,反之,file存儲將有着更好的性能的表現。然而,需要注意的是,Varnishd實際上使用的空間比使用-s選項指定的緩存空間更大,一般說來,其需要爲每個緩存對象多使用差不多1K左右的存儲空間,這意味着,對於100萬個緩存對象的場景來說,其使用的緩存空間將超出指定大小1G左右。另外,爲了保存數據結構等,varnish自身也會佔去不小的內存空間。
爲varnishd指定使用的緩存類型時,-s選項可接受的參數格式如下:
malloc[,size] 或
file[,path[,size[,granularity]]] 或
persistent,path,size {experimental}
注,file中的granularity用於設定緩存空間分配單位,默認單位是字節,所有其它的大小都會被圓整。
五、Varnish 工作流程
Varnish處理HTTP請求的過程大致分爲如下幾個步驟:
Receive狀態,也就是請求處理的入口狀態,根據VCL規則判斷該請求應該Pass或Pipe,還是進入Lookup(本地查詢)。
Lookup狀態,進入此狀態後,會在hash表中查找數據,若找到,則進入Hit狀態,否則進入miss狀態。
Pass狀態,在此狀態下,會進入後端請求,即進入Fetch狀態。
Fetch狀態,在Fetch狀態下,對請求進行後端獲取,發送請求,獲得數據,並進行本地存儲。
Deliver狀態, 將獲取到的數據發送給客戶端,然後完成本次請求。
六、Varnish 狀態引擎(state engine)
VCL用於讓管理員定義緩存策略,而定義好的策略將由varnish的management進程分析、轉換成C代碼、編譯成二進制程序並連接至child進程。varnish內部有幾個所謂的狀態(state),在這些狀態上可以附加通過VCL定義的策略以完成相應的緩存處理機制,因此VCL也經常被稱作“域專用”語言或狀態引擎,“域專用”指的是有些數據僅出現於特定的狀態中。
1.VCL狀態引擎
在VCL狀態引擎中,狀態之間具有相關性,但彼此間互相隔離,每個引擎使用return(x)來退出當前狀態並指示varnish進入下一個狀態。
Varnish開始處理一個請求時,首先需要分析HTTP請求本身,比如從首部獲取請求方法、驗正其是否爲一個合法的HTT請求等。當這些基本分析結束後就需要做出第一個決策,即varnish是否從緩存中查找請求的資源。這個決定的實現則需要由VCL來完成,簡單來說,要由vcl_recv方法來完成。如果管理員沒有自定義vcl_recv函數,varnish將會執行默認的vcl_recv函數。然而,即便管理員自定義了vcl_recv,但如果沒有爲自定義的vcl_recv函數指定其終止操作(terminating),其仍將執行默認的vcl_recv函數。事實上,varnish官方強烈建議讓varnish執行默認的vcl_recv以便處理自定義vcl_recv函數中的可能出現的漏洞。
2.VCL語法
VCL的設計參考了C和Perl語言,因此,對有着C或Perl編程經驗者來說,其非常易於理解。其基本語法說明如下:
(1)//、#或/* comment */用於註釋
(2)sub $name 定義函數
(3)不支持循環,有內置變量
(4)使用終止語句,沒有返回值
(5)域專用
(6)操作符:=(賦值)、==(等值比較)、~(模式匹配)、!(取反)、&&(邏輯與)、||(邏輯或)
VCL的函數不接受參數並且沒有返回值,因此,其並非真正意義上的函數,這也限定了VCL內部的數據傳遞只能隱藏在HTTP首部內部進行。VCL的return語句用於將控制權從VCL狀態引擎返回給Varnish,而非默認函數,這就是爲什麼VCL只有終止語句而沒有返回值的原因。同時,對於每個“域”來說,可以定義一個或多個終止語句,以告訴Varnish下一步採取何種操作,如查詢緩存或不查詢緩存等。
3.VCL內置函數
(1).vcl_recv函數 用於接收和處理請求。當請求到達並被成功接收後被調用,通過判斷請求的數據來決定如何處理請求。 此函數一般以如下幾個關鍵字結束。
pass:表示進入pass模式,把請求控制權交給vcl_pass函數。
pipe:表示進入pipe模式,把請求控制權交給vcl_pipe函數。
error code [reason]:表示返回“code”給客戶端,並放棄處理該請求。“code”是錯誤標識,例如200和405等,“reason”是錯誤提示信息。
(2).vcl_pipe函數 此函數在進入pipe模式時被調用,用於將請求直接傳遞至後端主機,在請求和返回的內容沒有改變的情況下,將不變的內容返回給客戶端,直到這個鏈接被關閉。 此函數一般以如下幾個關鍵字結束。
error code [reason]
pipe
(3).vcl_pass函數 此函數在進入pass模式時被調用,用於將請求直接傳遞至後端主機,後端主機在應答數據後將應答數據發送給客戶端,但不進行任何緩存,在當前連接下每次都返回最新的內容。 此函數一般以如下幾個關鍵字結束。
error code [reason] 。
pass。
(4).lookup 表示在緩存中查找被請求的對象,並且根據查找的結果把控制權交給函數vcl_hit或函數vcl_miss。
(5).vcl_hit函數 在執行lookup指令後,在緩存中找到請求的內容後將自動調用該函數。 此函數一般以如下幾個關鍵字結束。
deliver:表示將找到的內容發送給客戶端,並把控制權交給函數vcl_deliver。
error code [reason] 。
pass。
(6).vcl_miss函數 在執行lookup指令後,在緩存中沒有找到請求的內容時自動調用該方法,此函數可用於判斷是否需要從後端服務器獲取內容。 此函數一般以如下幾個關鍵字結束。
fetch:表示從後端獲取請求的內容,並把控制權交給vcl_fetch函數。
error code [reason] 。
pass。
(7).vcl_fetch 函數
在後端主機更新緩存並且獲取內容後調用該方法,接着,通過判斷獲取的內容來決定將內容放入緩存,還是直接返回給客戶端。此函數一般以如下幾個關鍵字結束。\
rror code [reason]
pass
deliver
(8).vcl_deliver 函數
將在緩存中找到請求的內容發送給客戶端前調用此方法。此函數一般以如下幾個關鍵字結束。
error code [reason]
deliver
(9).vcl_timeout 函數
在緩存內容到期前調用此函數。此函數一般以如下幾個關鍵字結束。
discard:表示從緩存中清除該內容。
fetch。
(10).vcl_discard 函數
在緩存內容到期後或緩存空間不夠時,自動調用該函數。該函數一般以如下幾個關鍵字結束。
keep:表示將內容繼續保留在緩存中。
discard。
4.內置公用變量
VCL內置的公用變量可以用在不同的VCL函數中。下面根據這些公用變量使用的不同階段依次進行介紹。
(1).當請求到達後,可以使用的公用變量
req.backend 指定對應的後端主機
server.ip 表示服務器端IP
client.ip 表示客戶端IP
req.request 指定請求的類型,例如GET、HEAD和POST等
req.url 指定請求的地址
req.proto 表示客戶端發起請求的HTTP協議版本
req.http.header 表示對應請求中的HTTP頭部信息
req. restarts ;/.l表示請求重啓的次數,默認最大值爲4
(2).Varnish在向後端主機請求時,可以使用的公用變量
beresp.request 指定請求的類型,例如GET合HEAD等
beresp.url 指定請求的地址
beresp .proto 表示客戶端發起請求的HTTP協議版本
beresp .http.header 表示對應請求中的HTTP頭部信息
beresp .ttl 表示緩存的生存週期,也就是cache保留多長時間,單位是秒
(3).從cache或後端主機獲取內容後,可以使用的公用變量
obj.status 表示返回內容的請求狀態代碼,例如200、302和504等
obj.cacheable 表示返回的內容是否可以緩存,也就是說,如果HTTP返回的是200、203、300、301、302、404或410等,並且有非0的生存期,則可以緩存
obj.valid 表示是否是有效的HTTP應答
obj.response 表示返回內容的請求狀態信息
obj.proto 表示返回內容的HTTP協議版本
obj.ttl 表示返回內容的生存週期,也就是緩存時間,單位是秒
obj.lastuse 表示返回上一次請求到現在的間隔時間,單位是秒
(4).對客戶端應答時,可以使用的公用變量
resp.status 表示返回給客戶端的HTTP狀態代碼
resp.proto 表示返回給客戶端的HTTP協議版本
resp.http.header 表示返回給客戶端的HTTP頭部信息
resp.response 表示返回給客戶端的HTTP狀態信息
在上面的講述中,只介紹了常用的VCL內置公用變量,如果需要了解和使用更多的公用變量信息,請登錄varnish官方網站查閱。https://www.varnish-cache.org/docs/3.0/
5.常用內置函數
(1).vcl_recv
vcl_recv是在Varnish完成對請求報文的解碼爲基本數據結構後第一個要執行的子例程,它通常有四個主要用途:
修改客戶端數據以減少緩存對象差異性;比如刪除URL中的www.等字符;
基於客戶端數據選用緩存策略;比如僅緩存特定的URL請求、不緩存POST請求等;
爲某web應用程序執行URL重寫規則;
挑選合適的後端Web服務器;
可以使用下面的終止語句,即通過return()向Varnish返回的指示操作:
pass:繞過緩存,即不從緩存中查詢內容或不將內容存儲至緩存中;
pipe:不對客戶端進行檢查或做出任何操作,而是在客戶端與後端服務器之間建立專用“管道”,並直接將數據在二者之間進行傳送;此時,keep-alive連接中後續傳送的數據也都將通過此管道進行直接傳送,並不會出現在任何日誌中;
lookup:在緩存中查找用戶請求的對象,如果緩存中沒有其請求的對象,後續操作很可能會將其請求的對象進行緩存;
error:由Varnish自己合成一個響應報文,一般是響應一個錯誤類信息、重定向類信息或負載均衡器返回的後端web服務器健康狀態檢查類信息;
vcl_recv也可以通過精巧的策略完成一定意義上的安全功能,以將某些特定的***扼殺於搖籃中。同時,它也可以檢查出一些拼寫類的錯誤並將其進行修正等。
Varnish默認的vcl_recv專門設計用來實現安全的緩存策略,它主要完成兩種功能:
僅處理可以識別的HTTP方法,並且只緩存GET和HEAD方法;
不緩存任何用戶特有的數據;
注,安全起見,一般在自定義的vcl_recv中不要使用return()終止語句,而是再由默認vcl_recv進行處理,並由其做出相應的處理決策。
下面是一個自定義的使用示例:
1 2 3 4 5 6 7 8 9 | sub vcl_recv {
if (req.http.User-Agent ~ "iPad" ||
req.http.User-Agent ~ "iPhone" ||
req.http.User-Agent ~ "Android" ) {
set req.http.X-Device = "mobile" ;
} else {
set req.http.X-Device = "desktop" ;
} } |
此例中的VCL創建一個X-Device請求首部,其值可能爲mobile或desktop,於是web服務器可以基於此完成不同類型的響應,以提高用戶體驗。
(2).vcl_fetch
如前面所述,相對於vcl_recv是根據客戶端的請求作出緩存決策來說,vcl_fetch則是根據服務器端的響應作出緩存決策。在任何VCL狀態引擎中返回的pass操作都將由vcl_fetch進行後續處理。vcl_fetch中有許多可用的內置變量,比如最常用的用於定義某對象緩存時長的beresp.ttl變量。通過return()返回給arnish的操作指示有:
deliver:緩存此對象,並將其發送給客戶端(經由vcl_deliver);
hit_for_pass:不緩存此對象,但可以導致後續對此對象的請求直接送達到vcl_pass進行處理;
restart:重啓整個VCL,並增加重啓計數;超出max_restarts限定的最大重啓次數後將會返回錯誤信息;
error code [reason]:返回指定的錯誤代碼給客戶端並丟棄此請求;
默認的vcl_fetch放棄了緩存任何使用了Set-Cookie首部的響應。
6.其它內置函數
VCL提供了幾個函數來實現字符串的修改,添加bans,重啓VCL狀態引擎以及將控制權轉回Varnish等。
regsub(str,regex,sub) 匹配正則表達式的字符串
regsuball(str,regex,sub):這兩個用於基於正則表達式搜索指定的字符串並將其替換爲指定的字符串;但regsuball()可以將str中能夠被regex匹配到的字符串統統替換爲sub,regsub()只替換一次;
ban(expression):
ban_url(regex):Bans所有其URL能夠由regex匹配的緩存對象;
purge:從緩存中挑選出某對象以及其相關變種一併刪除,這可以通過HTTP協議的PURGE方法完成;
hash_data(str):
return():當某VCL域運行結束時將控制權返回給Varnish,並指示Varnish如何進行後續的動作;其可以返回的指令包括:lookup、pass、pipe、hit_for_pass、fetch、deliver和hash等;但某特定域可能僅能返回某些特定的指令,而非前面列出的全部指令;
return(restart):重新運行整個VCL,即重新從vcl_recv開始進行處理;每一次重啓都會增加req.restarts變量中的值,而max_restarts參數則用於限定最大重啓次數。
七、安裝配置varnish
clients (IP:172.16.41.254) | | | (WAN:172.16.41.1) varnish (LAN:192.168.100.1) | | | +--------------------+ | | | | web1 web2 (192.168.100.51) (192.168.100.52) |
[root@varnish ~]# yum install -y varnish
[root@varnish ~]# yum install -y varnish |
[root@varnish ~]# vim /etc/sysconfig/varnish VARNISH_LISTEN_PORT=80 #--->修改監聽端口爲80 VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 #--->管理監聽IP VARNISH_ADMIN_LISTEN_PORT=6082 #--->管理監聽端口 VARNISH_STORAGE="malloc,500M"#--->修改緩存類型爲malloc,並制定緩存空間大小 VARNISH_VCL_CONF=/etc/varnish/test.vcl #--->vcl配置文件配置文件絕對路徑 |
[root@varnish ~]# vim /etc/varnish/test.vcl backend default { .host = "192.168.100.51"; #--->將該IP改爲後端web服務器的地址 .port = "80"; } |
①交互式模式②命令行模式
[root@varnish ~]# varnishadm 200 ----------------------------- Varnish Cache CLI 1.0 ----------------------------- Linux,2.6.32-431.el6.x86_64,x86_64,-smalloc,-smalloc,-hcritbit varnish-3.0.4 revision 9f83e8f Type 'help' for command list. Type 'quit' to close CLI session. varnish> #--->這裏可以輸入help來獲取幫助信息的. |
varnish> vcl.load test1 /etc/varnish/test.vcl 200 VCL compiled. |
varnish> vcl.use test1 200 varnish> quit |
sub vcl_deliver { #--->在響應報文中添加 if (obj.hits > 0){ #--->使用obj.hits內置變量去判斷(對於某個對象) set resp.http.X-Cache = "HIT"; #--->如果大於0就命中緩存 } else { set resp.http.X-Cache = "MISS"; #--->否則就未命中 } } |
sub vcl_deliver { #--->在響應報文中添加 if (obj.hits > 0){ #--->使用obj.hits內置變量去判斷(對於某個對象) set resp.http.X-Cache = "HITfrom "+ srever.ip; #--->如果大於0就命中緩存,並在varnish構建響應報文時附加上自己(服務器)的IP地址. } else { set resp.http.X-Cache = "MISS"; #--->否則就未命中 } } ############################################### #注意:varnish2版本中和3版本中的配置語法有差異! |
3.有兩個網頁文件,一個是index.html.一個是private.html.期望客戶端訪問第一個網頁時是通過緩存去取得內容,
[root@web1 ~]# echo "<h1>privarte</h1>" >/var/www/html/private.html |
sub vcl_recv { if (req.url ~ "private.html") { #--->通過請求的url進行匹配判斷. return(pass); } return(lookup); } |
4.如何根據請求內容進行緩存時長的設定?
sub vcl_fetch { if (req.url ~ "\.(jpg|jpeg|gif|png)$") { #--->如果請求的內容是圖片,則緩存7200s set bereps.ttl = 7200s; } if (req.url ~ "\.(html|css|js)$") { #---->如果請求的是html等文檔,則緩存1200s set bereps.ttl = 1200s; } } |
5.如果後端服務器更新了內容,而varnish設定的緩存時間過長,那麼就需要手動清理緩存
backend default { .host = "192.168.100.51"; .port = "80"; } acl purgers { #---->對於修剪緩存對象時的訪問控制;"purgers"是自定義的名字,用於後面調用; "127.0.0.1"; "172.16.0.0"/16; } sub vcl_recv { #---->在剛進入varnish時 if (req.request == "PURGE") { #---->如果請求中包含了PURGE這個方法 if (!client.ip ~ purgers){ #---->在進行判斷客戶端的IP是否屬於訪問控制中定義的,這裏是取反. error 405 "Method not allowed"; } } if (req.url ~ "private.html") { return(pass); } return(lookup); } sub vcl_hit { #---->如果請求的內容命中了,就可執行修剪操作. if (req.request == "PURGE"){ purge; error 200 "Purge Finished"; #---->構建錯誤響應報文(這裏雖然用的是error來定義的,但是給來code)通知完成修剪 } } sub vcl_miss { #---->如果未命中緩存,即使請求首部包含了PURGE這個方法,也沒有多大的意義. if (req.request == "PURGE"){ purge; error 404 "Not in cache"; #---->構建錯誤響應報文相應客戶端. } } sub vcl_pass { #---->如果請求被髮往pass那就更沒有必要去做這個操作了. if (req.request == "PURGE"){ error 502 "Purged on a passed object"; } } sub vcl_fetch { if (req.url ~ "\.(jpg|jpeg|gif|png)$") { set beresp.ttl = 60s; } if (req.url ~ "\.(html|css|js)$") { set beresp.ttl = 1200s; } } sub vcl_deliver { if (obj.hits > 0){ set resp.http.X-Cache = "HIT from " + server.ip; } else { set resp.http.X-Cache = "MISS"; } } |
[root@client ~]# curl -X PURGE http://172.16.41.1/index.html #---->在請求首部直接附加方法爲"PURGE" <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title>200 Purge Finished</title> </head> <body> <h1>Error 200 Purge Finished</h1> <p>Purge Finished</p> <h3>Guru Meditation:</h3> <p>XID: 1582592688</p> <hr> <p>Varnish cache server</p> </body> </html> [root@varnish ~]# |
[root@client ~]# curl -I http://172.16.41.1/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Sun, 27 Apr 2014 11:15:23 GMT ETag: "120567-e-4f80450add2ae" Content-Type: text/html; charset=UTF-8 Content-Length: 14 Accept-Ranges: bytes Date: Mon, 05 May 2014 21:51:17 GMT X-Varnish: 1582592689 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache:MISS #---->緩存已清除.. [root@varnish ~]# |
#---->清除一個緩存過期的資源 [root@varnish ~]# curl -X PURGE http://172.16.41.1/test.jpg ........ ........ <h1>Error 404 Not in cache</h1> ........ ........ [root@varnish ~]# #---->清除一個根本就不會存在於緩存中的內容: [root@varnish ~]# curl -X PURGE http://172.16.41.1/private.html ........ ........ <h1>Error 502 Purged on a passed object</h1> ........ ........ [root@varnish ~]# #---->關於沒有權限執行的操作(略) |
每個後端服務器當前探測的健康狀態探測方法通過.probe進行設定,其結果可由req.backend.healthy變量獲取,也可通過varnishlog中的Backend_health查看或varnishadm的debug.health查看。
backend web1 { .host = "www.magedu.com"; .probe = { .url = "/.healthtest.html"; .interval = 1s; .window = 5; .threshold = 2; } } |
.probe中的探測指令常用的有:
(1) .url:探測後端主機健康狀態時請求的URL,默認爲“/”;
(2) .request: 探測後端主機健康狀態時所請求內容的詳細格式,定義後,它會替換.url指定的探測方式;比如:
.request =
"GET /.healthtest.html HTTP/1.1"
"Host: www.magedu.com"
"Connection: close";
(3) .window:設定在判定後端主機健康狀態時基於最近多少次的探測進行,默認是8;
(4) .threshold:在.window中指定的次數中,至少有多少次是成功的才判定後端主機正健康運行;默認是3;
(5) .initial:Varnish啓動時對後端主機至少需要多少次的成功探測,默認同.threshold;
(6) .expected_response:期望後端主機響應的狀態碼,默認爲200;
(7) .interval:探測請求的發送週期,默認爲5秒;
(8) .timeout:每次探測請求的過期時長,默認爲2秒;
因此,如上示例中表示每隔1秒對此後端主機www.magedu.com探測一次,請求的URL爲http://www.magedu.com/.healthtest.html,在最近5次的探測請求中至少有2次是成功的(響應碼爲200)就判定此後端主機爲正常工作狀態。
如果Varnish在某時刻沒有任何可用的後端主機,它將嘗試使用緩存對象的“寬容副本”(graced copy),當然,此時VCL中的各種規則依然有效。因此,更好的辦法是在VCL規則中判斷req.backend.healthy變量顯示某後端主機不可用時,爲此後端主機增大req.grace變量的值以設定適用的寬容期限長度。
7.如何實現Varnish使用多臺後端主機
#示例 backend web1 { .host = "backweb1.maoqiu.com"; .port = "80"; } director webservers random { .retries = 5; { .backend = web1; .weight = 2; } { .backend = { .host = "backweb2.maoqiu.com"; .port = "80"; } .weight = 3; } } |
如上示例中,web1爲顯式定義的後端主機,而webservers這個directors還包含了一個“匿名”後端主機(backweb2.magedu.com)。webservers從這兩個後端主機中挑選一個主機的方法爲random,即以隨機方式挑選。
Varnish的director支持的挑選方法中比較簡單的有round-robin和random兩種。其中,round-robin類型沒有任何參數,只需要爲其指定各後端主機即可,挑選方式爲“輪叫”,並在某後端主機故障時不再將其視作挑選對象;random方法隨機從可用後端主機中進行挑選,每一個後端主機都需要一個.weight參數以指定其權重,同時還可以director級別使用.retires參數來設定查找一個健康後端主機時的嘗試次數。
Varnish 2.1.0後,random挑選方法又多了兩種變化形式client和hash。client類型的director使用client.identity作爲挑選因子,這意味着client.identity相同的請求都將被髮送至同一個後端主機。client.identity默認爲client.ip,但也可以在VCL中將其修改爲所需要的標識符。類似地,hash類型的director使用hash數據作爲挑選因子,這意味着對同一個URL的請求將被髮往同一個後端主機,其常用於多級緩存的場景中。然而,無論是client還hash,當其傾向於使用後端主機不可用時將會重新挑選新的後端其機。
另外還有一種稱作fallback的director,用於定義備用服務器,如下所示:
director b3 fallback { { .backend = www1; } { .backend = www2; } // will only be used if www1 is unhealthy. { .backend = www3; } // will only be used if both www1 and www2 // are unhealthy. } |
[root@web2 ~]# echo "<h1>web2</h1>" > /var/www/html/index.html [root@web2 ~]# service httpd start Starting httpd: [ OK ] [root@web2 ~]# |
backend web1 { #---->定義的後端web1 .host = "192.168.100.51"; .port = "80"; .probe = {#---->定義健康狀態檢測 .url = "/index.html"; .window = 5; .threshold = 2; .interval = 3s; } } backend web2 { #---->定義的後端web2 .host = "192.168.100.52"; .port = "80"; .probe = { #---->定義健康狀態檢測 .url = "/index.html"; .window = 5; .threshold = 2; .interval = 3s; } } director websrvs random { #---->後端主機定義好後可以定義到一個directory中,類似於nginx中的upstream. { .backend = web1; weight = 2; #---->權重 } { .backend = web2; weight = 1;#---->權重 } } #---->定義好後端後,要應用這個director.在sub vcl_recv中添加以下內容: set req.backend = websrvs; |
#---->兩個後端web服務的定義 backend web1 { .host = "192.168.100.51"; .port = "80"; .probe = { .url = "/index.html"; .window = 5; .threshold = 2; .interval = 3s; } } backend web2 { .host = "192.168.100.52"; .port = "80"; .probe = { .url = "/index.html"; .window = 5; .threshold = 2; .interval = 3s; } } #---->對於剛進入varnish的請求做判斷: sub vcl_recv { if (req.url ~ "\.(html|css|js)$") { #---->如果請求的 url中包含了這些內容,如果在沒有緩存的情況下,就轉到後端web1 set req.backend = web1; } else { #---->否則就轉到web2 set req.backend = web2; } 根據測試:當varnish中沒有緩存的情況下,訪問的不同內容都調到了後端不同的服務器上. 這樣的設定根據不同內容選擇不同的服務器組 |
8.如何在後端服務器上查看真實客戶端請求IP地址
#修改配置文件: [root@varnish ~]# vim /etc/varnish/test.vcl sub vcl_recv { if (req.http.X-Forward-For) { set req.http.X-Forward-For = req.http.X-Forward-For + "," + client.ip ; } else { set req.http.X-Forward-For = client.ip; } #修改後端服務器日誌記錄格式: [root@web2 ~]# vim /etc/httpd/conf/httpd.conf LogFormat "%{X-Forward-For}i%l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined |
if (req.http.referer ~ "http://.*") { if ( !(req.http.referer ~ "http://.*ixdba\.net" || req.http.referer ~ "http://.*google\.com" || req.http.referer ~ "http://.*yahoo\.cn"et || req.http.referer ~ "http://.*google\.cn" )) { set req.http.host = "www.maoqiuonline.net"; set req.url = "/templets/default/images/logo.gif"; } return (lookup); } |
這段配置的含義爲:Varnish服務器對接收或發送的請求進行判斷,如果referer標識存在,且referer標識不匹配下面域名列表中的任意一個,就將請求重定向到www.ixdba.net域名下的/templets/default/images/logo.gif圖片,而對找到匹配域名的請求執行lookup操作。
10.varnish常用管理命令:
client_conn
client_req
cache_hit
cache_miss
backend_conn
n_wrk
n_wrk_create
n_backend
n_expired
n_lru_moved
s_hdrbytes
s_bodybytes
命令率:
文檔命中率字節命中率
命中率低的原因?
緩存空間太小不存在明顯的熱點數據緩存服務器的可用性源文件更新過於頻繁
varnishstat
-1:-l: 列出所有參數;-f f1,f2,...: 僅列出指定的參數;
varnishtop:實時顯示日誌中的信息
-i tag: 僅顯示指定的tag,如RxHeader-I regexp: 以模式匹配tag對應值;-C:正則表達式匹配時不區分字符大小寫;
varnishreplay: 日誌重放工具,用於實現緩存預熱;