Varnish缓存服务详解及应用实现

一、varnish的基本介绍
   Varnish 的做者Poul-Henning Kamp是FreeBSD的内核开发者之一,他认为如今的计算机比起1975年已经复杂许多。在1975年时,储存媒介只有两种:内存与硬盘。但如今计算 机系统的内存除了主存外,还包括了cpu内的L一、L2,甚至有L3快取。硬盘上也有本身的快取装置,所以squid cache自行处理物件替换的架构不可能得知这些状况而作到最佳化,但操做系统能够得知这些状况,因此这部份的工做应该交给操做系统处理,这就是 Varnish cache设计架构。

   Varnish与通常服务器软件相似,就是一个web缓存代理服务器,分为master(management)进程和child(worker,主要 作cache的工做)进程。master进程读入命令,进行一些初始化,而后fork并监控child进程。child进程分配若干线程进行工做,主要包 括一些管理线程和不少woker线程。

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

   Child进程包含多种类型的线程,常见的如:
       Acceptor线程:接收新的链接请求并响应;
       Worker线程:child进程会为每一个会话启动一个worker线程,所以,在高并发的场景中可能会出现数百个worker线程甚至更多;
       Expiry线程:从缓存中清理过时内容;

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

   进程的工做过程原理及过程:css

   wKioL1N3LNmBY7lcAAJafMlNjYg162.jpg

二、varnish与squid的区别
   varnish和squid在中小规模的应用上,varnish足够轻量级,足够好用,可是在巨大的并发请求来讲,单个varnish所可以承载的并发 访问量大概在5000个链接请求左右,超出5000个可能就就得不稳定了;而在这里squid就能表现出良好的性能了,所以在大规模的企业级应用中仍然是 以squid居多,而在中小规模的本身公司的反向代理缓存中varnish居多;

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

四、VCL基本介绍
   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命令完成。


五、varnish的后端存储
   varnish的缓存对象在每次服务重启时都会被清空并从新创建,因此这些服务器都是不该该随便去重启的,varnish为了把数据更持久化的存储,引入了更多的存储机制,因此varnish支持多种不一样的后端存储;html

   varnish支持多种不一样类型的后端存储,这能够在varnishd启动时使用-s选项指定。后端存储的类型包括:
   (1)file:使用特定的文件存储所有的缓存数据,并经过操做系统的mmap()系统调用将整个缓存文件映射至内存区域(若是条件容许);
   (2)malloc:使用malloc()库调用在varnish启动时向操做系统申请指定大小的内存空间以存储缓存对象;
   (3)persistent(experimental):与file的功能相同,但能够持久存储数据(即重启varnish数据时不会被清除);仍处于测试期;

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

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

六、varnish的工做原理及工做流程
   官方提供的工做流程图:node

   wKiom1N3Bp6DgfV-AAIqXsglWKg749.jpg
vcl的工做方式是基于状态引擎(state engine)来实现的;上图说明:
   vcl_recv的结果若是能够查询缓存并能够识别,那就要到vcl_hash这步了,若是没法识别那就经过pipe(管道)送给vcl_pipe,如 果能识别,但不是一个可缓存的对象,那就经过pass送到vcl_pass去,vcl_hash以后就可查看缓存中有没有了,有这个请求的对象就表示命中 (vcl_hit),若是没有那就表示未命中(vcl_miss),若是命中的就能够直接经过deliver直接送给vcl_deliver响应了,若是 未命中就经过fetch交给vcl_fatch去后端服务器上去取数据,取回数据以后若是数据能够缓存就缓存(cache),本地缓存完以后再构建响应, 若是不能够缓存就不作缓存交给vcl_deliver响应了;而若是命中了交给vcl_pass,交给pass以后就要到Fetch objet from backend后端服务器上去取数据了,这是由于这个命中的对象多是过时或者是要作单独立额外的处理的;这就是vcl的状态引擎过程。mysql

 

1、安装实现过程:web

# 安装varnish,版本是3.0.4-1.el6正则表达式

[root@node0 ~]# rpm -ivh varnish-3.0.4-1.el6.x86_64.rpm varnish-docs-3.0.4-1.el6.x86_64.rpm varnish-libs-3.0.4-1.el6.x86_64.rpmsql

[root@node0 ~]# rpm -ql varnish  # 查看varnish的安装文件
[root@node0 ~]# vim /etc/sysconfig/varnish  # 查看配置文件
NFILES=131072        # 所可以打开的最大文件数
MEMLOCK=82000        # 用多大内存空间保存日志信息
DAEMON_COREFILE_LIMIT="unlimited"    # 进程核心转储所使用的内存空间,unlimited表示无上限
RELOAD_VCL=1        # 从新启动服务时是否从新读取VCL并从新编译的
VARNISH_VCL_CONF=/etc/varnish/default.vcl    # 默认读取的VCL文件
VARNISH_LISTEN_PORT=80    # 监听的端口,默认监听6081
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1    # 管理接口监听的地址
VARNISH_ADMIN_LISTEN_PORT=6082    # 管理接口监听的端口
VARNISH_SECRET_FILE=/etc/varnish/secret    # 使用的密钥文件
VARNISH_MIN_THREADS=1    # 最少线程数
VARNISH_MAX_THREADS=1000    # 最大线程数
VARNISH_THREAD_TIMEOUT=120    # 线程的超时时间
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin    # 基于文件存储时的文件路径
VARNISH_STORAGE_SIZE=1G    # 存储文件的大小
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"    # 存储的文件格式
VARNISH_TTL=120    # 联系后端服务器的超时时间
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}"    # 使用定义的各高级配置的参数

# 定义后端服务器
[root@node0 sysconfig]# cd /etc/varnish/

[root@node0 varnish]# cp default.vcl default.vcl.bak编程

[root@node0 varnish]# mv default.vcl test.vclvim

[root@node0 varnish]# vim test.vcl
backend webserver {
 .host = "172.16.27.1";  # 后端服务器的地址
 .port = "80";           # 后端服务监听的端口

}

# 启动服务

[root@node0 sysconfig]# service varnish start
Starting varnish HTTP accelerator:                         [  OK  ]
[root@node0 sysconfig]#

 

# 能够进入varnish的命令操做,进去后直接输入help就能够查看帮助信息;
[root@node0 varnish]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
200 201    
-----------------------------
Varnish Cache CLI 1.0
-----------------------------
Linux,2.6.32-431.el6.x86_64,x86_64,-smalloc,-hcritbit

Type 'help' for command list.
Type 'quit' to close CLI session.

varnish> help
200 377    
help [command]
ping [timestamp]
auth response
quit
banner
status
start
stop
stats
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>
purge.url <regexp>
purge <field> <operator> <arg> [&& <field> <oper> <arg>]...
purge.list

   安装配置好后端web服务器并启动,地址为172.16.27.1,然后经过varnish服务器地址访问后端服务器,这里仅仅只是定义一指向后端服务 器,也就说如今也只能工做起来,可是尚未定义相关的缓存属性等信息,那就先经过varnish服务器端访问一下先吧:

[root@node1 ~]# yum -y install httpd php php-mysql

[root@node1 ~]# cd /var/www/html

[root@node1 html]# vim index.html

<h1>www.tanxw.com and varnish fo backend</h1>
<h2>node1.tanxw.com</h2>

wKioL1N3LPuirP0DAADyvwScqLY595.jpg

 

2、设置响应是否命中,接着继续编写配置文件:

[root@node0 varnish]# vim test.vcl

sub vcl_deliver {            # 定义子例程

 if (obj.hits > 0){         # 判断若是命中了就在http响应首部设置X-Cache为HIT

   set resp.http.X-Cache = "HIT from " server.ip;  

 } else {                   # 不然就在http响应首部设置X-Cache为MISS

   set resp.http.X-Cache = "MISS";
 }

}

# 在varnish的命令行中从新编译从新加载配置文件

varnish> vcl.load test1 /etc/varnish/test.vcl

200 13      
VCL compiled.
vcl.use test1
200 0      

varnish> vcl.show test1
200 191

 

backend webserver {
 .host = "172.16.27.1";
 .port = "80";
}

sub vcl_deliver {
 if (obj.hits > 0){
   set resp.http.X-Cache = "HIT";
 } else {
   set resp.http.X-Cache = "MISS";
 }
}

[root@node0 varnish]# curl -I http://172.16.27.88/index.html  # 也能够在命令行请求

而后再到页面上访问看一下是否已经生效:

wKiom1N3LTag8tGIAAPzTTH4mcM097.jpg

 

3、指定某些文件不能查缓存,断续添加配置文件

[root@node0 varnish]# vim test.vcl   # 添加以下代码

sub vcl_recv {     # 定义请求的文件中若是匹配test.html就pass,就不查缓存

 if (req.url ~ "test.html"){
   return(pass);
 }
 return(lookup);

}

# 再到varnish的命令行中从新加载配置文件并应用

varnish> vcl.load test4 /etc/varnish/test.vcl
200 13      
VCL compiled.
vcl.use test4
200 0

# 在命令行请求看一下缓存,无论怎么请求X-Cache都是MISS

[root@node0 varnish]# curl -I http://172.16.27.88/test.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 17 May 2014 09:51:07 GMT
ETag: "120905-1a-4f99578180449"
Accept-Ranges: bytes
Content-Length: 26
Content-Type: text/html; charset=UTF-8
Date: Sat, 17 May 2014 10:01:27 GMT
X-Varnish: 1309371381
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: MISS

然后再请求test.html页面;

wKioL1N3bEWxPpfMAAIddvy86EY909.jpg

 

4、设定缓存时长和定义图片防盗链

[root@node0 varnish]# vim default.vcl

sub vcl_fetch {

 if (req.url ~ "\.(jpg|jpeg|gif|png)$") {  # 若是url是以图片格式结尾的缓存2小时
   set beresp.ttl = 7200s;
 }
 if (req.url ~ "\.(html|css|js)$") {   # 若是url是以html|css|js结尾的缓存20分钟
   set beresp.ttl = 1200s;
 }

}

sub vcl_recv {
 if (req.url ~ "test.html"){
   return(pass);
 }

# 图片防盗链
 if (req.http.referer ~ "http://.*") {
   if (!(req.http.referer ~ "http://.*tanxw\.com"
      ||req.http.referer ~ "http://.*google\.com"
      ||req.http.referer ~ "http://.*yahoo\.com"
      ||req.http.referer ~ "http://.*google\.cn"
      ||req.http.referer ~ "http://.*baidu\.com"
      )) {
       set req.http.host = "www.tanxw.com";
       set req.url = "/templets/default/images/logl.gif";
   }
       return (lookup);
 }
 return(lookup);
}

 

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

[root@node0 varnish]# vim default.vcl

acl purgers {    # 定义acl访问控制,只容许如下网段或主机执行purgers操做

   "127.0.0.1";
   "172.16.0.0"/16;
}

sub vcl_recv {
   if (req.request == "PURGE") {   # 若是请求方法是PURGE,而且客户端IP在上面定义的网段内的就容许执行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") {   # 如在pass中要清除缓存,直接返回错误码
       error 502 "PURGE on a passed object";
   }

}

# 保存退出,然后直接在命令行中进行测试一下:

[root@node0 varnish]# curl -I http://172.16.27.88/index.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 17 May 2014 08:07:17 GMT
ETag: "120904-47-4f99404c6fde2"
Content-Type: text/html; charset=UTF-8
Content-Length: 71
Accept-Ranges: bytes
Date: Sat, 17 May 2014 13:38:51 GMT
X-Varnish: 364681188 364681185
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: HIT from 172.16.27.88  # 缓存命中

[root@node0 varnish]# curl -X PURGE http://172.16.27.88/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 OK.</title>   # 缓存清除成功
 </head>
 <body>
   <h1>Error 200 Purged OK.</h1>
   <p>Purged OK.</p>
   <h3>Guru Meditation:</h3>
   <p>XID: 364681189</p>
   <hr>
   <p>Varnish cache server</p>
 </body>
</html>

[root@node0 varnish]# curl -I http://172.16.27.88/index.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 17 May 2014 08:07:17 GMT
ETag: "120904-47-4f99404c6fde2"
Content-Type: text/html; charset=UTF-8
Content-Length: 71
Accept-Ranges: bytes
Date: Sat, 17 May 2014 13:42:39 GMT
X-Varnish: 364681194
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: MISS   # 清除后缓存MISS了

 

 

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

.probe中的探测指令经常使用的有:
   (1) .url:探测后端主机健康状态时请求的URL,默认为“/”;
   (2) .request: 探测后端主机健康状态时所请求内容的详细格式,定义后,它会替换.url指定的探测方式;好比:
   .request =
       "GET /.healthtest.html HTTP/1.1"
       "Host: www.magedu.com"
       "Connection: close";
   (3) .window:设定在断定后端主机健康状态时基于最近多少次的探测进行,默认是8;
   (4) .threshold:在.window中指定的次数中,至少有多少次是成功的才断定后端主机正健康运行;默认是3;
   (5) .initial:Varnish启动时对后端主机至少须要多少次的成功探测,默认同.threshold;
   (6) .expected_response:指望后端主机响应的状态码,默认为200;
   (7) .interval:探测请求的发送周期,默认为5秒;
   (8) .timeout:每次探测请求的过时时长,默认为2秒;

backend webserver {
   .host = "www.magedu.com";    # 定义后端主机
   .probe = {

      .url = "/.healthtest.html";  # 向后端主机获取这个页面

      .interval = 1s;   # 每隔1秒钟尝试一次

      .window = 5;      # 最多尝试5次,判断采样的样本

      .threshold = 2;   # 若是采样5次,若是有2次是错误的,就认为是失败的,上线也是同样

   }
}

 

7、Varnish的命令行工具

varnishadm命令语法:varnishadm [-t timeout] [-S secret_file] [-T address:port] [-n name] [command [...]]

经过命令行的方式链接至varnishd进行管理操做的工具,指定要链接的varnish实例的方法有两种:
-n name —— 链接至名称为“name”的实例;
-T address:port —— 链接至指定套接字上的实例;
其运行模式有两种,当不在命令行中给出要执行的"command"时,其将进入交互式模式;不然,varnishadm将执行指定的"command"并退出。要查看本地启用的缓存,可以使用以下命令进行。
# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 storage.list

 

总结:

   varnish是一个很强大的缓存服务器,还能够作动静分离,这里限于篇幅,在这里就不一一例举了,有关信息能够参数官方文档,还能够作后端服务器的调度等,因而这里总结得过于仓促,有作得不到之处还望大神多多指出,若是有什么问题能够留言交流学习。

相关文章
相关标签/搜索