1、Varnish简介
Varnish 是一款高性能且开源的反向代理服务器(Reverse Proxy Server),挪威最大的线上报纸 Verdens Gang 使用3台Varnish代替了原本的12台Squid, 性能比以前更好。Varnish 的作者 Poul-Henning Kamp 是 FreeBSD 核心的开发人员之一,他认为现在的电脑比起1975年已经复杂许多。在那个时代,存储媒介只有两种:记忆体与硬盘。但现在电脑系统的记忆体除了主记忆体外,还包括了CPU内的L1、L2,甚至有L3快取。硬盘上也有自己的快取装置,因此squid cache自行处理物件替换的架构不可能得知这些情況而做到最佳化,但作业系统可以得知这些情況,所以这部份的工作有关交给作业系统处理,这就是 Varnish Cache 的设计架构。
一般來说,使用Varnish代替Squid的理由有三点:
1.1 Varnish采用了“Visual Page Cache”技术,在记忆体的利用上,Varnish 比 Squid 更具有优势,它避免了Squid频繁在记忆体、硬盘中交换文件,性能比 Squid 还高。
1.2 Varnish的稳定性高,进行相同工作的Squid服务器发生故障的几率似乎比Varnish高。
1.3 通过Varnish管理端口,可以使用正则表达式、批量地清除部分缓存,这一点是Squid不能具备的。
2、Linux 系统安裝 Varnish 的过程:
2.1 首先建立www用戶和组,以及Varnish缓存存放目录:
sudo /usr/sbin/groupadd www -g 48 sudo /usr/sbin/useradd -u 48 -g www www sudo mkdir -m 755 /data/varnish/vcache sudo chown -R www:www /data/varnish/vcache
2.2 建立 Varnish 日志目录:
sudo mkdir -m 755 /data/varnish/logs/ sudo chown -R www:www /data/varnish/logs/
2.3 代码快速获取地址:
wget http://down.hiphp.com/ports/nginx/pcre-7.9.tar.gz wget http://down.hiphp.com/ports/varnish/varnish-2.1.tar.gz
2.4 源代码安装Varnish
tar zxvf pcre-7.9.tar.gz cd pcre-7.9/ ./configure --prefix=/usr/local/webserver/pcre-7.9/ make && make install cd ../ 设置 PKG_CONFIG_PATH tar -zxvf varnish-2.1.tar.gz cd varnish-2.1 ./configure --prefix=/usr/local/varnish-2.1/ --enable-dependency-tracking --enable-debugging-symbols --enable-developer-warnings make make install
注:如果你的gcc版本是4.2.0或更高的版本,可以加上–enable-extra-warnings编译参数,在出错时,得到附加的警告信息。
我这里是用源码包安装的,如果你是redhat或centos可以用rpm包来安装
3、编辑配置文件 /usr/local/varnish-2.1/etc/varnish/default.vcl:
这里我对这段配置文件说明一下:
(1) Varnish通过反向代理访问192.168.1.104和192.168.1.105,端口为80的Web服务器;
(2) Varnish对hiphp.com或者hiphpimg.com域名进行缓存;
(3) Varnish对以.asp和.cgi已经带有?的URL直接从源服务器取;
(4) Varnish对fetch里边符合正则表达式的连接进行指定时间的缓存。
下面是我的范例配置文件:
backend web4 { .host = "192.168.1.104"; .port = "80"; .probe = { .timeout = 50 ms; .interval = 5s; .window = 10; .threshold = 8; .request = "GET /index.php HTTP/1.1" "Host: 192.168.1.104" "Connection: close" "Accept-Encoding: foo/bar" ; } } backend web5 { .host = "192.168.1.105"; .port = "80"; .probe = { .timeout = 50 ms; .interval = 5s; .window = 10; .threshold = 8; .request = "GET /index.php HTTP/1.1" "Host: 192.168.1.105" "Connection: close" "Accept-Encoding: foo/bar" ; } } director web random { { .backend = web4; .weight = 1; } { .backend = web5; .weight = 1; } } sub vcl_recv { set req.grace = 30s; 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") { /* 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); # } # if (req.http.user-agent ~ "(.*)(Nokia|Sony|Ericsson|Motorola|Samsung|Lg|Sie-|Philips|Panasonic|Alcatel|Lenovo|Cldc|Midp|Wap|Mobile)(.*)") # { # error 750 "ganji.cn"; # } if (req.http.host ~ "^(.*).hiphp.com" || req.http.host ~ "^(.*).hiphpimg.com") { set req.backend = web; } if (req.url ~ "\.(asp|cgi)($|\?)") { return (pass); } else { return (lookup); } } #sub vcl_error { # if (obj.status == 750) # { # if (req.http.host ~ "^(.*).ganji.com") # { # set obj.http.Location = "http://"regsub(req.http.host,"com","cn"); # } # } # set obj.status = 302; # return (deliver); #} sub vcl_pipe { # Note that only the first request to the backend will have # X-Forwarded-For set. If you use X-Forwarded-For and want to # have it set for all requests, make sure to have: # set req.http.connection = "close"; # here. It is not set by default as it might break some broken web # applications, like IIS with NTLM authentication. return (pipe); } sub vcl_pass { return (pass); } sub vcl_hash { set req.hash += req.url; if (req.http.host) { set req.hash += req.http.host; } else { set req.hash += server.ip; } return (hash); } sub vcl_hit { if (!obj.cacheable) { return (pass); } return (deliver); } sub vcl_miss { return (fetch); } sub vcl_fetch { set req.grace = 30s; if (!beresp.cacheable) { return (pass); } if (beresp.http.Set-Cookie) { return (pass); } #Not to cache if (req.url ~ "^/vip/") { return (pass); } #To cache if (req.request == "GET" && req.url ~ "\/[0-9]{4}\.htm$") { set beresp.ttl = 300s; } if (req.request == "GET" && req.url ~ "^/[a-z]+\d?/$") { set beresp.ttl = 300s; } if (req.request == "GET" && req.url ~ "^/fang(.*)$") { set beresp.ttl = 300s; } if (req.request == "GET" && req.url ~ "^/piao(.*)$") { set beresp.ttl = 20s; } if (req.request == "GET" && req.url ~ "\.(png|swf|txt|png|gif|jpg|css|js|htm)$") { set beresp.ttl = 3600s; } if (req.request == "GET" && req.url ~ "\/([0-9]+)_([0-9]*)\.htm$") { set beresp.ttl = 3104000s; } return (deliver); } sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT from bj-185.cache.ganji.com"; } else { set resp.http.X-Cache = "MISS from bj-185.cache.ganji.com"; } return (deliver); }
4、用适当权限启动 Varnish
ulimit -SHn 51200 /usr/local/varnish-2.1/sbin/varnishd -f /usr/local/varnish-2.1/etc/varnish/default.vcl -a 192.168.1.185:80 -u www -g www -p thread_pool_max=51200 -p thread_pools=8 -h classic,500009 -p lru_interval=20 -s file,/data/varnish/varnish_cache.data,51200M -w 1000,51200,10 -T 192.168.1.185:3500
5、配置开机自动启动 Varnish
编辑启动执行脚本/etc/rc.local並将上面內容贴到最下方,也可以写成一个启动脚本varnish.sh
6、启动 varnishncsa,用来将Varnish访问日志写入日志文件:
/usr/local/varnish-2.1/bin/varnishncsa -a -w /data/varnish/logs/varnish.log &
7、Varnish 优化
7.1 优化Linux核心参数
编辑/etc/sysctl.conf 在底部增加以下内容: net.ipv4.tcp_keepalive_time = 300 #表示当Keepalive起用的时候,TCP发送keepalive消息的频繁度。预设值是2小时,这里我改为5分钟。 net.ipv4.tcp_tw_reuse = 1 #表示开启重用。允許將TIME-WAIT sockets重新用於新的TCP連接,預設为0,表示开关,我將它开啟。 net.ipv4.tcp_tw_recycle = 1 #表示开启TCP連接中TIME-WAIT sockets的快速回收,预设为0,表示开关,我将它开启。 net.ipv4.tcp_max_tw_buckets = 5000 #表示系統同时保持TIME_WAIT套接字的最大属性,如果超过这个数字,TIME_WAIT套接字將立刻被清除並印出警告訊息。预设是180000,改为5000。
之后记得执行 /sbin/sysctl -p 使配置生效。
以下配置是官网上提供:(官网上说,这个配置可以支持4000-8000 req/s的压力.)
net.ipv4.ip_local_port_range = 1024 65536 #表示用來向外连接的端口范围。预设情况下很小:32768到61000,我将它改为1024到65536。 net.core.rmem_max=16777216 net.core.wmem_max=16777216 net.ipv4.tcp_rmem=4096 87380 16777216 net.ipv4.tcp_wmem=4096 65536 16777216 net.ipv4.tcp_fin_timeout = 3 #表示如果套接字由本端要求开放,这个参数决定了它保持在FIN-WAIT-2状态的时间。 net.core.netdev_max_backlog = 30000。 net.ipv4.tcp_no_metrics_save=1 net.core.somaxconn = 262144 net.ipv4.tcp_syncookies = 0 #表示开启SYN Cookies。当出现SYN等待对列溢出时,启用cookies來处理,可防范少量SYN攻击,预设为0。 net.ipv4.tcp_max_orphans = 262144 net.ipv4.tcp_max_syn_backlog = 262144 #表示SYN队列的长度,预设为1024,加大队列长度为262144,可容纳更多等待连接的网路连接数。 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2
7.2 系统环境优化
ulimit -HSn 131072 ulimit -HSc unlimited
7.3 VCL优化
vcl_recv: set req.grace = 30s; vcl_fetch: set obj.grace = 30s;
7.4 参数优化.(“telnet localhost 5000″后,输入”param.show”可以看到所有系统运行中的参数.输入”param.set thread_pools 8″可以调整参数.)
thread_pools 8 [pools] thread_pool_max 2000 [threads] thread_pool_min 100 [threads] thread_pool_timeout 10 [seconds] #这四个参数要一起看. #thread_pools是系统sess进入处理的pools.理想的情况下是一个cpu一个pool,如果pools过多会消耗cpu时间和mem.但是,pools多一点,处理并发的能力会更强. #thread_pool_min是每个pools的最小threads数.当pools侦测到可处理sess后,就分配给所属的空余threads处理. #thread_pool_max是所有pools所属的threads总和数的上限值.这个值不要设置的太高,一般是系统期望峰值的90%.太高了会发生"pile-ups",不知道怎么翻译,是不是"拥挤"? #thread_pool_timeout是thread的过期时间.当threads数大于thread_pool_min的时候,thread的空闲超过thread_pool_timeout时间,thread就被释放. listen_depth 1024 [connections] #tcp链接队列size.默认是512,适当调大一点,处理并发能力增强. lru_interval 20 [seconds] #优雅时间参数(不知道是不是应该这么翻译),意思就是,如果一个object,在内存中超过了这个时间还没有被重用,则把这个对象移动到 LRU(Least Recently Used)队列中.一种普遍的cache算法.个人理解,提高这个时间,会减少object在内存中的copy,以提高运行效率.
8、接下來看看如何管理Varnish:
8.1 查看 Varnish 服务器连接数与命中率:
/usr/local/varnish-2.1/bin/varnishstat
8.2 通过 Varnish 管理端口進行管理(用 –help查看可用的指令)
/usr/local/varnish-2.1/varnishadm -T 127.0.0.1:3500 help
8.3 通过Varnish管理端口,使用正则表达式批量清除缓存:
(1) 例如:清除类似http://www./a/abc.html的URL地址:
/usr/local/varnish-2.1/bin/varnishadm -T 127.0.0.1:3500 url.purge /a/
(2) 例如:清除类似http://www./tech的URL地址:
/usr/local/varnish-2.1/bin/varnishadm -T 127.0.0.1:3500 url.purge w*$
(3) 例如:清除所有缓存:
/usr/local/varnish-2.1/bin/varnishadm -T 127.0.0.1:3500 url.purge *$
8.4 下面是一个每天0点执行,按天切割Varnish日志,生成一个压缩文档,同时删除上个月旧日志的腳本(/usr/local/shell/cutlog.sh):
#!/bin/sh # This file run at 00:00 date=$(date -d "yesterday" +"%Y-%m-%d") pkill -9 varnishncsa mv /data/varnish/logs/varnish.log /data/varnish/logs/${date}.log /usr/local/varnish-2.1/bin/varnishncsa -a -w /data/varnish/logs/varnish.log & gzip -c /data/varnish/logs/${date}.log > /data/varnish/logs/${date}.log.gz rm -f /data/varnish/logs/${date}.log rm -f /data/varnish/logs/$(date -d "-1 month" +"%Y-%m*").log.gz
设定在每天早上00:00定时执行:
sudo /usr/bin/crontab -e
添加以下內容
0 0 * * * /usr/local/shell/cutlog.sh
8.5 如果你要确定varnish是否有正确执行,你可以用netstat -atp看有沒有一个正在监听的3500端口,或者用pa aux | grep varnishd看这个程序是否有在运行。
8.6 Varnish监控程序使用
/usr/local/varnish-2.1/bin/varnishtop -i rxheader -I Referer /usr/local/varnish-2.1/bin/varnishtop -i rxurl /usr/local/varnish-2.1/bin/varnishlog -o -c ReqStart 192.168.113.171