十、Vary
Vary头信息是web服务器发送的,代表什么引起了HTTP对象的变化。可以通过Accept-Encoding这样的头信息弄明白。当服务器发出”Vary:Accept-Encoding”,它等于告诉varnish,需要对每个来自客户端的不同的Accept-Encoding缓存不同的版本。所以,如果客户端只接收gzip编码。varnish就不会提供deflate编码的页面版本。
如果Accept-Encoding字段含有很多不同的编码,比如浏览器这样发送:

1
Accept-Encodign: gzip,deflate

另一个这样发送:

1
Accept-Encoding: deflate,gzip

因为Accept-Encoding头信息不通,varnish将保存两种不同的请求页面。规范Accept-Encoding头信息将确保你的不同尽可能的少。下面的VCL代码将规范Accept-Encoding的头信息:

01
02
03
04
05
06
07
08
09
10
11
12
13
if(req.http.Accept-Encoding) {
if(req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
# No point in compressing these
remove req.http.Accept-Encoding;
} elsif (req.http.Accept-Encoding ~ "gzip") {
setreq.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate") {
setreq.http.Accept-Encoding = "deflate";
} else{
# unkown algorithm
remove req.http.Accept-Encoding;
}
}

该代码设置了来自客户端的Accept-Encoding头信息,gzip具有更高优先级。

十一、Pitfall – Vary:User-Agent
一些应用或应用服务器,会随它们的内容发送”Vary:User-Agent”。这指示Varnish对每个不同的User-Agent缓存不同的副本。这非常的多。甚至相同浏览器的一个补丁都至少会产生10中不同的User-Agent头信息,这个产生的不同是和浏览器所运行的操作系统有关。
所以,如果你真的需要基于User-Agent变化,要确保规范头信息,否则你的命中率会非常的差。可以利用上面的代码作为模板。

十二、Purging and banning
最有效提升命中率的方法是增加你对象的ttl(time-to-live存活时间)。但是,你要知道,在微博时代,提供过时的内容是很不利于业务的。
解决方案是,当有新内容时,就通知varnish。这可以通过两个机制实现。HTTP清理(PURGE,以下称PURGE)和禁止(BAN,以下简称BAN)。首先让我们解释下HTTP PURGE
HTTP PURGE
PURGE(清理)是指当你选出一个缓存对象时,根据其变化的内容进行丢弃。通常PURGE是通过HTTP的PURGE方法进行调用的(即method=purge,这个purge是http协议中没有预定义的,应该是varnish中扩展的)。
HTTP PURGE类似于HTTP GET请求,只是method是PURGE。事实上,你可以调用任何你希望的method,不过许多人都倾向于使用PURGE。Squid支持相同的机制。为了在varnish支持PURGE,你需要以下代码:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
acl purge {
"localhost";
"192.168.55.0/24";
}
sub vcl_recv {
# allow PURGE from localhost and 192.168.55...
if(req.request == "PURGE") {
if(!client.ip ~ purge) {
error 405 "Not allowed.";
}
return(lookup);
}
}
sub vcl_hit {
if(req.request == "PURGE") {
purge;
error 200 "Purged.";
}
}
sub vcl_miss {
if(req.request == "PURGE") {
purge;
error 200 "Purged.";
}
}

正如你看到的。我们使用了新的VCL子程序,vcl_hit和vcl_miss。当我们调用lookup时,varnish将尝试在缓存中查找对象。要么命中,要么丢失,然后调用相应的子程序。在vcl_hit中,我们可以获得存于缓存中的对象,并且可以设置它的TTL。
所以,对于example.com,要让它的首页失效(表示要拿新的内容),可以这样请求varnish:

1
2
PURGE / HTTP/1.0
Host: example.com

之后,Varnish就会丢弃主页。这会移除所有变量,如vary所定义的。
Ban
这是另一个让内容失效的方法。禁止(BAN),你可以将其认为是一种过滤器。你禁止你的缓存提供某些内容。你可以根据我们有的元数据,进行禁止。
Varnish支持禁止功能,并且可以再CLI接口中获得。对于VG,如果想禁止属于example.com的png对象,他们可以分发以下内容:

1
ban req.http.host == "example.com"&& req.http.url ~ "\.png$"

真的很强大。
当在缓存中命中对象时且在投递之前,就会检查其是否BAN。一个对象只会被较新的BAN检查。
只对beresp.*起作用的BAN,由背景工作线程运行着,称为ban lurker。ban lurker将检查堆,看看是否匹配对象,并且去除匹配对象。ban lurker的频度(活跃度),可以通过ban_lurker_sleep参数控制。
禁止那些较老的,对于缓存中最老的对象不经验证就直接丢弃。如果你有很多具有长TTL对象,这些对象很少被访问,那么你会累积大量的禁止。这会影响CPU的利用率和性能。
你可以通过HTTP向varnish添加BAN。这样做需要一些VCL:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
sub vcl_recv {
if(req.request == "BAN") {
# Same ACL check as above:
if(!client.ip ~ purge) {
error 405 "Not allowed.";
}
ban("req.http.host == "+ req.http.host +
"&& req.url == "+ req.url);
# Throw a synthetic page so the
# request won't go to the backend.
error 200 "Ban added";
}
}

该VCL代码段启用varnish,去处理一个HTTP BAN method,对URL添加禁止,包括host部分。
十三、Edge Side Includes
Edge Side Includes(边界情况包含)是一种语言,用来包含在其他web页面中的web页面片断。可以认为他是一个通过HTTP实现的HTML包含语句。
在许多web站点,许多内容是各页面间共享的。为每个页面重新生成这些内容是很浪费的,并且ESI(Edge Side Includes的缩写)致力于让你为每个片断单独决定缓存策略。
在varnish中,我们只实现了ESI的一个小的子集。自2.1起,我们就有三个ESI语句:

  • esi:include

  • esi:remove

  • <!–esi …–>

基于变量和cookie的内容替换还没有实现,但是已经在计划中了。
例子:esi include
让我们看看如何使用它。这段简单的cgi脚本,输出了日期:

1
2
3
4
5
#!/bin/sh
echo'Content-type: text/html'
echo''
date"+%Y-%m-%d %H:%M"

现在,让我们做个包含ESI include语句的HTML文件:

1
2
3
4
5
6
<HTML>
<BODY>
The time is: <esi:includesrc="/cgi-bin/date.cgi"/>
at this very moment.
</BODY>
</HTML>

要让esi工作,你需要在VCL中激活ESI,比如像下面那样:

1
2
3
4
5
6
7
8
9
sub vcl_fetch {
if(req.url == "/test.html") {
setberesp.do_esi = true; /* Do ESI processing */
setberesp.ttl = 24 h; /* Sets the TTL on the HTML above */
} elseif (req.url == "/cgi-bin/date.cgi") {
setberesp.ttl = 1m; /* Sets a one minute TTL on */
/* the included object */
}
}

例子:esi remove
该remove关键字,允许你remove输出。当ESI无法获得时,你可以使用此,做各种各样的回退,代码如下:

1
2
3
4
<esi:includesrc="http://www.example.com/ad.html"/>
<esi:remove>
<ahref="http://www.example.com">www.example.com</a>
</esi:remove>

例子:<!—esi…–>
这是一个特殊的构造,允许ESI标记的HTML呈现,而无需处理。当处理页面时,ESI处理器将移除开始(<–esi)和结尾(–>),然而仍然会处理其内容。如果页面没有被处理,它将会留下,编程HTML/XML的注释标签。例如:

1
2
3
<!--esi
<p>Warning: ESI Disabled!</p>
</p> -->

这保证了如果没有处理ESI标记,它也不会影响最后HTML的呈现。

十四、Running inside a virtual machine(VM)
虽然可以将varnish运行在虚拟的硬件上,但是出于高性能,我们不建议这样。
OpenVz
如果你运行在64位OpenVz(或并行VPS),你必须在启动varnish前减少最大栈尺寸。默认分配给每个线程的内存有点多,这会导致varnish随着线程数(==流量)增加而down掉。
在启动脚本中,运行以下,降低最大栈尺寸:

1
ulimit-s 256

十五、Advanced Backend Configuration
某些情况,你可能需要让varnish缓存几个服务器的内容。你可能希望varnish映射所有URL到一个或多个主机。这里有许多选项
比如,我们需要引入一个Java应用到PHP网站。我们的Java应用会处理以/java/开头的URL。
我们将东西起起来,运行在8000端口。现在来看看default.vcl:

1
2
3
4
backend default {
.host = "127.0.0.1";
.port = "8080";
}

我们添加新的后端:

1
2
3
4
backend java {
.host = "127.0.0.1";
.port = "8000";
}

现在我们要指示,发送不同URL的规则。看看vcl_recv:

1
2
3
4
5
6
7
sub vcl_recv {
if(req.url ~ "^/java/") {
setreq.backend = java;
} else{
setreq.backend = default.
}
}

非常简单。现在先让我们停一下,考虑一下这里的情况。如你所见,你可以根据任意情况定义如何选择后端。如果你发送移动设备的请求到不同的后端,可以做类似的操作,if(req.User-agent ~ /mobile/)