目录
Varnish简介及特点
Varnish日志
Varnish系统结构
Varnish所支持的算法
VCL介绍
Varnish后端存储
Varnish实战
Varnish简介及特点
Varnish是一款高性能且开源的反向代理服务器和HTTP缓存加速器;在1975年时,储存媒介只有两种:内存与硬盘。但现在计算机系统的内存除了主存外,还包括了CPU内的L1、L2,甚至有L3快取。硬盘上也有自己的快取装置,因此Squid Cache自行处理物件替换的架构不可能得知这些情况而做到最佳化,但操作系统可以得知这些情况,所以这部份的工作应该交给操作系统处理,这就是 Varnish cache设计架构。
其特点如下:
-
Varnish基于内存缓存,重启后数据将消失
利用虚拟内存方式,io性能好
支持设置0~60秒内的精确缓存时间
VCL配置管理比较灵活
32位机器上缓存文件大小为最大2G
具有强大的管理功能,例如top,stat,admin,list等
状态机设计巧妙,结构清晰
利用二叉堆管理缓存文件,达到积极删除目的
Varnish日志
为了与系统的其它部分进行交互,Child进程使用了可以通过文件系统接口进行访问的共享内存日志(shared memory log),因此,如果某线程需要记录信息,其仅需要持有一个锁,而后向共享内存中的某内存区域写入数据,再释放持有的锁即可。而为了减少竞争,每个worker线程都使用了日志数据缓存。共享内存日志大小一般为90M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。varnish提供了多个不同的工具如varnishlog、varnishncsa或varnishstat等来分析共享内存日志中的信息并能够以指定的方式进行显示。
Varnish系统结构
Varnish主要有两个进程:Management与Child进程(也称为Cache进程);如下图:
Management进程主要实现了应用新更新的配置,编译VCL配置、监控Varnish、初始化Varnish及提供一个管理接口等;Management进程会每隔一段时间检测一下Child进程是否运行正常,如果在指定的时间内没有响应则Memagement会重启Child进程。
Varnish所支持的算法
Varnish的director支持的挑选方法中比较简单的有round-robin和random两种。其中,round-robin类型没有任何参数,只需要为其指定各后端主机即可,挑选方式为“轮叫”,并在某后端主机故障时不再将其视作挑选对象;random方法随机从可用后端主机中进行挑选,每一个后端主机都需要一个.weight参数以指定其权重,同时还可以director级别使用.retires参数来设定查找一个健康后端主机时的尝试次数。在2.1.0以后的版本后,random挑选方法又多了两种变化形式client和hash。client类型的director使用client.identity作为挑选因子,这意味着client.identity相同的请求都将被发送至同一个后端主机。client.identity默认为cliet.ip,但也可以在VCL中将其修改为所需要的标识符。类似地,hash类型的director使用hash数据作为挑选因子,这意味着对同一个URL的请求将被发往同一个后端主机,其常用于多级缓存的场景中。然而,无论是client还hash,当其倾向于使用后端主机不可用时将会重新挑选新的后端其机。
VCL介绍
VCL是varnish配置缓存策略的工具,它是一种基于“域”(domain specific)的简单编程语言,可以使用指定运算符:“=”,比较运算符“==”,逻辑运算符“!,&&,!!”等;支持使用正则表达式进行字符串匹配、允许用户使用set自定义变量、支持if判断语句,也有内置的函数和变量等;VCL策略在启用前,会由management进程将其转换为C代码,而后再由gcc编译器将C代码编译成二进制程序。编译完成后,management负责将其连接至varnish实例,即child进程。
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]和pipe结束。
4.lookup
表示在缓存里查找被请求的对象,并且根据查找的结果把控制权交给vcl_hit()和vcl_miss()
5.vcl_hit()
在执行lookup指令后,如果在缓存中找到请求的内容,将自动调用该函数.
此函数一般以关键字error code [reason]、deliver和pass结束。
6.vcl_fetch()
在从后端主机更新缓存并且获取内容后调用该方法,接着,通过判断获取的内容来决定是否将内容放入缓存,还是直接返回给客户端。一般以关键字error code [reason]、deliver和pass结束。
7.vcl_deliver()
在缓存中找到请求的内容后,发送给客户端前调用此方法。一般以关键字error code [reason]、deliver结束。
8.vcl_timeout()
此函数在缓存内容到期前调用。一般以关键字discard和fetch结束。
9.vcl_discard()
在缓存内容到期后或缓存空间不够时,自动调用该方法。一般以关键字discard和keep结束。
每种函数的关联如下图所示:
Varnish后端存储
varnish支持多种不同类型的后端存储,这可以在varnishd启动时使用-s选项指定。后端存储包括:
1、file:使用特定的文件存储全部的缓存数据,并通过操作系统的mmap()系统调用将整个缓存文件映射至内存区域(如果条件允许)
2、malloc:使用malloc()库调用在varnish启动时向操作系统申请指定大小的内存空间以存储缓存对像
varnish无法追踪某缓存对象是否存入了缓存文件,从而也就无从得知磁盘上的缓存文件是否可用,因此,file存储方法在varnish停止或重启时会清除数据。而persistent方法的出现对此有了一个弥补,但persistent仍处于测试阶段,例如目前尚无法有效处理要缓存对象总体大小超出缓存空间的情况,所以,其仅适用于有着巨大缓存空间的场景。
选择使用合适的存储方式有助于提升系统性,从经验的角度来看,建议在内存空间足以存储所有的缓存对象时使用malloc的方法,反之,file存储将有着更好的性能的表现。然而,需要注意的是,varnishd实际上使用的空间比使用-s选项指定的缓存空间更大,一般说来,其需要为每个缓存对象多使用差不多1K左右的存储空间,这意味着,对于100万个缓存对象的场景来说,其使用的缓存空间将超出指定大小1G左右。另外,为了保存数据结构等,varnish自身也会占去不小的内存空间。
Varnish实战
Varnish安装,命令使用和配置文件介绍
1、配置yum源,使用yum安装Varnish# rpm --nosignature -i http://repo.varnish-cache.org/redhat/varnish-3.0/el5/noarch/varnish-release-3.0-1.el5.centos.noarch.rpm# yum -y install varnish2、查看安装Varnish所生成的文件# rpm -ql varnish/etc/rc.d/init.d/varnish #Varnish服务启动脚本/etc/rc.d/init.d/varnishlog #Varnish日志服务启动脚本/etc/rc.d/init.d/varnishncsa #Varnish日志服务启动脚本/etc/sysconfig/varnish #Varnish启动服务的主配置文件/etc/varnish #配置文件目录/etc/varnish/default.vcl #默认VCL文件/usr/bin/varnish_reload_vcl #Varnish的VCL管理工具/usr/bin/varnishadm #Varnish管理工具/usr/bin/varnishhist/usr/bin/varnishlog #Varnish日志管理工具/usr/bin/varnishncsa #Varnish日志管理工具/usr/bin/varnishreplay/usr/bin/varnishsizes/usr/bin/varnishstat #Varnish状态查看工具/usr/bin/varnishtest/usr/bin/varnishtop3、Varnish服务配置文件NFILES=131072 MEMLOCK=82000NPROCS="unlimited"# DAEMON_COREFILE_LIMIT="unlimited" #内核最大打开的文件数RELOAD_VCL=1 #是否自动加载VCLVARNISH_VCL_CONF=/etc/varnish/default.vcl #默认加载的VCL文件VARNISH_LISTEN_PORT=80 #监听端口,默认为6081VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 #管理的IP地址VARNISH_ADMIN_LISTEN_PORT=6082 #管理端口VARNISH_SECRET_FILE=/etc/varnish/secret #密钥文件VARNISH_MIN_THREADS=50 #最小线程数量VARNISH_MAX_THREADS=1000 #最大线程数量VARNISH_THREAD_TIMEOUT=120 #线程超时时间VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin #缓存文件位置VARNISH_STORAGE_SIZE=1G #设置文件缓存大小变量VARNISH_MEMORY_SIZE=64M #设置内存缓存大小变量#VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" #默认存储到文件中,这里可以修改存储位置VARNISH_STORAGE="malloc,${VARNISH_MEMORY_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}" #所有启动加载选项4、启动Varnish[root@varnish ~]# service varnish start[root@varnish ~]# netstat -anput|grep varnishtcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 20303/varnishdtcp 0 0 127.0.0.1:6082 0.0.0.0:* LISTEN 20301/varnishdtcp 0 0 :::80 :::* LISTEN 20303/varnishd5、Varnishd命令# varnishadm -hvarnishadm: invalid option -- 'h'usage: varnishadm [-n ident] [-t timeout] [-S secretfile] -T [address]:port command [...] -n is mutually exlusive with -S and -T注释:常用选项 -S:指定密钥文件 -T:指定服务器地址与端口;如管理一个Varnish服务器,连接到Varnish服务器:# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082varnish> helphelp [command]ping [timestamp]auth responsequit #退出bannerstatus #查看状态start #启动Varnishstop #关闭Varnishvcl.load#加载一个VCL文件vcl.inline vcl.use #使用VCL文件vcl.discard vcl.list #查看VCL文件列表vcl.show #查看VCL文件配置param.show [-l] [ ]param.set panic.showpanic.clearstorage.listbackend.listbackend.set_health matcher stateban.url ban [&& ]...ban.list
实验环境介绍
安装一个论坛程序,在Varnish服务器上做动静分离,将动态页面的请求都转发到"lamp"服务器组,"lamp"服务器组的数据库安装在"lamp1"服务器上面;将静态页面转发到"web"服务器组,对请求的静态内容做缓存。
安装LAMP服务并假设论坛
1、在lamp1与lamp2服务器上安装服务并测试######在Lamp1服务器上安装Httpd、Php、Mysql,启动服务[root@lamp1 ~]# yum -yinstall httpd php mysql-server php-mysql[root@lamp1 ~]# servicehttpd start[root@lamp1 ~]# servicemysqld start------------------------------------------------------------------------######在Lamp2服务器上安装Httpd、Php,启动服务[root@lamp2 ~]# yum -yinstall httpd php php-mysql[root@lamp2 ~]# servicehttpd start2、在Lamp1与Lamp2服务器创建php测试页面并访问验证[root@lamp1 ~]# echo" /var/www/html/index.php
3、安装论坛程序 点此下载[root@lamp1 ~]# mysqlmysql> create databasebbs;mysql> grant all onbbs.* to 'bbsuser'@'172.16.%.%' identified by 'bbspass';mysql> flushprivileges;注释:为论坛创建一个数据库并授权用户访问------------------------------------------------------------------------######安装论坛程序[root@lamp1 ~]# unzipDiscuz_X3.0_SC_UTF8.zip[root@lamp1 ~]# cp -rfupload/* /var/www/html/[root@lamp1 ~]# chmod -R+w /var/www/html/{config,data,uc_server,uc_client} #添加可写权限[root@lamp1 ~]# chown -Rapache /var/www/html/* #修改属主权限4、访问"lamp1"服务器地址安装论坛
7、将论坛程序拷贝到lamp2服务器上一份并访问测试
[root@lamp1~]# scp -rp /var/www/html/* 172.16.14.3:/var/www/html/[root@lamp2 ~]# service httpd restart #重启lamp2服务器的WEB服务
安装web服务器并创建测试页访问
[root@web ~]# yum -y install httpd[root@web ~]# service httpd start[root@web ~]# echo"WEB
" > /var/www/html/index.html #创建测试页
Varnish安装及配置
[root@varnish ~]# cd /etc/varnish/[root@varnish varnish]# cp default.vcl default.vcl.bak[root@varnish varnish]# vim default.vcl######定义ACLacl purgers { #定义acl,实现IP地址过滤 "127.0.0.1"; "172.16.0.0"/16;}######定义健康状态检测probe dynamic { #设置动态网站服务器健康状态检测 .url = "/index.html"; .interval = 5s; .timeout = 1s; .expected_response = 200;} #这里设置了两个健康状态检测主要是为了区分动、静网站probe static { #设置动态网站服务器健康状态检测 .url = "/index.html"; #定义检测的页面 .interval = 5s; #探测请求的发送周期,默认为5秒 .timeout = 1s; #每次探测请求的过期时间 .expected_response = 200;}######定义后端服务器backend app1 { #定义一个后端服务器 .host = "172.16.14.2"; #服务器地址 .port = "80"; #服务器监听端口 .probe = dynamic; #健康状态检测}backend app2 { .host = "172.16.14.3"; .port = "80"; .probe = dynamic;}backend web { .host = "172.16.14.4"; .port = "80"; .probe = static;}######定义后端服务器组,实现负载均衡效果director apps random { #定义一个后端服务器组,实现负载均衡效果 { .backend = app1; #调用前面已定义过的后端主机 .weight = 2; #设置权重 } { .backend = app2; .weight = 2; }}######定义vcl_recv函数,实现请求到达并成功接收后调用此函数中定义的规则sub vcl_recv {######定义动、静分离,以".php"或".php?后面跟所有文件"结尾的请求都发送到动态服务器,其他请求都发送到静态服务器 if (req.url ~ "\.php(\?\.*|$)") { set req.backend = apps; } else { set req.backend = web; } return(lookup);######定义允许清除缓存的IP地址,调用的是前面定义的ACL if (req.request == "PURGE") { if (!client.ip ~ purgers) { error 405 "Method not allowed"; } return(lookup); }######重新定义http请求首部,让后端服务器可以记录请求客户端的真实IP地址 if (req.restarts == 0) { if (req.http.x-forwarded-for) { set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip; } else { set req.http.X-Forwarded-For = client.ip; } }######除了定义的请求方法外,其他请求都到后端服务器 if (req.request != "GET" && req.request != "HEAD" && req.request != "PUT" && req.request != "POST" && req.request != "TRACE" && req.request != "OPTIONS" && req.request != "DELETE") { return (pipe); } if (req.request != "GET" && req.request != "HEAD") { return (pass); }######定义不缓存认证与Cookie信息 if (req.http.Authorization || req.http.Cookie) { return (pass); }######定义压缩功能 if (req.http.Accept-Enconding) { if (req.url ~ "\.(jpg|jpeg|gif|bmp|png|flv|gz|tgz|tbz|mp3)$") { remove req.http.Accept-Encoding; remove req.http.Cookie; } else if (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; } else if (req.http.Accept-Encoding ~ "deflate") { set req.http.Accept-Encoding = "deflate"; } else { remove req.http.Accept-Encoding; } }######定义指定格式结尾的文件去除Cookie信息 if (req.request == "GET" && req.url ~ "\.(jpeg|jpg|gif|png|bmp|swf)$") { unset req.http.cookie; }######定义防盗链设置 if (req.http.referer ~ "http://.*") { if (!(req.http.referer ~ "http://.*\.baidu\.com" || req.http.referer ~ "http://.*\.google\.com.*")) { set req.http.host = "www.allen.com"; set req.url = "http://172.16.14.4/error.html"; } }}######定义vcl_hash函数sub vcl_hash { hash_data(req.url); if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); } return(hash);}######定义vcl_hit函数sub vcl_hit { if (req.request == "PURGE") { #语法方法为"PURGE" purge; #清除缓存 error 200 "Purged."; #返回错误状态码为"200" } return(deliver);}######定义vcl_miss函数sub vcl_miss { if (req.request == "PURGE") { purge; error 404 "Not In Cache."; } return(fetch);}######定义vcl_psss函数sub vcl_pass { if (req.request == "PURGE") { error 502 "Purged On A Passed Object."; } return(pass);}######定义vcl_fetch函数sub vcl_fetch {######定义缓存,如果匹配到已定义文件结尾的缓存1天,其他则缓存1小时 if (req.request == "GET" && req.url ~ "\.(html|jpg|png|bmp|jpeg|gif|js|ico|swf|css)$") { set beresp.ttl = 1d; set beresp.http.expires = beresp.ttl; } else { set beresp.ttl = 1h; } return(deliver);}######定义在http首部中,如果请求命中显示"HIT",未命中则显示"MISS"sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; }}>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[root@varnish ~]# service varnish restart #重启服务生效,重启服务器后所有缓存将被清除,不过不用重启也可以用这种方法使其生效>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[root@varnish ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082varnish> help #获取帮助varnish> vcl.load acl_1 default.vcl #加载acl文件,acl_1为配置名称200 VCL compiled.varnish> vcl.list #查看加载的acl文件列表200 active 7 bootavailable 0 acl_1varnish> vcl.use acl_1 #应用acl文件200varnish> quit #退出>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>注释: -S:指定varnish的密钥文件 -T:指定varnish服务器地址及管理端口,默认端口为"6082"
验证服务
1、对服务器做压力测试如下:###后端服务器不经过缓存测试[root@localhost ~]# ab -c 100 -n 1000 http://172.16.14.2/index.phpConcurrency Level: 1000Time taken for tests: 6.812 secondsComplete requests: 10000Failed requests: 0Write errors: 0Non-2xx responses: 10051Total transferred: 2281577 bytesHTML transferred: 0 bytesRequests per second: 1468.04 [#/sec] (mean) #每秒请求并发Time per request: 681.179 [ms] (mean)Time per request: 0.681 [ms] (mean, across all concurrent requests)Transfer rate: 327.10 [Kbytes/sec] received>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>###[root@localhost ~]# ab -c 1000 -n 10000 http://172.16.14.1/index.phpConcurrency Level: 1000Time taken for tests: 2.594 secondsComplete requests: 10000Failed requests: 0Write errors: 0Non-2xx responses: 10056Total transferred: 3117360 bytesHTML transferred: 0 bytesRequests per second: 3855.05 [#/sec] (mean)Time per request: 259.400 [ms] (mean)Time per request: 0.259 [ms] (mean, across all concurrent requests)Transfer rate: 1173.59 [Kbytes/sec] received2、测试缓存是否能命中[root@lamp2 ~]# curl -I http://172.16.14.1/index.phpHTTP/1.1 301 Moved PermanentlyServer: Apache/2.2.15 (CentOS)X-Powered-By: PHP/5.3.3location: forum.phpContent-Type: text/html; charset=UTF-8Content-Length: 0Accept-Ranges: bytesDate: Wed, 09 Oct 2013 01:09:01 GMTX-Varnish: 2142028839Age: 0Via: 1.1 varnishConnection: keep-aliveX-Cache: MISS #第一次请求,未命中显示"MISS">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[root@lamp2 ~]# curl -I http://172.16.14.1/index.phpHTTP/1.1 301 Moved PermanentlyServer: Apache/2.2.15 (CentOS)X-Powered-By: PHP/5.3.3location: forum.phpContent-Type: text/html; charset=UTF-8Content-Length: 0Accept-Ranges: bytesDate: Wed, 09 Oct 2013 01:09:08 GMTX-Varnish: 2142028841 2142028839Age: 7Via: 1.1 varnishConnection: keep-aliveX-Cache: HIT #第二次请求,命中则显示"HIT"3、验证动、静分离的效果[root@web ~]# service httpd stop #停止提供静态页面的httpd服务Stopping httpd: [ OK ]注意:提供静态页面的服务停止后,所有图片都不能显示;如果把服务再启动起来就可以访问正常了。4、验证手动清除缓存[root@lamp2 ~]# curl -X PURGE http://172.16.14.1/index.php200 Purged. Error 200 Purged.
Purged.
Guru Meditation:
XID: 580728625
Varnish cache server
#注释:已经成功清除缓存>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[root@lamp2 ~]# curl -I http://172.16.14.1/index.phpHTTP/1.1 301 Moved PermanentlyServer: Apache/2.2.15 (CentOS)X-Powered-By: PHP/5.3.3location: forum.phpContent-Type: text/html; charset=UTF-8Content-Length: 0Accept-Ranges: bytesDate: Wed, 09 Oct 2013 02:03:03 GMTX-Varnish: 580728626Age: 0Via: 1.1 varnishConnection: keep-aliveX-Cache: MISS