Varnish配置應用
來自: http://wangzhijian.blog.51cto.com/6427016/1745993
一、Varnish簡介
Varnish是一款高性能的開源緩存代理服務器。Varnish分為Master(management)進程和Child(worker,也叫cache進程)進程。
Master 進程讀入存儲配置文件,調用合適的存儲類型,然后創建/讀入相應大小的緩存文件,接著 master 初始化管理該存儲空間的結構體,然后 fork 并監控 child 進程。
Child 進程在主線程的初始化的過程中,將前面打開的存儲文件整個 mmap 到內存中,此時創建并初始化空閑結構體,掛到存儲管理結構體,以待分配。
分配緩存的過程:根據所讀到object的大小,創建相應大小的緩存文件。為了讀寫方便,程序會把每個object的大小變為最接近其大小的內存頁面倍數。然后從現有的空閑存儲結構體中查找,找到最合適的大小的空閑存儲塊,分配給它。如果空閑塊沒有用完,就把多余的內存另外組成一個空閑存儲塊,掛到管理結構體上。如果緩存已滿,就根據LRU機制,把最舊的object釋放掉。
釋放緩存的過程:有一個超時線程,檢測緩存中所有object的生存期,如果超初設定的TTL(Time To Live)沒有被訪問,就刪除之,并且釋放相應的結構體及存儲內存。注意釋放時會檢查該存儲內存塊前面或后面的空閑內存塊,如果前面或后面的空閑內存和該釋放內存是連續的,就將它們合并成更大一塊內存。
Management進程主要實現應用新的配置、編譯VCL、監控varnish、初始化varnish以及提供一個命令行接口等。Management進程會每隔幾秒鐘探測一下Child進程以判斷其是否正常運行,如果在指定的時長內未得到Child進程的回應,Management將會重啟此Child進程。
Child 進程分配若干線程進行工作,主要包括一些管理線程和很多worker線程,可分為:
Accept線程:接受請求,將請求掛在overflow隊列上;
Work線程:有多個,負責從overflow隊列上摘除請求,對請求進行處理,直到完成,然后處理下一個請求;
Epoll線程:一個請求處理稱為一個session,在session周期內,處理完請求后,會交給Epoll處理,監聽是否還有事件發生;
Expire線程:對于緩存的object,根據過期時間,組織成二叉堆,該線程周期檢查該堆的根,處理過期的文件,對過期的數據進行刪除或重取操作。
二、Varnish安裝
# yum install epel-releaserpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-4.1.el7.rpm
yum install varnish</pre>
三、Varnish配置
①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命令完成。
②VCL使用
主要有以下幾點:
a. 塊是由花括號分隔,語句以分號結束,使用‘ # ’符號可以添加注釋。
b. VCL 使用指定運算符“=”、比較運算符“==”、邏輯運算符“!,&&,!!”等形式,還支持正則表達式和用“~”進行 ACL 匹配運算。
c. VCL 沒有用戶自己定義的變量,你可以在 backend、request 或 object 上設置變量值,采用 set 關鍵字進行設置。例如 set req.backend_hint = cluster1.backend();
d. 兩個字符串的連接,他們之間沒有任何運算符。例如 set resp.http.X-Cache = "HIT via" + " " + server.hostname;
e. \”字符在 VCL 里沒有特別的含義,這點與其他語言略有不同。
f. VCL 可以使用 set 關鍵字設置任何 HTTP 頭,可以使用 remove 或是 unset 關鍵字移除 HTTP 頭。
g. VCL 有 if/else 的判斷語句,但是沒有循環語句。
③設置Varnish參數
# cat /etc/varnish/varnish.params |grep -v "#" RELOAD_VCL=1 ##重新啟動服務時是否重新讀取VCL并重新編譯 VARNISH_VCL_CONF=/etc/varnish/default.vcl ##默認讀取的VCL文件 VARNISH_LISTEN_PORT=80 ##設置監聽的端口(默認監聽6081端口) VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 ##管理接口監聽的地址 VARNISH_ADMIN_LISTEN_PORT=6082 ##管理接口監聽的端口 VARNISH_SECRET_FILE=/etc/varnish/secret ##使用的密鑰文件 VARNISH_STORAGE="malloc,256M" ##存儲文件的大小 VARNISH_USER=varnish ##varnish默認用戶 VARNISH_GROUP=varnish ##varnish默認組④定義VCL backend
# cp /etc/varnish/default.vcl /etc/varnish/default.vcl.bakcat /etc/varnish/default.vcl |grep -v "#"
vcl 4.0; ##指明varnish版本 backend web1 { ##創建后端主機 .host = "192.168.10.132"; ##注:可使用域名,須添加hosts .port = "80"; }
systemctl start varnish ##啟動服務
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 ##varnish命令操作
200
Varnish Cache CLI 1.0
Linux,3.10.0-229.el7.x86_64,x86_64,-junix,-smalloc,-smalloc,-hcritbit varnish-4.1.0 revision 3041728 Type 'help' for command list. Type 'quit' to close CLI session. help 200
help [<command>] ping [<timestamp>] auth <response> quit banner status start stop vcl.load <configname> <filename> [auto|cold|warm] vcl.inline <configname> <quoted_VCLstring> [auto|cold|warm] vcl.use <configname> vcl.state <configname> [auto|cold|warm] vcl.discard <configname> vcl.list param.show [-l] [<param>] param.set <param> <value> panic.show panic.clear [-z] storage.list vcl.show [-v] <configname> backend.list [-p] [<backend_expression>] backend.set_health <backend_expression> <state> ban <field> <operator> <arg> [&& <field> <oper> <arg> ...] ban.list</pre>
![]()
![]()
⑤定義 VCL 后端的集合 director
VCL 可以把多個 backends 聚合成一個組,這些組被叫做 director,這樣可以增強性能和彈力,當組里一個 backend 掛掉后,可以選擇另一個健康的 backend。VCL 有多種 director,不同的 director 采用不同的算法選擇 backend,主要有以下幾種:
a. The random director
Random director 會根據所設置的權值(weight)來選擇 backend,.retries 參數表示嘗試找到一個 backend 的最大次數,.weight 參數表示權值
b. The round-robin director
Round-robin director 在選擇 backend 時,會采用循環的方式依次選擇。
c. The client director
Client director 根據 client.identity 來選擇 backend,您可以設置 client.identity 的值為 session cookie 來標識 backend。
# vi /etc/varnish/default.vcl vcl 4.0; ##指明varnish版本 backend server1 { ##定義后端服務器1 .host = "192.168.10.132"; .port = "80"; } backend server2 { ##定義后端服務器2 .host = "192.168.10.133"; .port = "80"; } import directors; ##定義directors sub vcl_init { ##定義vcl_init子例程 new cluster1 = directors.round_robin(); ##創建一個輪叫調度的directors cluster1.add_backend(server1); cluster1.add_backend(server2); } sub vcl_recv { ##定義vcl_recv子例程 set req.backend_hint = cluster1.backend(); ##指定后端directors }⑥設置響應是否命中
sub vcl_deliver { ##定義子例程 if (obj.hits > 0) { set resp.http.X-Cache = "HIT via" + " " + server.ip; } else { set resp.http.X-Cache = "MISS via" + " " + server.ip; } ##判斷如果命中就在http響應首部設置X-Cache為HIT,否則 就在http響應首部設置X-Cache為MISS。 }
![]()
![]()
⑦指定某些文件不能查緩存
sub vcl_recv { if (req.url ~ "^/test.html$") { return(pass); } ##定義請求的文件中如果匹配test.html就pass,不查緩存 }
![]()
![]()
⑧進行健康檢查
backend server1 { .host = "192.168.10.132"; .probe = { .url = "/"; ##指定哪個url需要varnish請求 .timeout = 1s; ##指定超時等待時間 .interval = 5s; ##指定檢查時間間隔 .window = 5; ##最多嘗試5次 .threshold = 3; ##至少有3次成功就宣告backend健康 } } backend server2 { .host = "192.168.10.133"; .probe = { .url = "/"; .timeout = 1s; .interval = 5s; .window = 5; .threshold = 3; } }⑨設定緩存時長
sub vcl_backend_response { if (bereq.url ~ "\.(jpg|jpeg|gif|png)$") { set beresp.ttl = 2h; } ##如果url是以圖片格式結尾的緩存2小時 if (bereq.url ~ "\.(html|css|js|jsp)$") { set beresp.ttl = 30m; } ##如果url是以html|css|js|jsp結尾的緩存30分鐘 }⑩優雅模式(Grace mode)
sub vcl_backend_response { set beresp.grace = 2m; } ##指定保持緩存對象超出TTL值2分鐘(就是該過期的緩存不讓它過期) sub vcl_hit { if (obj.ttl >= 0s) { // A pure unadultered hit, deliver it return (deliver); ##如果TTL值大于等于0秒,將已獲取的請求內容直接##發送給客戶端
} if (obj.ttl + obj.grace > 0s) { // Object is in grace, deliver it // Automatically triggers a background fetch return (deliver); } ##如果TTL值加上優雅模式設定值大于0秒,將已獲取
##的請求內容直接發送給客戶端
// fetch & deliver once we get the result return (fetch); ##否則從后端獲取請求的內容 }</pre>
四、VCL相關
注:varnish4.0后版本所使用參數和以前版本有所改變,詳見:
https://www.varnish-cache.org/docs/4.0/whats-new/index.html
https://www.varnish-cache.org/docs/4.1/whats-new/index.html
①VCL主要內置函數
a. vcl_recv 函數
用于接收和處理請求。當請求到達并成功接收后被調用,通過判斷請求的數據來決定如何處理請求。例如如何響應、怎么響應、使用哪個后端服務器等。
此函數一般以如下關鍵字結束:
1). pass:表示進入 pass 模式,把請求控制權交給 vcl_pass 函數;
2). pipe:表示進入 pipe 模式,把請求控制權交給 vcl_pipe 函數;
3). lookup:表示進入 lookup 模式,把請求控制權交給 lookup 指令處理,在緩存中查找被請求的對象,并且根據查找的結果把控制權交給函數 vcl_hit 或函數 vcl_miss;
4). synth code [reason]:表示返回“code”給客戶端,并放棄處理該請求。“code”是錯誤標識,例如 200 和 405 等。“reason”是錯誤提示信息。
b. vcl_pipe 函數
此函數在進入 pipe 模式時被調用,用于將請求直接傳遞至后端主機,在請求和返回的內容沒有改變的情況下,將不變的內容返回給客戶端,直到這個連接被關閉。
此函數一般以 如下關鍵字結束:
1). synth code [reason]
2).pipe
c. vcl_pass 函數
此函數在進入 pass 模式時被調用,用于將請求直接傳遞至后端主機。后端主機在應答數據后將應答數據發送給客戶端,但不進行任何緩存,在當前連接下每次都返回最新的內容。
此函數一般以如下關鍵字結束:
1). synth code [reason];
2). pass;
3). restart 重新啟動流程,增加啟動次數,如果重新啟動次數高于 max_restarts 發出一個錯誤警告
d. vcl_hash函數
如果想把一個數據添加到 hash 上時,調用此函數。
此函數一般以 Hash 該關鍵字結束。
e. vcl_hit 函數
在執行 lookup 指令后,在緩存中找到請求的內容后將自動調用該函數。
此函數一般以如下關鍵字結束:
1).deliver:表示將找到的內容發送給客戶端,并把控制權交給函數 vcl_deliver;
2).synth code [reason];
3).pass;
4).restart 重新啟動流程,增加啟動次數,如果重新啟動次數高于 max_restarts 發出一個錯誤警告
f. vcl_miss 函數
在執行 lookup 指令后,在緩存中沒有找到請求的內容時自動調用該方法。此函數可用于判斷是否需要從后端服務器獲取內容。
此函數一般以如下關鍵字結束:
1).fetch:表示從后端獲取請求的內容,并把控制權交給 vcl_backend_response 函數;
2).synth code [reason];
3).pass。
g. vcl_backend_response 函數
在后端主機更新緩存并且獲取內容后調用該方法,接著,通過判斷獲取的內容來決定是將內容放入緩存,還是直接返回給客戶端。
此函數一般以如下關鍵字結束:
1).synth code [reason]
2).pass
3).deliver
4).esi
5).restart
h.vcl_deliver函數
將在緩存中找到請求的內容發送給客戶端前調用此方法。
此函數一般以如下關鍵字結束:
1).synth code [reason]
2).deliver
3).restart
i.vcl_backend_error函數
出現錯誤時調用此函數。
此函數一般以如下關鍵字結束:
1).deliver
2).restart
②varnish處理HTTP請求的過程
![]()
Varnish 處理 HTTP 請求的過程如下:
1. Receive狀態(vd_recv):請求處理的入口狀態,根據VCL規則判斷該請求應該pass(vd_pass)或是pipe(vd_pipe),還是進入lookup(本地查詢)
2. Lookup狀態:進入該狀態后,會在hash表中查詢數據,若找到,則進入hit狀態(vcl_hit),否則進入miss狀態(vcl_miss)
3. Pass狀態(vcl_pass):在此狀態下,會直接進入后端請求,即進入fetch狀態(vcl_backend_response)
4.Backend_response狀態:由原來的vcl_fetch獨立為vcl_backend_fetch和vcl_backend_response2個函數,在該狀態下,對請求進行后端獲取,發送請求,獲得數據,并根據設置進行本地存儲
5. Deliver狀態(vcl_deliver):將獲取到的數據發送給客戶端,然后完成本次請求
內置函數(也叫子例程):
1.vcl_recv:用于接收和處理請求,當請求到達并成功接收后被調用,通過判斷請求的數據
來決定如何處理請求;
2.vcl_pipe:此函數在進入pipe模式時被調用,用于將請求直接傳遞至后端主機,并將后端響應原樣返回客戶端;
3.vcl_pass:此函數在進入pass模式時被調用,用于將請求直接傳遞至后端主機,但后端主機的響應并不緩存直接返回客戶端;
4.vcl_hit:在執行lookup指令后,在緩存中找到請求的內容后將自動調用該函數;
5.vcl_miss:在執行lookup 指令后,在緩存中沒有找到請求的內容時 自動調用該方法,此函數可用于判斷是否需要從后端服務器獲取內容;
6.vcl_hash:在vcl_recv調用后為請求創建一個hash值時,調用此函數,此hash值將作為varnish中搜索緩存對象的key;
7.vcl_purge:pruge操作執行后調用此函數,可用于構建一個響應;
8.vcl_deliver:將在緩存中找到請求的內容發送給客戶端前調用此方法;
9.vcl_backend_fetch:向后端主機發送請求前,調用此函數,可修改發往后端的請求;
10.vcl_backend_response:獲得后端主機的響應后,可調用此函數;
11.vcl_backend_error:當從后端主機獲取源文件失敗時,調用此函數;
12.vcl_init:VCL加載時調用此函數,經常用于初始化varnish模塊(VMODs);
13.vcl_fini:當所有請求都離開當前VCL,且當前VCL被棄用時,調用此函數,經常用于清理varnish模塊.
詳情: https://www.varnish-cache.org/docs/4.0/users-guide/vcl-built-in-subs.html
③varnish變量解釋
![]()
VCL 內置的公共變量(變量也叫object)可以用在不同的 VCL 函數中:
req:The request object,請求到達時可用的變量
bereq:The backend request object,向后端主機請求時可用的變量
beresp:The backend response object,從后端主機獲取內容時可用的變量
resp:The HTTP response object,對客戶端響應時可用的變量
obj:存儲在內存中時對象屬性相關的可用的變量
a. 請求到達時可用公共變量
req.backend_hint: 指定對應的后端主機
server.ip: 表示服務器 IP
client.ip: 表示客戶端 IP
req.method: 只是請求的類型,例如 GET、HEAD 等
req.url: 指定請求的地址
req.proto: 表示客戶端發起請求的 HTTP 協議版本
req.http.header: 表示對應請求中的 HTTP 頭部信息
req.restarts: 表示重啟次數,默認最大值為4
b. 向后端主機請求時可用公共變量
beresp.requset: 指定請求類型,例如 GET、HEAD 等
beresp.url: 表示請求地址
beresp.proto: 表示客戶端發起請求的 HTTP 協議版本
beresp.http.header: 表示對應請求中 HTTP 頭部信息
beresp.ttl: 表示緩存的生存周期,cache 保留時間(單位秒)
c. 后端主機獲取內容時可使用公共變量
obj.status: 返回內容的請求狀態碼,例如 200、302、504 等
obj.cacheable: 返回的內容是否可以緩存
obj.valid: 是否有效的 HTTP 請求
obj.response: 返回內容的請求狀態信息
obj.proto: 返回內容的 HTTP 版本
obj.ttl: 返回內容的生存周期,也就是緩存時間(單位秒)
obj.lastuse: 返回上一次請求到現在的時間間隔(單位秒)
d.對客戶端相應時可使用公共變量
resp.status: 返回給客戶端的 HTTP 代碼狀態
resp.proto: 返回給客戶端的 HTTP 協議版本
resp.http.header: 返回給客戶端的 HTTP 頭部消息
resp.response: 返回給客戶端的 HTTP 頭部狀態
</div>