本教程为官方教程的完整翻译。并做了一定总结、整理。
整个Varnish的学习需要分成两个部分,PART1:部署篇,PART2:应用篇
第一部分:部署篇
作为一个Server Service,部署是最基本的,同时也没有太多需要说明的。按照官方的教程,一运行便可了。但是需要注意的是,–perfix什么的,具体怎么设置的,可要好好的记住,否则,以后要查起来,可就没有头绪了。另外,装了什么样的插件也要清楚的。
下面介绍下,安装方法。
针对CentOS系统,有两种方法安装,一种是通过rpm,另一种是通过源码安装。
一、通过rpm安装:
使用varnish-cache.org库,运行
1
|
rpm --nosignature -i http:
//repo
.varnish-cache.org
/redhat/varnish-3
.0
/el5/noarch/varnish-release-3
.0-1.noarch.rpm
|
然后运行,
1
|
yum
install
varnish
|
–no-signature只在初始安装时需要,因为Varnish的GPG key还不再yum的keyring中。
二、通过源码编译安装:
1、获得源码文件
从http://repo.varnish-cache.org/source上下载tar包,找release版的。
或者你可以通过git克隆一个。Git clone git://git.varnish-cache.org/varnish-cache
要注意通过git签出的版本会比一般情况需要更多依赖包,尤其是Python Docutils和Sphinx。
2、所需要的依赖包
3、配置和编译
确保上面的依赖包都满足,这样才能配置。基本命令如下
1
2
3
|
cd
varnish-cache
sh autogen.sh
sh configure
|
make
通常configure脚本接收多个参数,现在你可以不用管它,Varnish中几乎所有的一切都是运行时参数。
在你安装之前,你可能希望运行回归测试(再次验证程序正确与否的测试),这会消耗一些时间,喝杯咖啡去吧:
别因为一两个测试失败而担心,有些测试对时间太敏感(请告诉我们是哪些,我们可以修复它),如果出现大量失败情况,尤其是b00000.vtc测试失败时,那就不能怠慢了,只有解决它才行。
4、安装
最后,通过测试后:
1
|
make
install
|
Varnish将安装在/usr/local。Varnishd二进制文件在/usr/local/sbin/varnishd中,默认的配置文件在/usr/local/etc/varnish/default.vcl
第二部分:应用篇
当我们部署好之后,自然就是要学习怎么使用,Linux下,尤其是Server的Linux怎么可能有GUI呢。所以,学习配置文件的语法和控制台就很关键了。
下面是要的学习内容目录,我们还是依次进行。
一、Backend servers
Varnish有后端(或称为源)服务器的概念。后端服务器是指Varnish提供加速服务的那台,通常提供内容。
第一件要做的事情是告诉Varnish,哪里能找到要加速的内容。打开varnish配置文件,源码包安装的在/usr/local/etc/varnish/default.vcl中,rpm安装的在/etc/varnish/default.vcl中。
顶部有如下注释:
1
2
3
4
|
# backend default {
# .host = "127.0.0.1";
# .port = "8080";
# }
|
我们修改下,把8080变成80
1
2
3
4
|
backend default {
.host =
"127.0.0.1"
;
.port =
"80"
;
}
|
这段配置,定义了一个Varnish中的一个后端(Backend),叫做default。当Varnish需要从这个后端获得内容时,它就会连接到127.0.0.1的80端口上。
Varnish可以有多个后端,你甚至可以为了负载均衡将几个后端加入到一个集群后端中。
现在我们完成了Varnish的基本配置,接下来让我们在8080端口上把Varnish起起来,对它做些实验性测试。
二、Starting Varnish
在开始下面的内容之前,你先要确保现在你的varnish没有在运行,如果有,就用pkill varnishd去关闭它。然后到根目录,输入以下代码:
1
|
# varnishd –f /usr/local/etc/varnish/default.vcl –s malloc,1G -T 127.0.0.1:2000 –a 0.0.0.0:8080
|
我添加了一些选项:
-f /urs/local/etc/varnish/default.vcl
-f选项指定了将使用哪个配置文件
-s malloc,1G
-s选项用于指定varnish使用何种存储类型保存内容。这里我使用malloc,这个代表我只使用内存存储。如果还有其他后端,用:ref:tutorial-storage来表示。1G指定了分配多少内存——这里是一个G。
-T 127.0.0.1:2000
Varnish含有内置文本管理界面。可以通过它对varnish进行管理,最主要你还不用停掉varnish。你可以给管理界面分配端口。确保你的管理界面没有暴露给外界,因为通过Varnish管理界面你可以很容易地访问系统根目录。我建议直接绑定在localhost上,就别远程了。如果你的系统上有不可信任的user,就用防火墙规则只要限制界面访问根目录就行。
-a 0.0.0.0:8080
对于进入的HTTP请求,我指定varnish监听8080。对于生产环境,你可能需要让varnish监听在80端口,这个是默认的。(关键看前面有没有负载均衡)
现在我已经启动了Varnish。用浏览器访问下http://varnishServerIP:8080/。你应该会看到你的web应用的运行的。
Varnish运行后,应用的访问速度更快主要取决于一些因素。如果你的应用为每个session使用cookie的话(很多PHP和Java应用无论是否需要都会发送一个session cookie),或者应用使用验证的话,这些varnish都不会缓存。现在先放一放,别考虑这些,等到Achieving a high hitrate的时候,我们再来好好谈。
通过查看日志,我们可以用来确定varnish是不是真的起作用了。
三、Logging in Varnish
在Varnish中,日志的工作方式,是一个很好特性。Varnish将日志记录到共享内存片段,而不是记录到一个普通文件中。当记录到内存片段的最后处,会再从头开始记,覆写老数据。这个比记录到文件要快的多,而且不需要磁盘空间。
另一方面,如果你没有执行程序去将这些日志写到磁盘中的话,他们是会消失的。
Varnishlog是一个用来查看Varnish日志的程序。Varnishlog提供给我们原始日志。这里还有其他客户端,之后我们会介绍。
在启动varnish的终端窗口,我们输入varnishlog,然后按回车。
你会看到如下内容,使用”.”可以缓缓滚动:
1
2
|
0 CLI - Rd ping
0 CLI - Wr 200 PONG 1273698726 1.0
|
这是varnish主进程,检查缓存进程,看是否一切正常。
现在在浏览器,重新加载页面,显示你的web应用。你会看到如下内容:
1
2
3
4
5
6
7
|
11 SessionOpen c 127.0.0.1 58912 0.0.0.0:8080
11 ReqStart c 127.0.0.1 58912 595005213
11 RxRequest c GET
11 RxURL c /
11 RxProtocol c HTTP/1.1
11 RxHeader c Host: localhost:8080
11 RxHeader c Connection: keep-alive
|
第一列可以是任意的数字,它代表具体的请求。数字相同,表示他们是同属于一个HTTP事务的。第二列是日志信息的标签。所有的日志条目都是用一个标签去标记,该标签代表何种行为被记录。以Rx开头的标签代表varnish正在接受数据,Tx代表正在发送数据。
第三列表示数据的是来自或者要发送给客户(c),另外,还有为b的情况,代表数据来自或要发送给后端(b)。第四列是被记录的数据。
现在,你可以使用varnishlog去过滤下。基本的选项如下:
-b 只显示varnish和后端服务器之间通信的记录条。当你想优化缓存命中率的时候,非常有用。
-c 和-b类似,只是针对与客户端的通信情况。
-i tag 只有显示带有特定标签的行。”varnishlog –I SessionOpen”将只显示新会话的情况。注意标签是大小写敏感的。
-I 通过正则表达式过滤数据,并显示匹配行。”$varnishlog –c –I RxHeader –I Cookie”,将显示所有来自客户端的cookie头信息。
-o 根据请求id,将记录条目分组。
现在Varnish差不多工作正常,现在要将Varnish的端口编程80,进行调优。
四、Sizing your cache
给Varnish选择多少内存,是个很艰巨的问题。你需要考虑以下事情:
l 你的热门数据集有多大。对一个门户或者新闻站来说,这个数据集可能就只是首页和它相关内容的大小。这里包括的两部分,一部分是只首页本身的文字图片内容,另一部分是首页会链接到的页面或对象(比如图片),这个很容易理解,首页的内容是最可能被点击的,命中率也会很高。
l 产生一个对象的花费有多大?有时候,如果从后端返回并不太消耗资源,同时你的内存又有限的话,我们应该缓存一部分图片,而不是去缓存所有图片。
l 使用varnishstat或其他工具监控n_lru_nuked计数器。如果你有很多LRU活动的话,那么你的缓存正因空间限制在清除对象,此时你就要考虑增加缓存大小了。
清楚缓存任何对象都会携带保存在实际存储区域之外的开销。所以,即便你指定-s malloc,16G,varnish可能实际使用了两倍。Varnish中每个对象的花销大概是1k。所以,如果在你的缓存中有很多小对象的话,花销是非常大的。
五、Put Varnish on port 80
直到现在,为了测试,我们都把varnish运行在一个高位端口上。你应该测试你的应用,如果它工作正常,我们就要切换了,Varnish运行在80端口上,你的web服务器运行在高位端口上。
首先停止varnishd:
1
|
# pkill varnishd
|
并停止你的web服务器。修改web服务器的配置,将其绑定到8080端口上,替换掉原来的80。现在打开varnish的default.vcl并且改变default后端的端口到8080。
启动你的web服务器并且开启varnish:
1
|
# varnishd -f /usr/local/etc/varnish/default.vcl -s malloc,1G -T 127.0.0.1:2000
|
注意,我们移除了-a选项。因为,现在我们采用Varnish的默认设置即可,它会自动绑定到http的80端口上。现在每个访问你站点的人,都会通过varnish访问。
六、Varnish Configuration Language – VCL
Varnish有强大的配置系统。许多其他的系统使用配置指令,基本上就是开或关很多开关。Varnish使用领域专用语言(DSL)作为Varnish配置语言,简写VCL。当请求到达开始执行时,Varnish会将这些配置转换成二进制代码。
VCL文件被分成多个子程序。不同的子程序在不同时候运行。有的在获得请求时候运行,有的当文件从后端获取后运行。
Varnish将在它工作的不同场景执行这些子程序。因为是代码,所以逐行执行并不是问题。在某些情况你在这个子程序调用一个action,然后该子程序执行结束。
如果不想在你的子程序中调用一个action,并且到达了最末尾,此时varnish会执行一些VCL的内置代码。在default.vcl中的注释部分,你会看到这些VCL代码。
99%的情况,你都会要改动两个子程序,vcl_recv和vcl_fetch。
vcl_recv
vcl_recv是在请求开始时调用的。完成该子程序后,请求就被接收并解析了。用于确定是否需要服务请求,怎么服务,如果可用,使用哪个后端。
在vcl_recv中,你也可以修改请求。通常你可以修改cookie,或添加/移除请求头信息。
注意在vcl_recv中,只可以使用请求对象req。
vcl_fetch
vcl_fetch是在文档从后端被成功接收后调用的。通常用于调整响应头信息,触发ESI处理,万一请求失败就换个后端服务器。
在vcl_fecth中,你还可以使用请求对象req。还有个后端响应对象beresp。Beresp包含了后端的HTTP头信息。
actions
最常用的action如下:
pass:当返回pass的时候,请求和随后的响应都将被传到后端服务器,或从那里传回。不会被缓存。pass可以在vcl_recv中被返回。
hit_for_pass:类似与pass,但是只有vcl_fetch可以用。不像pass,hit_for_pass将在缓存中创建一个hitforpass对象。这有个副作用,就是缓存了不像缓存的东西。同时会将未缓存的请求传到后端。在vcl_recv中这样的逻辑不是必须的,因为它发生在任何潜在对象队列发生之前。
lookup:当在vcl_recv中返回lookup时,就等于你告诉varnish发送缓存中的内容,即使该请求应该是被pass的。在vcl_fetch中不能使用lookup。
pipe:pipe也可以在vcl_recv中返回。pipe缩短了客户和后端的环路链接,并且varnish将只是待在哪里,来回偏移字节。varnish不会在意来回发送的数据,所以你的日志是不完整的。注意一个客户会基于相同链接发送几个请求,当使用HTTP 1.1时。所以在实际返回pipe之前,你需要让varnish添加”Connection:close”的头信息。
deliver:投递缓存对象给客户。经常在vcl_fetch中使用。
请求,响应和对象
在VCL中,有三种重要的数据结构。请求:来自客户端;响应:来自后端服务器;对象:存储在缓存中。
在VCL中你应该知道以下结构。
req:请求对象。当varnish接受了请求,req就会创建并生产。许多在vcl_recv中要做的工作都需要用到req。
beresp:后端响应对象。包含了从后端返回的对象的头信息。vcl_fetch中,你会使用beresp对象。
obj:缓存了的对象。大多数是驻留在内存中的只读对象。obj.ttl是可以写的,剩下的都是只读的。
操作符
VCL中可用的操作符如下,稍后可以看例子:
=:赋值
==:比较
~:匹配。可使用正则表达式或ACLs
!:取反
&&:逻辑与
||:逻辑或
例1 – 操作头信息
移除所有在web服务器的/images目录中的对象的cookie:
1
2
3
4
5
|
sub vcl_recv {
if
(req.url ~
"^/images"
) {
unset
req.http.cookie;
}
}
|
现在,当请求传到后端服务器时,他是不带有cookie头信息的。需要在意的是if语句,它根据正则表达式匹配了URL(属于请求对象)。注意匹配操作符。如果它匹配,请求的cookie头信息就会被删除。
例2 – 操作beresp
这里如果匹配某种条件,我们就重写beresp的TTL属性
1
2
3
4
5
6
|
sub vcl_fetch {
if
(req.url ~
"\.(png|gif|jpg)$"
) {
unset
beresp.http.
set
-cookie;
set
beresp.ttl = 1h;
}
}
|
例3 – ACLs
你创建了一个使用acl关键字的访问控制列表。你可以使用匹配操作符去判断客户端的IP地址是否与一个ACL匹配。
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
|
# Who is allowed to purge....
acl
local
{
"localhost"
;
"192.168.1.0"
/24
; /* and everyone on the
local
network */
!
"192.168.1.23"
; /* except
for
the dialin router */
}
sub vcl_recv {
if
(req.request ==
"PURGE"
) {
if
(client.ip ~
local
) {
return
(lookup);
}
}
}
sub vcl_hit {
if
(req.request ==
"PURGE"
) {
set
obj.ttl = 0s;
error 200
"Purged."
;
}
}
sub vcl_miss {
if
(req.request ==
"PURGE"
) {
error 404
"Not in cache."
;
}
}
|
七、Statistics
现在你的varnish已经运行了,现在让我们看看它是如何工作的吧。有几个工具可以帮助你。
1、 varnishtop
varnishtop工具读取共享内存日志,并且显示一个持续更新的最常见的记录条的列表。
通过使用-I,-i,-X和-x选项进行适当过滤,可以用于显示排名,有请求文档、客户端、用户代理(浏览器)或其他记录在日志中的信息。
varnish –i rxurl将显示客户端请求的URL。varnishtop –i txurl 将显示你的后端被什么请求最多。varnishtop –i RxHeader –I Accept-Encoding将显示最常见的客户端发来的Accept-Encoding header。
2、 varnishhist
varnishhist工具读取varnishd(1)共享内存日志,并且提供不断更新的直方图,用以展示它们处理的最近的N个请求的分布。N值和垂直刻度在左上角显示。水平刻度是对数的。命中用管道符号(“|”)标记,未命中使用哈希符号(“#”)标记。
3、 varnishsizes
varnishsizes和varnishhist相似,但它会显示对象的大小,不显示完成请求消耗的时间。可以很直观的告诉你,你正在处理的对象有多大。
4、 varnishstat
varnish有很多计数器。统计丢失数,命中数,存储信息数,创建了的线程数,删除的对象数,几乎一切。varnishstat将转存这些计数器。当对varnish进行调优时,这就很有用了。
这里有一些程序可以定期获取varnishstat,很好地绘出这些计数器的图形。Munin是其中一个。你可以在http://munin-monitoring.org找到。在varnish的源码中有一个munin的插件。
八、Achieving a high hitrate
现在Varnish已经运行,并且你可以通过Varnish访问你的站点。除非你的应用是专门为在一个web加速器后工作而写的,否则为了在Varnish中获得高命中率,你可能需要在配置或应用上做一些修改。
除非varnish完全确定缓存你的数据室安全,否则varnish是不会缓存的。所以,为了让你明白varnish是如何确定的,并使如何缓存页面的,我将通过一些很有用工具去引导你:
注意你需要一个工具去观察传输于你和web服务器之间的HTTP头信息。在varnish服务器上,首先是使用varnishlog和varnishtop,但有时候需要客户端工具去搞清楚。下面就是我们用到的。
工具:varnishtop
你可以使用varnishtop确定出后端命中最多的URL。varnishtop –i txurl是必须的命令。你可以在前一节Statistics中,看到一些其他的varnishtop的例子。
工具:varnishlog
当你已经确定了最常发送给后端的URL是多少时,你可以使用varnishlog去查看完整的请求。varnishlog –c –o /foo/bar将给你来自客户端(-c)的完整(-o)的匹配/foo/bar的请求。
对于扩展诊断头信息,可以参看:http://www.varnish-cache.org/trac/wiki/VCLExampleHitMissHeader
工具:lwp-request
lwp-request是perl的World-Wide Web library中的一部分。它是一些基本的程序,这些程序可以处理HTTP请求,并且给你结果。我通常使用两个程序,GET和HEAD。
vg.no是第一个使用varnish的站点,站点的创建者很明白varnish。所以,让我们看看他们的HTTP头信息。我们对他们的首页发一个GET请求:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
$ GET -H 'Host: www.vg.no' -Used http://vg.no/
GET http://vg.no/
Host: www.vg.no
User-Agent: lwp-request/5.834 libwww-perl/5.834
200 OK
Cache-Control: must-revalidate
Refresh: 600
Title: VG Nett - Forsiden - VG Nett
X-Age: 463
X-Cache: HIT
X-Rick-Would-Never: Let you down
X-VG-Jobb: http://www.finn.no/finn/job/fulltime/result?keyword=vg+multimedia Merk:HeaderNinja
X-VG-Korken: http://www.youtube.com/watch?v=Fcj8CnD5188
X-VG-WebCache: joanie
X-VG-WebServer: leon
|
OK。我们来解释一下。GET通常发送HTTP0.9请求,其缺少Host头信息。所以我通过-H添加了一个Host头信息。-U打印请求头信息,-s打印响应状态,-e打印响应头信息,-d丢弃实际内容。我们并不关心内容,只要头信息。
你可以发现,VG在他们的头信息中添加了一些信息。像X-Rick-Would-Never表示了vg.no的某种奇怪的幽默。其他,像X-VG-Webcache是用来调试的。
所以,你可以检查一个站点的特定URL是否设置了cookie,只需要:
1
|
GET -Used http://example.com/ |grep ^Set-Cookie
|
工具:实时HTTP头信息
Firefox也有个插件。Live HTTP Headers可以显示你发送和接受到的头信息。你可以通过google找到“Live HTTP Header”,或者到https://addons.mozilla.org/en-US/firefox/addon/3829/可以找到。
HTTP头信息的角色
随着每个HTTP请求和响应变成一群携带原数据的头信息。varnish将查看这些头信息,以确定这里的内容是否适合缓存,以及缓存多久。
请注意,当考虑这些头信息时候,实际上varnish只考虑真实web服务器中varnish自己的那部分。考虑的理论依据都在于你的控制。
术语surrogate origin cache没有在IETF so RFC2616中很好的定义。所以varnish不同的工作方式可能会和你的预期不同。
让我们看看你应该知道的重要的头信息:
Cache-Control
Cache-Control指定了缓存如何处理内容。varnish关心max-age参数,并用它来计算对象的TTL。
“Cache-Control:nocache”是被忽略的,如果你需要,你也可以方便的增加支持。
所以,确保你发出的Cache-Control头信息具有max-age。你可以看看Varnish软件的联盟服务器发出了什么:
1
2
|
$ GET -Used http://www.varnish-software.com/|grep ^Cache-Control
Cache-Control: public, max-age=600
|
Age
varnish添加了一个Age头信息,以指示在Varnish中该对象被保持了多久。你可以通过varnishlog像下面那样抓出Age:
1
|
varnishlog -i TxHeader -I ^Age
|
Pragma
一个HTTP 1.0服务器可能会发送”Pragma:nocache”。Varnish忽略这种头信息。在VCL中你可以很方便的增加对这种头信息的支持。
在vcl_fetch中:
1
2
3
|
if
(beresp.http.Pragma ~
"nocache"
) {
pass;
}
|
Authorization
如果varnish看到授权头信息时,它会pass该请求。如果这不是你希望的,你可以unset这个头信息。
1
2
3
4
5
|
sub vcl_fetch {
if
(req.url ~
"^/legacy_broken_cms/"
) {
set
beresp.ttl = 5d;
}
}
|
该例会为你网站上过去的遗留物,将TTL设置为5天。
Forcing caching for certain requests and certain responses
由于你还在使用那些麻烦的不能很好工作的后端,你可能会想再varnish中覆写更多的内容。我们推荐你尽可能多的使用默认缓存规则。尽管强制varnish在缓存中查找一个对象很简单,但我们还是不推荐。
Normalizing your namespace
一些站点可以通过很多主机名访问。比如, http://www.varnish-software.com/ , http://varnish-software.com/和http://varnishsoftware.com/。这些都指向同一个站点。由于varnish不知道他们的区别,varnish会为每个主机名的每个页面做缓存。你可以通过在你的web服务器配置中设置跳转或者使用VCL来缓解这个情况:
1
2
3
|
if
(req.http.host ~
"(?i)^(www.)?varnish-?software.com"
) {
set
req.http.host =
"varnish-software.com"
;
}
|
Ways of increasing your hitrate even more
接下来的章节,会阐述进一步提高命中率的方法,尤其在cookies的章节中。
九、Cookies
varnish不会缓存来自后端的具有Set-Cookie头信息的对象。同样,如果客户端发送了一个Cookie头信息,varnish将绕过缓存,直接发给后端。
这可能太过保守。很多站点使用Google Analytics(GA)去分析他们的流量。GA通过设置cookie去跟踪。这个cookie是供客户端的JavaScript程序使用的,服务器是不需要的。
对很多web应用,它会完全忽视cookies,除非你正在访问网站的特定部分。在vcl_recv中的下面的VCL代码块将忽略cookies,除非你访问/admin/:
1
2
3
|
if
( !( req.url ~ ^
/admin/
) ) {
unset
req.http.Cookie;
}
|
非常简单。然后,如果你需要做更复杂的处理,像在几个cookie中移除其中一个,事情就麻烦了。遗憾的是,varnish没有很好的工具去操作cookie。我们不得不使用正则表达式去做这件事情。如果你熟悉正则表达式,你就明白怎么办了。如果你不熟悉,我建议你找本书好好学习下,或者通过pcrepattern手册页面或其他的在线教程。
让我展示给你看看,varnish软件是用什么的。我们使用一些GA的跟踪cookie或其他类似工具的cookie。这些cookie都是供JavaScript使用的。varnish和其联盟站不需要这些cookie,并且因为varnish会因为这些cookie不缓存页面,所以当客户端发送cookie时,我们将在VCL中丢弃这些非必要的cookie。
下面的VCL中,我们丢弃了所有的以“_”开头的cookie:
1
2
3
4
|
//
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*"
,
""
);
|
该例取自Varnish的wiki,在那里你可以找到其他使用VCL做的例子。
十、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 – 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语句:
基于变量和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…–>
这是一个特殊的构造,允许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/"
) {
set
req.backend = java;
}
else
{
set
req.backend = default.
}
}
|
非常简单。现在先让我们停一下,考虑一下这里的情况。如你所见,你可以根据任意情况定义如何选择后端。如果你发送移动设备的请求到不同的后端,可以做类似的操作,if(req.User-agent ~ /mobile/)
十六、Directors
Director(不知道怎么翻译合适,所以就保留了)
你也可以将几个后端分组为一组后端。这些组称为directors。这可以提高性能和灵活度。你可以定义几个后端,并把它们归到一个director中
1
2
3
4
5
6
|
backend server1 {
.host =
"192.168.0.10"
;
}
backend server2{
.host =
"192.168.0.10"
;
}
|
现在创建一个director:
01
02
03
04
05
06
07
08
09
10
|
director example_director round-robin {
{
.backend = server1;
}
# server2
{
.backend = server2;
}
# foo
}
|
该director是一个循环director。这代表该director将根据循环基础分发进入的请求。这也是一个随机director,它以随机风格分发请求。
但是,如果你的一个服务器down了怎么办?varnish可以将所有的请求,指向一个健康的服务器吗?当然了。这就是下面要说的健康检查。
十七、Health checks
让我们建立一个director,该director具有两个后端并带健康检查。首先让我们定义后端:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
backend server1 {
.host =
"server1.example.com"
;
.probe = {
.url =
"/"
;
.interval = 5s;
.timeout = 1 s;
.window = 5;
.threshold = 3;
}
}
backend server2 {
.host =
"server2.example.com"
;
.probe = {
.url =
"/"
;
.interval = 5s;
.timeout = 1 s;
.window = 5;
.threshold = 3;
}
}
|
probe是新的内容。varnish将使用probe对每个后端进行健康检查。其选项有:
url:定义什么样的URL需要varnish(感觉这里应该是做处理的意思)请求。
interval:查询的间隔时长
timeout:probe的超时时间
window:varnish将保持一个结果的滑动窗(该滑动窗不是实际的窗体,是一种流量控制方法,允许发送方在停止并等待确认前可以连续发送多个分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。)。这里窗口有5个确认点。
threshold:上次查询的.window数量为多少时,就代表后端是健康的。
initial:当varnish启动时候,用多少个probe去探测健康情况——默认情况,此数量与threshold的数量一致。
现在我们定义director:
01
02
03
04
05
06
07
08
09
10
|
director example_director round-robin {
{
.backend = server1;
}
# server2
{
.backend = server2;
}
}
|
使用这个director,就通你使用其他director或后端一样。varnish不会发送流量到那些标记为不健康主机。
如果所有的后端都down掉了,varnish也会提供老的内容。关于如何启用这个功能,可以参看Misbehaving servers。
请注意,varnish将为所有已加载的VCL,持续探测是否活动(active)。varnish将合并相同的probe——所以,注意,如果你做了很多VCL加载,就不要改变probe的配置。卸载VCL,将丢弃probe。
十八、Misbehaving servers
varnish有个重要的特性,它可以保护你免受web-和应用服务器的不良行为。
Grace mode
当几个客户端正访问相同页面时,varnish会发送一个请求到后端,并且让其他请求等待,当从后端取回一个副本时。在某些产品中,这称为请求黑那个,varnish会自动做这个。
如果你每秒需要相应成千上万的点击,等待的请求队列就会很巨大。这里有两个潜在问题,一个是thundering herd problem(这个无法翻译。。。wiki有专门的对应解释),突然增加一千个线程去提供内容,会让负载变得很高。第二个,没有人喜欢等。为了解决这个问题,我们指示varnish去保持缓存的对象超过他们的TTL(就是该过期的,不让它过期),并且去提供旧的内容给正在等待的请求。
所以,为了提供旧的内容,首先我们必须有内容去提供。所以,我们使用以下VCL,以使varnish保持所有对象超出了他们的TTL30分钟。
1
2
3
|
sub vcl_fetch {
set
beresp.grace = 30m;
}
|
这样,varnish还不会提供旧对象。为了启用varnish去提供旧对象,我们必须在请求上开启它。下面表示,我们接收15s的旧对象:
1
2
3
|
sub vcl_recv {
set
req.grace = 15s;
}
|
你可能想知道,为什么,如果我们无法提供这些对象,我们在缓存中保持这些对象30分钟?如果你开启健康检查,你可以检查后端是否出问题。如果出问题了,我们可以提供长点时间的旧内容。
1
2
3
4
5
|
if
(! req.backend.healthy) {
set
req.grace = 5m;
}
else
{
set
req.grace = 15s;
}
|
所以,总结下,优雅模式解决了两个问题:
1、 通过提供旧的内容,避免请求扎堆。
2、 如果后端坏了,提供旧的内容。
Saint mode
有时候,服务器会比较奇怪。他们开始抛出随机错误。你可以指示varnish去处 理这些错误,用一种更加优雅得方式——神圣模式。神圣模式可以让你抛弃一个后端服务器的某个页面,并尝试从其他服务器获取,或提供缓存中的旧内容。让我们看看如何在VCL中开启:
1
2
3
4
5
6
7
|
sub vcl_fetch {
if
(beresp.status == 500) {
set
beresp.saintmode = 10s;
restart;
}
set
beresp.grace = 5m;
}
|
当我们设置beresp.saintmode为10秒时,varnish会不请求该服务器10秒。或多或少可以算是一个黑名单。restart被执行时,如果我们有其他后端可以提供该内容,varnish会请求它们。当没有其他后端可用,varnish就会提供缓存中的旧内容。
这真的是可以救命的。
清楚grace-和saint 模式的限制
当请求正在被获取时,如果你的请求失败,会被扔到vcl_error中。由于vcl_error对数据集的访问有很大显示,所以你不能启用优雅模式和神圣模式。在以后发布的版本中会解决这个问题,但是这里我们还是可以做些什么的。
1、 声明总是出状况的后端
2、 在vcl_error中设置magic marker
3、 重启事务
4、 注意vcl_recv中的magic marker,并设置后端为之前提到的。
5、 varnish现在将提供旧任何可获得的数据
God mode
还没有实现。:-)
十九、Advanced topics
该教程涉及了varnish中的基础。如果你通读了它,你现在应该已经有运行varnish的能力了。这里是一个简短的我们在本教程中没有谈到的专题概览。
更多VCL
VCL比至少我们所说的要复杂一点。这里有一些更多我们没有谈到的子程序和action可用。要查看VCL的完整教程,可以参看VCL的手册页面——reference-vcl
使用内嵌C扩展varnish
你可以使用内嵌C去扩展varnish。注意,这种方式可能会把varnish搞乱。因为C语言在varnish缓存处理中运行,所以如果你的代码出现一点错误,varnish就会崩溃。
我看到的第一个内嵌C应用是写入syslog:
01
02
03
04
05
06
07
08
09
10
|
# The include statements must be outside the subroutines.
C{
#include <syslog.h>
}C
sub vcl_something {
C{
syslog(LOG_INFO,
"Something happened at VCL line XX."
);
}C
}
|
Edge Side Includes
varnish可以通过把不同页面放到一起,缓存、创建web页面。这些片断可以有自己的缓存策略。如果你的网站有一个显示最热的5篇文章的列表,该列表可能被作为片断缓存起来,并且被包含在其他页面中。使用属性可以很好地提升命中率并降低服务器负载。ESI看上去是这样的:
1
2
3
4
5
6
|
<
HTML
>
<
BODY
>
The time is: <
esi:include
src
=
"/cgi-bin/date.cgi"
/>
at this very moment.
</
BODY
>
</
HTML
>
|
通过在vcl_fetch中,设置do_esi为true,来让ESI工作:
1
2
3
4
5
|
sub vcl_fetch {
if
(req.url ==
"/test.html"
) {
set
beresp.do_esi =
true
; /* Do ESI processing */
}
}
|
二十、Troubleshooting Varnish
有时候varnish会发神经。为了帮助你理解发生了,这里有一些你可以检查的地方。varnishlog,/var/log/syslog, /var/log/messages都是varnish会留下相关线索的地方。
当varnish没有启动
有时varnish会不启动。在你的机器上,为何varnish不启动,这里有个经常引起的原因。从/dev/null的权限错误,到其他进程阻塞了端口。
以debug模式启动varnish,查看发生了什么。
通过以下代码启动varnish:
1
|
# varnishd -f /usr/local/etc/varnish/default.vcl -s malloc,1G -T 127.0.0.1:2000 -a 0.0.0.0:8080 -d
|
注意,-d选项(开启debug模式)。这将给你一些关于发生什么的信息。让我们看看varnish将如何响应那些监听此端口上的东西:
1
|
# varnishd -n foo -f /usr/local/etc/varnish/default.vcl -s malloc,1G -T 127.0.0.1:2000 -a 0.0.0.0:8080 -d
|
01
02
03
04
05
06
07
08
09
10
|
storage_malloc: max size 1024 MB.
Using old SHMFILE
Platform: Linux,2.6.32-21-generic,i686,-smalloc,-hcritbit
200 193
-----------------------------
Varnish Cache CLI.
-----------------------------
Type 'help' for command list.
Type 'quit' to close CLI session.
Type 'start' to launch worker process.
|
现在varnish已经启动。只有主进程运行着,在调试模式下,缓存是不会运行的。现在你正处于控制台。你可以通过分发“start”,指示主进程开始缓存。
1
2
3
4
|
start
bind(): Address already in use
300 22
Could not open sockets
|
这里我们发现个问题。有些其他什么绑定在了varnish的HTTP端口上了。如果这没有什么帮助,就在IRC上尝试下trace、truss或come find us。
Varnish崩溃
当varnish损坏,子进程就会崩溃。常常母进程将通过再次重启子进程去解决。任何错误都会被记录在syslog中。看上去会像以下:
1
2
3
4
5
|
Mar 8 13:23:38 smoke varnishd[15670]: Child (15671) not responding to CLI, killing it.
Mar 8 13:23:43 smoke varnishd[15670]: last message repeated 2 times
Mar 8 13:23:43 smoke varnishd[15670]: Child (15671) died signal=3
Mar 8 13:23:43 smoke varnishd[15670]: Child cleanup complete
Mar 8 13:23:43 smoke varnishd[15670]: child (15697) Started
|
尤其是,如果你在Linux上看到“Error in munmap”错误,你可能需要增加可获得的映射数量。Linux被限制最大64k映射(映射表示将文件或其他对象映射到内存)。设置sysctl.conf中的vm.max_max_count,将使你提高此限制。你可以通过统计/proc/$PID/maps的数量,来检查你程序使用的映射数量。
记录在这里的,是一个很奇怪的问题。但是充满希望的Google将提供答案,如果你曾经遇到此问题的话。
Varnish提示Guru meditation(一种错误提示方式,见wiki)
首先在varnishlog中,找到相关日志条目,这可能会提供一些线索。因为varnishlog记录了很多数据,所以可能很难查到我们需要的条目。你可以通过执行一下的命令,设置varnishlog记录所有的503错误:
1
|
$ varnishlog -c -m TxStatus:503
|
如果错误只是发生在一会儿前,事务可能还存在于共享内存日志碎片中。要让varnishlog去处理所有共享内存日志只要添加-d选项:
1
|
$ varnishlog -d -c -m TxStatus:503
|
要进一步了解不同参数的解释和过滤能力,请看varnishlog手册页面。
Varnish不缓存
请查看Achieving a high hitrate
http://anykoro.sinaapp.com/?p=261