轻量级别的Cache和反向代理软件---Varnish
1、Varnish描述
1.1 Varnish的结构与特点
Varnish是一个轻量级别的Cache和反向代理软件,先进的设计理念和成熟的设计框架是Varnish的主要特点:
- 基于内存进行缓存,重启后数据将消失
- 利用虚拟内存方式,I/O性能好.
- 支持设置0~60秒的精确缓存时间
- VCL配置管理比较灵活
- 32位机器上缓存文件大小为最大2GB
- 具有强大的管理功能,例如:top stat admin list等
- 状态设计巧妙,结构清晰
- 利用二叉堆管理缓存文件,达到可以积极删除目的。
1.2 Varnish与Squid的对比
说到Varnish,就不能不提Squid,Squid是一个高性能的代理缓存服务器,它和Varnish的相比较有诸多的异同点,下面进行分析:
相同点:
l 都是一个反向代理服务器
l 都是开源软件
异同点,也是Varnish的优点:
l Varnish的稳定性很高,两者在完成相同的负荷的工作时,Squid发生的故障几率要高于Varnish,因为Squid需经常重启.
l Varnish访问速度更快,Varnish采用用了”Visual Page Cache”技术,所有缓存的数据都直接从内存读取,而且Squid是从硬盘读取缓存数据,因此Varnish访问速度更快
l Varnish可以支持更多的并发连接,因为Varnish的TCP连接要比Squid释放快,所以在高并发连接情况可以支持更多地TCP连接。
l Varnish可以通过管理端口,使用证则表达式清除部分缓存,而Squid做不到。
Varnish缺点如下:
l 在高并发状态下CPU,I/O和内存等资源开销都要高于Squid。
l Varnish进程一旦挂起,崩溃或者重启,缓存数据都会从内存当中完全释放,此时所有的请求会发送后端的WEB服务器,在高并发的情况下,这会给后端的服务器造成很大压力。
2、Varnish安装
2.1、安装前环境
Varnish-Server1 à10.0.0.201
Web—Server2à10.0.0.202
2.2、创建Varnish用户缓存目录和日志
# useradd varnish -s /sbin/nologin 创建varnish用户
# mkdir /varnish/cache –p 创建varnish缓存目录
# mkdir /varnish/log 创建varnish日志
# chown -R varnish:varnish /varnish/ 修改赋权组
2.3、安装PCRE
为了兼容正则表达式,编译 2.0以上版本时否则会报错pcre找不到。
# yum install gcc
# yum install gcc-c++ libstdc++-devel
# yum install -y httpd-devel pcre perl pcre-devel zlib zlib-devel GeoIP GeoIP-devel
# wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.34.tar.bz2
# tar -xvf pcre-8.34.tar.bz2
# cd pcre-8.34/
# ./configure --prefix=/usr/local/prce/
# make && make install
注:如果你在编译的vasnish中的bin目录没有发现varnishstat, varnishtop, varnishhist这个三个程序的话,是因为编译前没有安装与操作系统位数对应的ncurses-devel 安装ncurses-devel
yum install ncurses-devel.x86_64
然后再次编译varnish 即可
2.4、安装Varnish
# wget http://repo.varnish-cache.org/source/varnish-3.0.0.tar.gz
# tar -xzvf varnish-3.0.0.tar.gz
# cd varnish-3.0.0
#./configure --prefix=/usr/local/varnish PKG_CONFIG_PATH=/usr/lib/pkgconfig
# make && make install
# cd /usr/local/varnish/sbin
./varnishd -V
3、Varnish配置
3.1、VCL使用说明
VCL,即Varnish Configaution Language 用来定义Varnish的存储策略
VCL内置函数
(1)、vcl_recv函数
用于接收和处理请求,当请求到达并成功接收后被调用.
函数一般以如下几个关键字结束
pass表示进入pass模式,把请求控制权交给vcl_pass函数
pige 表示进入pige模式,把请求控制权交给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指令后,在缓存中找到请求的内容后将自动调用该函数
此函数一般以如下几个关键字结束:
fetch 表示从后端获取请求的内容,并且把控制权交给vcl_fetch函数
deliver 表示将找到的内容发送给客户,并且把控制权交给函数vcl_deliver
error code[reason]
pass
(6)、vcl_miss函数
在执行lookup指令后,在缓存中没有找到请求的内容时自动调用该方法,此函数可以于判断是否需要从后端服务器获取内容
此函数一般以如下几个关键字结束:
fetch:表示从后端获取请求内容,并且把控制权交给vcl_fetch函数
error code[reason]
pass
(7)、vcl_miss函数
在后端主机更新缓存并且获取内容后调用该方法,接着,通过判断获取的内容来决定是将内容放入缓存,还是直接返回给客户端,
此函数一般以如下几个关键字结束:
error code[reason]
pass
deliver
(8)、vcl_deliver函数
将在缓存中找到请求的内容发送给客户端调用此方法。
此函数一般以如下几个关键字结束:
error code[reason]
deliver
(9)、vcl_timeout函数
在缓存内容到期前调用此函数.
此函数一般以如下几个关键字结束:
discard:表示从缓存中清除内容
fetch
(10)、vcl_discard函数
在缓存内容到期后或者缓存空间不足时,自动调用该函数.
此函数一般以如下几个关键字结束:
keep:表示将内容继续保留在缓存当中
discard
2.3.2、配置Varnish配置实战
由于本版不同,Varnish配置文件的写法也存在一定的差异,Varnish3.x版本不但在配置写法上和2.x版本不同,修复了很多不应用BUG,本文以3.x版本为基准
Varnish安装完成后,默认的配置文件/usr/local/varnish/etc/varnish/default.vcl
此配置文件默认是全部注释掉的.配置文件如下:
先编辑配置文件
# vi /usr/local/varnish/etc/varnish/default.vcl
# This is a basic VCL configuration file for varnish. See the vcl(7) # man page for details on VCL syntax and semantics. # # Default backend definition. Set this to point to your content # server. # # This is a basic VCL configuration file for varnish. See the vcl(7) # man page for details on VCL syntax and semantics. # # Default backend definition. Set this to point to your content # server. # #设置后端WEB服务 backend webtest1 { .host = "10.0.0.202"; .port = "80"; .connect_timeout = 1s; .first_byte_timeout = 5s; .between_bytes_timeout = 2s; } #可以定义多台WEB #backend webtest2 { # .host = "192.168.100.6"; # .port = "80"; # .connect_timeout = 1s; # .first_byte_timeout = 5s; # .between_bytes_timeout = 2s; #} #多台WEB可以定义负载均衡 #director lb_test random { # { # .backend = webtest1; # .weight = 5; # } # { # .backend = test2; # .weight = 5; # } #} #定义那些IP或者IP段具有访问权 acl purge { "localhost"; "127.0.0.1"; "192.168.1.0"/24; "10.0.0.0"/16; } sub vcl_recv { #开启压缩模式,图片格式取消压缩 if (req.http.Accept-Encoding) { if (req.url ~ "\.(jpg|png|gif|jpeg|flv)" ) { 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; } } #根据host设置后端服务器 if (req.http.Host ~ "(?i)(www.test1.com|www.test2.com|10.0.0.201)") { #set req.backend = lb_test; #如果开启负载用此项 set req.backend = webtest1; }else if (req.http.Host ~ "(?i)image.wdj.com"){ set req.backend = webtest1; }else { error 408 "Hostname not found"; } #如果为purge请求,客户端ip不在访问列表中,返回405拒绝 if (req.request == "PURGE") { if (!client.ip ~purge) { error 405 "Not Allowed"; } #本地缓存查找 return(lookup); } #如果为GET请求,url后缀为jpg,png,gif等 取出cookie if (req.request == "GET"&&req.url ~ "(?i)\.(jpg|png|gif|swf|jpeg|ico)$") { unset req.http.cookie; } #如果GET请求,url为php,则穿过cache,不缓存 if (req.request =="GET"&&req.url ~ "(?i)\.php($|\?)"){ return (pass); } #简单防盗链 if (req.http.referer ~ "http://.*") { if ( !(req.http.referer ~ "http://.*test1\.com" || req.http.referer ~ "http://.*test2\.com" || req.http.referer ~ "http://.*wdj\.com" || req.http.referer ~ "http://.*google\.com" || req.http.referer ~ "http://.*baidu\.com" || req.http.referer ~ "http://.*yahoo\.cn" )) { error 404 "Not Found!"; } } #获取客户端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; } # } #不是以下请求进入pipe模块 if (req.request != "GET" && req.request != "HEAD" && req.request != "PUT" && req.request != "POST" && req.request != "TRACE" && req.request != "OPTIONS" && req.request != "DELETE") { /* Non-RFC2616 or CONNECT which is weird. */ return (pipe); } #不是GET 和HEAD请求不缓存 if (req.request != "GET" && req.request != "HEAD") { /* We only deal with GET and HEAD by default */ return (pass); } if (req.http.Authorization) { /* Not cacheable by default */ return (pass); } return (lookup); } # sub vcl_pipe { return (pipe); } # sub vcl_pass { return (pass); } #使用url+host hash算法查找数据 sub vcl_hash { hash_data(req.url); if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); } return (hash); } # 如果请求为purge 将清除缓存 sub vcl_hit { if (req.request == "PURGE") { set obj.ttl = 0s; error 200 "Purged"; } return (deliver); } sub vcl_miss { return (fetch); } # sub vcl_fetch { if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*") { /* * Mark as "Hit-For-Pass" for the next 2 minutes */ set beresp.ttl = 0 s; return (hit_for_pass); } if (beresp.http.Pragma ~"no-cache" || beresp.http.Cache-Control ~"no-cache" || beresp.http.Cache-Control ~"private") { return (deliver); } #为特定格式文件设置缓存时间,有多种的可以直接在后米啊加 if (req.request == "GET"&&req.url ~ "(?i)\.(js|css|mp3|jpg|png|gif|swf|jpeg|ico)$") { set beresp.ttl = 30d; } if (req.request == "GET"&&req.url ~ "(?i)\.(html|htm)$") { set beresp.ttl = 1d; } return (deliver); } # 设置返回状态 sub vcl_deliver { set resp.http.x-hits = obj.hits; if (obj.hits > 0) { set resp.http.X-Cache = "Hit test.com"; }else { set resp.http.X-Cache = "Miss test.com"; } set resp.http.Server = "BWM"; return (deliver); } # 定义错误 sub vcl_error { set obj.http.Content-Type = "text/html; charset=utf-8"; set obj.http.Retry-After = "5"; synthetic {" <?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>"} + obj.status + " " + obj.response + {"</title> </head> <body> <h1>Error "} + obj.status + " " + obj.response + {"</h1> <p>"} + obj.response + {"</p> <h3>Guru Meditation:</h3> <p>XID: "} + req.xid + {"</p> <hr> <p>Varnish cache server</p> </body> </html> "}; return (deliver); } sub vcl_init { return (ok); } sub vcl_fini { return (ok); }
4、Varnish运行
4.1、varnish启动
-u 以什么用运行
-g 以什么组运行
-f varnish配置文件
-a 绑定IP和端口
-s varnish缓存文件位置与大小
-w 最小,最大线程和超时时间
-t 缓存时间s
-T varnish管理端口,主要用来清除缓存
-p client_http11=on 支持http1.1协议
-P(大P) /usr/local/varnish/var/varnish.pid 指定其进程码文件的位置,实现管理
停止varnish
varnish两种缓存方式:
(1)基于malloc内存+swap交换模式
/usr/local/varnish/sbin/varnishd -f /usr/local/varnish/etc/varnish/default.vcl -s malloc,200M -a 10.0.0.201:80 -w 1024,512000,10 -t 10.0.0.201:3000
(2)基于file是mmap的文件内存映射的机制.
/usr/local/varnish/sbin/varnishd -u varnish -g varnish -f /usr/local/varnish/etc/varnish/default.vcl -a 10.0.0.201:80 -s file,/varnish/cache/varnish_cache.data,1G -w 1024,51200,10 -t 3600 -T 10.0.0.201:3000
企业里里面开启为100G
4.2、启动日志
# /usr/local/varnish/bin/varnishncsa -w /varnish/log/varnish.log &
日志加入开机启动:
echo "/usr/local/varnish/bin/varnishncsa -w /data/varnish/logs/varnish.log &" >> /etc/rc.local
参数: -w 指定varnish访问日志要写入的目录与文件
4.3、缓存验证:
可以通过浏览器访问对应的网页来查看Varnish缓存的效果。如果Varnish缓存成功,第二次打开网页的速度会明显比第一次快,但是这种方式并不能够充分说明问题。下面用命令行方式,通过查看网页头来查看命中情况。
- [root@varnish-server ~]# curl -I http://www.ixdba.net/a/mz/2010/0421/11.html
- HTTP/1.1 200 OK
- Server: Apache/2.2.14 (Unix) PHP/5.3.1 mod_perl/2.0.4 Perl/v5.10.1
- Last-Modified: Sat, 10 Jul 2010 11:25:15 GMT
- ETag: "5e850b-616d-48b06c6031cc0"
- Content-Type: text/html
- Content-Length: 24941
- Date: Fri, 09 Jul 2010 08:29:16 GMT
- X-Varnish: 1364285597
- Age: 0
- Via: 1.1 varnish
- Connection: keep-alive
- X-Cache: MISS from www.ixdba.net #这里的“MISS”表示此次访问没有从缓存读取
再次打开这个页面,查看网页的头信息。
· [root@varnish-server ~]# curl -I http://www.ixdba.net/a/mz/2010/0421/11.html
· HTTP/1.1 200 OK
· Server: Apache/2.2.14 (Unix) PHP/5.3.1 mod_perl/2.0.4 Perl/v5.10.1
· Last-Modified: Sat, 10 Jul 2010 11:25:15 GMT
· ETag: "5e850b-616d-48b06c6031cc0"
· Content-Type: text/html
· Content-Length: 24941
· Date: Fri, 09 Jul 2010 08:30:35 GMT
· X-Varnish: 1364398612 1364285597
· Age: 79
· Via: 1.1 varnish
· Connection: keep-alive
· X-Cache: HIT from www.ixdba.net #由“HIT”可知,第二次访问此页面时,从缓存中读取内容,也就是缓存命中
参考URL:http://book.51cto.com/art/201202/314875.htm
5、Varnish 日志切割
5.1、日志切割
# vim /data/shell/cut_varnish_log.sh
#!/bin/sh logs_path=/varnish/logs vlog=${logs_path}/varnish.log date=$(date -d "yesterday" +"%Y-%m-%d") pkill -9 varnishncsa mkdir -p ${logs_path}/$(date -d "yesterday" +"%Y")/$(date -d "yesterday" +"%m")/ mv /varnish/logs/varnish.log ${logs_path}/$(date -d "yesterday" +"%Y")/$(date -d "yesterday" +"%m")/varnish-${date}.log /usr/local/varnish/bin/varnishncsa -w /varnish/logs/varnish.log &
使用计划任务,每天晚上凌晨00点运行日志切割脚本
echo "0 0 * * * /data/shell/cut_varnish_log.sh" >> /etc/crontab
6、Varnish日常管理
6.1、日常管理
/usr/local/varnish/bin/varnishadm -T 10.0.0.201:3000 purge "req.http.host ~ www.server110.com$ && req.url ~ /static/image/tp.php"
详细介绍:
10.0.0.201:3000 ###为被清除缓存服务器地址
www.server201.com ###为被清除的域名
/static/image/tp.php ###为被清除的url地址列表
清除所有缓存
/usr/local/varnish/bin/varnishadm -T 192.168.9.201:3000 ban.url
^.*$
清除image目录下所有缓存
/usr/local/varnish/bin/varnishadm -T 192.168.9.201:3000 ban.url
/image/
查看Varnish服务器连接数与命中率
/usr/local/varnish/bin/varnishstat –n /varnish/cache/varnish_cache.data
7、Varnish内置公用变量
公用变量名称 含义
req.backend 指定对应的后端主机
server.ip
表示服务器端IP
client.ip
表示客户端IP
req.request 指定请求的类型,例如GET、HEAD、POST等
req.url
指定请求的地址
req.proto 表示客户端发起请求的HTTP协议版本
req.http.header 表示对应请求中的http头部信息
req. restarts 表示请求重启的次数,默认最大值为4
Varnish
在向后端主机请求时,可以使用的公用变量如表3所示:
表3
公用变量名称 含义
beresp.request 指定请求的类型,例如GET、HEAD等
beresp.url 指定请求的地址
beresp .proto 表示客户端发起请求的HTTP协议版本
beresp .http.header 表示对应请求中的http头部信息
beresp .ttl 表示缓存的生存周期,也就是cache保留多长时间,单位是秒
从cache或者后端主机获取内容后,可以使用的公用变量如表4所示:
表4
公用变量名称 含义
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 表示返回上一次请求到现在的间隔时间,单位是秒
对客户端应答时,可以使用的公用变量如表5所示:
表5
公用变量名称 含义
resp.status 表示返回给客户端的HTTP状态代码
resp.proto 表示返回给客户端的HTTP协议版本
resp.http.header 表示返回给客户端的HTTP头部信息
resp.response 表示返回给客户端的HTTP状态信息
在上面的讲述中,我们只是介绍了常用的VCL内置公用变量,如果需要了解和使用更多的公用变量信息,请登录varnish官方网站查阅。