天天看點

Varnish的配置語言vcl及其内置變量介紹

一、Varnish的配置語言VCL

Varnish的所有配置都是通過VCL(varnish configure language)來配置的。它是一種基于“域”(domain specific)的簡單程式設計語言,它支援有限的算術運算和邏輯運算操作、允許使用正規表達式進行字元串比對、允許使用者使用set自定義變量、支援if判斷語句,也有内置的函數和變量等。使用VCL編寫的緩存政策通常儲存至.vcl檔案中,其需要編譯成二進制的格式後才能由varnish調用。事實上,整個緩存政策就是由幾個特定的子例程如vcl_recv、vcl_fetch等組成,它們分别在不同的位置(或時間)執行,如果沒有事先為某個位置自定義子例程,varnish将會執行預設定義的代碼,這些代碼就是default.vcl中被注釋的代碼。

VCL政策在啟用前,會由management程序将其轉換為C代碼,而後再由gcc編譯器将C代碼編譯成二進制程式。編譯完成後,management負責将其連接配接至varnish執行個體,即child程序。正是由于編譯工作在child程序之外完成,它避免了裝載錯誤格式VCL的風險。是以,varnish修改配置的開銷非常小,其可以同時保有幾份尚在引用的舊版本配置,也能夠讓新的配置即刻生效。編譯後的舊版本配置通常在varnish重新開機時才會被丢棄,如果需要手動清理,則可以使用varnishadm的vcl.discard指令完成。

二、VCL文法

VCL的設計參考了C和Perl語言,是以,對有着C或Perl程式設計經驗者來說,其非常易于了解。其基本文法說明如下:

(1)//、#或/* comment */用于注釋

(2)sub $name 定義函數

(3)不支援循環,有内置變量

(4)使用終止語句(return)将控制權傳回給varnish,沒有傳回值

(5)域專用

(6)操作符:=(指派)、==(等值比較)、~(模式比對)、!(取反)、&&(邏輯與)、||(邏輯或)

VCL的函數不接受參數并且沒有傳回值,是以,其并非真正意義上的函數,這也限定了VCL内部的資料傳遞隻能隐藏在HTTP首部内部進行。VCL的return語句用于将控制權從VCL狀态引擎傳回給Varnish,而非預設函數,這就是為什麼VCL隻有終止語句而沒有傳回值的原因。同時,對于每個“域”來說,可以定義一個或多個終止語句,以告訴Varnish下一步采取何種操作,如查詢緩存或不查詢緩存等。

三、Varnish的内置函數(或者稱為子例程)

(1)、vcl_recv函數

用于接收用戶端請求、解析請求、決定是否響應請求、如何處理請求。當成功接收用戶端請求後被調用。

它通常有四個主要用途:

(1)修改用戶端資料以減少緩存對象差異性;比如删除URL中的www.等字元;

(2)基于用戶端資料選用緩存政策;比如僅緩存特定的URL請求、不緩存POST請求等;

(3)為某web應用程式執行URL重寫規則;

(4)挑選合适的後端Web伺服器;

vcl_recv函數可以通過具有的傳回狀态或動作來決定如何處理請求。

該函數有如下傳回狀态或動作:

pass:表示該請求不再本地緩存中查找,并進入pass模式,且将處理請求控制權交給vcl_pass函數。通常請求的内容包含一些cookie資訊、使用者認證的使用者名和密碼等私有資訊時都一般不查詢緩存。還有請求的是POST、PUT等方法時通常也不需要查詢緩存。如果請求的内容時動态内容時,一般也不從緩存中查詢。

pipe:表示該請求不再本地緩存中查找,進入pipe模式,并将請求控制權交給vcl_pipe函數。此模式下varnish不會對用戶端做任何的檢查或操作,而是在用戶端和後端伺服器直接建立管道,并将資料直接在這個管道中傳輸。此時,keep-alive連接配接中後續的資料也會通過管道進行傳輸,并且不會出現在任何日志中。通常varnish無法識别的請求,則交給vcl_pipe函數處理,繼而交給後端原始伺服器進行處理。

error code [reason]:varnish傳回錯誤代碼給用戶端并丢棄該請求。code表示錯誤代碼,如404,405等等。Reason表示錯誤提示資訊。

lookup:表示該請求在緩存中查找請求的資源。并根據查找的結果将請求控制權交給vcl_hit或vcl_miss函數。

(2)、pipe函數

當vcl_recv函數傳回pipe狀态時被調用,并進入pipe模式,用于将用戶端請求直接傳遞給後端伺服器,在請求和傳回的内容沒有改變的情況下,将不變的内容直接傳回給用戶端。直到這個連接配接關閉。

該函數具有如下傳回狀态或動作:

error code reason:傳回錯誤代碼并丢棄該請求

pipe:将請求交給後端原始伺服器進行處理。

(3)、vcl_pass函數

當vcl_recv函數或者vcl_hit函數或vcl_miss函數傳回pass狀态時該函數被調用,用于将用戶端請求直接轉發給後端伺服器,後端伺服器響應給用戶端時,varnish通常不對響應資料進行緩存。由于直接将請求轉發給後端伺服器,是以該連接配接下的響應資料都是最新的。

該函數具有如下執行動作:

error code  reason:傳回錯誤代碼并丢棄該請求

pass:表示從後端伺服器擷取資料,并将請求控制權交給vcl_fetch函數

restart:重新開機整個vcl,并且該請求重新進行檢查。即該請求重新接受vcl_recv函數的檢查。執行restart動作後,會計算重新開機計數,當超過max_restarts最大重新開機計數會傳回錯誤資訊。

(4)、vacl_hash函數

當vcl_recv函數傳回lookup狀态時,該函數被調用。該函數的主要作用是對請求的uri做hash計算。并将hash計算的結果作為key來存儲,其請求的内容作為value來存儲。是以,varnish是一個key:value的緩存伺服器。

(5)、vcl_hit函數

當vcl_recv函數傳回lookup狀态後,并對請求的uri做hash計算後,如果在緩存中找到請求資料,則表示命中,則自動調用該函數。

deliver:表示從緩存中找到的資料傳回給用戶端,并将控制權交給vcl_deliver函數。

pass:進入pass模式,并将控制權交給vcl_fetch。這種模式下,雖然在緩存中找到了資料,但是不使用緩存中的資料,有可能是過期的資源,需要對資源做新鮮度檢測或者其他機制需要從後端伺服器擷取最新資源。

restart:重新開機整個vcl,并且該請求重新接受vcl_recv函數的檢查。執行restart動作後,會計算重新開機計數,當超過max_restart最大計數器後會傳回錯誤資訊。

(6)、vcl_miss函數

當vcl_recv函數傳回lookup狀态後,并對請求的uri做hash計算後,如果在緩存中沒有找到請求資料,則自動調用該函數。

fetch:表示從後端伺服器擷取請求資料,并叫控制權交給vcl_fetch

pass::進入pass模式,并将控制權交給vcl_pass。

error code reason:表示傳回錯誤代碼和資訊并丢棄該請求。

restart:重新開機整個vcl,并且該請求重新接受vcl_recv函數的檢查。執行restart動作後,會計算重新開機計數,當超過max_restart最大計數器後将傳回錯誤資訊。

(7)、vcl_fetch函數

vcl_fetch函數主要用來将請求轉發給後端的原始伺服器,并對原始伺服器的響應的資料做緩存決策的。

通常當vcl_pass函數傳回pass狀态時或vcl_miss函數傳回fetch狀态時,調用vcl_fetch函數。這些過程都是需要從後端伺服器擷取最新資料,并且根據傳回的狀态或動作來判斷擷取的資料是否需要緩存,還是将響應資料直接傳回給用戶端。

該函數具有如下動作:

deliver:表示從後端伺服器擷取到的資源或資料進行緩存。并将控制權交給vcl_deliver函數。

hit_for_pass:表示從後端伺服器擷取到的資料或資源不進行緩存,且将控制權交給vcl_deliver函數。并且後續對該對象的請求直接交給vcl_pass函數。

      error code reason:表示傳回錯誤代碼和資訊并丢棄該請求。

(8)、vcl_deliver函數

将請求的資料傳回給用戶端調用此函數。通常當vcl_hit傳回deliver狀态時或者vcl_fetch函數傳回deliver和hit_for_pass狀态時,該函數會被調用。

deliver:将請求資料直接傳回給用戶端

(9)、vcl_timeout函數

在緩存内容到期前調用此函數,該函數有如下執行動作:

discard:表示從緩存中清除

fetch:表示從後端伺服器擷取資源進行更新

(10)、vcl_discard函數

在緩存内容到期後調用此函數,該函數具有如下執行動作:

keep:表示過期緩存對象仍然保留在緩存中。

四、Varnish的工作流程圖

Varnish的配置語言vcl及其内置變量介紹

Varnish處理請求流程大緻可以分為如下幾個步驟:

(1)、首先當請求到達時,接受vcl_recv函數的檢查,在這個函數中,可以傳回行pass、pipe、lookup、error等狀态,不同的狀态則将請求轉給不通的函數或子例程程序處理。

(2)當vcl_recv函數傳回lookup狀态時,則會在緩存中檢視是否有該緩存對象。如果有,則表示緩存命中,則在接受vcl_hit函數的檢查。在該函數下可以将緩存對象直接傳回給用戶端,即執行deliver操作。也可以不使用本地緩存對象,從後端伺服器擷取最新請求對象,即執行pass操作。如果本地沒有該緩存對象,則表示緩存丢失,則會接受vcl_miss函數檢查。該函數會從後端伺服器擷取最新資源,即将會執行pass或fetch操作,并将控制權交給vcl_fetch函數。

(3)如果在vcl_recv函數執行pass操作,則表示不再本地緩存查找,直接從後端伺服器進行擷取,即将會接受vcl_pass函數的檢查。

(4)如果在vcl_recv函數處執行pipe操作,則表示不再本地緩存中查找,并且用戶端和後端伺服器建立管道,後續用戶端的keep-alive連接配接中的請求資料傳輸将在管道中進行,并且請求資料接受vcl_pipe函數的檢查。直到資料傳輸完成時關閉該管道連接配接。

(5)當需要從後端伺服器擷取資源或資料時,需要調用vcl_fetch函數,并且根據某種條件判斷該資料或資源是否緩存在本地,如果該資源緩存在varnish中,則執行deliver操作;如果varnish擷取到的最新資料不緩存在本地,則執行hit_for_pass操作。最後,不管是從本地緩存中響應的資料還是從後端原始伺服器擷取到的資料傳回給用戶端都需要調用vcl_deliver函數來完成。

五、VCL的内置變量

VCL中有許多的内置變量,這些内置變量在不同的階段被使用。

定義後端原始伺服器相關資訊時,可以使用的變量

.host 表示後端原始伺服器ip

.port 後端原始伺服器監聽的端口

當varnish接受用戶端請求時,可以使用的内置變量

    server.ip     指定varnish的ip

    server.port   varnish監聽的端口

    client.identity 用戶端辨別,該辨別通常為用戶端ip或者伺服器打給用戶端的cookie資訊。

    client.ip     表示用戶端ip

     req.backend    表示将請求轉發給哪個後端原始伺服器

    req.backend.healthy  表示後端原始伺服器是否處于健康狀态

    req.request :表示請求方法,如GET,HEAD,POST等

    req.url       請求的url

    req.proto     表示用戶端發起請求使用的http協定版本

    req.http.<header>:  表示用戶端請求封包中的http頭部資訊

    req.restarts:表示請求重新開機的次數;預設最大值為4。如果該值為0,表示該請求隻經過vcl_recv一次檢測。

當varnish向後端伺服器發送請求時,可以使用的内置變量:

    bereq.url    請求的方法

    bereq.proto  表示varnish發起請求使用的http協定版本

    bereq.request 請求的方法

    bereq.http.header 請求封包的首部

    bereq.connect_timeout varnish連接配接原始伺服器的逾時時長

當原始伺服器把響應封包發給varnish時,可以使用的内置變量:

    beresp.status   原始伺服器響應的狀态碼

    beresp.proto    原始伺服器響應時使用的http協定版本

    beresp.http.header  原始伺服器響應封包中http首部資訊

    beresp.ttl: 設定緩存的緩存時長 ,機關為秒

    beresp.backend.name  響應此請求的原始伺服器的主機名

    beresp.backend.ip    響應此請求的原始伺服器的ip

當varnish對用戶端響應時,可以使用的内置變量:

resp.status: 傳回給用戶端的狀态碼

resp.proto:傳回給用戶端的http的協定版本

resp.http.<header>: 傳回給客戶響應封包的首部

resp.response: 傳回給用戶端的http狀态資訊

當某對象或資源進入緩存後,可以使用的内置變量:

obj.status: 傳回該對象的響應狀态碼,如200,302等

obj.cacheable: 表示傳回内容是否可以緩存,如果http傳回的狀态碼為200,203,301,302,404,410,并且有非0的生存期,則可以緩存。

obj.valid: 是否為有效的http應答,即該緩存對象是否是有效的;

obj.response:表示傳回該對象的狀态資訊

obj.ttl: 緩存對象剩下的緩存時長,機關為秒

obj.hits:請求的對象被deliver的次數,如果該值大于0,表示該對象已經被緩存。

obj.lastuse:表示上一次請求到現在所經過的時間

六、varnish的緩存政策

作為一個高性能的緩存伺服器,并不是所有的資料有需要緩存的。有些時候,某些資料不緩存,其效果更好。是以,varnish需要對請求的資料做判斷,已決定何種資料需要緩存。

varnish的緩存政策通常包括如下幾點:

1、通常靜态資源需要緩存。如html文檔、圖檔等資源;

2、varnish通常隻能識别正常的http請求方法,比如GET、HEAD、PUT、POST、OPTIONS、TRACE、DELETE等等。如果用戶端請求的方法不是這些的話,則varnish不會執行任何處理,直接将請求轉發給後端的原始伺服器。

3、如果請求的GET、HEAD方法時,其請求的内容通常需要緩存。如果其PUT、POST等方法時,由于一些上傳或送出的資料,是以緩存下來無意義,是以如果是這樣的請求方法時,其内容通常也不會緩存下來。

4、不緩存使用者的私有資訊。對于使用者的cookie資訊,通常是不能緩存的,這是因為使用者請求封包中帶有cookie資訊,varnish在緩存時,會将其uri和cookie資訊一起做hash計算并将其結果作為key,由于每個用戶端的cookie資訊都是唯一的,而且varnish是一個key:value的緩存伺服器,是以這樣會導緻不同的用戶端即使請求同一個資源時,也是不會被某命中的,隻有同一個用戶端請求同一個資源時才會被命中。是以,這種場景下,不僅不會提高用戶端的通路速度,而且無疑會增加原始伺服器的負載。但是,在某些時候,使用者請求資源時,根本就不需要提供cookie資訊時,我們可以在varnish中将請求封包中的cookie首部給删除掉,這樣就可以對該請求的資源進行緩存了。

如下面的一個例子,如果請求的不是正常的http方法時,則不交給varnish處理,直接交給後端伺服器處理。

if (req.request != "GET" &&

    req.request != "HEAD" &&

    req.request != "PUT" &&

    req.request != "POST" &&

    req.request != "TRACE" &&

    req.request != "OPTIONS" &&

    req.request != "DELETE") {

    return (pipe);

  }

  //如果請求的即不是GET方法也不是HEAD方法,則不查詢緩存

  if (req.request != "GET" && req.request != "HEAD") {

    return (pass);

 //如果請求首部中包含用戶端認證資訊,如使用者名和密碼則不查詢緩存

  if (req.http.Authorization) {

下面一個事例中,請求封包帶有cookie首部,但是這些請求的資源中不需要cookie時,應當采取的緩存政策。

如果請求的url中不包括login或admin這些字元時,則應該将請求封包中的cookie首部删除。因為請求的資源是不需要用戶端提供cookie的。

sub vcl_recv {

    if (!(req.url ~ "wp-(login|admin)")) {

        unset req.http.cookie;

    }

}

如果請求的url中不包括login或admin字元串時,那麼當varnish從後端原始伺服器擷取資料時,将響應封包中的set-cookie首部删除掉。

sub vcl_fetch {

        unset beresp.http.set-cookie;

七、管理Varnish緩存内容

1、提高緩存命中率

提高緩存命中率有效的途徑有:

a、增加緩存對象的生存時間(TTL)

b、加大記憶體

2、清理緩存

增大緩存的ttl值也可能帶來副作用,有時候某個緩存的有效時間還沒到期時,該緩存對象已經更新了。是以,對于這種情況,對于緩存管理者來說,需要手動清理已經過期或者舊緩存對象。對于Varnish來說,它提供了三種方式來完成這類任務:HTTP修剪(HTTP purging)、禁用某類緩存對象和強制緩存未命中(forced cache misses)

a、HTTP修剪(purge)

purge用于清理緩存中的某特定對象及其變種(variants),是以,在有着明确要修剪的緩存對象時可以使用此種方式。HTTP協定的PURGE方法可以實作purge功能,不過,其僅能用于vcl_hit和vcl_miss中,它會釋放記憶體空間并移除指定緩存對象的所有Vary:-變種,并等待下一個針對此内容的用戶端請求到達時重新整理此内容。另外,其一般要與return(restart)一起使用。下面是個在VCL中配置的示例。

//首先定義一個可以執行PURGE操作的位址,這裡使用acl的方式來定義

acl purgers {

        "127.0.0.1";

        "192.168.108.0"/24;

        if (req.request == "PURGE") {

                if (!client.ip ~ purgers){

//如果用戶端的ip不屬于acl中的話,那麼varnish将丢棄該請求,并響應如下錯誤資訊。

                        error 405 "The Method is not allowed";

                }

//如果請求的方法是PURGE,且用戶端的ip是屬于acl中的話,則在緩存中查找

                return(lookup);

        }

//如果緩存命中的話,則執行purge操作,并傳回錯誤資訊

sub vcl_hit {

        if (req.request == "PURGE"){

                purge;

                error 200 " already purged";

//如果緩存為命中的話,也執行purge操作,并傳回錯誤資訊

sub vcl_miss {

                error 404 "Not in cache";

用戶端在發起HTTP請求時,隻需要為所請求的URL使用PURGE方法即可,其指令使用方式如下:

# curl -I -X PURGE http://varniship/path/to/someurl

http pugre功能是還可以通過Varnish的telnet管理端口發送purge指令來清除不需要的緩存對象。其指令格式為:

/usr/local/varnish/bin/varnishadm  -T 192.168.0.102:2000 purge.url  <REGEXP>

REGEXP:是基于正規表達式比對的url

如:# /usr/local/varnish/bin/varnishadm –T 192.168.0.102:2000 purge.url  /a/varnish1.html

删除所有的緩存對象,可以使用如下指令

       如:# /usr/local/varnish/bin/varnishadm –T 192.168.0.102:2000 purge.url ^.*$

檢視最近删除的緩存對象清單,可以使用如下指令

       如:# /usr/local/varnish/bin/varnishadm –T 192.168.0.102:2000 purge.list

除了在指令行中删除緩存對象這種方式外,可以在通過登入到telnet的管理界面中進行删除動作。如:

[root@varnish-server ~]#telnet 192.168.0.102  2000             

Trying 192.168.0.103...  

Connected to localhost.localdomain (192.168.0.103).  

Escape character is '^]'.  

200 154       

-----------------------------  

Varnish HTTP accelerator CLI.  

Type 'help' for command list.  

Type 'quit' to close CLI session.  

purge.url  /a/mz/2010/0421/11.html  #這裡是清除這個頁面緩存 

200 0         

purge.url  ^/zm/a/d.*$   #這裡是清除/zm/a/d/目錄下所有以字母d開頭的緩存頁面 

200 0  

b、強制緩存未命中

在vcl_recv中使用return(pass)能夠強制到上遊伺服器取得請求的内容,但這也會導緻無法将其緩存。使用purge會移除舊的緩存對象,但如果上遊伺服器當機而無法取得新版本的内容時,此内容将無法再響應給用戶端。使用req.has_always_miss=ture,可以讓Varnish在緩存中搜尋相應的内容但卻總是回應“未命中”,于是vcl_miss将後續地負責啟動vcl_fetch從上遊伺服器取得新内容,并以新内容緩存覆寫舊内容。此時,如果上遊伺服器當機或未響應,舊的内容将保持原狀,并能夠繼續服務于那些未使用req.has_always_miss=true的用戶端,直到其過期失效或由其它方法移除。

c、禁用某類緩存對象(banning)

ban()操作是在Varnish 3中才有的,Varnish 2中的purge()操作在Varnish 3中被替換為了ban()操作,而Varnish 3也使用了purge操作,但為其賦予了新的功能。ban()這個操作有人說支援也有人反對,是以,對于這種方式暫且不說。

八、Varnish的後端存儲類型

varnish支援多種不同類型的後端存儲,這可以在varnishd啟動時使用-s選項指定。後端存儲的類型包括:

(1)file:使用特定的檔案存儲全部的緩存資料,并通過作業系統的mmap()系統調用将整個緩存檔案映射至記憶體區域(如果條件允許);

(2)malloc:使用malloc()庫調用在varnish啟動時向作業系統申請指定大小的記憶體空間以存儲緩存對象;

(3)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的磁盤空間會自動增長,直到最大值。所有其它的大小都會被圓整。

varnish的緩存類型可以在varnish腳本的配置檔案/etc/sysconfig/varnish中進行配置。

九、Varnish定義多後端主機

Varnish中可以使用director指令将一個或多個近似的後端主機定義為一個邏輯組,并可以指定相應的排程方式(也叫挑選方法)來将請求發送至這些主機上。不同的director可以使用同一個後端主機,而某director中也可以使用“匿名”後端主機(在director中使用.backend直接進行定義)。每個director都必須有其專用名,且在定義後必須在VCL中進行調用,VCL中任何可以指定後端主機的位置均可以按需将其替換為調用某已定義的director。

定義多個後端主機的配置執行個體

.backend web1 {          #顯式定義一台後端伺服器

       .host = “backend1.xsl.com”;

       .port = “80”;

director webserver random {  #定義一個後端伺服器邏輯組,其排程方式為random(即權重随機)

       .retries = 5;

       {

              .backend = web1;

              .weight =2;

{

       .backend = {    #定義一個匿名後端主機

              .host = “backend2.xsl.com”;

              .port = ‘80’;

       }

       .weight = 3;

說明:如果定義了多個後端主機時,不管是使用.backend方式定義的,還是使用director方式定義的,都需要調用這些主機才會生效。如果在varnish隻定義了一個後端主機,則不需要調用就會生效。

對于上例而言,web1為顯式定義的後端主機。如果有多台的話,需要使用director指令來定義。在上例中,使用director指令定義了一個邏輯組叫做webserver。其中webserver組中需要指定後端伺服器的位址及其ip等參數。在webserver組中,定義了2個後端伺服器,web1為顯式定義的後端主機,“backend2.xsl.com”為匿名主機。

使用director定義某個邏輯組時,由于有多台主機,是以,需要指定排程方式,以便将請求轉給相應的後端伺服器。director中有2種調調方式:

       round-robin:輪調,即請求依次循環的轉發給下一個後端主機。該排程方式沒有其他參數,隻需要指定後端原始主機即可。

       random:随機排程,即從後端原始主機中随機挑選一台伺服器對請求進行響應。在random方式下需要為每一個後端主機指定其weight值,是以這是基于權重的随機算法。Varnish 2.1.0後,random挑選方法又多了兩種變化形式client和hash。client類型的director使用client.identity作為挑選因子,這意味着client.identity相同的請求都将被發送至同一個後端主機。client.identity預設為cliet.ip,但也可以在VCL中将其修改為所需要的辨別符。類似地,hash類型的director使用hash資料作為挑選因子,這意味着對同一個URL的請求将被發往同一個後端主機,其常用于多級緩存的場景中。然而,無論是client還hash,當其傾向于使用後端主機不可用時将會重新挑選新的後端主機。

在director級别下,還可以使用.retries參數來設定查找一個健康的後端主機的嘗試次數。

十、varnish實作資源重定向(動靜分離)

varnish可以根據使用者請求資源的不同,将其請求定向到不同的原始伺服器進行處理。

在一個中等以上的架構環境中,我們需要将使用者請求的靜态資源如html文檔則交給靜态伺服器進行響應;如果請求的是圖檔這樣的靜态資源,則交給圖檔伺服器進行處理。如果請求的是php等動态資源時,則交給動态伺服器處理。不能被varnish正常識别的請求也交給靜态伺服器進行處理。除此之外,所有的請求都需要先查詢緩存,然後在進一步處理。

其拓撲圖為:

Varnish的配置語言vcl及其内置變量介紹

如下執行個體,定義了3個後端主機。并根據請求的資源不同,将其請求轉發至不同的後端主機上。

//靜态伺服器

backend web1 {

        .host="172.16.1.100";

        .port="80";

//圖檔伺服器

backend web2 {

        .host="172.16.1.200";

//動态伺服器

backend web3 {

      .host="172.16.1.33";

       .port="80";

//如果請求的方法不是正常的http方法,則varnish無法識别,則将請求交給後端原始伺服器web1進行處理。

      if (req.request != "GET" &&

            req.request != "HEAD" &&

            req.request != "PUT" &&

            req.request != "POST" &&

             req.request != "TRACE" &&

            req.request != "OPTIONS" &&

            req.request != "DELETE") {

          set req.backend = web1;

           return (pipe);

      }

//如果請求的資源是以.html、.css、.js、.txt結尾的檔案,則直接将請求轉發至web1伺服器上。

        if (req.url ~ "\.(html|css|js|txt)$") {

                set req.backend=web1;

//如果請求的資源是以.png、.jpg、.img結尾的檔案,則直接将請求轉發至web2伺服器上。

        if (req.url ~ "\.(png|jpg|img)$") {

                set req.backend=web2;

//如果請求的是動态資源,則不查詢緩存,直接轉交給後端原始伺服器web3進行處理

        if (req.url ~ "\.php") {

            set req.backend=web3

                return(pass);

//其他的請求則都需要查詢緩存

        return(lookup);

為了測試請求的資源是否命中,在http的響應首部中添加了一個X-Cache首部。

sub vcl_deliver {

        if (obj.hits > 0 ){

                set resp.http.X-Cache = "Hit from " + server.ip;

        }else {

                set resp.http.X-Cache = "Miss";

//為了友善測試,在http的響應首部中添加了一個X-Server首部,該首部的value為響應該請求的原始伺服器。

        if (!beresp.http.X-Server) {

                set beresp.http.X-Server=beresp.backend.ip;

十一、Varnish的後端狀态檢測

Varnish可以檢測後端主機的健康狀态,在判定後端主機失效時能自動将其從可用後端主機清單中移除,而一旦其重新變得可用還可以自動将其設定為可用。為了避免誤判,Varnish在探測後端主機的健康狀态發生轉變時(比如某次探測時某後端主機突然成為不可用狀态),通常需要連續執行幾次探測均為新狀态才将其标記為轉換後的狀态。

Varnish檢測後端伺服器的健康狀況是通過.probe進行設定,其結果可由req.backend.healthy變量擷取,也可通過varnishlog中的Backend_health檢視或varnishadm的debug.health檢視。

Varnish檢測後端伺服器健康狀況的配置執行個體:

       .host = "www.xsl.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.xsl.com探測一次,請求的URL為http://www.xsl.com/.healthtest.html,在最近5次的探測請求中至少有2次是成功的(響應碼為200)就判定此後端主機為正常工作狀态。

十二、varnish的防盜鍊功能

varnish的防盜鍊功能是根據請求首部中的referer來進行判斷的。referer首部表示該請求是從哪個頁面跳轉過來的。

在vcl_recv子例程中添加如下配置段:

if(req.http.referer ~ "http://.*") {

//如下行是定義哪些是不符合的請求

                if(!(req.http.referer ~ "http://.*google\.com" || req.http.referer ~ "http://.*yahoo\.cn" || req.http.referer ~ "http://.*google\.cn" )) {

                        set req.http.host = "www.xsl.com";

                        set req.url = "/default.jpg";

                } else {

                        return(lookup);