Varnish

来自Ubuntu中文
119.147.225.219留言2013年4月18日 (四) 18:21的版本
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳到导航跳到搜索

Varnish是一款高性能的开源HTTP加速器,挪威最大的在线报纸 Verdens Gang 使用3台Varnish代替了原来的12台Squid,性能比以前更好。可以看出,Varnish的缓存性能非同一般!

如果您希望可以快速搭建LVAMP(Linux+Varnish+Apache+MySQL+PHP)环境,推荐您使用LVAMP一键包:http://yzs.me/1517.html 安装很快,使用也非常简单。

如果您希望自行搭建该环境,可以看以下的教程:

本文以Ubuntu 12.04为例(注意Ubuntu 10.04安装的是Varnish2)。

安装Varnish之前,首先得安装好一个“后端”,例如Apache或Nginx,具体的安装方法我就不在这里说了。

本教程为官方教程的完整翻译。并做了一定总结、整理。 整个Varnish的学习需要分成两个部分,PART1:部署篇,PART2:应用篇


第一部分:部署篇 作为一个Server Service,部署是最基本的,同时也没有太多需要说明的。按照官方的教程,一运行便可了。但是需要注意的是,–perfix什么的,具体怎么设置的,可要好好的记住,否则,以后要查起来,可就没有头绪了。另外,装了什么样的插件也要清楚的。 下面介绍下,安装方法。 针对Ubuntu 系统,有两种方法安装,一种是通过下载官方的源码编译安装,另一种是通过Ubuntu的更新源安装。

一、安装: 从更新源安装: Ubutnu自带Varnish的源 首先更新一下: apt-gte update

然后安装: apt-get install varnish

编译安装: 1、获得源码文件 从http://repo.varnish-cache.org/source上下载tar包,找release版的。 或者你可以通过git克隆一个。Git clone git://git.varnish-cache.org/varnish-cache 要注意通过git签出的版本会比一般情况需要更多依赖包,尤其是Python Docutils和Sphinx。 3、配置和编译 确保上面的依赖包都满足,这样才能配置。基本命令如下 cd varnish-cache sh autogen.sh sh configure make

通常configure脚本接收多个参数,现在你可以不用管它,Varnish中几乎所有的一切都是运行时参数。 在你安装之前,你可能希望运行回归测试(再次验证程序正确与否的测试),这会消耗一些时间,喝杯咖啡去吧:

make check 别因为一两个测试失败而担心,有些测试对时间太敏感(请告诉我们是哪些,我们可以修复它),如果出现大量失败情况,尤其是b00000.vtc测试失败时,那就不能怠慢了,只有解决它才行。 4、安装 最后,通过测试后: make install Varnish将安装在/usr/local。Varnishd二进制文件在/usr/local/sbin/varnishd中,默认的配置文件在/usr/local/etc/varnish/default.vcl


第二部分:应用篇 当我们部署好之后,自然就是要学习怎么使用,Linux下,尤其是Server的Linux怎么可能有GUI呢。所以,学习配置文件的语法和控制台就很关键了。 下面是要的学习内容目录,我们还是依次进行。

Backend servers Starting Varnish Logging in Varnish Sizing your cache Put Varnish on port 80 Varnish Configuration Language – VCL Statistics Achieving a high hitrate Cookies Vary Pitfall – Vary: User-Agent Purging and banning Edge Side Includes Running inside a virtual machine (VM) Advanced Backend configuration Directors Health checks Misbehaving servers Advanced topics Troubleshooting Varnish 一、Backend servers Varnish有后端(或称为源)服务器的概念。后端服务器是指Varnish提供加速服务的那台,通常提供内容。 第一件要做的事情是告诉Varnish,哪里能找到要加速的内容。打开varnish配置文件,源码包安装的在/usr/local/etc/varnish/default.vcl中,从Ubuntu安装的在/etc/varnish/default.vcl中。 顶部有如下注释:

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

我们修改下,把8080变成80 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

  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,然后按回车。 你会看到如下内容,使用”.”可以缓缓滚动:

0 CLI - Rd ping 0 CLI - Wr 200 PONG 1273698726 1.0 这是varnish主进程,检查缓存进程,看是否一切正常。 现在在浏览器,重新加载页面,显示你的web应用。你会看到如下内容:

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: sub vcl_recv {

 if (req.url ~ "^/images") {
   unset req.http.cookie;
 }

} 现在,当请求传到后端服务器时,他是不带有cookie头信息的。需要在意的是if语句,它根据正则表达式匹配了URL(属于请求对象)。注意匹配操作符。如果它匹配,请求的cookie头信息就会被删除。 例2 – 操作beresp 这里如果匹配某种条件,我们就重写beresp的TTL属性

sub vcl_fetch {

  if (req.url ~ "\.(png|gif|jpg)$") {
    unset beresp.http.set-cookie;
    set beresp.ttl = 1h;
 }

} 例3 – ACLs 你创建了一个使用acl关键字的访问控制列表。你可以使用匹配操作符去判断客户端的IP地址是否与一个ACL匹配。

  1. 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请求:

? $ 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,只需要:

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软件的联盟服务器发出了什么: $ GET -Used http://www.varnish-software.com/%7Cgrep ^Cache-Control Cache-Control: public, max-age=600 Age varnish添加了一个Age头信息,以指示在Varnish中该对象被保持了多久。你可以通过varnishlog像下面那样抓出Age: varnishlog -i TxHeader -I ^Age Pragma 一个HTTP 1.0服务器可能会发送”Pragma:nocache”。Varnish忽略这种头信息。在VCL中你可以很方便的增加对这种头信息的支持。 在vcl_fetch中:

if (beresp.http.Pragma ~ "nocache") {

  pass;

} Authorization 如果varnish看到授权头信息时,它会pass该请求。如果这不是你希望的,你可以unset这个头信息。

Overriding the time-to-live(ttl) 有时你的后端会误操作。根据你的安装,在varnish中覆写ttl会比修复你某些麻烦的后端要简单的多。 你需要VCL去确定你想要的对象,然后你将beresp.ttl的值设置成你想设置的值。 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来缓解这个情况: 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 Vary Purging and banning Edge Side Includes 九、Cookies varnish不会缓存来自后端的具有Set-Cookie头信息的对象。同样,如果客户端发送了一个Cookie头信息,varnish将绕过缓存,直接发给后端。 这可能太过保守。很多站点使用Google Analytics(GA)去分析他们的流量。GA通过设置cookie去跟踪。这个cookie是供客户端的JavaScript程序使用的,服务器是不需要的。 对很多web应用,它会完全忽视cookies,除非你正在访问网站的特定部分。在vcl_recv中的下面的VCL代码块将忽略cookies,除非你访问/admin/:

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:

// 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字段含有很多不同的编码,比如浏览器这样发送:

Accept-Encodign: gzip,deflate 另一个这样发送: Accept-Encoding: deflate,gzip 因为Accept-Encoding头信息不通,varnish将保存两种不同的请求页面。规范Accept-Encoding头信息将确保你的不同尽可能的少。下面的VCL代码将规范Accept-Encoding的头信息: 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,你需要以下代码: 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:

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

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. !/bin/sh

echo 'Content-type: text/html' echo date "+%Y-%m-%d %H:%M" 现在,让我们做个包含ESI include语句的HTML文件:

The time is: at this very moment. 要让esi工作,你需要在VCL中激活ESI,比如像下面那样: 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无法获得时,你可以使用此,做各种各样的回退,代码如下:

<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的注释标签。例如: 这保证了如果没有处理ESI标记,它也不会影响最后HTML的呈现。

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

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

   .host = "127.0.0.1";
   .port = "8080";

} 我们添加新的后端: backend java {

   .host = "127.0.0.1";
   .port = "8000";

} 现在我们要指示,发送不同URL的规则。看看vcl_recv: 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中

backend server1 {

   .host = "192.168.0.10";

} backend server2{

   .host = "192.168.0.10";

} 现在创建一个director:

director example_director round-robin { {

       .backend = server1;

}

  1. server2

{

       .backend = server2;

}

  1. foo

} 该director是一个循环director。这代表该director将根据循环基础分发进入的请求。这也是一个随机director,它以随机风格分发请求。 但是,如果你的一个服务器down了怎么办?varnish可以将所有的请求,指向一个健康的服务器吗?当然了。这就是下面要说的健康检查。

十七、Health checks 让我们建立一个director,该director具有两个后端并带健康检查。首先让我们定义后端: 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:

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分钟。 sub vcl_fetch {

 set beresp.grace = 30m;

} 这样,varnish还不会提供旧对象。为了启用varnish去提供旧对象,我们必须在请求上开启它。下面表示,我们接收15s的旧对象:

sub vcl_recv {

 set req.grace = 15s;

} 你可能想知道,为什么,如果我们无法提供这些对象,我们在缓存中保持这些对象30分钟?如果你开启健康检查,你可以检查后端是否出问题。如果出问题了,我们可以提供长点时间的旧内容。

if (! req.backend.healthy) {

  set req.grace = 5m;

} else {

  set req.grace = 15s;

} 所以,总结下,优雅模式解决了两个问题: 1、 通过提供旧的内容,避免请求扎堆。 2、 如果后端坏了,提供旧的内容。 Saint mode 有时候,服务器会比较奇怪。他们开始抛出随机错误。你可以指示varnish去处 理这些错误,用一种更加优雅得方式——神圣模式。神圣模式可以让你抛弃一个后端服务器的某个页面,并尝试从其他服务器获取,或提供缓存中的旧内容。让我们看看如何在VCL中开启:

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:

  1. 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看上去是这样的:


The time is: at this very moment. 通过在vcl_fetch中,设置do_esi为true,来让ESI工作:

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

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”,指示主进程开始缓存。

start bind(): Address already in use 300 22 Could not open sockets 这里我们发现个问题。有些其他什么绑定在了varnish的HTTP端口上了。如果这没有什么帮助,就在IRC上尝试下trace、truss或come find us。 Varnish崩溃 当varnish损坏,子进程就会崩溃。常常母进程将通过再次重启子进程去解决。任何错误都会被记录在syslog中。看上去会像以下:

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错误:

$ varnishlog -c -m TxStatus:503 如果错误只是发生在一会儿前,事务可能还存在于共享内存日志碎片中。要让varnishlog去处理所有共享内存日志只要添加-d选项:

$ varnishlog -d -c -m TxStatus:503 要进一步了解不同参数的解释和过滤能力,请看varnishlog手册页面。 Varnish不缓存 请查看Achieving a high hitrate