Varnish and Website Performance
本节集中解决如何调优varnish server,同时如何使用varnish优化你的web站点.
一共有三小节.第一小节你应该想到varnish的各种工具和功能,下一小节如何从cache中清除已经缓存的内容.清除内容是一项基本功能,因为它允许给缓存的对象增加 TTL.TTL越大varnish保持在缓存中的时间越久,这意味着varnish处理更多的请求,只将少部分的请求传递到相对较慢的后端.
最后一小节,处理web内容的压缩.当从后端获得内容时varnish可以压缩它,然后传递被压缩后的内容.这种方式可以减少客户现在内容的时间,从而提高你的web站点的性能.
Achieving a high hitrate
现在,varnish已经启动和运行,你可以通过varnish访问您的Web应用程序。除非你的应用程序是专门工作在网络加速器之后的,你可能需要在varnish的配置或应用程序上做一些改变,以提高varnish的命中率。
varnish不会缓存你的数据,除非它是绝对肯定操作是安全的。因此,对于你为了了解varnish如何决定是否和如何来缓存页面,我们会通过一些工具引导你,你可以找到一些工具便于理解在varnish的配置中发生了什么。
注意,你需要一个工具来查看穿越varnish和后端之间的HTTP头。在varnish服务器,最简单的方法是使用varnishlog和varnishtop,但有时客户端工具也是有意义的。这些工具是我们常用 的
Tool: varnishtop
可以使用varnishtop来找出什么url正在被命中。varnishtop -i BereqURL 是基本的命令,向你展示varsnih发送到后端的top请求。你可以在Statistics 中查看varnishtop 用法的其他列子。
Tool: varnishlog
当你找出一个频繁发送到后端的URL,你可以使用varnishlog查看该请求。varnishlog -q 'ReqURL ~ "^/foo/bar"' 向你展示来自客户端匹配到/foo/bar的请求。
关于varnishlog如何工作的更多信息请查看Logging in Varnish 或者产看varnishlog的man手册。
扩展的http头,http://www.varnish-cache.org/trac/wiki/VCLExampleHitMissHeader
Tool: lwp-request
lwp-request 是Perl www库中的工具。可以发送请求同时向你展示结果的基本程序。 该工具主要是查看http响应头。很多工具都可以实现。比如linux自带的curl工具。
# curl -I http://vg.no/
HTTP/1.1 301 Moved Permanently
Server: Apache/2.2.15 (CentOS)
X-VG-WebServer: vgphoenix-web-02
Location: http://www.vg.no/
Content-Type: text/html; charset=iso-8859-1
X-VG-SolveMe: uggc://jjj.it.ab/ynxfrgngg.ugzy
Date: Mon, 20 Jul 2015 03:28:46 GMT
Connection: keep-alive
X-Cache: HIT:40
Vary: Accept-Encoding,User-Agent
X-VG-WebCache: hmg9-varnish-01
X-Age: 46
Age: 0
还firefox的firebug工具,chrome自带的工具都可以查看。
Cookies
默认情况,varnish不缓存从后端响应的http头中带有Set-Cookie的对象。如果客户端发送的请求带有Cookie header,varnish将忽略缓存,直接将请求传递到后端。
这可能是多度保守。大量站点使用Google Analytics来分析他们的流量,GA设置cookie信息来跟踪你。该cookie被客户端的js所使用,服务端不感兴趣的。
Cookies from the client
对于大量的web应用完全清楚cookie是有意义的,除非你指定不需要忽略cookie的web站点的部分。
在vcl_recv中的vcl片段将忽略cookie,除非你访问的url中匹配到/admin/
if (!(req.url ~ "^/admin/")) {
unset req.http.Cookie;
}
相当简单。然而如果你需要做些复杂的事情,像移除几个cookie中的一个,这就变得困难了。不幸的是,varnish没有一些好的工具来操控cookies。我们不得不使用正则表达式来实现。如果你熟练正则表达式你将理解到发生了什么,不然我们还是建议你先看看pcrepattern(PCRE - Perl-compatible regular expressions),或则查看丰富的在线文档。
我们来看看Varnish Software(VS) 的例子。非常简明的设置,varnish cache在前端,后端使用Drupal-based。VS使用一些跟踪谷歌分析的cookie还有一些其他相似的工具。所有的cookie变成一个集合,被js所使用。varnish和Drupal都不需要查看这些cookie,同时自从客户端发送cookie到varnish,varnish将终止正在缓存的页面,varnish需要在VCL中丢弃不必要的cookie。
下面的VCL中我们丢弃以下划线开始的所有cookie:
# Remove has_js and Google Analytics __* cookies.
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", "");
# Remove a ";" prefix, if present.
set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
下面的例子,我们移除所有的cookie除了叫做COOKIE1和COOKIE2,你会惊奇的发现,如此完美:
sub vcl_recv {
if (req.http.Cookie) {
set req.http.Cookie = ";" + req.http.Cookie;
set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
set req.http.Cookie = regsuball(req.http.Cookie, ";(COOKIE1|COOKIE2)=", "; \1=");
set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
if (req.http.Cookie == "") {
unset req.http.Cookie;
}
}
}
一个稍微简单的例子。使用相同的功能,能几乎完成。从“other”cookie中过滤出“the one”我们需要的cookie,复制它给header中的另一个,然后复制回请求中,再删除原来的cookie header。验证请求的正确性。:
sub vcl_recv {
# save the original cookie header so we can mangle it
set req.http.X-Varnish-PHP_SID = req.http.Cookie;
# using a capturing sub pattern, extract the continuous string of
# alphanumerics that immediately follows "PHPSESSID="
set req.http.X-Varnish-PHP_SID =
regsuball(req.http.X-Varnish-PHP_SID, ";? ?PHPSESSID=([a-zA-Z0-9]+)( |;| ;).*","\1");
set req.http.Cookie = req.X-Varnish-PHP_SID;
unset req.X-Varnish-PHP_SID;
}
在Varnish Cache Wiki 有更多varnish在VCL中能做什么的惊奇例子。
Cookies coming from the backend
使用默认设置的话,后端server如果设置了Set-Cookie头,varnish将不缓存该内容。一个hit-for-pass 的对象被创建。因此如果后端行为诡异同时设置些不需要的cookie,unset 'Set-Cookie'头,一切将变得ok。
Cache-Control
Cache-Control头通知缓存,如何处理该内容。Varnish关心' max-age '参数,并且是用它来计算一个对象的TTL。
因此,确定你用max-ag设置了'Cache-Control'头。你可以看看Varnish Software的Drupalserver的响应。
$ curl -I http://www.varnish-software.com/|grep 'Cache-Control'
Cache-Control: max-age=1209600
Age
Varnish 增加'Age'头来标明对象被varnish缓存多久。你可以使用* varnishlog -I RespHeader:^Age*来从varnishlog筛选出Age。
Pragma
HTTP 1.0server可能发送Pragma: nocache 头。varnish忽略pragma头。你很容易增加对该pragma头的支持。 在vclbackendresponse:
if (beresp.http.Pragma ~ "nocache") {
set beresp.uncacheable = true;
set beresp.ttl = 120s; # how long not to cache this url.
}
Authorization
如果varnish看到header中有'Authorization'头,它将pass请求。如果你不需要这个头,你可以unset。
Overriding the time-to-live (TTL)
有时候你的后端会抽下风。在varnish可以容易的重写TTL,然后你再修复你笨重的后端。 需要在VCL中指定想要设置的对象,然后设置'beresp.ttl'
sub vcl_backend_response {
if (bereq.url ~ "^/legacy_broken_cms/") {
set beresp.ttl = 5d;
}
}
Forcing caching for certain requests and certain responses
你的后端可能仍然是笨重的,且不能很好的工作,你可能想存放更多的资源到varnish。我们建议你依赖尽可能多的默认缓存规则。强制varnish在高速缓存中查找对象是很容易的,但它并不是真正的建议。
Normalizing your namespace
有些站点是有许多域名的。 http://www.varnish-software.com/, http://varnish-software.com/ and http://varnishsoftware.com/ 都指向同一个站点。而varnish不知道他们是相同的。varnish会根据不同的域名缓存同一个对象不同的版本。你可以减缓这种情况,通过在webserver中配置redirect或者使用下面的VCL:
if (req.http.host ~ "(?i)^(www.)?varnish-?software.com") {
set req.http.host = "varnish-software.com";
}
HTTP Vary
HTTP Vary 并不是微不足道的概念。这是迄今为止最被误解的HTTP标头。
大量的响应头告诉客户端关于该HTTP对象是否该被传递。客户端可以基于他们的参数选择请求不同的http对象。他们的参数选择包括像编码和域名。当客户端倾向于UK English时,通过Accept-Language: en-uk 表明。缓存需要保持不同的版本,通过响应中的'Vary'头来实现。
当后端server被Vary:Accept-Language标示时,它告诉Varnish需要对每个不同的Accept-Language保存不同的版本。
如果两个客户端分别接受"en-us, en-uk" 和 "da, de",同时如果后端标明不同Accept-Language,varnish将缓存和提供两个不同的版本。
请注意Vary头需要完全匹配。因此如果Vary中是 "en-us, en-uk" 和"en-us,en-uk"(多个空格),varnish将保存同一个页面的两个副本。
获得搞得命中率的同时使用Vary,关键是标准化后端的header。记住,哪怕是有一点不同,会导致不同的缓存条目。
下面的vcl代码将标准化Accept-Language头,不管"en", "de" 或者 "fr",以这个优先顺序。
if (req.http.Accept-Language) {
if (req.http.Accept-Language ~ "en") {
set req.http.Accept-Language = "en";
} elsif (req.http.Accept-Language ~ "de") {
set req.http.Accept-Language = "de";
} elsif (req.http.Accept-Language ~ "fr") {
set req.http.Accept-Language = "fr";
} else {
# unknown language. Remove the accept-language header and
# use the backend default.
unset req.http.Accept-Language
}
}
Vary parse errors
如果varnish解析Vary头出错,将会返回503网络错误。或者客户端的头大小超过的65k. 在这种情况下SLT_Error日志会被增加。
Pitfall - Vary: User-Agent
user-Agent的陷阱。
一些应用或者应用服务器,把'Vary: User-Agent'随着内容一起发送。这个行为通知varnish缓存每个'User-Agent'版本的单独副本。即时是同一个浏览器的单个补丁版本对于不同的运行操作系统也至少有10中不同的User-Agent。
如果你真的需要基于User-Agent多样化,一定要初始化标准化header或者你能忍受varnish的命中率。使用上述代码作为一个模板。