十、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"
) {
set
req.http.Accept-Encoding =
"gzip"
;
} elsif (req.http.Accept-Encoding ~
"deflate"
) {
set
req.http.Accept-Encoding =
"deflate"
;
}
else
{
# unkown algorithm
remove req.http.Accept-Encoding;
}
}
|
该代码设置了来自客户端的Accept-Encoding头信息,gzip具有更高优先级。
十一、Pitfall �C 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
<!�Cesi …�C>
基于变量和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:include
src
=
"/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"
) {
set
beresp.do_esi =
true
; /* Do ESI processing */
set
beresp.ttl = 24 h; /* Sets the TTL on the HTML above */
} elseif (req.url ==
"/cgi-bin/date.cgi"
) {
set
beresp.ttl = 1m; /* Sets a one minute TTL on */
/* the included object */
}
}
|
例子:esi remove
该remove关键字,允许你remove输出。当ESI无法获得时,你可以使用此,做各种各样的回退,代码如下:
1
2
3
4
|
<
esi:include
src
=
"http://www.example.com/ad.html"
/>
<
esi:remove
>
<
a
href
=
"http://www.example.com"
>www.example.com</
a
>
</
esi:remove
>
|
例子:<!―esi…�C>
这是一个特殊的构造,允许ESI标记的HTML呈现,而无需处理。当处理页面时,ESI处理器将移除开始(<�Cesi)和结尾(�C>),然而仍然会处理其内容。如果页面没有被处理,它将会留下,编程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/"
) {
set
req.backend = java;
}
else
{
set
req.backend = default.
}
}
|
非常简单。现在先让我们停一下,考虑一下这里的情况。如你所见,你可以根据任意情况定义如何选择后端。如果你发送移动设备的请求到不同的后端,可以做类似的操作,if(req.User-agent ~ /mobile/)