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; #健康的标准 } }
你也可以将多个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; } }
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被重新加载时候调用,作用是通知varnish主进程varnish配置文件开始reload的了,需要刷新内存中的配置了。
sub vcl_init { return (ok); }
在请求开始时调用的。完成该子程序后,请求就被接收并解析了。用于确定是否需要服务请求,怎么服务,如果可用,使用哪个后端。
在recv中,你可以随意的定制任何组合的匹配规则,在定制规则时,只能对请求对象req做判断,条件匹配之后,可以用的处理方式有:
pass:不做任何缓存,直接转发
pipe:当前client的所有请求varnish不再做处理,直接建立一条该cilent与后端server的“专线”。
lookup:直接从缓存中返回数据。
当前client的所有请求varnish不再做处理,直接建立一条该cilent与后端server的“专线”。只能自己调用自己:
sub vcl_pipe { return (pipe); }
不做任何缓存,直接转发,可以调用pass或者restart,一般是pass。
sub vcl_pass { return (pass); }
缓存的hash规则。
sub vcl_hash { hash_data(req.url); if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); } return (hash); }
命中缓存之后的处理手段。可以调pass、deliver
sub vcl_hit { if (obj.ttl <= 0s) { return (pass); } return (deliver); #返回缓存给client。 }
没命中。可以调pass、fetch
sub vcl_miss { return (fetch); #重新去backend获取数据,之后按照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); }
把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); }
当触发了一个错误(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); }
当所有的请求都已经退出完毕时,告诉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); }