天天看點

高性能緩存伺服器Varnish詳解

一、簡介

   Varnish是一款高性能的開源HTTP加速器,挪威最大的線上報紙 Verdens Gang 使用3台Varnish代替了原來的12台Squid,性能比以前更好。

<a></a>

   varnish項目是2006年釋出的第一個版本0.9.距今已經八年多了,此文檔之前也提過varnish還不穩定,那是2007年時候編寫的,經過varnish開發團隊和網友們的辛苦耕耘,現在的varnish已經很健壯。很多門戶網站已經部署了varnish,并且反應都很好,甚至反應比squid還穩定,且效率更高,資源占用更少。相信在反向代理,web加速方面,varnish已經有足夠能力代替squid。

   varnish的官網為https://www.varnish-cache.org,rpm,rpm包的下載下傳位置為:http://repo.varnish-cache.org。

二、關于Varnish

1.varnish系統架構

   varnish主要運作兩個程序:Management程序和Child程序(也叫Cache程序)。

   Management程序主要實作應用新的配置、編譯VCL、監控varnish、初始化varnish以及提供一個指令行接口等。Management程序會每隔一段時間探測一下Child程序以判斷其是否正常運作,如果在指定的時長内未得到Child程序的回應,Mangagement将會重新開機此Child程序。

   Child程序包含多種類型的線程,常見的如:

       Acceptor程序:接受新的連接配接請求并響應

       worker程序:child程序會為每個使用者啟動一個worker程序,是以,在高并發的場景中可能會出現數百個worker程序甚至更多

       Expiry程序:從緩存中清理過期内容

       Varnish依賴“工作區(workspace)”以降低線程在申請或修改記憶體時出現競争的可能性。在varnish内部有多種不同的工作區,其中最關鍵的當屬用于管理會話資料的session工作區

2.varnish日志

   為了與系統的其他部分進行互動,Child程序使用了可以通過檔案系統接口進行通路的共享記憶體日志(shared memory log),是以,如果某線程需要記錄資訊,其僅需要持有一個鎖,而後向共享記憶體中的某記憶體區域寫入資料,再釋放持有的鎖即可。而為了減少競争,每個worker線程都使用了日志資料緩存

   共享記憶體大小一般為90M,其分為兩部分,前一部分為計數器,後半部分為用戶端請求的資料。varnish通過了多個不同的工具,如varnishlog、varnishncsa或varnishstst等來分析共享記憶體日志中的資訊并能夠以指定的方式進行顯示

   3.varnish的後端存儲

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

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

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

     (3)persistent(experimental):與file的功能相同,但是可以持久存儲資料(即重新開機varnish時資料不會被清楚),仍處于測試階段

     varnish無法追蹤某緩存對象是否存入了緩存檔案,而後也就無法得知磁盤上的緩存檔案是否可用,是以,file存儲在varnish停止或重新開機是會清除資料。而persistent方法的出現對此有一個彌補,但persistent仍處于測試階段,例如目前尚無法有效處理要緩存對象總體大小超出緩存空間的情況,所有,其僅适用于有着巨大緩存空間的場景。

    選擇使用合适的存儲方式有助于途勝系統性能,從經驗的角度來看,建議在記憶體空間足以存儲所有資料的緩存對象時使用malloc的方法,反之,file存儲将會有着更好的性能表現,然而,需要注意的是,varnishd實際上是用的空間比使用-s選項指定的緩存空間更大,一般說來,其需要為每個緩存對象多使用差不多1k左右的存儲空間,這意味着,對于100萬個緩存對象來說,其使用的緩存空間将超出指定大小1G左右,另外,為了儲存資料結構等,varnish自身也會占去不少的記憶體空間。

    為varnish指定使用的緩存類型時,-s選項可接受的參數格式如下:

       malloc[,size]或file[,path[,size[,granularity]]]或persistent,path,size{experimental}

三、VCL

   1.簡介

     VCL(Varnish Configuration Language)是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指令來完成

2.VCL狀态引擎

    在VCL狀态引擎中,狀态之間具有相關性,但彼此間互相隔離,每個引擎使用return(x)來退出目前狀态并訓示varnish進入下一個狀态

    varnish開始處理一個請求時,首先需要分析HTTP請求本身,比如從首部擷取請求方法、驗證其是否為一個合法的HTTP請求等,當這些基本分析結束後就需要做出第一個決策,即varnish是否從緩存中查找請求的資源,這個決定的實作則需要有VCL來完成,簡單來說,要有vcl_recv方法來完成,如果說管理者沒有定義vcl_recv函數,varnish将會執行預設的vcl_recv函數,然而,即便管理者自定義了vcl_recv,但如果沒有為自定義的vcl_recv函數指定其終止操作(terminating),其仍将會指定預設的vcl_recv函數,事實上,varnish官方強烈建議讓varnish執行預設的vcl_recv以便處理自定義vcl_recv函數中可能出現的漏洞

3.VCL文法

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

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

    (2)sub name 定義函數

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

    (4)使用終止語句,沒有傳回值

    (5)域專用

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

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

4.VCL的内置函數

     VCL提供了結果函數來實作字元串的修改,如添加bans,重新開機VCL狀态引擎因将控制權轉回varnish等

    regsub(str,reget,sub):基于正規表達式搜尋指定的字元串并将其替換成指定的字元串,隻替換比對到的第一個

    regsuball(str,reget,sub):基于正規表達式搜尋指定的字元串并将其統統替換成指定的字元串

    ban(expression):

    ban_url(regex):Bans所有其URL能夠由regex比對的緩存對象

    purge:從緩存中挑選出某對象以及其相關變種一并删除,這可以通過通過HYTP協定的PURGE方法完成

    hash_data(str):

    return():當某個VCL與運作結束時,将控制權傳回給Varnish,并訓示Varnish如何進行後續的操作:其可以傳回的指令包括:lookup、pass、pipe、hit_for_pass、fetch、deliver和hash等:但某特定域可能技能傳回某些特定的指令,而非前面列出的全部指令:

    return(restart):重新運作整個VCL,即重新從vcl_recv開始進行處理;每一次重新開機都會增加req.restaets變量中的值,而max_restaets參數則用于限定最大重新開機次數

5.vcl_recv

     vcl_recv是在varnish完成對請求封包的解碼為基本資料結構後第一個要指定的子例程,他通常有四個主要用途:

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

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

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

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

    可以使用下面的終止語句,即通過return()向varnish傳回訓示操作

      pass:繞過緩存,即不從緩存中查詢内容或不将内容存儲至緩存中;

      pipe:不對用戶端進行檢查或做出任何操作,而是在用戶端與後端伺服器之間建立專業“通道”,并直接将資料在二者之間進行傳送:此時,keep-alive連接配接中後續傳送的資料也都将在通過此管道進行直接傳送,并不會出現在任何日志中

      lookup:在緩存中查找使用者請求的對象,如果緩存中沒有其指定的對象,後續操作很可能會将其請求的對象進行緩存

      error:有varnish自己合成一個響應封包,一般是響應一個錯誤類資訊、重定向類資訊或緩存均衡器傳回的後端web伺服器健康狀态檢查類資訊

     vcl_recv也可以通過精巧的政策完成一定意義上的安全功能,以将某特定的攻擊扼殺于搖籃中,同時,它也可以檢查出一些拼寫的錯誤并将其進行修改

     varnish預設的vcl_recv專門設計用來實作安全的緩政策,它主要完成兩種功能:

       (1)僅處理可以識别的HTTP方法,并且隻緩存GET和HEAD方法

       (2)不緩存任何使用者特有的資料

     安全起見,一般都在自定義的vcl_recv中不要使用return()終止語句,而是再由預設vcl_recv進行處理,并有其做出響應的的處理決策

6.vcl_fetch

     如前面所述,想對于vcl_recv是根據用戶端的請求做出緩存政策來說,vcl_fetch則是根據伺服器端的響應做出緩存決策,在任何VCL狀态引擎中發揮pass操作都将有vcl_fetch進行後續處理。vcl_fetch中有許多可用的内置變量,比如最常見的用于定義某對象緩存時長的beresp.ttl變量,通過return()傳回給varnish的操作指令有:

       deliver:緩存此對象,并将其發送給用戶端(經由vcl_deliver)

       hit_for_pass:不緩存此對象,但可以導緻後續對此對象的請求直接送達到vcl_pass進行處理

       restart:重新開機整個VCL,并增加重新開機次數,超出max_restarts限定的最大重新開機次數将會發揮錯誤資訊

       error code [reson]:傳回指定的錯誤代碼給用戶端并丢棄此請求,“code”是錯誤辨別,例如200、405等,“reason”是錯誤提示資訊。

      預設的vcl_fetch放棄了緩存任何使用了Set-Cookie首部的響應

7.vcl_deliver

     在緩存中找到緩存内容,發送給用戶端時調用此參數,通過return()傳回給varnish的操作指令有:  

8.val_pass

      此函數在進入pass模式時被調用,用于将請求直接傳遞至後端主機,後端主機應答資料後發送給用戶端,但是不緩存任何資料,在目前連接配接下,每次都是犯回最新的内容,通過return()傳回給varnish的操作指令有:  

        error code [reson]:傳回指定的錯誤代碼給用戶端并丢棄此請求,“code”是錯誤辨別,例如200、405等,“reason”是錯誤提示資訊。

        pass:繞過緩存,即不從緩存中查詢内容或不将内容存儲至緩存中;

9.vcl_pipe

       不對用戶端進行檢查或做出任何操作,而是在用戶端與後端伺服器之間建立專業“通道”,并直接将資料在二者之間進行傳送:此時,keep-alive連接配接中後續傳送的資料也都将在通過此管道進行直接傳送,并不會出現在任何日志中,通過return()傳回給varnish的操作指令有:  

         error code [reson]:傳回指定的錯誤代碼給用戶端并丢棄此請求,“code”是錯誤辨別,例如200、405等,“reason”是錯誤提示資訊。

        pipe:不對用戶端進行檢查或做出任何操作,而是在用戶端與後端伺服器之間建立專業“通道”,并直接将資料在二者之間進行傳送:此時,keep-alive連接配接中後續傳送的資料也都将在通過此管道進行直接傳送,并不會出現在任何日志中

10.lookup

      表示在緩存裡查找被請求的對象,并且根據查找的資料把控制權交給vcl_miss或vcl_hit

11.vcl_hit

       在執行lookup指令後,如果在緩存中找到請求的内容,将自動調用此函數,通過return()傳回給varnish的操作指令有:

        deliver:緩存此對象,并将其發送給用戶端(經由vcl_deliver)

         pass:繞過緩存,即不從緩存中查詢内容或不将内容存儲至緩存中;

12.vcl_miss函數

      在執行lookup指令後,如果在緩存中找不到請求的内容,将自動調用此函數,此函數可以判斷是否在後端伺服器上擷取内容,通過return()傳回給varnish的操作指令有:

13.VCL處理流程圖

       通過上面對VCL函數的介紹,大家應該對每個函數實作的功能有一個了解,,起始每個函數之間是有聯系的,如下圖所示

<a href="http://s3.51cto.com/wyfs02/M02/25/E9/wKioL1NntzzC7FOVAAFmrUckEms067.jpg" target="_blank"></a>

四、與緩存相關的HTTP首部

   HTTP協定提供了很多個首部以實作頁面緩存及緩存失效的相關功能,這其中常用的有:

 1.Expires

        用于指定某web對象的過期時間/日期,通常為GMT格式,一般不應該将此設定的未來過長的時間,一年的長度對大多數場景來說足矣,其常用于為靜态内容或JavaScript樣式表或圖檔指定緩存周期

2.Cache-Control

       用于定義所有的緩存機制都必須遵循的緩存訓示,這些訓示是一些特定的指令,包括pubilc、private、no-cache(表示可以存儲,但在重新驗證其有效性之前不能用于響應用戶端請求)、no-store、max-age、s-maxage以及must-revalidate等,Cache-Control中設定的時間會覆寫Expires中指定的時間

3.Etag

        響應首部,用于子啊響應封包中為某web資源定義版本辨別符

4.Last-Mofified

        響應封包,使用者回應用戶端關于Last-Mofified-Since或If-None-Match首部的請求,以通知用戶端其請求的web對象最近修改時間

5.Last-Mofified-Since

       條件式請求首部,如果在此首部指定的時間後其請求的web内容發生了更改,則伺服器響應更改後的内容,否則,則響應304(not modified)

6.If-None-Match

       條件式請求首部,web伺服器為某web内容定義了Etag首部,用戶端請求時能擷取并保護這個首部的值(即标簽),而後在後續的請求中會通過If-None-Match首部附加其認可的标簽清單并讓伺服器端檢驗器原始内容是否有可以與此清單中的某标簽比對的标簽,如果有,則傳回304,否則,則傳回原始内容

7.Vary

       響應首部,原始伺服器根據請求來源的不同響應的可能會有所不同的首部,常用的是Vary:Accept-Encoding,用于通知緩存機制其内容看起來可能不同于使用者請求時Accept-Encoding-hearder首部辨別的編碼格式

8.Age

       緩存伺服器可以發送一個額外的響應首部,用于指定響應的有效期限,浏覽器通常根據此首部絕對内容的緩存時長:如果響應封包中還使用了max-age指令,那麼緩存的有效時長為max-age減去Age結果

五、安裝使用varnish

1.ip規劃與使用說明

    192.168.1.201 varnish 

    192.168.1.202 web(靜态頁面)

    192.168.1.203 web(圖檔伺服器)

    說明:本處安裝的varnish3.0.5系列的包,故本處使用的一些指令可能會不适用于varnish2系列和varnish4系列

2.下載下傳與安裝

     可以使用rpm包的安裝方式,也可以使用源碼編譯安裝的方式,官方也提供有rpm,本處使用的為rpm安裝的方式

1

2

3

4

<code>[root@node1 ~]# wget http:</code><code>//repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-3.0.5-1.el6.x86_64.rpm</code>

<code>[root@node1 ~]# wget http:</code><code>//repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-libs-3.0.5-1.el6.x86_64.rpm</code>

<code>[root@node1 ~]# wget http:</code><code>//repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-docs-3.0.5-1.el6.x86_64.rpm</code>

<code>[root@node1 ~]# rpm -ivh </code><code>var</code><code>nish*</code>

3.為192.168.1.202提供靜态頁面  

<code>[root@node2 ~]# echo </code><code>"web1"</code> <code>&gt; /</code><code>var</code><code>/www/html/index.html</code>

<code>[root@node2 ~]# service httpd start</code>

4.修改varnish的啟動檔案

5

<code>[root@node1 ~]# vi /etc/sysconfig/</code><code>var</code><code>nish</code>

<code>修改一下參數</code>

<code>VARNISH_LISTEN_PORT=</code><code>80</code> <code>//表示将varnish對外的監聽端口改為80</code>

<code>VARNISH_STORAGE=</code><code>"malloc,100M"</code> <code>//将資料緩存在記憶體中,記憶體大小為100M</code>

<code>其餘的使用預設即可</code>

5.配置varnish

     本處使用varnishadm指令行接口來重新整理varnish

      指令格式:varnishadm [-n ident] [-t timeout] [-S secretfile] -T [address]:port command [...]

      通過指令行的方式連接配接至varnishd進行管理操作的工具,指定要連接配接的varnish執行個體有兩種方法:

       -T [address]:port 來接指定套接字上的執行個體

       -n ident 連接配接指定名稱的執行個體

    其運作模式有兩種,當不在指令行中給出指定要指定的“command”時,要将進入互動模式,否則,varnish将指定指定的“command”并退出。

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

<code>[root@node1 ~]# </code><code>var</code><code>nishadm -S /etc/</code><code>var</code><code>nish/secret -T </code><code>127.0</code><code>.</code><code>0.1</code><code>:</code><code>6082</code>

<code>help [command]</code>

<code>ping [timestamp]  </code><code>//ping節點是否線上</code>

<code>auth response</code>

<code>quit</code>

<code>banner</code>

<code>status </code><code>//運作狀态</code>

<code>start  </code><code>//統計資料</code>

<code>stop</code>

<code>vcl.load &lt;configname&gt; &lt;filename&gt; </code><code>//加載某個檔案,作為配置檔案</code>

<code>vcl.inline &lt;configname&gt; &lt;quoted_VCLstring&gt;</code>

<code>vcl.</code><code>use</code> <code>&lt;configname&gt; </code><code>//編譯目前的執行個體</code>

<code>vcl.discard &lt;configname&gt;</code>

<code>vcl.list</code>

<code>vcl.show &lt;configname&gt; </code><code>//檢視目前的執行個體</code>

<code>param.show [-l] [&lt;param&gt;]</code>

<code>param.</code><code>set</code> <code>&lt;param&gt; &lt;value&gt;</code>

<code>panic.show</code>

<code>panic.clear</code>

<code>storage.list</code>

<code>backend.list</code>

<code>backend.set_health matcher state</code>

<code>ban.url &lt;regexp&gt;</code>

<code>ban &lt;field&gt; &lt;operator&gt; &lt;arg&gt; [&amp;&amp; &lt;field&gt; &lt;oper&gt; &lt;arg&gt;]...</code>

<code>ban.list</code>

       ①、定義一個檔案test.vcl,定義一個後端伺服器為192.168.1.201,由于varnish不能檢視是否命中緩存,并寫VCL,檢視緩存是否能命中

<code>backend web1 {</code>

<code>  </code><code>.host=</code><code>"192.168.1.202"</code><code>; </code><code>//定義後端主機</code>

<code>  </code><code>.port=</code><code>"80"</code><code>;  </code><code>//後端主機監聽的端口</code>

<code>}</code>

<code>sub vcl_deliver{</code>

<code>  </code><code>if</code> <code>(obj.hits &gt;</code><code>0</code> <code>){</code>

<code>        </code><code>set</code> <code>resp.http.X-Cache= </code><code>"HIT from "</code> <code>+ client.ip; </code><code>//如果命中,則顯示HIT from +用戶端ip位址</code>

<code>        </code><code>} </code><code>else</code> <code>{</code>

<code>        </code><code>set</code> <code>resp.http.X-Cache= </code><code>"MISS"</code><code>; </code><code>//否則顯示"MISS"</code>

<code>        </code><code>}</code>

<code>編譯</code>

<code>var</code><code>nish&gt; vcl.load test1 test.vcl</code>

<code>200</code>     

<code>VCL compiled.</code>

<code>var</code><code>nish&gt; vcl.</code><code>use</code> <code>test1</code>

<code>200</code>

       測試

26

27

28

29

30

<code>[root@node1 ~]# curl -I http:</code><code>//192.168.1.201/index.html</code>

<code>HTTP/</code><code>1.1</code> <code>200</code> <code>OK</code>

<code>Server: Apache/</code><code>2.2</code><code>.</code><code>15</code> <code>(CentOS)</code>

<code>Last-Modified: Wed, </code><code>23</code> <code>Apr </code><code>2014</code> <code>16</code><code>:</code><code>10</code><code>:</code><code>19</code> <code>GMT</code>

<code>ETag: </code><code>"20101-5-4f7b7f8092275"</code>

<code>Content-Type: text/html; charset=UTF-</code><code>8</code>

<code>Content-Length: </code><code>5</code>

<code>Accept-Ranges: bytes</code>

<code>Date</code><code>: Wed, </code><code>23</code> <code>Apr </code><code>2014</code> <code>16</code><code>:</code><code>49</code><code>:</code><code>38</code> <code>GMT</code>

<code>X-Varnish: </code><code>1409659707</code>

<code>Age: </code><code>0</code>

<code>Via: </code><code>1.1</code> <code>var</code><code>nish</code>

<code>Connection: keep-alive</code>

<code>X-Cache: MISS</code>

<code>第一次請求為MISS,表示沒有命中緩存</code>

<code>X-Cache: HIT from </code><code>192.168</code><code>.</code><code>1.201</code>

<code>可以看到第二次請求命中,可以多請求幾次,以後每次都會是緩存命中</code>

       ②、比如說我們有一個test.html頁面不想緩存,

31

32

33

34

35

<code>sub vcl_recv {</code>

<code>  </code><code>if</code> <code>(req.url ~ </code><code>"^/test.html$"</code><code>){</code>

<code>        </code><code>return</code><code>(pass);</code>

<code> </code><code>}</code>

<code>var</code><code>nish&gt; vcl.load test3 test.vcl</code>

<code>var</code><code>nish&gt; vcl.</code><code>use</code> <code>test3</code>

<code>我們請求test.html</code>

<code>[root@node1 ~]# curl -I http:</code><code>//192.168.1.201/test.html</code>

<code>HTTP/</code><code>1.1</code> <code>404</code> <code>Not Found</code>

<code>Content-Type: text/html; charset=iso-</code><code>8859</code><code>-</code><code>1</code>

<code>Date</code><code>: Wed, </code><code>23</code> <code>Apr </code><code>2014</code> <code>16</code><code>:</code><code>55</code><code>:</code><code>26</code> <code>GMT</code>

<code>X-Varnish: </code><code>1409659726</code>

<code>可以看到第一次沒有命中</code>

<code>Date</code><code>: Wed, </code><code>23</code> <code>Apr </code><code>2014</code> <code>16</code><code>:</code><code>55</code><code>:</code><code>27</code> <code>GMT</code>

<code>X-Varnish: </code><code>1409659727</code>

<code>可以看到第二次也沒有命中,請求多次都不會命中</code>

       ③、為每一種資源定義緩存時長

<code>sub vcl_fetch {</code>

<code> </code><code>if</code> <code>(req.request == </code><code>"GET"</code> <code>&amp;&amp; req.url ~ </code><code>"\.html$"</code><code>){</code>

<code>        </code><code>set</code> <code>beresp.ttl = 3600s;</code>

<code> </code><code>if</code> <code>(req.request == </code><code>"GET"</code> <code>&amp;&amp; req.url ~ </code><code>"\.{jpg|jpeg|png|gif}$"</code><code>){</code>

<code>        </code><code>set</code> <code>beresp.ttl = 86400s;</code>

      ④、修剪緩存對象

       1.緩存内容修剪

       提高緩存命中率的最有效途徑之一是增加緩存對象的生存時間(TTL),但是這也可能會帶來副作用,比如緩存的内容在到達為其指定的有效期之前就已經失效,是以,手動檢查緩存對象的有效性或者重新整理緩存是緩存很有可能稱為伺服器管理者的日常工作之一,相應地,varnish為完成這類的任務提供了三種途徑:HTTP修剪(HTTP purging)、禁用某緩存對象(banning)和強制緩存未命中(forced cache misses)

       這裡需要特殊說明的是,varnish2中的purge()操作在varnish3中被替換成了ban()操作,而varnish3也使用了purge操作,但未其賦予了新功能,且隻能用于vcl_hit或vcl_miss中替換varnish2中重用的set obj.ttl=0s

       在具體執行某清理工作時,需要實作确認如下問題:

       僅需要檢驗一個特定的緩存對象,還是多個

       目的是釋放記憶體空間,還是僅替換緩存的内容

       是不是需要很長世間才能完成内容替換

       這類操作是個日常工作,還是僅此一次的特殊需求

       2.移除單個緩存對象

       purge用于清理緩存中的特定對象及其變種(variants),是以,在有着明确要修剪的緩存對象時可以使用此種方法,http協定的PURGE方法可以實作purge功能,不過,其僅能用于vcl_hit或vcl_miss中,它會釋放記憶體工作并移除指定緩存對象的所有vary變種,并期待下一個針對此内容的用戶端請求道此時重新整理此内容,另外,其一般要與return(restart)一起使用。

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

<code>acl purgers {</code>

<code>        </code><code>"127.0.0.1"</code><code>;</code>

<code>        </code><code>"192.168.1.0"</code><code>/</code><code>24</code><code>;</code>

<code>        </code><code>if</code> <code>(req.request == </code><code>"PURGE"</code><code>) {</code>

<code>                </code><code>if</code> <code>(!client.ip ~ purgers) {</code>

<code>                        </code><code>error </code><code>405</code> <code>"Method not allowed"</code><code>;</code>

<code>                </code><code>}</code>

<code>                </code><code>return</code> <code>(lookup);</code>

<code>sub vcl_hit {</code>

<code>                </code><code>purge;</code>

<code>                </code><code>error </code><code>200</code> <code>"Purged"</code><code>;</code>

<code>sub vcl_miss {</code>

<code>                </code><code>error </code><code>404</code> <code>"Not in cache"</code><code>;</code>

<code>sub vcl_pass {</code>

<code>                </code><code>error </code><code>502</code> <code>"PURGE on a passed object"</code><code>;</code>

<code>說明:一般把這一項應該放在最前面,以免被别的規則比對到</code>

<code>var</code><code>nish&gt; vcl.load test6 test.vcl</code>

<code>var</code><code>nish&gt; vcl.</code><code>use</code> <code>test6</code>

<code>測試</code>

<code>Date</code><code>: Wed, </code><code>23</code> <code>Apr </code><code>2014</code> <code>17</code><code>:</code><code>25</code><code>:</code><code>12</code> <code>GMT</code>

<code>X-Varnish: </code><code>1409659756</code> <code>1409659755</code>

<code>Age: </code><code>1</code>

<code>清除緩存</code>

<code>[root@node1 ~]# curl -X PURGE http:</code><code>//192.168.1.201/index.html</code>

<code>&lt;?xml version=</code><code>"1.0"</code> <code>encoding=</code><code>"utf-8"</code><code>?&gt;</code>

<code>&lt;!DOCTYPE html PUBLIC </code><code>"-//W3C//DTD XHTML 1.0 Strict//EN"</code>

<code> </code><code>"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"</code><code>&gt;</code>

<code>&lt;html&gt;</code>

<code>  </code><code>&lt;head&gt;</code>

<code>    </code><code>&lt;title&gt;</code><code>200</code> <code>Purged&lt;/title&gt;</code>

<code>  </code><code>&lt;/head&gt;</code>

<code>  </code><code>&lt;body&gt;</code>

<code>    </code><code>&lt;h1&gt;Error </code><code>200</code> <code>Purged&lt;/h1&gt;</code>

<code>    </code><code>&lt;p&gt;Purged&lt;/p&gt;</code>

<code>    </code><code>&lt;h3&gt;Guru Meditation:&lt;/h3&gt;</code>

<code>    </code><code>&lt;p&gt;XID: </code><code>1409659762</code><code>&lt;/p&gt;</code>

<code>    </code><code>&lt;hr&gt;</code>

<code>    </code><code>&lt;p&gt;Varnish cache server&lt;/p&gt;</code>

<code>  </code><code>&lt;/body&gt;</code>

<code>&lt;/html&gt;</code>

<code>在請求測試</code>

<code>Date</code><code>: Wed, </code><code>23</code> <code>Apr </code><code>2014</code> <code>17</code><code>:</code><code>27</code><code>:</code><code>35</code> <code>GMT</code>

<code>X-Varnish: </code><code>1409659763</code>

       3.強制緩存為命中

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

       4、Banning

       ban()是一種從已緩存對象中過濾(filter)出某此特定的對象并将其移除的緩存内容重新整理機制,不過,它并不阻止新的内容進入緩存或響應于請求。在Varnish中,ban的實作是指将一個ban添加至ban清單(ban-list)中,這可以通過指令行接口或VCL實作,它們的使用文法是相同的。ban本身就是一個或多個VCL風格的語句,它會在Varnish從緩存哈希(cache hash)中查找某緩存對象時對搜尋的對象進行比較測試,是以,一個ban語句就是類似比對所有“以/downloads開頭的URL”,或“響應首部中包含nginx的對象”。例如:

       ban req.http.host == "wangfeng7399.com" &amp;&amp; req.url ~ "\.gif$"

       定義好的所有ban語句會生成一個ban清單(ban-list),新添加的ban語句會被放置在清單的首部。緩存中的所有對象在響應給用戶端之前都會被ban清單檢查至少一次,檢查完成後将會為每個緩存建立一個指向與其比對的ban語句的指針。Varnish在從緩存中擷取對象時,總是會檢查此緩存對象的指針是否指向了ban清單的首部。如果沒有指向ban清單的首部,其将對使用所有的新添加的ban語句對此緩存對象進行測試,如果沒有任何ban語句能夠比對,則更新ban清單。

       對ban這種實作方式持反對意見有有之,持贊成意見者亦有之。反對意見主要有兩種,一是ban不會釋放記憶體,緩存對象僅在有用戶端通路時被測試一次;二是如果緩存對象曾經被通路到,但卻很少被再次通路時ban清單将會變得非常大。贊成的意見則主要集中在ban可以讓Varnish在恒定的時間内完成向ban清單添加ban的操作,例如在有着數百萬個緩存對象的場景中,添加一個ban也隻需要在恒定的時間内即可完成。其實作方法本處不再詳細說明。

       ⑤、Varnish檢測後端主機的健康狀态

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

       每個後端伺服器目前探測的方法通過.probe進行設定,其結果可由req.backend.healthy變量擷取,也可以通過varnishlog中的Backend_health檢視或varnishadm的debug.health檢視

       .probe中的探測指令常用的有:

          .url:探測後端主機監控狀态時請求的URL,預設為“/”

          .request:探測後端主機健康狀态時所請求内容的詳細格式,定義後,它會替換.url指定的探測方法

          .window:設定在判斷後端伺服器健康狀态是基于最近多少次的探測進行,預設為8

          .threshold:在.window中指定的次數中,至少有多少次是成功的才判定後端主機正健康運作

          .initial:varnish啟動時對後端主機至少需要多少次的成功探測,預設同.threshold

          .expencted_response:期望後端主機響應的狀态碼,預設為200

          .interval:探測請求的發送周期,預設為5秒

          .timeout:每次探測請求的過期時長,預設為2秒

       如果varnish在某時刻沒有任何可用的後端主機,它将嘗試使用緩存對象的“寬容副本”(graced copy),擔任,此時VCL中的各種規則依然有效,是以,更好的辦法是在VCL規則中判斷req.backend.healthy變量顯示後端某主機不可用時,為此後端主機增大req.grace變量的值以設定适當的寬容期限長度。

       ⑥、使用多台後端主機

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

       varnish的dorector支援的挑選方法中比較簡單的有round-robin和random兩種,其中,round-robin類型沒有任何參數,挑選方法為“輪詢”,并在某後端主機故障時不在将其視為挑選對象,random方法随機從可用後端主機中挑選,每一個後端主機都需要一個.wegith參數來指定其權重,同時還可以director級别使用.retires參數來設定查找一個健康後端主機時的嘗試次數

       varnish2.1.0後,random挑選方法又多了兩種變化形式client和hash。client類型的director使用client.identity作為挑選因子,這意味着client.identity相同的請求都将被發送至同一個後端主機,client.identity預設為client.ip,但也可以在VCL中将其修改為所需要的辨別符,類似地,hash類型的director使用hash資料作為挑選因子,這意味着同一個URL的請求都将發往同一個後端主機,其常用于多級緩存的場景中,然而,無論是client還是hash,當期傾向于使用後端主機不可用時将會重新挑選新的後端主機。

       另外還有一中成為fallback的director,用于定義備用伺服器

<code>  </code><code>.host=</code><code>"192.168.1.202"</code><code>;</code>

<code>  </code><code>.port=</code><code>"80"</code><code>;</code>

<code>  </code><code>.probe = {</code>

<code>        </code><code>.url=</code><code>"/index.html"</code><code>;</code>

<code>        </code><code>.interval =2s;</code>

<code>        </code><code>.window =</code><code>5</code><code>;</code>

<code>        </code><code>.threshold=</code><code>2</code>

<code>backend web2 {</code>

<code>  </code><code>.host=</code><code>"192.168.1.203"</code><code>;</code>

<code>定義每個2s檢查後端主機的首頁,如果最近</code><code>5</code><code>次的探測請求中至少有</code><code>2</code><code>次是成功的就判斷此後端主機為正常工作狀态</code>

<code>director websv random {</code>

<code>  </code><code>.retries =</code><code>4</code><code>;</code>

<code>  </code><code>{</code>

<code>     </code><code>.backend = web1;</code>

<code>     </code><code>.weight =</code><code>2</code><code>;</code>

<code>     </code><code>.backend = web2;</code>

<code>     </code><code>.weight =</code><code>4</code><code>;</code>

<code>定義主機組</code>

<code>set</code> <code>req.backend =websv;</code>

<code>調用主機組</code>

<code>[root@node1 ~]# curl http:</code><code>//192.168.1.201/index.html</code>

<code>web1</code>

<code>我們将web1的httpd關掉</code>

<code>web2</code>

       ⑦、後端伺服器的分離

       varnish支援基于URL将後端的伺服器分離  

<code>在vcl_decv中添加如下行</code>

<code>       </code><code>if</code> <code>(req.url ~</code><code>"\.(html)$"</code><code>){</code>

<code>           </code><code>set</code> <code>req.backend = web1;</code>

<code>        </code><code>}</code><code>else</code><code>{</code>

<code>           </code><code>set</code> <code>req.backend = web2;</code>

<code>通過curl通路index.html</code>

本文轉自wangfeng7399 51CTO部落格,原文連結:http://blog.51cto.com/wangfeng7399/1407418,如需轉載請自行聯系原作者

繼續閱讀