一、關于Varnish
1、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等來分析共享內存日志中的信息并能夠以指定的方式進行顯示。
3、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命令完成。
4、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
wget -c http://repo.varnish-cache.org/source/varnish-3.0.1.tar.gz
tar xzvf varnish-3.0.1.tar.gz
cd varnish-3.0.1
./configure --prefix=/usr/local/varnish
make
make install
groupadd varnish
useradd -d /var/lib/varnish -g varnish -s /sbin/nologin varnish
ln -s /usr/local/varnish/sbin/varnishd /usr/sbin/varnishd
啟動varnish:
varnishd -f /usr/local/varnish/etc/varnish/default.vcl -s malloc,1G -g varnish -u varnish -T 127.0.0.1:2000
關閉varnish:
pkill varnish
啟動參數介紹:
-f /usr/local/etc/varnish/default.vcl
這個 –f 選項指定varnishd使用哪個配置文件。
-s malloc,1G
這個 –s 選項用來確定varnish使用的存儲類型和存儲容量,我使用的是malloc類型(malloc是一個C函數,用于分配內存空間), 1G 定義多少內存被malloced,1G = 1gigabyte。
-T 127.0.0.1:2000
Varnish有一個基于文本的管理接口,啟動它的話可以在不停止varnish的情況下來管理varnish。您可以指定管理軟件監聽哪個接口。當然您不能讓全世界的人都能訪問您的varnish管理接口,因為他們可以很輕松的通過訪問varnish管理接口來獲得您的root訪問權限。我推薦只讓它監聽本機端口。如果您的系統里有您不完全信任的用戶,您可以通過防火墻規則來限制他訪問varnish的管理端口。
-a 0.0.0.0:8080
這一句的意思是制定varnish監聽所有IP發給8080端口的http請求,如果在生產環境下,您應該讓varnish監聽80,這也是默認的。
vcl配置文件的介紹請執行如何命令查看:
man /usr/local/varnish/share/man/man7/vcl.7
三、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服務器來說也已經足夠。
5、
九、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;
}
}


