varnish3.0運維-vcl說明
描述
VCL是專門爲了varnish定製的特殊範圍語言。
當varnish配置文件被重新加載時,varnish的管理進程會依靠vcl重新編譯配置文件,並且會將編譯結果存放在一塊內存中。
語法
VCL語法其實比較簡單,規則類似c、perl;在正則表達式方面,VCL不會讓你失望。在VCL中,已經定義好了幾個函數:
deliver、error、fetch、hash、hit_for_pass、lookup、ok、pass、pipe、restart。下面會說到。
咱們按照Varnish配置文件從上往下依次的介紹。
後端聲明
後端server定義在以關鍵字backend開始的對象中:
backend www { .host = "www.example.com"; .port = "http"; }
當需要調用backend時,你可以寫些個自己的location規則:
if (req.http.host ~ "(?i)^(www.)?example.com$") { set req.backend = www; }
當然了,和其他反向代理一樣,varnish也支持後端健康監測:
backend www { .host = "192.168.122.100"; .port = "80"; .probe = { .url = "/monitor.html"; #健康監測的監控地址 .timeout = 3s; #first_byte的超時時間 .interval = 5s; #檢測間隔時間 .window = 5; #每次檢測創建5個確認點 .threshold =3; #有3個確認點ok,就認爲該server ok .expected_response = 200; #健康的標準 } }
Directors
你也可以將多個backend放在一個director中:
director b2 random { .retries = 5; { // We can refer to named backends .backend = b1; .weight = 7; } { // Or define them inline .backend = { .host = "fs2"; } .weight = 3; } }
Acls
Varnish在做acl時,和nginx類似:
acl local { "localhost"; // myself "192.0.2.0"/24; // and everyone on the local network ! "192.0.2.23"; // except for the dialin router }
定義好acl之後,可以這麼調用:
if (client.ip ~ local) { return (pipe); }
函數、子程序
vcl_init
當vcl被重新加載時候調用,作用是通知varnish主進程varnish配置文件開始reload的了,需要刷新內存中的配置了。
sub vcl_init { return (ok); }
vcl_recv
在請求開始時調用的。完成該子程序後,請求就被接收並解析了。用於確定是否需要服務請求,怎麼服務,如果可用,使用哪個後端。
在recv中,你可以隨意的定製任何組合的匹配規則,在定製規則時,只能對請求對象req做判斷,條件匹配之後,可以用的處理方式有:
pass:不做任何緩存,直接轉發
pipe:當前client的所有請求varnish不再做處理,直接建立一條該cilent與後端server的“專線”。
lookup:直接從緩存中返回數據。
vcl_pipe
當前client的所有請求varnish不再做處理,直接建立一條該cilent與後端server的“專線”。只能自己調用自己:
sub vcl_pipe { return (pipe); }
vcl_pass
不做任何緩存,直接轉發,可以調用pass或者restart,一般是pass。
sub vcl_pass { return (pass); }
vcl_hash
緩存的hash規則。
sub vcl_hash { hash_data(req.url); if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); } return (hash); }
vcl_hit
命中緩存之後的處理手段。可以調pass、deliver
sub vcl_hit { if (obj.ttl <= 0s) { return (pass); } return (deliver); #返回緩存給client。 }
vcl_miss
沒命中。可以調pass、fetch
sub vcl_miss { return (fetch); #重新去backend獲取數據,之後按照vcl_fetch規則走 }
vcl_fetch
vcl_fetch是在文檔從後端被成功接收後調用的。通常用於調整響應頭信息,觸發ESI處理,萬一請求失敗就換個後端服務器。在vcl_fecth中,你還可以使用請求對象req。還有個後端響應對象beresp。Beresp包含了後端的HTTP頭信息。可以調用deliver、hit_for_pass、restart
sub vcl_fetch { if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*") { /* * Mark as "Hit-For-Pass" for the next 2 minutes */ set beresp.ttl = 120 s; return (hit_for_pass); #類似與pass,但是隻有vcl_fetch可以用。不像pass,hit_for_pass將在緩存中創建一個hitforpass對象。類似個pass的標記。 } return (deliver); }
vcl_deliver
把cache返回client之前的處理過程。
sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT from cache."; #打個標記 set resp.http.X-Cache-Hits = obj.hits; #計數器 } else { set resp.http.X-Cache = "MISS from cache."; #打個標記 } return (deliver); }
vcl_error
當觸發了一個錯誤(400、503等等)或是某些未知的錯誤。
sub vcl_error { set obj.http.Content-Type = "text/html; charset=utf-8"; synthetic {" <html> <head> <title>Page Wrong.</title> <style> body { background: #efefef; text-align: center; color: white; font-family: Trebuchet MS, sans-serif; } #page { width: 500px;margin: 100px auto 0; padding: 30px; background: #888888; border-radius: 14px; -moz-border-radius: 14px; -webkit-border-radius: 14px;border: 0} a,a:link,a:visited{color: #cccccc;} .error {color: #222222} .commany {color: #868686} </style> </head> <body> <div id="page"> <h1>Page Unavailable</h1> <p>The page you requested is temporarily unavailable.</p> <div>(Error "} + obj.status + " " + obj.response + {")</div> </div> <div>DangDang Cache Server</div> </body> </html> "}; return (deliver); }
vcl_fini
當所有的請求都已經退出完畢時,告訴varnish,可以清空內存數據了。
sub vcl_fini { return (ok); }
對象
在VCL中,有三種重要的數據結構。請求:來自客戶端;響應:來自後端服務器;對象:存儲在緩存中。在VCL中你應該知道以下結構:
req:請求對象。當varnish接受了請求,req就會創建並生產。許多在vcl_recv中要做的工作都需要用到req。
beresp:後端響應對象。包含了從後端返回的對象的頭信息。vcl_fetch中,你會使用beresp對象。
obj:緩存了的對象。大多數是駐留在內存中的只讀對象。obj.ttl是可以寫的,剩下的都是隻讀的。
配置示例
# Default backend definition. Set this to point to your content # server. backend dztree { .host = "10.64.5.141"; .port = "80"; .probe = { .url = "/"; .timeout = 3s; .interval = 5s; .threshold =8; } } backend zabbix { .host = "172.16.224.23"; .port = "8801"; .probe = { .url = "/"; .timeout = 3s; .interval = 5s; .threshold =8; } } backend zabbix_self { .host = "127.0.0.1"; .port = "8080"; } sub vcl_recv { #rewrite. if (req.url ~ "^/dztree") { set req.backend = dztree; } elseif (req.url ~ "^/zabbix") { set req.backend = zabbix; } else { set req.backend = zabbix_self; } #client ip. if (req.restarts == 0) { if (req.http.x-forwarded-for) { set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip; } else { set req.http.X-Forwarded-For = client.ip; } } #Use anonymous, cached pages if all backends are down. if (!req.backend.healthy) { unset req.http.Cookie; } #When backend is down, then cache can be use in grace time. set req.grace = 10m; #do not cache these paths. if (req.url ~ "^/admin") { return (pass); } if (req.request != "GET" && req.request != "HEAD" && req.request != "PUT" && req.request != "POST" && req.request != "TRACE" && req.request != "OPTIONS" && req.request != "DELETE") { /* Non-RFC2616 or CONNECT which is weird. */ return (pipe); } if (req.request != "GET" && req.request != "HEAD") { /* We only deal with GET and HEAD by default */ return (pass); } if (req.http.Authorization || req.http.Cookie) { /* Not cacheable by default */ return (pass); } return (lookup); } sub vcl_pipe { return (pipe); } sub vcl_pass { return (pass); } sub vcl_hash { hash_data(req.url); if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); } return (hash); } sub vcl_hit { if (obj.ttl <= 0s) { return (pass); } return (deliver); } sub vcl_miss { return (fetch); } sub vcl_fetch { if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*") { /* * Mark as "Hit-For-Pass" for the next 2 minutes */ set beresp.ttl = 120 s; return (hit_for_pass); } return (deliver); } sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT from cache."; set resp.http.X-Cache-Hits = obj.hits; } else { set resp.http.X-Cache = "MISS from cache."; } return (deliver); } sub vcl_error { set obj.http.Content-Type = "text/html; charset=utf-8"; synthetic {" <html> <head> <title>Page Wrong.</title> <style> body { background: #efefef; text-align: center; color: white; font-family: Trebuchet MS, sans-serif; } #page { width: 500px;margin: 100px auto 0; padding: 30px; background: #888888; border-radius: 14px; -moz-border-radius: 14px; -webkit-border-radius: 14px;border: 0} a,a:link,a:visited{color: #cccccc;} .error {color: #222222} .commany {color: #868686} </style> </head> <body> <div id="page"> <h1>Page Unavailable</h1> <p>The page you requested is temporarily unavailable.</p> <div>(Error "} + obj.status + " " + obj.response + {")</div> </div> <div>DangDang Cache Server</div> </body> </html> "}; return (deliver); } sub vcl_init { return (ok); } sub vcl_fini { return (ok); }