一、varnish功能及特性简介

1、varnish简介

 Varnish 的作者Poul-Henning Kamp是FreeBSD的内核开发者之一,他认为现在的计算机比起1975年已经复杂许多。在1975年时,储存媒介只有两种:内存与硬盘。但现在计算机系统的内存除了主存外,还包括了CPU内的L1、L2,甚至有L3快取。硬盘上也有自己的快取装置,因此Squid Cache自行处理物件替换的架构不可能得知这些情况而做到最佳化,但操作系统可以得知这些情况,所以这部份的工作应该交给操作系统处理,这就是 Varnish cache设计架构。 很多门户网站已经部署了varnish,并且反应都很好,甚至反应比squid还稳定,且效率更高,资源占用更少。相信在反向代理,web加速方面,varnish已经有足够能力代替squid。

2、特点

   Varnish是一个轻量级的Cache和反向代理软件。先进的设计理念和成熟的设计框架是Varnish的主要特点。现在的Varnish总共代码量不大,虽然功能在不断改进,但是还需要继续丰富和加强。下面总结了Varnish的一些特点。

  • 基于内存进行缓存,重启后数据将消失。 

  • 利用虚拟内存方式,I/O性能好。 

  • 支持设置0~60秒内的精确缓存时间。 

  • VCL配置管理比较灵活。 

  • 32位机器上缓存文件大小为最大2GB。 

  • 具有强大的管理功能,例如top,stat,admin,list等。 

  • 状态机设计巧妙,结构清晰。 

  • 利用二叉堆管理缓存文件,达到积极删除目的。


二、、varnish设计结构

Varnish主要运行两个进程:Management进程和Child进程(也叫Cache进程)。如下图:

wKioL1YI7OKxio7UAAE02BxXtwc238.jpg

1、Management进程

   Management进程主要实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等。Management进程会每隔几秒钟探测一下Child进程以判断其是否正常运行,如果在指定的时长内未得到Child进程的回应,Management将会重启此Child进程。 Management 管理接口:

  • CLI interface 命令行接口 

  • Telnet interface telnet接口 

  • Web interface Web管理接口 

2、Child/Cache 进程

Child/Cache 进程包含多种类型的线程,常见的如:

  • Accept 线程:接收新的连接请求并响应; 

  • Worker 线程:child进程会为每个会话启动一个worker线程,因此,在高并发的场景中可能会出现数百个worker线程甚至更多; 

  • Object Expiry 线程:从缓存中清理过期内容; 

  • Commad line 线程 : 管理接口 

  • Storage/hashing 线程 :缓存存储 

  • Log/stats 线程:日志管理线程 

  • Backend Communication 线程:管理后端主机线程 

  • Varnish依赖“工作区(workspace)”以降低线程在申请或修改内存时出现竞争的可能性。在varnish内部有多种不同的工作区,其中最关键的当属用于管理会话数据的session工作区。 

3、Varnish日志

   为了与系统的其它部分进行交互,Child进程使用了可以通过文件系统接口进行访问的共享内存日志(shared memory log),因此,如果某线程需要记录信息,其仅需要持有一个锁,而后向共享内存中的某内存区域写入数据,再释放持有的锁即可。而为了减少竞争,每个worker线程都使用了日志数据缓存。共享内存日志大小一般为90M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。varnish提供了多个不同的工具如varnishlog、varnishncsa或varnishstat等来分析共享内存日志中的信息并能够以指定的方式进行显示。

4、VCL(Varnish Configuation Language)简介

   Varnish Configuration Language (VCL)是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命令完成。

5、Varnish 的后端存储

Varnish支持多种不同类型的后端存储,这可以在varnishd启动时使用-s选项指定。后端存储的类型包括:

  • file:使用特定的文件存储全部的缓存数据,并通过操作系统的mmap()系统调用将整个缓存文件映射至内存区域(如果条件允许); 

  • malloc:使用malloc()库调用在varnish启动时向操作系统申请指定大小的内存空间以存储缓存对象; 

  • persistent(experimental):与file的功能相同,但可以持久存储数据(即重启varnish数据时不会被清除);仍处于测试期; 

   Varnish无法追踪某缓存对象是否存入了缓存文件,从而也就无从得知磁盘上的缓存文件是否可用,因此,file存储方法在varnish停止或重启时会清除数据。而persistent方法的出现对此有了一个弥补,但persistent仍处于测试阶段,例如目前尚无法有效处理要缓存对象总体大小超出缓存空间的情况,所以,其仅适用于有着巨大缓存空间的场景。

   选择使用合适的存储方式有助于提升系统性,从经验的角度来看,建议在内存空间足以存储所有的缓存对象时使用malloc的方法,反之,file存储将有着更好的性能的表现。然而,需要注意的是,Varnishd实际上使用的空间比使用-s选项指定的缓存空间更大,一般说来,其需要为每个缓存对象多使用差不多1K左右的存储空间,这意味着,对于100万个缓存对象的场景来说,其使用的缓存空间将超出指定大小1G左右。另外,为了保存数据结构等,varnish自身也会占去不小的内存空间。 以下为varnishd指定使用的缓存类型时,-s选项可接受的参数格式如下: 

  • malloc[,size] 或 

  • file[,path[,size[,granularity]]] 或 

  • persistent,path,size {experimental} 

注,file中的granularity用于设定缓存空间分配单位,默认单位是字节,所有其它的大小都会被圆整。


三、varnish工作流程

Varnish处理HTTP请求的过程大致分为如下几个步骤:

  • Receive状态,也就是请求处理的入口状态,根据VCL规则判断该请求应该Pass或Pipe,还是进入Lookup(本地查询)。 

  • Lookup状态,进入此状态后,会在hash表中查找数据,若找到,则进入Hit状态,否则进入miss状态。 

  • Pass状态,在此状态下,会进入后端请求,即进入Fetch状态。 

  • Fetch状态,在Fetch状态下,对请求进行后端获取,发送请求,获得数据,并进行本地存储。 

  • Deliver状态, 将获取到的数据发送给客户端,然后完成本次请求。

wKioL1YI7R6xvb6XAAMZtFoJlSU009.jpg


四、Varnish 状态引擎(state engine)

   VCL用于让管理员定义缓存策略,而定义好的策略将由varnish的management进程分析、转换成C代码、编译成二进制程序并连接至child进程。varnish内部有几个所谓的状态(state),在这些状态上可以附加通过VCL定义的策略以完成相应的缓存处理机制,因此VCL也经常被称作“域专用”语言或状态引擎,“域专用”指的是有些数据仅出现于特定的状态中。

1、VCL状态引擎

   在VCL状态引擎中,状态之间具有相关性,但彼此间互相隔离,每个引擎使用return(x)来退出当前状态并指示varnish进入下一个状态。 Varnish开始处理一个请求时,首先需要分析HTTP请求本身,比如从首部获取请求方法、验正其是否为一个合法的HTT请求等。当这些基本分析结束后就需要做出第一个决策,即varnish是否从缓存中查找请求的资源。这个决定的实现则需要由VCL来完成,简单来说,要由vcl_recv方法来完成。如果管理员没有自定义vcl_recv函数,varnish将会执行默认的vcl_recv函数。然而,即便管理员自定义了vcl_recv,但如果没有为自定义的vcl_recv函数指定其终止操作(terminating),其仍将执行默认的vcl_recv函数。事实上,varnish官方强烈建议让varnish执行默认的vcl_recv以便处理自定义vcl_recv函数中的可能出现的漏洞。 

2、VCL语法

VCL的设计参考了C和Perl语言,因此,对有着C或Perl编程经验者来说,其非常易于理解。其基本语法说明如下:

(1)//、#或/* comment */用于注释

(2)sub $name 定义函数

(3)不支持循环,有内置变量

(4)使用终止语句,没有返回值

(5)域专用

(6)操作符:=(赋值)、==(等值比较)、~(模式匹配)、!(取反)、&&(逻辑与)、||(逻辑或)

   VCL的函数不接受参数并且没有返回值,因此,其并非真正意义上的函数,这也限定了VCL内部的数据传递只能隐藏在HTTP首部内部进行。VCL的return语句用于将控制权从VCL状态引擎返回给Varnish,而非默认函数,这就是为什么VCL只有终止语句而没有返回值的原因。同时,对于每个“域”来说,可以定义一个或多个终止语句,以告诉Varnish下一步采取何种操作,如查询缓存或不查询缓存等。

3、VCL内置函数

(1).vcl_recv函数 用于接收和处理请求。当请求到达并被成功接收后被调用,通过判断请求的数据来决定如何处理请求。 此函数一般以如下几个关键字结束。

  • pass:表示进入pass模式,把请求控制权交给vcl_pass函数。 

  • pipe:表示进入pipe模式,把请求控制权交给vcl_pipe函数。 

  • error code [reason]:表示返回“code”给客户端,并放弃处理该请求。“code”是错误标识,例如200和405等,“reason”是错误提示信息。 

(2).vcl_pipe函数 此函数在进入pipe模式时被调用,用于将请求直接传递至后端主机,在请求和返回的内容没有改变的情况下,将不变的内容返回给客户端,直到这个链接被关闭。 此函数一般以如下几个关键字结束。

  • error code [reason] 

  • pipe 

(3).vcl_pass函数 此函数在进入pass模式时被调用,用于将请求直接传递至后端主机,后端主机在应答数据后将应答数据发送给客户端,但不进行任何缓存,在当前连接下每次都返回最新的内容。 此函数一般以如下几个关键字结束。 

  • error code [reason] 。 

  • pass。 

(4).lookup 表示在缓存中查找被请求的对象,并且根据查找的结果把控制权交给函数vcl_hit或函数vcl_miss。 

(5).vcl_hit函数 在执行lookup指令后,在缓存中找到请求的内容后将自动调用该函数。 此函数一般以如下几个关键字结束。 

  • deliver:表示将找到的内容发送给客户端,并把控制权交给函数vcl_deliver。 

  • error code [reason] 。 

  • pass。 

(6).vcl_miss函数 在执行lookup指令后,在缓存中没有找到请求的内容时自动调用该方法,此函数可用于判断是否需要从后端服务器获取内容。 此函数一般以如下几个关键字结束。 

  • fetch:表示从后端获取请求的内容,并把控制权交给vcl_fetch函数。 

  • error code [reason] 。 

  • pass。 

(7).vcl_fetch 函数 在后端主机更新缓存并且获取内容后调用该方法,接着,通过判断获取的内容来决定将内容放入缓存,还是直接返回给客户端。此函数一般以如下几个关键字结束。

  • rror code [reason] 

  • pass 

  • deliver 

(8).vcl_deliver 函数 将在缓存中找到请求的内容发送给客户端前调用此方法。此函数一般以如下几个关键字结束。 

  • error code [reason] 

  • deliver 

(9).vcl_timeout 函数 在缓存内容到期前调用此函数。此函数一般以如下几个关键字结束。 

  • discard:表示从缓存中清除该内容。 

  • fetch。 

(10).vcl_discard 函数 在缓存内容到期后或缓存空间不够时,自动调用该函数。该函数一般以如下几个关键字结束。 

  • keep:表示将内容继续保留在缓存中。 

  • discard。 

4、内置公用变量

VCL内置的公用变量可以用在不同的VCL函数中。下面根据这些公用变量使用的不同阶段依次进行介绍。

(1).当请求到达后,可以使用的公用变量

  • req.backend 指定对应的后端主机 

  • server.ip 表示服务器端IP 

  • client.ip 表示客户端IP 

  • req.request 指定请求的类型,例如GET、HEAD和POST等 

  • req.url 指定请求的地址 

  • req.proto 表示客户端发起请求的HTTP协议版本 

  • req.http.header 表示对应请求中的HTTP头部信息 

  • req. restarts ;/.l表示请求重启的次数,默认最大值为4 

(2).Varnish在向后端主机请求时,可以使用的公用变量

  • beresp.request 指定请求的类型,例如GET合HEAD等 

  • beresp.url 指定请求的地址 

  • beresp .proto 表示客户端发起请求的HTTP协议版本 

  • beresp .http.header 表示对应请求中的HTTP头部信息 

  • beresp .ttl 表示缓存的生存周期,也就是cache保留多长时间,单位是秒 

(3).从cache或后端主机获取内容后,可以使用的公用变量

  • obj.status 表示返回内容的请求状态代码,例如200、302和504等 

  • obj.cacheable 表示返回的内容是否可以缓存,也就是说,如果HTTP返回的是200、203、300、301、302、404或410等,并且有非0的生存期,则可以缓存 

  • obj.valid 表示是否是有效的HTTP应答 

  • obj.response 表示返回内容的请求状态信息 

  • obj.proto 表示返回内容的HTTP协议版本 

  • obj.ttl 表示返回内容的生存周期,也就是缓存时间,单位是秒 

  • obj.lastuse 表示返回上一次请求到现在的间隔时间,单位是秒 

(4).对客户端应答时,可以使用的公用变量

  • resp.status 表示返回给客户端的HTTP状态代码 

  • resp.proto 表示返回给客户端的HTTP协议版本 

  • resp.http.header 表示返回给客户端的HTTP头部信息 

  • resp.response 表示返回给客户端的HTTP状态信息 

5.常用内置函数

(1).vcl_recv

vcl_recv是在Varnish完成对请求报文的解码为基本数据结构后第一个要执行的子例程,它通常有四个主要用途:

  • 修改客户端数据以减少缓存对象差异性;比如删除URL中的www.等字符; 

  • 基于客户端数据选用缓存策略;比如仅缓存特定的URL请求、不缓存POST请求等; 

  • 为某web应用程序执行URL重写规则; 

  • 挑选合适的后端Web服务器; 

可以使用下面的终止语句,即通过return()向Varnish返回的指示操作:

  • pass:绕过缓存,即不从缓存中查询内容或不将内容存储至缓存中; 

  • pipe:不对客户端进行检查或做出任何操作,而是在客户端与后端服务器之间建立专用“管道”,并直接将数据在二者之间进行传送;此时,keep-alive连接中后续传送的数据也都将通过此管道进行直接传送,并不会出现在任何日志中; 

  • lookup:在缓存中查找用户请求的对象,如果缓存中没有其请求的对象,后续操作很可能会将其请求的对象进行缓存; 

  • error:由Varnish自己合成一个响应报文,一般是响应一个错误类信息、重定向类信息或负载均衡器返回的后端web服务器健康状态检查类信息; 

vcl_recv也可以通过精巧的策略完成一定意义上的安全功能,以将某些特定的***扼杀于摇篮中。同时,它也可以检查出一些拼写类的错误并将其进行修正等。 Varnish默认的vcl_recv专门设计用来实现安全的缓存策略,它主要完成两种功能: 

  • 仅处理可以识别的HTTP方法,并且只缓存GET和HEAD方法; 

  • 不缓存任何用户特有的数据; 

注,安全起见,一般在自定义的vcl_recv中不要使用return()终止语句,而是再由默认vcl_recv进行处理,并由其做出相应的处理决策。

下面是一个自定义的使用示例:

sub vcl_recv {

  if (req.http.User-Agent ~ "iPad" ||

    req.http.User-Agent ~ "iPhone" ||

    req.http.User-Agent ~ "Android") {

      set req.http.X-Device = "mobile";

  } else {

      set req.http.X-Device = "desktop";

  }

}

此例中的VCL创建一个X-Device请求首部,其值可能为mobile或desktop,于是web服务器可以基于此完成不同类型的响应,以提高用户体验。

(2).vcl_fetch

如前面所述,相对于vcl_recv是根据客户端的请求作出缓存决策来说,vcl_fetch则是根据服务器端的响应作出缓存决策。在任何VCL状态引擎中返回的pass操作都将由vcl_fetch进行后续处理。vcl_fetch中有许多可用的内置变量,比如最常用的用于定义某对象缓存时长的beresp.ttl变量。通过return()返回给arnish的操作指示有: 

  • deliver:缓存此对象,并将其发送给客户端(经由vcl_deliver); 

  • hit_for_pass:不缓存此对象,但可以导致后续对此对象的请求直接送达到vcl_pass进行处理; 

  • restart:重启整个VCL,并增加重启计数;超出max_restarts限定的最大重启次数后将会返回错误信息; 

  • error code [reason]:返回指定的错误代码给客户端并丢弃此请求; 

默认的vcl_fetch放弃了缓存任何使用了Set-Cookie首部的响应。 

6、其它内置函数

VCL提供了几个函数来实现字符串的修改,添加bans,重启VCL状态引擎以及将控制权转回Varnish等。

  • regsub(str,regex,sub) 匹配正则表达式的字符串 

  • regsuball(str,regex,sub):这两个用于基于正则表达式搜索指定的字符串并将其替换为指定的字符串;但regsuball()可以将str中能够被regex匹配到的字符串统统替换为sub,regsub()只替换一次; 

  • ban(expression): 

  • ban_url(regex):Bans所有其URL能够由regex匹配的缓存对象; 

  • purge:从缓存中挑选出某对象以及其相关变种一并删除,这可以通过HTTP协议的PURGE方法完成; 

  • hash_data(str): 

  • return():当某VCL域运行结束时将控制权返回给Varnish,并指示Varnish如何进行后续的动作;其可以返回的指令包括:lookup、pass、pipe、hit_for_pass、fetch、deliver和hash等;但某特定域可能仅能返回某些特定的指令,而非前面列出的全部指令; 

  • return(restart):重新运行整个VCL,即重新从vcl_recv开始进行处理;每一次重启都会增加req.restarts变量中的值,而max_restarts参数则用于限定最大重启次数。

7、各种变量所能使用的引擎

wKiom1YI7pzgUjfSAAGf3ud9ksA319.jpg


五、安装与配置 Varnish

1、实验环境介绍

wKiom1YI7v6iWIazAACHxWEqIHI688.jpg

2、安装varnish

[root@proxy ~]# rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-3.0.el6.rpm
[root@proxy ~]# yum install varnish

3、默认启动脚本的配置文件说明

[root@proxy ~]# cat /etc/sysconfig/varnish 
# Configuration file for varnish
#
# /etc/init.d/varnish expects the variable $DAEMON_OPTS to be set from this
# shell script fragment.
#
# Maximum number of open files (for ulimit -n)
NFILES=131072 #打开最大文件数
# Locked shared memory (for ulimit -l)
# Default log size is 82MB + header
MEMLOCK=82000 #默认日志大小
# Maximum number of threads (for ulimit -u)
NPROCS="unlimited" #最大线程数
# Maximum size of corefile (for ulimit -c). Default in Fedora is 0
# DAEMON_COREFILE_LIMIT="unlimited" #最大内核打开的文件数
# Set this to 1 to make init script reload try to switch vcl without restart.
# To make this work, you need to set the following variables
# explicit: VARNISH_VCL_CONF, VARNISH_ADMIN_LISTEN_ADDRESS,
# VARNISH_ADMIN_LISTEN_PORT, VARNISH_SECRET_FILE, or in short,
# use Alternative 3, Advanced configuration, below
RELOAD_VCL=1 #是否自动加载VCL
# This file contains 4 alternatives, please use only one.
## Alternative 1, Minimal configuration, no VCL #方案1,最小配置,不方便
#
# Listen on port 6081, administration on localhost:6082, and forward to
# content server on localhost:8080. Use a fixed-size cache file.
#
#DAEMON_OPTS="-a :6081 \
#       -T localhost:6082 \
#       -b localhost:8080 \
#       -u varnish -g varnish \
#       -s file,/var/lib/varnish/varnish_storage.bin,1G"
## Alternative 2, Configuration with VCL #方案2,配置组件
#
# Listen on port 6081, administration on localhost:6082, and forward to
# one content server selected by the vcl file, based on the request. Use a
# fixed-size cache file.
#
#DAEMON_OPTS="-a :6081 \
#       -T localhost:6082 \
#       -f /etc/varnish/default.vcl \
#       -u varnish -g varnish \
#       -S /etc/varnish/secret \
#       -s file,/var/lib/varnish/varnish_storage.bin,1G"
## Alternative 3, Advanced configuration #方案3,高级配置
#
# See varnishd(1) for more information.
#
# # Main configuration file. You probably want to change it :)
VARNISH_VCL_CONF=/etc/varnish/default.vcl #默认的VCL存放位置
#
# # Default address and port to bind to
# # Blank address means all IPv4 and IPv6 interfaces, otherwise specify
# # a host name, an IPv4 dotted quad, or an IPv6 address in brackets.
# VARNISH_LISTEN_ADDRESS=
VARNISH_LISTEN_PORT=6081 #服务监听端口
#
# # Telnet admin interface listen address and port
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 #管理IP
VARNISH_ADMIN_LISTEN_PORT=6082 #管理端口
#
# # Shared secret file for admin interface
VARNISH_SECRET_FILE=/etc/varnish/secret #默认的加密文件
#
# # The minimum number of worker threads to start
VARNISH_MIN_THREADS=50 #最小线程数
#
# # The Maximum number of worker threads to start
VARNISH_MAX_THREADS=1000 #最大线程数
#
# # Idle timeout for worker threads
VARNISH_THREAD_TIMEOUT=120 #线程超时时间
#
# # Cache file location
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin #缓存文件的位置
#
# # Cache file size: in bytes, optionally using k / M / G / T suffix,
# # or in percentage of available disk space using the % suffix.
VARNISH_STORAGE_SIZE=1G #设置存储的大小
#
# # Backend storage specification #后端存储规范,这里是我们主要配置的地方
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" #默认存储在文件里,我们这里修改为malloc
#
# # Default TTL used when the backend does not specify one #默认TTL时使用的后端不指定一个
VARNISH_TTL=120
#
# # DAEMON_OPTS is used by the init script. If you add or remove options, make
# # sure you update this section, too. #所有的启动选项
DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \
       -f ${VARNISH_VCL_CONF} \
       -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
       -t ${VARNISH_TTL} \
       -w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \
       -u varnish -g varnish \
       -S ${VARNISH_SECRET_FILE} \
       -s ${VARNISH_STORAGE}"
#
## Alternative 4, Do It Yourself. See varnishd(1) for more information. #方案4,设置你自己的配置。看到varnishd(1)更多的信息。
#
# DAEMON_OPTS=""

4、简单配置使用varnish

[root@proxy ~]# vim /etc/sysconfig/varnish 
VARNISH_LISTEN_PORT=80 #这里设置的监听端口设置为80
VARNISH_STORAGE="malloc,64M" #我们这里设置的是64M内存空间。

5、启动服务

[root@proxy ~]# service varnish start
Starting Varnish Cache:       [确定]

6、查看启动端口

[root@proxy ~]# ss -tunlp |grep varnish
tcp    LISTEN     0      10             127.0.0.1:6082                  *:*      users:(("varnishd",19626,6))
tcp    LISTEN     0      128                   :::80                   :::*      users:(("varnishd",19627,8))
tcp    LISTEN     0      128                    *:80                    *:*      users:(("varnishd",19627,7))

7、连接管理接口

[root@proxy ~]#  varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
200        
-----------------------------
Varnish Cache CLI 1.0
-----------------------------
Linux,2.6.32-504.el6.x86_64,x86_64,-smalloc,-smalloc,-hcritbit
varnish-3.0.7 revision f544cd8
Type 'help' for command list.
Type 'quit' to close CLI session.
varnish>

8、命令帮助信息

varnish> help
200
help [command] #查看帮助
ping [timestamp]
auth response
quit
banner #查看banner
status #查看状态
start
stop
vcl.load <configname> <filename> #加载vcl
vcl.inline <configname> <quoted_VCLstring>
vcl.use <configname>#使用vcl
vcl.discard <configname>
vcl.list #查看vcl列表
vcl.show <configname>#查看vcl内容
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
测试一下
varnish> status
200
Child in state running

9、将后端web服务器启动服务并测试

[root@node1 ~]# rpm -q httpd
httpd-2.2.15-45.el6.centos.x86_64
[root@node1 ~]# service httpd start
正在启动 httpd:          [确定]
[root@node1 ~]# cat /www/a.com/htdoc/index.html 
<h1>This is node1 !</h1>
[root@proxy ~]# curl 
<h1>This is node1 !</h1>


六、Varnish实例演示

1、增加vcl配置文件

[root@proxy ~]# cat /etc/varnish/test.vcl
backend webserver {
 .host = "192.168.1.9";
 .port = "80";
}
 sub vcl_recv {
 set req.backend = webserver;
}

2、进入管理界面并启动设置的配置vcl文件

[root@proxy ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
200        
-----------------------------
Varnish Cache CLI 1.0
-----------------------------
Linux,2.6.32-504.el6.x86_64,x86_64,-smalloc,-smalloc,-hcritbit
varnish-3.0.7 revision f544cd8
Type 'help' for command list.
Type 'quit' to close CLI session.
varnish> vcl.load test1 test.vcl
200        
VCL compiled.
varnish> vcl.list
200        
active          0 boot
available       0 test1
varnish> vcl.use test1
200

3、测试是否能访问web服务

[root@proxy ~]# curl -I http://192.168.1.8
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Thu, 10 Sep 2015 10:04:27 GMT
ETag: "5fd33-19-51f61b9729227"
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
Date: Sat, 12 Sep 2015 22:33:01 GMT
X-Varnish: 1200757626
Age: 0
Via: 1.1 varnish
Connection: keep-alive

4、显示是否命中缓存

更改配置文件:

[root@proxy ~]# vim /etc/varnish/test.vcl
backend webserver {
 .host = "192.168.1.9";
 .port = "80";
}
 sub vcl_recv {
 set req.backend = webserver;
}
sub vcl_deliver {
     if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
        set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
  }

载入配置文件:

varnish> vcl.load test2 test.vcl
200        
VCL compiled.
varnish> vcl.use test2
200

测试:

[root@proxy ~]# curl -I http://192.168.1.8
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Thu, 10 Sep 2015 10:04:27 GMT
ETag: "5fd33-19-51f61b9729227"
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
Date: Sat, 12 Sep 2015 22:35:15 GMT
X-Varnish: 1200757627
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: Miss via 192.168.1.8
[root@proxy ~]# curl -I http://192.168.1.8
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Thu, 10 Sep 2015 10:04:27 GMT
ETag: "5fd33-19-51f61b9729227"
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
Date: Sat, 12 Sep 2015 22:35:17 GMT
X-Varnish: 1200757628 1200757627
Age: 1
Via: 1.1 varnish
Connection: keep-alive
X-Cache: Hit from 192.168.1.8

5、不让缓存命中的使用

修改配置文件:

[root@proxy ~]# cat /etc/varnish/test.vclbackend webserver {
 .host = "192.168.1.9";
 .port = "80";
}
 sub vcl_recv {
    if (req.url ~ "^/test1.html$") {
        return(pass);
    }
 set req.backend = webserver;
}
sub vcl_deliver {
     if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
        set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
  }

增加测试文件:

[root@node1 ~]# cat /www/a.com/htdoc/test1.html
<h1>node1.test1</h1>

载入配置文件:

varnish> vcl.load test3 test.vcl
200        
VCL compiled.
varnish> vcl.use test3
200

测试配置文件是否启用:

[root@proxy ~]# curl -I http://192.168.1.8/test1.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 12 Sep 2015 12:12:23 GMT
ETag: "5fd2f-15-51f8bbea887cd"
Accept-Ranges: bytes
Content-Length: 21
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
Accept-Ranges: bytes
Date: Sat, 12 Sep 2015 22:38:57 GMT
X-Varnish: 1200757629
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: Miss via 192.168.1.8
[root@proxy ~]# curl -I http://192.168.1.8/test1.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 12 Sep 2015 12:12:23 GMT
ETag: "5fd2f-15-51f8bbea887cd"
Accept-Ranges: bytes
Content-Length: 21
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
Accept-Ranges: bytes
Date: Sat, 12 Sep 2015 22:39:00 GMT
X-Varnish: 1200757630
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: Miss via 192.168.1.8

6、控制某个目录下的文件不让缓存

[root@proxy ~]# vim /etc/varnish/test.vcl
backend webserver {
 .host = "192.168.1.9";
 .port = "80";
}
sub vcl_recv {
    if (req.url ~ "^/bbs/") {
        return(pass);
    }
    set req.backend = webserver;
}
 sub vcl_deliver {
     if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
        set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
 }

7、控制以.php结尾的文件不让缓存

[root@proxy ~]# vim /etc/varnish/test.vcl
backend webserver {
 .host = "192.168.1.9";
 .port = "80";
}
  sub vcl_recv {
    if (req.url ~ "\.php$") {
        return(pass);
    }
    set req.backend = webserver;
}
 sub vcl_deliver {
     if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
        set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
 }

8、关于wordpress用户登录时的缓存设置

# 将发送到Wordpress中的不是以登录或者管理的cookie给删除。
 sub vcl_recv {
  if (!(req.url ~ "wp-(login|admin)")) {
   unset req.http.cookie;
  }
 }
#  删除客户端中在Wordpress不是以登录或者管理的cookie信息
 sub vcl_fetch {
  if (!(req.url ~ "wp-(login|admin)")) {
   unset beresp.http.set-cookie;
  }
 }

9、根据响应的内容做出决策案例

[root@proxy ~]# vim /etc/varnish/test.vcl
backend webserver {
 .host = "192.168.1.9";
 .port = "80";
}
sub vcl_recv {
    if (req.url ~ "^/test.html$") {
        return(pass);
    }
    set req.backend = webserver;
}
sub vcl_fetch {
    if (req.request == "GET" && req.url ~ "\.(html|css|js|jpg|jpeg|png|gif)$") {
        set beresp.ttl = 3600s;
    }
}
sub vcl_deliver {
     if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
        set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
 }

载入配置文件:

varnish> vcl.load test4 test.vcl
200        
VCL compiled.
varnish> vcl.use test4
200

测试:

[root@proxy ~]# date
2015年 09月 13日 星期日 19:44:59 CST
[root@proxy ~]# curl -I http://192.168.1.8
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Thu, 10 Sep 2015 10:04:27 GMT
ETag: "5fd33-19-51f61b9729227"
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
Date: Sun, 13 Sep 2015 11:45:18 GMT
X-Varnish: 1200757635
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: Miss via 192.168.1.8


七、Varnish 检测后端主机的健康状态

   Varnish可以检测后端主机的健康状态,在判定后端主机失效时能自动将其从可用后端主机列表中移除,而一旦其重新变得可用还可以自动将其设定为可用。为了避免误判,Varnish在探测后端主机的健康状态发生转变时(比如某次探测时某后端主机突然成为不可用状态),通常需要连续执行几次探测均为新状态才将其标记为转换后的状态。 每个后端服务器当前探测的健康状态探测方法通过.probe进行设定,其结果可由req.backend.healthy变量获取,也可通过varnishlog中的Backend_health查看或varnishadm的debug.health查看。

backend web1 {
  .host = "www.test.com";
  .probe = {
    .url = "/.healthtest.html";
    .interval = 1s;
    .window = 3;
    .threshold = 1;
  }
}

.probe中的探测指令常用的有:

(1) .url:探测后端主机健康状态时请求的URL,默认为“/”;

(2) .request: 探测后端主机健康状态时所请求内容的详细格式,定义后,它会替换.url指定的探测方式;比如:

.request =

  "GET /.healthtest.html HTTP/1.1"

  "Host: www.test.com"

  "Connection: close";

(3) .window:设定在判定后端主机健康状态时基于最近多少次的探测进行,默认是8;

(4) .threshold:在.window中指定的次数中,至少有多少次是成功的才判定后端主机正健康运行;默认是3;

(5) .initial:Varnish启动时对后端主机至少需要多少次的成功探测,默认同.threshold;

(6) .expected_response:期望后端主机响应的状态码,默认为200;

(7) .interval:探测请求的发送周期,默认为5秒;

(8) .timeout:每次探测请求的过期时长,默认为2秒;

   因此,如上示例中表示每隔1秒对此后端主机www.test.com探测一次,请求的URL为http://www.test.com/.healthtest.html,在最近5次的探测请求中至少有2次是成功的(响应码为200)就判定此后端主机为正常工作状态。

   如果Varnish在某时刻没有任何可用的后端主机,它将尝试使用缓存对象的“宽容副本”(graced copy),当然,此时VCL中的各种规则依然有效。因此,更好的办法是在VCL规则中判断req.backend.healthy变量显示某后端主机不可用时,为此后端主机增大req.grace变量的值以设定适用的宽容期限长度。


八、Varnish 对应两台Web服务器的配置实例

1、根据条件进行判断

[root@proxy ~]# vim /etc/varnish/test.vcl
backend webserver1 {
    .host = "192.168.1.9";
    .port = "80";
}
backend webserver2 {
    .host = "192.168.1.10";
    .port = "80";
}
sub vcl_recv {
    if (req.url ~ "\.php$") {
      set req.backend = webserver1;
    }
    if (req.url ~ "\.(html|css|js|jpg|jpeg|png|gif)$") {
      set req.backend = webserver2;
    }
}
sub vcl_deliver {
     if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
        set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
 }

2、定义为Web集群

[root@proxy ~]# vim /etc/varnish/test.vcl
backend webserver1 {
    .host = "192.168.1.9";
    .port = "80";
}
backend webserver2 {
    .host = "192.168.1.10";
    .port = "80";
}
director webserver random {
    { .backend = webserver1; .weight = 1; }
    { .backend = webserver2; .weight = 1; }
}
sub vcl_recv {
      set req.backend = webserver;
}
sub vcl_deliver {
     if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
        set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
 }

3、配置使用集群服务

启动node2节点的httpd服务并提供测试页:

[root@node2 ~]# service httpd start
[root@node2 ~]# cat /www/a.com/htdoc/index.html 
<h1>This is node2 !</h1>

测试web服务是否启动:

[root@proxy ~]# curl http://192.168.1.9
<h1>This is node1 !</h1>
[root@proxy ~]# curl http://192.168.1.10
<h1>This is node2 !</h1>

加载配置文件:

varnish> vcl.load test5 test.vcl
200        
VCL compiled.
varnish> vcl.use test5
200

测试varnish的配置是否生效:

[root@proxy ~]# curl  
 <h1>This is node1 !</h1>
[root@proxy ~]# curl  
 <h1>This is node2 !</h1>

由于缓存的机制故可能一直定义到一个节点上,需要等缓存失效后才会可能定义到另外一节点。

4、定义健康查检

[root@proxy ~]# vim /etc/varnish/test.vcl
backend webserver1 {
    .host = "192.168.1.9";
    .port = "80";
    .probe = {
    .url = "/index.html";
    .interval = 1s;
    .window = 3;
    .threshold = 1;
  }
}
backend webserver2 {
    .host = "192.168.1.10";
    .port = "80";
    .probe = {
    .url = "/test1.html";
    .interval = 1s;
    .window = 3;
    .threshold = 1;
  }
}
director webserver random {
    { .backend = webserver1; .weight = 3; }
  { .backend = webserver2; .weight = 1; }
}
sub vcl_recv {
      set req.backend = webserver;
}
sub vcl_deliver {
     if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
        set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
 }


九、Varnish 对应多台Web服务器的配置实例(Web集群)

   Varnish中可以使用director指令将一个或多个近似的后端主机定义为一个逻辑组,并可以指定的调度方式(也叫挑选方法)来轮流将请求发送至这些主机上。不同的director可以使用同一个后端主机,而某director也可以使用“匿名”后端主机(在director中直接进行定义)。每个director都必须有其专用名,且在定义后必须在VCL中进行调用,VCL中任何可以指定后端主机的位置均可以按需将其替换为调用某已定义的director。

backend web1 {
  .host = "backweb1.test.com";
  .port = "80";
}
director webservers random {
 .retries = 5;
 {
  .backend = web1;
  .weight = 2;
 }
 {
  .backend = {
   .host = "backweb2.test.com";
   .port = "80";
  }
 .weight  = 3;
 }
}

如上示例中,web1为显式定义的后端主机,而webservers这个directors还包含了一个“匿名”后端主机(backweb2.test.com)。webservers从这两个后端主机中挑选一个主机的方法为random,即以随机方式挑选。

   Varnish的director支持的挑选方法中比较简单的有round-robin和random两种。其中,round-robin类型没有任何参数,只需要为其指定各后端主机即可,挑选方式为“轮叫”,并在某后端主机故障时不再将其视作挑选对象;random方法随机从可用后端主机中进行挑选,每一个后端主机都需要一个.weight参数以指定其权重,同时还可以director级别使用.retires参数来设定查找一个健康后端主机时的尝试次数。

   Varnish 2.1.0后,random挑选方法又多了两种变化形式client和hash。client类型的director使用client.identity作为挑选因子,这意味着client.identity相同的请求都将被发送至同一个后端主机。client.identity默认为cliet.ip,但也可以在VCL中将其修改为所需要的标识符。类似地,hash类型的director使用hash数据作为挑选因子,这意味着对同一个URL的请求将被发往同一个后端主机,其常用于多级缓存的场景中。然而,无论是client还hash,当其倾向于使用后端主机不可用时将会重新挑选新的后端其机。


十、Varnish 缓存修剪

1、缓存内容修剪

   提高缓存命中率的最有效途径之一是增加缓存对象的生存时间(TTL),但是这也可能会带来副作用,比如缓存的内容在到达为其指定的有效期之间已经失效。因此,手动检验缓存对象的有效性或者刷新缓存是缓存很有可能成为服务器管理员的日常工作之一,相应地,Varnish为完成这类的任务提供了三种途径:HTTP 修剪(HTTP purging)、禁用某类缓存对象(banning)和强制缓存未命令(forced cache misses)。 这里需要特殊说明的是,Varnish 2中的purge()操作在Varnish 3中被替换为了ban()操作,而Varnish 3也使用了purge操作,但为其赋予了新的功能,且只能用于vcl_hit或vcl_miss中替换Varnish 2中常用的set obj.ttl=0s。 

在具体执行某清理工作时,需要事先确定如下问题:

  • 仅需要检验一个特定的缓存对象,还是多个?  

  • 目的是释放内存空间,还是仅替换缓存的内容?  

  • 是不是需要很长时间才能完成内容替换?  

  • 这类操作是个日常工作,还是仅此一次的特殊需求? 

2、移除单个缓存对象

   purge用于清理缓存中的某特定对象及其变种(variants),因此,在有着明确要修剪的缓存对象时可以使用此种方式。HTTP协议的PURGE方法可以实现purge功能,不过,其仅能用于vcl_hit和vcl_miss中,它会释放内存工作并移除指定缓存对象的所有Vary:-变种,并等待下一个针对此内容的客户端请求到达时刷新此内容。另外,其一般要与return(restart)一起使用。下面是个在VCL中配置的示例。

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";
  }
}

客户端在发起HTTP请求时,只需要为所请求的URL使用PURGE方法即可,其命令使用方式如下:

# curl -I PURGE http://varniship/path/to/someurl

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 == "test.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 缓存修剪案例

1、修改配置文件

[root@proxy ~]# vim /etc/varnish/test.vcl
acl purgers {
  "127.0.0.1";
  "192.168.1.9";
}
backend webserver1 {
    .host = "192.168.1.9";
    .port = "80";
  .probe = {
    .url = "/index.html";
    .interval = 1s;
    .window = 3;
    .threshold = 1;
  }
}
backend webserver2 {
    .host = "192.168.1.10";
    .port = "80";
  .probe = {
    .url = "/test1.html";
    .interval = 1s;
    .window = 3;
    .threshold = 1;
  }
}
director webserver random {
    { .backend = webserver1; .weight = 3; }
  { .backend = webserver2; .weight = 1; }
}
sub vcl_recv {
  if (req.request == "PURGE") {
  if (!client.ip ~ purgers) {
    error 405 "Method not allowed";
  }
  }
  set req.backend = webserver;
}
sub vcl_deliver {
     if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
        set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
 }

从新载入配置文件:

varnish> vcl.load test7 test.vcl
200        
VCL compiled.
varnish> vcl.use test7
200  
[root@proxy ~]# curl -X PURGE http://192.168.1.8

<?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>405 Method not allowed</title>
  </head>
  <body>
    <h1>Error 405 Method not allowed</h1>
    <p>Method not allowed</p>
    <h3>Guru Meditation:</h3>
    <p>XID: 1200757733</p>
    <hr>
    <p>Varnish cache server</p>
  </body>
</html>

大家可以看到,我们用192.168.1.8去PURGR是不允许的,下面我们来继续定义 ,修改配置文件:

[root@proxy ~]# vim /etc/varnish/test.vcl
acl purgers {
  "127.0.0.1";
  "192.168.1.9";
}
backend webserver1 {
    .host = "192.168.1.9";
    .port = "80";
  .probe = {
    .url = "/index.html";
    .interval = 1s;
    .window = 3;
    .threshold = 1;
  }
}
backend webserver2 {
    .host = "192.168.1.10";
    .port = "80";
  .probe = {
    .url = "/test1.html";
    .interval = 1s;
    .window = 3;
    .threshold = 1;
  }
}
director webserver random {
    { .backend = webserver1; .weight = 3; }
  { .backend = webserver2; .weight = 1; }
}
sub vcl_recv {
  if (req.request == "PURGE") {
  if (!client.ip ~ purgers) {
    error 405 "Method not allowed";
  }
  return (lookup);
  }
  set req.backend = webserver;
}
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";
  }
}
sub vcl_deliver {
     if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
        set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
 }

载入配置文件:                                     

varnish> vcl.load test8 test.vcl
200
VCL compiled.
varnish> vcl.use test8
200
[root@proxy ~]# curl -X PURGE http://192.168.1.8
<?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>
Purged
  <h3>Guru Meditation:</h3>
XID: 1526788379
  <hr>
Varnish cache server
 </body>
</html>

Varnish服务器的请求进行判断,如果referer存在,且referer不匹配下面的域名列表中的任意一个,就将请求重写为img.test.com/p_w_picpaths/default.jpg,其他情况都lookup。

backend web1 {  #定义后端服务
  .host = "172.16.32.10"; #后端的主机
   .port = "80"; #监听的端口
   .probe = {
    .url = "/index.html";
        .interval = 1s;
        .window = 5 ;
        .threshold =2;
}
}
backend web2 {
   .host = "172.16.249.88";
   .port = "80";
   .probe = {
    .url = "/index.html";
        .interval = 1s;
        .window = 5 ;
        .threshold =2;
}                                            
}
#director webservice random {
#      .retries = 5;
# {
#   .backend = web1;
#   .weight = 2;
#}
# {
#   .backend = web2;
#   .weight = 1;
#}
#}
acl purgers {
        "127.0.0.1";
        "172.16.249.91";
}
sub vcl_deliver { #响应子函数
  if (obj.hits > 0) { #检测对象命中
    set resp.http.X-Cache = "HIT from " + server.ip; #如果命中,则将响应的X-Cache设置为hit
} else {
    set resp.http.X-Cache = "Miss";
}
}
sub vcl_recv {
    if (req.http.referer) {
               if (  !(req.http.referer ~ "http://.*google\.com"
                    || req.http.referer ~ "http://.*sohu\.com\.cn"
                    || req.http.referer ~ "http://.*google\.com"
                    || req.http.referer ~ "http://.*yahoo\.cn"
                    || req.http.referer ~ "http://.*google\.cn"
                 )) {
                       set req.http.host = "172.16.32.10";
                       set req.url = "/p_w_picpaths/default.jpg";
               }
          }
          else {
                    lookup;
          }
   if (req.url ~ "\.(html|js|text|css)$") {
    set req.backend = web1;
} else {
    set req.backend = web2;
}
   if (req.url ~ "test.html"){
    return(pass); #如果此请求以test.html结尾,则表示当前缓存不处理直接由源服务器处理
}
    return(lookup);
# 其它的类型都将查询缓存
  if (req.request == "PURGE" ){
      if (!client.ip ~ purgers){
    error 405 "Method not allowed";                                         }
    return (lookup);
}
 if (req.request != "GET" &&
       req.request != "OPTIONS" &&
       req.request != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return (pipe);
    }
    if (req.request != "GET" && req.request != "HEAD") {
        /* We only deal with GET and HEAD by default */
                 return (pass);
     }
    if (req.http.Authorization || req.http.Cookie) {
         /* Not cacheable by default */
        return (pass);
     }
     return (lookup);
 }
sub vcl_fetch {
  if (req.url ~"\.(jpg|jpeg|gif|png)$"){
    set beresp.ttl=7200s; #在响应时,如果源服务器没有设定缓存时长,则由缓存服务器指定缓存时长
}
  if (req.url ~ "\.(html|css|js)$"){
    set beresp.ttl =1200s;
}
}
sub vcl_hit {
   if (req.request == "PURGE") {
    purge;
    error 200 "Purged";
}
}
sub vcl_miss {
  if (req.request == "PUGE"){
    purge;
    error 404 "not in cache";
}
}
sub vcl_pass {
   if (req.request == "PURGE"){
    error 502 "PURGE on a passed object";
}
}