1.Varnish是一个HTTP加速缓存代理,性能优势取代了之前squid的缓存机制,4台varnish等于12台的squid,它性能稳定,效率高,目前很多电商企业都是利用Varnish达到较高的读取速度。
Varnish的工作原理
当客户端第一次访问的时候,它直接通过数据库读取的目标,并保存目标在自己的缓存空间里,当第二次访问的时候,即可快速的返回给客户,如果没有访问到数据,直接到到后端服务器(nginx)获取,并返回给客户,这样的机制就达到了对数据库的减压效果,减速数据库的读写压力。当它缓存空间满的话,会自动清除旧的数据,释放缓存的过程是这样的:有一个超时线程,检测缓存中所有 object 的生存期,如果超初设定的 TTL(Time To Live)没有被访问,就删除之,并且释放相应的结构体及存储内存。
2.Varnish配置文件配置
vcl 4.0; import directors; import std; backend web01 { .host = "127.0.0.1"; # <span style="line-height: 22.3872px; font-family: SimSun;">指定后端主机的IP地址或者域名</span> .port = "8080"; # 指定后端主机的服务端口 .probe = { .url = "/"; # 哪个 url 需要 varnish 请求 .interval = 10s; # 检查的时间间隔 .timeout = 2s; # 等待多长时间超时 .window = 3; # 维持 3 个 sliding window 的结果 .threshold = 3; # 至少有三次 window 是成功的,就宣告 backend 健康#director当后端服务器宕机的时候,能给健康的主机发送请求,如果两台都宕了,varnish也照常使用 director example_director round-robin { { .backend = server1; } # server2 { .backend = server2; } } } } backend web02 { .host = "127.0.0.1"; .port = "8081"; .probe = { .url = "/"; .interval = 10s; .timeout = 2s; .window = 3; .threshold = 3; } } #director当后端服务器宕机的时候,能给健康的主机发送请求,如果两台都宕了,varnish也照常使用 director example_director round-robin { { .backend = server1; } # server2 { .backend = server2; } }
#只允许本机控制,限制其余网段不能控制。
acl manager { "localhost"; ! "192.168.2.0"/24; ! "192.168.12.0"/24; ! "192.168.2.200"; } sub vcl_init { new www = directors.round_robin(); www.add_backend(web01); www.add_backend(web02); } sub vcl_recv { if (req.http.host !~ "(?).xjh.com$") { set req.backend_hint = www.backend(); } if (req.http.host !~ "(?).xjh.com$") { return (synth(403,"Forbidden")); } if (req.http.Upgrade ~ "(?i)websocket") { return (pipe); } #if (req.http.host ~ "xjh.com") { # set req.http.host = "www.qa.xjh.com"; #} #if (req.http.User-Agent ~ "(?i)iphone" { #set req.http.X-UA-Device = "mobile-iphone"; #} if (req.method == "PURGE" || (req.http.Pragma ~ "no-cache" && client.ip ~ manager)) { if (client.ip !~ manager) { return(synth(403, "Access denied.")); } ban("req.http.host == " + req.http.host +" && req.url == " + req.url); return(synth(200, "Ban added")); } if (req.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") { std.log("dynamic URI - url=" + req.url); return (pass); } if (req.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") { std.log("static URI - url=" + req.url); unset req.http.cookie; return (hash); } if (req.method == "GET" && req.method == "HEAD") { std.log("METHOD URI - url=" + req.url); return (hash); } 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; } } if (req.method != "GET" && req.method != "HEAD" && req.method != "PUT" && req.method != "POST" && req.method != "TRACE" && req.method != "OPTIONS" && req.method != "PATCH" && req.method != "DELETE") { return (pipe); } if (req.http.Authorization) { return (pass); } if (req.http.Accept-Encoding) { if (req.url ~ "\.(bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)$") { unset req.http.Accept-Encoding; } elseif (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; } elseif (req.http.Accept-Encoding ~ "deflate") { set req.http.Accept-Encoding = "deflate"; } else { unset req.http.Accept-Encoding; } } #if (req.url ~ "^/images") { # unset req.http.cookie; #} std.log("OTHER URI - url=" + req.url); return (hash); } sub vcl_pipe { if (req.http.upgrade) { set bereq.http.upgrade = req.http.upgrade; } } sub vcl_hash { hash_data(req.url); if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); } std.log("HASH URI - url=" + req.url); return (lookup); } sub vcl_hit { std.log("HIT URI - url=" + req.url); if (obj.ttl > 0s) { return (deliver); } if (obj.ttl + obj.grace > 0s) { return (deliver); } if (!std.healthy(req.backend_hint) && (obj.ttl + obj.grace > 0s)) { return (deliver); } else { return (fetch); } return (deliver); } sub vcl_miss { std.log("Miss URI - url=" + req.url); return (fetch); } sub vcl_backend_response { #set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, ".*s-maxage=([0-9]+).*", "\1") + "s", 0s); if (beresp.ttl > 0s) { unset beresp.http.Set-Cookie; } if (beresp.http.Set-Cookie) { set beresp.uncacheable = true; return (deliver); } if (beresp.http.Cache-Control && beresp.ttl > 0s) { set beresp.grace = 1m; unset beresp.http.Set-Cookie; } if (beresp.http.Content-Length ~ "[0-9]{8,}") { set bereq.http.x-pipe = "1"; return (retry); } if (bereq.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") { set beresp.uncacheable = true; return (deliver); } if (bereq.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico)($|\?)") { unset beresp.http.set-cookie; set beresp.ttl = 12h; } elseif (bereq.url ~ "\.(gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") { set beresp.ttl = 30m; } else { set beresp.ttl = 10m; } #if ((!beresp.http.Cache-Control && !beresp.http.Expires) || # beresp.http.Pragma ~ "no-cache" || # beresp.http.Cache-Control ~ "(no-cache|no-store|private)") { # set beresp.ttl = 120s; # set beresp.uncacheable = true; # return (deliver); #} if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*") { unset beresp.http.set-cookie; set beresp.ttl = 120s; set beresp.uncacheable = true; return (deliver); } return (deliver); set beresp.grace = 2m; } sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT from " + req.http.host; set resp.http.X-Cache-Hits = obj.hits; } else { set resp.http.X-Cache = "MISS from " + req.http.host; } unset resp.http.X-Powered-By; unset resp.http.Server; unset resp.http.Via; unset resp.http.X-Varnish; unset resp.http.Age; unset resp.http.qq; } sub vcl_backend_error { if (beresp.status == 500 || beresp.status == 501 || beresp.status == 502 || beresp.status == 503 || beresp.status == 504) { return (retry); } } sub vcl_fini { return (ok); }3.故障排错
如果启动不了Varnish有可能是VCL编译有问题,或者就是有些插件没装。需要查看log日志。
4.Varnish提升缓存
Varnish的一个关键特色就是它有能力防御web和应用服务器宕机。
1. 当几个客户端请求同一个页面的时候,varnish只发送一个请求到后端服务器,然后让那个其他几个请求挂起等待返回结果,返回结果后,复制请求的结果发送给客户端.如果您的服务每秒有数千万的点击率,那么这个队列是庞大的,没有用户喜欢等待服务器响应。为了使用过期的cache给用户提供服务,我们需要增加他们的TTL,保存所有cache中的内容在TTL过期以后30分钟不删除,使用以下VCL:
sub vcl_fetch { set beresp.grace = 30m; }2. Varnish还不会使用过期的目标给用户提供服务,所以我们需要配置以下代码,在cache过期后的15秒内,使用旧的内容提供服务:
sub vcl_recv { set req.grace = 15s; }3. 你会考虑为什么要多保存过去的内容30分钟?当然,如果你使用了健康检查,你可以通过健康状态设置保存的时间:
if (! req.backend.healthy) { set req.grace = 5m; } else { set req.grace = 15s; } Saint mode4. 有时候,服务器很古怪,他们发出随机错误,您需要通知varnish使用更加优雅的方式处理它,这种方式叫神圣模式(saint mode)。Saint mode允许您抛弃一个后端服务器或者另一个尝试的后端服务器或者cache中服务陈旧的内容。让我们看看VCL中如何开启这个功能的:
sub vcl_fetch { if (beresp.status == 500) { set beresp.saintmode = 10s; restart; } set beresp.grace = 5m; }当我们beresp.saintmode 10秒,这个url的请求varnish将在10秒内不访问服务器。一个黑名单,或多或少。一个restart就会执行,如果您有其他的后端有能力提供这些服务器内容,varnish会尝试他们。当您没有可用的后端服务器,varnish将使用它过期的cache提供服务内容。