Varnish

一、關於Varnish


1、Varnish簡介

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。


2、Varnish系統架構


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


Management進程主要實現應用新的配置、編譯VCL、監控varnish、初始化varnish以及提供一個命令行接口等。Management進程會每隔幾秒鐘探測一下Child進程以判斷其是否正常運行,如果在指定的時長內未得到Child進程的迴應,Management將會重啓此Child進程。


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

Acceptor線程:接收新的連接請求並響應;

Worker線程:child進程會爲每個會話啓動一個worker線程,因此,在高併發的場景中可能會出現數百個worker線程甚至更多;

Expiry線程:從緩存中清理過期內容;


Varnish依賴“工作區(workspace)”以降低線程在申請或修改內存時出現競爭的可能性。在varnish內部有多種不同的工作區,其中最關鍵的當屬用於管理會話數據的session工作區。


2、Varnish日誌


爲了與系統的其它部分進行交互,Child進程使用了可以通過文件系統接口進行訪問的共享內存日誌(shared memory log),因此,如果某線程需要記錄信息,其僅需要持有一個鎖,而後向共享內存中的某內存區域寫入數據,再釋放持有的鎖即可。而爲了減少競爭,每個worker線程都使用了日誌數據緩存。


共享內存日誌大小一般爲90M,其分爲兩部分,前一部分爲計數器,後半部分爲客戶端請求的數據。varnish提供了多個不同的工具如varnishlog、varnishncsa或varnishstat等來分析共享內存日誌中的信息並能夠以指定的方式進行顯示。


4、VCL


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選項指定。後端存儲的類型包括:

(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官網[https://www.varnish-cache.org/installation/redhat]

Varnish 4.0:

If you are on RHEL 6 or a compatible distribution, use:

  1. rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-4.0.el6.rpm

  2. yum install varnish

For RHEL 7 and compatible distributions, use:

  1. rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-4.0.el7.rpm

  2. yum install varnish

Varnish 3.0:

If you are on RHEL 5 or a compatible distribution, use:

  1. rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-3.0.el5.rpm

  2. yum install varnish

For RHEL 6 and compatible distributions, use:

  1. rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-3.0.el6.rpm

  2. yum install varnish



三、HTTP協議與Varnish


1、緩存相關的HTTP首部


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

(1)Expires:用於指定某web對象的過期日期/時間,通常爲GMT格式;一般不應該將此設定的未來過長的時間,一年的長度對大多場景來說足矣;其常用於爲純靜態內容如JavaScripts樣式表或圖片指定緩存週期;

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

(3)Etag:響應首部,用於在響應報文中爲某web資源定義版本標識符;

(4)Last-Mofified:響應首部,用於迴應客戶端關於Last-Modified-Since或If-None-Match首部的請求,以通知客戶端其請求的web對象最近的修改時間;

(5)If-Modified-Since:條件式請求首部,如果在此首部指定的時間後其請求的web內容發生了更改,則服務器響應更改後的內容,否則,則響應304(not modified);

(6)If-None-Match:條件式請求首部;web服務器爲某web內容定義了Etag首部,客戶端請求時能獲取並保存這個首部的值(即標籤);而後在後續的請求中會通過If-None-Match首部附加其認可的標籤列表並讓服務器端檢驗其原始內容是否有可以與此列表中的某標籤匹配的標籤;如果有,則響應304,否則,則返回原始內容;

(7)Vary:響應首部,原始服務器根據請求來源的不同響應的可能會有所不同的首部,最常用的是Vary: Accept-Encoding,用於通知緩存機制其內容看起來可能不同於用戶請求時Accept-Encoding-header首部標識的編碼格式;

(8)Age:緩存服務器可以發送的一個額外的響應首部,用於指定響應的有效期限;瀏覽器通常根據此首部決定內容的緩存時長;如果響應報文首部還使用了max-age指令,那麼緩存的有效時長爲“max-age減去Age”的結果;


四、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的內置函數


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參數則用於限定最大重啓次數。


4、vcl_recv


vcl_recv是在Varnish完成對請求報文的解碼爲基本數據結構後第一個要執行的子例程,它通常有四個主要用途:

(1)修改客戶端數據以減少緩存對象差異性;比如刪除URL中的www.等字符;

(2)基於客戶端數據選用緩存策略;比如僅緩存特定的URL請求、不緩存POST請求等;

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

(4)挑選合適的後端Web服務器;


可以使用下面的終止語句,即通過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進行處理,並由其做出相應的處理決策。


下面是一個自定義的使用示例:


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服務器可以基於此完成不同類型的響應,以提高用戶體驗。


5、vcl_fetch


如前面所述,相對於vcl_recv是根據客戶端的請求作出緩存決策來說,vcl_fetch則是根據服務器端的響應作出緩存決策。在任何VCL狀態引擎中返回的pass操作都將由vcl_fetch進行後續處理。vcl_fetch中有許多可用的內置變量,比如最常用的用於定義某對象緩存時長的beresp.ttl變量。通過return()返回給arnish的操作指示有:

(1)deliver:緩存此對象,並將其發送給客戶端(經由vcl_deliver);

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

(3)restart:重啓整個VCL,並增加重啓計數;超出max_restarts限定的最大重啓次數後將會返回錯誤信息;

(4)error code [reason]:返回指定的錯誤代碼給客戶端並丟棄此請求;


默認的vcl_fetch放棄了緩存任何使用了Set-Cookie首部的響應。


五、修剪緩存對象


1、緩存內容修剪


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


這裏需要特殊說明的是,Varnish 2中的purge()操作在Varnish 3中被替換爲了ban()操作,而Varnish 3也使用了purge操作,但爲其賦予了新的功能,且只能用於vcl_hit或vcl_miss中替換Varnish 2中常用的set obj.ttl=0s。




在具體執行某清理工作時,需要事先確定如下問題:

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

(2)目的是釋放內存空間,還是僅替換緩存的內容?

(3)是不是需要很長時間才能完成內容替換?

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


2、移除單個緩存對象


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


acl purgers {

"127.0.0.1";

"192.168.0.0"/24;

}

sub vcl_recv {

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

if (!client.ip ~ purgers) {

error 405 "Method not allowed";

}

return (lookup);

}

}

sub vcl_hit {

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

purge;

error 200 "Purged";

}

}

sub vcl_miss {

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

purge;

error 404 "Not in cache";

}

}

sub vcl_pass {

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

error 502 "PURGE on a passed object";

}

}


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

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


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 == "magedu.com" && 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查看。


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變量的值以設定適用的寬容期限長度。


七、Varnish使用多臺後端主機


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


backend web1 {

.host = "backweb1.magedu.com";

.port = "80";

}


director webservers random {

  .retries = 5;

  {

    .backend = web1;

    .weight  = 2;

  }

  {

    .backend  = {

      .host = "backweb2.magedu.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默認爲cliet.ip,但也可以在VCL中將其修改爲所需要的標識符。類似地,hash類型的director使用hash數據作爲挑選因子,這意味着對同一個URL的請求將被髮往同一個後端主機,其常用於多級緩存的場景中。然而,無論是client還hash,當其傾向於使用後端主機不可用時將會重新挑選新的後端其機。


八、varnish管理進階


1、可調參數


Varnish有許多參數,雖然大多數場景中這些參數的默認值都可以工作得很好,然而特定的工作場景中要想有着更好的性能的表現,則需要調整某些參數。可以在管理接口中使用param.show命令查看這些參數,而使用param.set則能修改這些參數的值。然而,在命令行接口中進行的修改不會保存至任何位置,因此,重啓varnish後這些設定會消失。此時,可以通過啓動腳本使用-p選項在varnishd啓動時爲其設定參數的值。然而,除非特別需要對其進行修改,保持這些參數爲默認值可以有效降低管理複雜度。


2、共享內存日誌


共享內存日誌(shared memory log)通常被簡稱爲shm-log,它用於記錄日誌相關的數據,大小爲80M。varnish以輪轉(round-robin)的方式使用其存儲空間。一般不需要對shm-log做出更多的設定,但應該避免其產生I/O,這可以使用tmpfs實現,其方法爲在/etc/fstab中設定一個掛載至/var/lib/varnish目錄(或其它自定義的位置)臨時文件系統即可。


3、線程模型(Trheading model)


varnish的child進程由多種不同的線程組成,分別用於完成不同的工作。例如:

cache-worker線程:每連接一個,用於處理請求;

cache-main線程:全局只有一個,用於啓動cache;

ban lurker線程:一個,用於清理bans;

acceptor線程:一個,用於接收新的連接請求;

epoll/kqueue線程:數量可配置,默認爲2,用於管理線程池;

expire線程:一個,用於移除老化的內容;

backend poll線程:每個後端服務器一個,用於檢測後端服務器的健康狀況;


在配置varnish時,一般只需爲關注cache-worker線程,而且也只能配置其線程池的數量,而除此之外的其它均非可配置參數。與此同時,線程池的數量也只能在流量較大的場景下才需要增加,而且經驗表明其多於2個對提升性能並無益處。


4、線程相關的參數(Threading parameters)


varnish爲每個連接使用一個線程,因此,其worker線程的最大數決定了varnish的併發響應能力。下面是線程池相關的各參數及其配置:


thread_pool_add_delay      2 [milliseconds]

thread_pool_add_threshold  2 [requests]

thread_pool_fail_delay     200 [milliseconds]

thread_pool_max            500 [threads]

thread_pool_min            5 [threads]

thread_pool_purge_delay    1000 [milliseconds]

thread_pool_stack          65536 [bytes]

thread_pool_timeout        120 [seconds]

thread_pool_workspace      16384 [bytes]

thread_pools               2 [pools]

thread_stats_rate          10 [requests]


其中最關鍵的當屬thread_pool_max和thread_pool_min,它們分別用於定義每個線程池中的最大線程數和最少線程數。因此,在某個時刻,至少有thread_pool_min*thread_pools個worker線程在運行,但至多不能超出thread_pool_max*thread_pools個。根據需要,這兩個參數的數量可以進行調整,varnishstat命令的n_wrk_queued可以顯示當前varnish的線程數量是否足夠,如果隊列中始終有不少的線程等待運行,則可以適當調大thread_pool_max參數的值。但一般建議每臺varnish服務器上最多運行的worker線程數不要超出5000個。



當某連接請求到達時,varnish選擇一個線程池負責處理此請求。而如果此線程池中的線程數量已經達到最大值,新的請求將會被放置於隊列中或被直接丟棄。默認線程池的數量爲2,這對最繁忙的varnish服務器來說也已經足夠。


九、Varnish的命令行工具


1、varnishadm命令


命令語法:varnishadm [-t timeout] [-S secret_file] [-T address:port] [-n name] [command [...]]


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

-n name —— 連接至名稱爲“name”的實例;

-T address:port —— 連接至指定套接字上的實例;


其運行模式有兩種,當不在命令行中給出要執行的"command"時,其將進入交互式模式;否則,varnishadm將執行指定的"command"並退出。要查看本地啓用的緩存,可使用如下命令進行。

# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 storage.list


sub vcl_deliver {


  if (obj.hits > 0) {

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

  } else {

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

  }

}


sub vcl_deliver {

        if (obj.hits > 0) {

                set resp.http.X-Cache = "HIT via" + " " + server.hostname;

        } else {

                set resp.http.X-Cache = "MISS via" + " " + server.hostname;

        }

}




sub vcl_recv {

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

                return(pass);

        }

}


sub vcl_fetch {


        if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") {

                set beresp.ttl = 3600s;

        }

}



sub vcl_fetch {

if (beresp.http.cache-control !~ "s-maxage") {

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

set beresp.ttl = 30s;

unset beresp.http.Set-Cookie;

}

if (req.url ~ "\.html(\?|$)") {

set beresp.ttl = 10s;

unset beresp.http.Set-Cookie;

}

} else {

if (beresp.ttl > 0s) {

unset beresp.http.Set-Cookie;

}

}

}



sub vcl_error {

synthetic "<html><body><!-- Something was wrong! --></body></html>";

set obj.status = 200;

return (deliver);

}


1.1, 

request

response




Cache-Control   = "Cache-Control" ":" 1#cache-directive

    cache-directive = cache-request-directive

         | cache-response-directive

    cache-request-directive =

           "no-cache"                          

         | "no-store" (backup)                          

         | "max-age" "=" delta-seconds         

         | "max-stale" [ "=" delta-seconds ]  

         | "min-fresh" "=" delta-seconds      

         | "no-transform"                      

         | "only-if-cached"                   

         | cache-extension                   

     cache-response-directive =

           "public"                               

         | "private" [ "=" <"> 1#field-name <"> ] 

         | "no-cache" [ "=" <"> 1#field-name <"> ]

         | "no-store"                            

         | "no-transform"                         

         | "must-revalidate"                     

         | "proxy-revalidate"                    

         | "max-age" "=" delta-seconds            

         | "s-maxage" "=" delta-seconds           

         | cache-extension                        







sub vcl_deliver {

        set resp.http.X-Age = resp.http.Age;

        unset resp.http.Age;


        if (obj.hits > 0) {

                set resp.http.X-Cache = “HIT”;

        } else {

                set resp.http.X-Cache = “MISS”;

        }

}


acl purgers {

        “127.0.0.1”;

        “192.168.0.0”/24;

}


sub vcl_recv {

        if (req.request == “PURGE”) {

                if (!client.ip ~ purgers) {

                        error 405 “Method not allowed”;

                }

                return (lookup);

        }

}


sub vcl_hit {

        if (req.request == “PURGE”) {

                purge;

                error 200 “Purged”;

        }

}

sub vcl_miss {

        if (req.request == “PURGE”) { 

                purge;

                error 404 “Not in cache”;

        }

}

sub vcl_pass {

        if (req.request == “PURGE”) {

                error 502 “PURGE on a passed object”;

        }

}




sed /pattern/string/g


在請求的時候使用curl -H PURGE URL




regsub(str, regex, sub)

regsub($req.url, www\.maged\.com, www.mageedu.com)



purge: 讓緩存失效的;

ban()


URL rewrite


VCL - functions

  regsub(str, regex, sub)

  regsuball(str, regex, sub)

  ban_url(regex)

  ban(expression)

  purge;

  return(restart)

  return()

  hash_data()





子例程


vcl_recv {


}


cookie


# Drop any cookies sent to Wordpress.

sub vcl_recv {

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

unset req.http.cookie;

}

}

# Drop any cookies Wordpress tries to send back to the client.

sub vcl_fetch {

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

unset beresp.http.set-cookie;

}

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章