一、简介
Varnish是一款高性能的开源HTTP加速器,挪威最大的在线报纸 Verdens Gang 使用3台Varnish代替了原来的12台Squid,性能比以前更好。
Varnish 的作者Poul-Henning Kamp是FreeBSD的内核开发者之一,他认为现在的计算机比起1975年已经复杂许多。在1975年时,储存媒介只有两种:内存与硬盘。但现在计算机系统的内存除了主存外,还包括了CPU内的L1、L2,甚至有L3快取。硬盘上也有自己的快取装置,因此Squid Cache自行处理物件替换的架构不可能得知这些情况而做到最佳化,但操作系统可以得知这些情况,所以这部份的工作应该交给操作系统处理,这就是 Varnish cache设计架构。
varnish项目是2006年发布的第一个版本0.9.距今已经八年多了,此文档之前也提过varnish还不稳定,那是2007年时候编写的,经过varnish开发团队和网友们的辛苦耕耘,现在的varnish已经很健壮。很多门户网站已经部署了varnish,并且反应都很好,甚至反应比squid还稳定,且效率更高,资源占用更少。相信在反向代理,web加速方面,varnish已经有足够能力代替squid。
varnish的官网为https://www.varnish-cache.org,rpm,rpm包的下载位置为:http://repo.varnish-cache.org。
二、关于Varnish
1.varnish系统架构
varnish主要运行两个进程:Management进程和Child进程(也叫Cache进程)。
Management进程主要实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等。Management进程会每隔一段时间探测一下Child进程以判断其是否正常运行,如果在指定的时长内未得到Child进程的回应,Mangagement将会重启此Child进程。
Child进程包含多种类型的线程,常见的如:
Acceptor进程:接受新的连接请求并响应
worker进程:child进程会为每个用户启动一个worker进程,因此,在高并发的场景中可能会出现数百个worker进程甚至更多
Expiry进程:从缓存中清理过期内容
Varnish依赖“工作区(workspace)”以降低线程在申请或修改内存时出现竞争的可能性。在varnish内部有多种不同的工作区,其中最关键的当属用于管理会话数据的session工作区
2.varnish日志
为了与系统的其他部分进行交互,Child进程使用了可以通过文件系统接口进行访问的共享内存日志(shared memory log),因此,如果某线程需要记录信息,其仅需要持有一个锁,而后向共享内存中的某内存区域写入数据,再释放持有的锁即可。而为了减少竞争,每个worker线程都使用了日志数据缓存
共享内存大小一般为90M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。varnish通过了多个不同的工具,如varnishlog、varnishncsa或varnishstst等来分析共享内存日志中的信息并能够以指定的方式进行显示
3.varnish的后端存储
varnish支持多种不同类型的后端存储。这可以在varnish启动时使用-s选项指定。后端存储的类型包括
(1)file:使用特定的文件存储全部的缓存数据,并通过操作系统的mmap()系统调用整个缓存文件映射至内存区域(如果条件允许)
(2)mallco: 使用mallco()库调用在varnish启动时向操作系统申请指定的大小的内存空间以存储缓存数据
(3)persistent(experimental):与file的功能相同,但是可以持久存储数据(即重启varnish时数据不会被清楚),仍处于测试阶段
varnish无法追踪某缓存对象是否存入了缓存文件,而后也就无法得知磁盘上的缓存文件是否可用,因此,file存储在varnish停止或重启是会清除数据。而persistent方法的出现对此有一个弥补,但persistent仍处于测试阶段,例如目前尚无法有效处理要缓存对象总体大小超出缓存空间的情况,所有,其仅适用于有着巨大缓存空间的场景。
选择使用合适的存储方式有助于途胜系统性能,从经验的角度来看,建议在内存空间足以存储所有数据的缓存对象时使用malloc的方法,反之,file存储将会有着更好的性能表现,然而,需要注意的是,varnishd实际上是用的空间比使用-s选项指定的缓存空间更大,一般说来,其需要为每个缓存对象多使用差不多1k左右的存储空间,这意味着,对于100万个缓存对象来说,其使用的缓存空间将超出指定大小1G左右,另外,为了保存数据结构等,varnish自身也会占去不少的内存空间。
为varnish指定使用的缓存类型时,-s选项可接受的参数格式如下:
malloc[,size]或file[,path[,size[,granularity]]]或persistent,path,size{experimental}
三、VCL
1.简介
VCL(Varnish Configuration Language)是varnish配置缓存策略的工具,它是一种基于“域”(domain specific)的简单编程语言,他支持有限的算术运算和逻辑运算操作、允许使用正则表达式进行字符串匹配、允许用户使用set自定义变量、支持if判断语句,也要内置的函数和变量等。使用VCL编写的缓存策略通常保存至.vcl文件中,其需要编译成二进制的格式后才能有varnish调用。事实上,整个缓存策略就是由几个特定的子历程如vcl_recv、vcl_fetch等组成,他们分别在不同的位置(或时间)执行,如果没有实现为某个位置自定义子例程,varnish将会执行默认的定义
VCL策略在启用前,会由management进程将其转换为C代码,而后再有gcc编译器将C代码编译成二进制程序。编译完成后,management负责将其连接至varnish实例,即Child进程。正式由于编译工作在child进程之外完成,它避免了转载错误格式VCL的风险,因此,varnish修改配置的开销非常小,其可以同时保有几分尚在引用的旧版本配置,也能够让新的配置即刻生效,编译后的旧版本配置通常在varnish重启时才会被丢弃,如果需要手动清理,则可以使用varnishadm的vcl.discard命令来完成
2.VCL状态引擎
在VCL状态引擎中,状态之间具有相关性,但彼此间互相隔离,每个引擎使用return(x)来退出当前状态并指示varnish进入下一个状态
varnish开始处理一个请求时,首先需要分析HTTP请求本身,比如从首部获取请求方法、验证其是否为一个合法的HTTP请求等,当这些基本分析结束后就需要做出第一个决策,即varnish是否从缓存中查找请求的资源,这个决定的实现则需要有VCL来完成,简单来说,要有vcl_recv方法来完成,如果说管理员没有定义vcl_recv函数,varnish将会执行默认的vcl_recv函数,然而,即便管理员自定义了vcl_recv,但如果没有为自定义的vcl_recv函数指定其终止操作(terminating),其仍将会指定默认的vcl_recv函数,事实上,varnish官方强烈建议让varnish执行默认的vcl_recv以便处理自定义vcl_recv函数中可能出现的漏洞
3.VCL语法
VCL的设计参考了C和perl语言,因此,对有着C或Perl编程经验者来说,其非常容易理解。其基本语法说明如下:
(1)//、#或/* comment */用于注释
(2)sub name 定义函数
(3)不支持循环,有内置变量
(4)使用终止语句,没有返回值
(5)域专用
(6)操作符:=(赋值)、==(等值比较)、~(模式匹配)、!(非,取反)、&&(逻辑与)、||(逻辑或)
VCL的函数不接受参数并且没有返回值,因此,其并非真正意义上的函数,这也限定了VCL内部的数据传递只能隐藏在HTTP首部内部进行。VCL的return语句用于将控制权从VCL状态引擎返回给varnish,而非默认函数,这就是为什么VCL只有终止语句而没有返回值的原因,同时,对于每个“域”来说,可以定义一个或多个终止语句,以告诉varnish下一步采取何种操作,如查询缓存或不查询缓存
4.VCL的内置函数
VCL提供了结果函数来实现字符串的修改,如添加bans,重启VCL状态引擎因将控制权转回varnish等
regsub(str,reget,sub):基于正则表达式搜索指定的字符串并将其替换成指定的字符串,只替换匹配到的第一个
regsuball(str,reget,sub):基于正则表达式搜索指定的字符串并将其统统替换成指定的字符串
ban(expression):
ban_url(regex):Bans所有其URL能够由regex匹配的缓存对象
purge:从缓存中挑选出某对象以及其相关变种一并删除,这可以通过通过HYTP协议的PURGE方法完成
hash_data(str):
return():当某个VCL与运行结束时,将控制权返回给Varnish,并指示Varnish如何进行后续的操作:其可以返回的指令包括:lookup、pass、pipe、hit_for_pass、fetch、deliver和hash等:但某特定域可能技能返回某些特定的指令,而非前面列出的全部指令:
return(restart):重新运行整个VCL,即重新从vcl_recv开始进行处理;每一次重启都会增加req.restaets变量中的值,而max_restaets参数则用于限定最大重启次数
5.vcl_recv
vcl_recv是在varnish完成对请求报文的解码为基本数据结构后第一个要指定的子例程,他通常有四个主要用途:
(1)修改客户端数据以减少缓存对象差异性,比如删除URL中的www.等字符串
(2)基于客户端数据选用缓存策略:比如仅缓存特定的额URL请求、不缓存POST请求等
(3)为某web应用程序执行URL重写
(4)挑选合适的后端服务器;
可以使用下面的终止语句,即通过return()向varnish返回指示操作
pass:绕过缓存,即不从缓存中查询内容或不将内容存储至缓存中;
pipe:不对客户端进行检查或做出任何操作,而是在客户端与后端服务器之间建立专业“通道”,并直接将数据在二者之间进行传送:此时,keep-alive连接中后续传送的数据也都将在通过此管道进行直接传送,并不会出现在任何日志中
lookup:在缓存中查找用户请求的对象,如果缓存中没有其指定的对象,后续操作很可能会将其请求的对象进行缓存
error:有varnish自己合成一个响应报文,一般是响应一个错误类信息、重定向类信息或缓存均衡器返回的后端web服务器健康状态检查类信息
vcl_recv也可以通过精巧的策略完成一定意义上的安全功能,以将某特定的***扼杀于摇篮中,同时,它也可以检查出一些拼写的错误并将其进行修改
varnish默认的vcl_recv专门设计用来实现安全的缓策略,它主要完成两种功能:
(1)仅处理可以识别的HTTP方法,并且只缓存GET和HEAD方法
(2)不缓存任何用户特有的数据
安全起见,一般都在自定义的vcl_recv中不要使用return()终止语句,而是再由默认vcl_recv进行处理,并有其做出响应的的处理决策
6.vcl_fetch
如前面所述,想对于vcl_recv是根据客户端的请求做出缓存策略来说,vcl_fetch则是根据服务器端的响应做出缓存决策,在任何VCL状态引擎中发挥pass操作都将有vcl_fetch进行后续处理。vcl_fetch中有许多可用的内置变量,比如最常见的用于定义某对象缓存时长的beresp.ttl变量,通过return()返回给varnish的操作指令有:
deliver:缓存此对象,并将其发送给客户端(经由vcl_deliver)
hit_for_pass:不缓存此对象,但可以导致后续对此对象的请求直接送达到vcl_pass进行处理
restart:重启整个VCL,并增加重启次数,超出max_restarts限定的最大重启次数将会发挥错误信息
error code [reson]:返回指定的错误代码给客户端并丢弃此请求,“code”是错误标识,例如200、405等,“reason”是错误提示信息。
默认的vcl_fetch放弃了缓存任何使用了Set-Cookie首部的响应
7.vcl_deliver
在缓存中找到缓存内容,发送给客户端时调用此参数,通过return()返回给varnish的操作指令有:
deliver:缓存此对象,并将其发送给客户端(经由vcl_deliver)
error code [reson]:返回指定的错误代码给客户端并丢弃此请求,“code”是错误标识,例如200、405等,“reason”是错误提示信息。
8.val_pass
此函数在进入pass模式时被调用,用于将请求直接传递至后端主机,后端主机应答数据后发送给客户端,但是不缓存任何数据,在当前连接下,每次都是犯回最新的内容,通过return()返回给varnish的操作指令有:
error code [reson]:返回指定的错误代码给客户端并丢弃此请求,“code”是错误标识,例如200、405等,“reason”是错误提示信息。
pass:绕过缓存,即不从缓存中查询内容或不将内容存储至缓存中;
9.vcl_pipe
不对客户端进行检查或做出任何操作,而是在客户端与后端服务器之间建立专业“通道”,并直接将数据在二者之间进行传送:此时,keep-alive连接中后续传送的数据也都将在通过此管道进行直接传送,并不会出现在任何日志中,通过return()返回给varnish的操作指令有:
error code [reson]:返回指定的错误代码给客户端并丢弃此请求,“code”是错误标识,例如200、405等,“reason”是错误提示信息。
pipe:不对客户端进行检查或做出任何操作,而是在客户端与后端服务器之间建立专业“通道”,并直接将数据在二者之间进行传送:此时,keep-alive连接中后续传送的数据也都将在通过此管道进行直接传送,并不会出现在任何日志中
10.lookup
表示在缓存里查找被请求的对象,并且根据查找的数据把控制权交给vcl_miss或vcl_hit
11.vcl_hit
在执行lookup指令后,如果在缓存中找到请求的内容,将自动调用此函数,通过return()返回给varnish的操作指令有:
deliver:缓存此对象,并将其发送给客户端(经由vcl_deliver)
error code [reson]:返回指定的错误代码给客户端并丢弃此请求,“code”是错误标识,例如200、405等,“reason”是错误提示信息。
pass:绕过缓存,即不从缓存中查询内容或不将内容存储至缓存中;
12.vcl_miss函数
在执行lookup指令后,如果在缓存中找不到请求的内容,将自动调用此函数,此函数可以判断是否在后端服务器上获取内容,通过return()返回给varnish的操作指令有:
error code [reson]:返回指定的错误代码给客户端并丢弃此请求,“code”是错误标识,例如200、405等,“reason”是错误提示信息。
pass:绕过缓存,即不从缓存中查询内容或不将内容存储至缓存中;
13.VCL处理流程图
通过上面对VCL函数的介绍,大家应该对每个函数实现的功能有一个了解,,起始每个函数之间是有联系的,如下图所示
四、与缓存相关的HTTP首部
HTTP协议提供了很多个首部以实现页面缓存及缓存失效的相关功能,这其中常用的有:
1.Expires
用于指定某web对象的过期时间/日期,通常为GMT格式,一般不应该将此设定的未来过长的时间,一年的长度对大多数场景来说足矣,其常用于为静态内容或JavaScript样式表或图片指定缓存周期
2.Cache-Control
用于定义所有的缓存机制都必须遵循的缓存指示,这些指示是一些特定的指令,包括pubilc、private、no-cache(表示可以存储,但在重新验证其有效性之前不能用于响应客户端请求)、no-store、max-age、s-maxage以及must-revalidate等,Cache-Control中设定的时间会覆盖Expires中指定的时间
3.Etag
响应首部,用于子啊响应报文中为某web资源定义版本标识符
4.Last-Mofified
响应报文,用户回应客户端关于Last-Mofified-Since或If-None-Match首部的请求,以通知客户端其请求的web对象最近修改时间
5.Last-Mofified-Since
条件式请求首部,如果在此首部指定的时间后其请求的web内容发生了更改,则服务器响应更改后的内容,否则,则响应304(not modified)
6.If-None-Match
条件式请求首部,web服务器为某web内容定义了Etag首部,客户端请求时能获取并保护这个首部的值(即标签),而后在后续的请求中会通过If-None-Match首部附加其认可的标签列表并让服务器端检验器原始内容是否有可以与此列表中的某标签匹配的标签,如果有,则返回304,否则,则返回原始内容
7.Vary
响应首部,原始服务器根据请求来源的不同响应的可能会有所不同的首部,常用的是Vary:Accept-Encoding,用于通知缓存机制其内容看起来可能不同于用户请求时Accept-Encoding-hearder首部标识的编码格式
8.Age
缓存服务器可以发送一个额外的响应首部,用于指定响应的有效期限,浏览器通常根据此首部绝对内容的缓存时长:如果响应报文中还使用了max-age指令,那么缓存的有效时长为max-age减去Age结果
五、安装使用varnish
1.ip规划与使用说明
192.168.1.201 varnish
192.168.1.202 web(静态页面)
192.168.1.203 web(图片服务器)
说明:本处安装的varnish3.0.5系列的包,故本处使用的一些命令可能会不适用于varnish2系列和varnish4系列
2.下载与安装
可以使用rpm包的安装方式,也可以使用源码编译安装的方式,官方也提供有rpm,本处使用的为rpm安装的方式
[root@node1 ~]# wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-3.0.5-1.el6.x86_64.rpm [root@node1 ~]# wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-libs-3.0.5-1.el6.x86_64.rpm [root@node1 ~]# wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-docs-3.0.5-1.el6.x86_64.rpm [root@node1 ~]# rpm -ivh varnish*
3.为192.168.1.202提供静态页面
[root@node2 ~]# echo "web1" > /var/www/html/index.html [root@node2 ~]# service httpd start
4.修改varnish的启动文件
[root@node1 ~]# vi /etc/sysconfig/varnish 修改一下参数 VARNISH_LISTEN_PORT=80 //表示将varnish对外的监听端口改为80 VARNISH_STORAGE="malloc,100M" //将数据缓存在内存中,内存大小为100M 其余的使用默认即可
5.配置varnish
本处使用varnishadm命令行接口来刷新varnish
命令格式:varnishadm [-n ident] [-t timeout] [-S secretfile] -T [address]:port command [...]
通过命令行的方式连接至varnishd进行管理操作的工具,指定要连接的varnish实例有两种方法:
-T [address]:port 来接指定套接字上的实例
-n ident 连接指定名称的实例
其运行模式有两种,当不在命令行中给出指定要指定的“command”时,要将进入交互模式,否则,varnish将指定指定的“command”并退出。
[root@node1 ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 help [command] ping [timestamp] //ping节点是否在线 auth response quit banner status //运行状态 start //统计数据 stop vcl.load <configname> <filename> //加载某个文件,作为配置文件 vcl.inline <configname> <quoted_VCLstring> vcl.use <configname> //编译当前的实例 vcl.discard <configname> vcl.list vcl.show <configname> //查看当前的实例 param.show [-l] [<param>] param.set <param> <value> panic.show panic.clear storage.list backend.list backend.set_health matcher state ban.url <regexp> ban <field> <operator> <arg> [&& <field> <oper> <arg>]... ban.list
①、定义一个文件test.vcl,定义一个后端服务器为192.168.1.201,由于varnish不能查看是否命中缓存,并写VCL,查看缓存是否能命中
backend web1 { .host="192.168.1.202"; //定义后端主机 .port="80"; //后端主机监听的端口 } sub vcl_deliver{ if (obj.hits >0 ){ set resp.http.X-Cache= "HIT from " + client.ip; //如果命中,则显示HIT from +客户端ip地址 } else { set resp.http.X-Cache= "MISS"; //否则显示"MISS" } } 编译 varnish> vcl.load test1 test.vcl 200 VCL compiled. varnish> vcl.use test1 200
测试
[root@node1 ~]# curl -I http://192.168.1.201/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Wed, 23 Apr 2014 16:10:19 GMT ETag: "20101-5-4f7b7f8092275" Content-Type: text/html; charset=UTF-8 Content-Length: 5 Accept-Ranges: bytes Date: Wed, 23 Apr 2014 16:49:38 GMT X-Varnish: 1409659707 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS 第一次请求为MISS,表示没有命中缓存 [root@node1 ~]# curl -I http://192.168.1.201/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Wed, 23 Apr 2014 16:10:19 GMT ETag: "20101-5-4f7b7f8092275" Content-Type: text/html; charset=UTF-8 Content-Length: 5 Accept-Ranges: bytes Date: Wed, 23 Apr 2014 16:49:38 GMT X-Varnish: 1409659707 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: HIT from 192.168.1.201 可以看到第二次请求命中,可以多请求几次,以后每次都会是缓存命中
②、比如说我们有一个test.html页面不想缓存,
sub vcl_recv { if (req.url ~ "^/test.html$"){ return(pass); } } varnish> vcl.load test3 test.vcl 200 VCL compiled. varnish> vcl.use test3 200 我们请求test.html [root@node1 ~]# curl -I http://192.168.1.201/test.html HTTP/1.1 404 Not Found Server: Apache/2.2.15 (CentOS) Content-Type: text/html; charset=iso-8859-1 Accept-Ranges: bytes Date: Wed, 23 Apr 2014 16:55:26 GMT X-Varnish: 1409659726 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS 可以看到第一次没有命中 [root@node1 ~]# curl -I http://192.168.1.201/test.html HTTP/1.1 404 Not Found Server: Apache/2.2.15 (CentOS) Content-Type: text/html; charset=iso-8859-1 Accept-Ranges: bytes Date: Wed, 23 Apr 2014 16:55:27 GMT X-Varnish: 1409659727 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS 可以看到第二次也没有命中,请求多次都不会命中
③、为每一种资源定义缓存时长
sub vcl_fetch { if (req.request == "GET" && req.url ~ "\.html$"){ set beresp.ttl = 3600s; } if (req.request == "GET" && req.url ~ "\.{jpg|jpeg|png|gif}$"){ set beresp.ttl = 86400s; } }
④、修剪缓存对象
1.缓存内容修剪
提高缓存命中率的最有效途径之一是增加缓存对象的生存时间(TTL),但是这也可能会带来副作用,比如缓存的内容在到达为其指定的有效期之前就已经失效,因此,手动检查缓存对象的有效性或者刷新缓存是缓存很有可能称为服务器管理员的日常工作之一,相应地,varnish为完成这类的任务提供了三种途径:HTTP修剪(HTTP purging)、禁用某缓存对象(banning)和强制缓存未命中(forced cache misses)
这里需要特殊说明的是,varnish2中的purge()操作在varnish3中被替换成了ban()操作,而varnish3也使用了purge操作,但未其赋予了新功能,且只能用于vcl_hit或vcl_miss中替换varnish2中重用的set obj.ttl=0s
在具体执行某清理工作时,需要实现确认如下问题:
仅需要检验一个特定的缓存对象,还是多个
目的是释放内存空间,还是仅替换缓存的内容
是不是需要很长世间才能完成内容替换
这类操作是个日常工作,还是仅此一次的特殊需求
2.移除单个缓存对象
purge用于清理缓存中的特定对象及其变种(variants),因此,在有着明确要修剪的缓存对象时可以使用此种方法,http协议的PURGE方法可以实现purge功能,不过,其仅能用于vcl_hit或vcl_miss中,它会释放内存工作并移除指定缓存对象的所有vary变种,并期待下一个针对此内容的客户端请求道此时刷新此内容,另外,其一般要与return(restart)一起使用。
acl purgers { "127.0.0.1"; "192.168.1.0"/24; } sub vcl_recv { if (req.request == "PURGE") { if (!client.ip ~ purgers) { error 405 "Method not allowed"; } return (lookup); } } sub vcl_hit { if (req.request == "PURGE") { purge; error 200 "Purged"; } } sub vcl_miss { if (req.request == "PURGE") { purge; error 404 "Not in cache"; } } sub vcl_pass { if (req.request == "PURGE") { error 502 "PURGE on a passed object"; } } 说明:一般把这一项应该放在最前面,以免被别的规则匹配到 varnish> vcl.load test6 test.vcl 200 VCL compiled. varnish> vcl.use test6 200 测试 [root@node1 ~]# curl -I http://192.168.1.201/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Wed, 23 Apr 2014 16:10:19 GMT ETag: "20101-5-4f7b7f8092275" Content-Type: text/html; charset=UTF-8 Content-Length: 5 Accept-Ranges: bytes Date: Wed, 23 Apr 2014 17:25:12 GMT X-Varnish: 1409659756 1409659755 Age: 1 Via: 1.1 varnish Connection: keep-alive X-Cache: HIT from 192.168.1.201 清除缓存 [root@node1 ~]# curl -X PURGE http://192.168.1.201/index.html <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title>200 Purged</title> </head> <body> <h1>Error 200 Purged</h1> <p>Purged</p> <h3>Guru Meditation:</h3> <p>XID: 1409659762</p> <hr> <p>Varnish cache server</p> </body> </html> 在请求测试 [root@node1 ~]# curl -I http://192.168.1.201/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Wed, 23 Apr 2014 16:10:19 GMT ETag: "20101-5-4f7b7f8092275" Content-Type: text/html; charset=UTF-8 Content-Length: 5 Accept-Ranges: bytes Date: Wed, 23 Apr 2014 17:27:35 GMT X-Varnish: 1409659763 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS
3.强制缓存为命中
在VCL_RECV中使用return(pass)能够强制到上游服务器取得请求内容,但这也会导致无法将其缓存,使用purge会移除就的缓存对象,但如果上游服务器宕机而无法取得新版本的内容时,此内容将无法在响应给客户端。使用req.has_always_miss=ture,可以让Varnish在缓存中搜寻相应的内容但却总是回应“未命中”,于是vcl_miss将后续地负责启动vcl_fetch从上游服务器取得新内容,并以新内容缓存覆盖旧内容。此时,如果上游服务器宕机或未响应,旧的内容将保持原状,并能够继续服务于那些未使用req.has_always_miss=true的客户端,直到其过期失效或由其它方法移除。
4、Banning
ban()是一种从已缓存对象中过滤(filter)出某此特定的对象并将其移除的缓存内容刷新机制,不过,它并不阻止新的内容进入缓存或响应于请求。在Varnish中,ban的实现是指将一个ban添加至ban列表(ban-list)中,这可以通过命令行接口或VCL实现,它们的使用语法是相同的。ban本身就是一个或多个VCL风格的语句,它会在Varnish从缓存哈希(cache hash)中查找某缓存对象时对搜寻的对象进行比较测试,因此,一个ban语句就是类似匹配所有“以/downloads开头的URL”,或“响应首部中包含nginx的对象”。例如:
ban req.http.host == "wangfeng7399.com" && req.url ~ "\.gif$"
定义好的所有ban语句会生成一个ban列表(ban-list),新添加的ban语句会被放置在列表的首部。缓存中的所有对象在响应给客户端之前都会被ban列表检查至少一次,检查完成后将会为每个缓存创建一个指向与其匹配的ban语句的指针。Varnish在从缓存中获取对象时,总是会检查此缓存对象的指针是否指向了ban列表的首部。如果没有指向ban列表的首部,其将对使用所有的新添加的ban语句对此缓存对象进行测试,如果没有任何ban语句能够匹配,则更新ban列表。
对ban这种实现方式持反对意见有有之,持赞成意见者亦有之。反对意见主要有两种,一是ban不会释放内存,缓存对象仅在有客户端访问时被测试一次;二是如果缓存对象曾经被访问到,但却很少被再次访问时ban列表将会变得非常大。赞成的意见则主要集中在ban可以让Varnish在恒定的时间内完成向ban列表添加ban的操作,例如在有着数百万个缓存对象的场景中,添加一个ban也只需要在恒定的时间内即可完成。其实现方法本处不再详细说明。
⑤、Varnish检测后端主机的健康状态
varnish可以坚持后端主机的健康状态,在判断后端主机失效时能自动将其从可用后端主机列表中移除,而一旦其重新变得可用还可以自动给将其设定为可用。为了避免误判,varnish在探测后端的健康状态发生转变时(比如某次探测是某后端主机突然变成不可以状态),通常需要连续执行几次探测均为新状态才能将其标记为转换后的状态。
每个后端服务器当前探测的方法通过.probe进行设定,其结果可由req.backend.healthy变量获取,也可以通过varnishlog中的Backend_health查看或varnishadm的debug.health查看
.probe中的探测指令常用的有:
.url:探测后端主机监控状态时请求的URL,默认为“/”
.request:探测后端主机健康状态时所请求内容的详细格式,定义后,它会替换.url指定的探测方法
.window:设定在判断后端服务器健康状态是基于最近多少次的探测进行,默认为8
.threshold:在.window中指定的次数中,至少有多少次是成功的才判定后端主机正健康运行
.initial:varnish启动时对后端主机至少需要多少次的成功探测,默认同.threshold
.expencted_response:期望后端主机响应的状态码,默认为200
.interval:探测请求的发送周期,默认为5秒
.timeout:每次探测请求的过期时长,默认为2秒
如果varnish在某时刻没有任何可用的后端主机,它将尝试使用缓存对象的“宽容副本”(graced copy),担任,此时VCL中的各种规则依然有效,因此,更好的办法是在VCL规则中判断req.backend.healthy变量显示后端某主机不可用时,为此后端主机增大req.grace变量的值以设定适当的宽容期限长度。
⑥、使用多台后端主机
varnish中可以使用director指令将一个或多个相似的后端主机定义为一个逻辑组,并可以指定调度方式(也叫挑选方法)来轮流将请求发送至这些主机上,不同的director可以使用同一个后端主机,而某director也可以使用“匿名”后端主机(在director中直接进行定义)。每个director都必须有其专用名,且在定义后必须在VCL中进行调用,VCL中任何可以指定后端主机的位置均可以按需将其替换为调用某已定义的director。
varnish的dorector支持的挑选方法中比较简单的有round-robin和random两种,其中,round-robin类型没有任何参数,挑选方法为“轮询”,并在某后端主机故障时不在将其视为挑选对象,random方法随机从可用后端主机中挑选,每一个后端主机都需要一个.wegith参数来指定其权重,同时还可以director级别使用.retires参数来设定查找一个健康后端主机时的尝试次数
varnish2.1.0后,random挑选方法又多了两种变化形式client和hash。client类型的director使用client.identity作为挑选因子,这意味着client.identity相同的请求都将被发送至同一个后端主机,client.identity默认为client.ip,但也可以在VCL中将其修改为所需要的标识符,类似地,hash类型的director使用hash数据作为挑选因子,这意味着同一个URL的请求都将发往同一个后端主机,其常用于多级缓存的场景中,然而,无论是client还是hash,当期倾向于使用后端主机不可用时将会重新挑选新的后端主机。
另外还有一中成为fallback的director,用于定义备用服务器
backend web1 { .host="192.168.1.202"; .port="80"; .probe = { .url="/index.html"; .interval =2s; .window =5; .threshold=2 } } backend web2 { .host="192.168.1.203"; .port="80"; .probe = { .url="/index.html"; .interval =2s; .window =5; .threshold=2 } } 定义每个2s检查后端主机的主页,如果最近5次的探测请求中至少有2次是成功的就判断此后端主机为正常工作状态 director websv random { .retries =4; { .backend = web1; .weight =2; } { .backend = web2; .weight =4; } } 定义主机组 sub vcl_recv { set req.backend =websv; } 调用主机组 [root@node1 ~]# curl http://192.168.1.201/index.html web1 我们将web1的httpd关掉 [root@node1 ~]# curl http://192.168.1.201/index.html web2
⑦、后端服务器的分离
varnish支持基于URL将后端的服务器分离
在vcl_decv中添加如下行 if (req.url ~"\.(html)$"){ set req.backend = web1; }else{ set req.backend = web2; } 通过curl访问index.html [root@node1 ~]# curl http://192.168.1.201/index.html web1
转载于:https://blog.51cto.com/wangfeng7399/1407418