原文地址
前言:其实使用Varnish真的有一年多的时间了,算是使用较早的了,从原来1.1.4到现在的2.0.4基本每个版本我都用过,而且也为了不同的网站架构做过很多的改动,效果一直都让我很满意。但我一直没有怎么写过文章,的确是因为觉得他还是太简单了,但还是总遇到不少人问,而且中文这方面的资料确实不多,很多都是零散和过时的,也确实是因为用这个的人好像不多。我自己刚开始的时候也是叫天不应的那种情况,没人帮忙,只好自己慢慢测试了。
今天又有一朋友问我,我兴致大发,聊了不少,还是比较懒啊,我直接把对话整理了下,这样也好更直接明了,比直述的强,我想很多人都有这些疑問。对话中如果你仔细的话还会看到一些网站中用到的方案我想都是比较实用的。最后还会附上一个VCL的样本,这段脚本是把不同的网站架构情况集中到了一个上面,而且尽量运用了varnish的一些最新的功能,并且这些都是经过了生产实践后的一个精华浓缩。希望更多的人来研究Varnish让他越来越好。
---------------
首先是大Y问我一个配置方面的问题,问题前半部分削掉看主要的
——————————————————————————————————
大Y
10:44 ?
我们这边全部都是走的域名~不允许直接指定IP
Ajian
11:00 ?
backend xxxx {
.host = "s01.cache.xxx.com";
.port = "80";
} 这个位置 host最好是用IP 用域名也得做hosts 不然会慢的 域名的 这个地方的IP 跟你说的走域名不允许走IP 是不一样的 是告诉varnish 那个要访问的域名在哪台服务器上而已
后面的配置中才会有域名相关的配置
大Y
11:01 ?
用域名也得做hosts 不然会慢的??
Ajian
11:02 ?
你想 直接访问一个机器是IP 快还是域名快?
11:03 ?
域名还需要走DNS 而你做hosts的会是本地解析 就会相对不做的快些
大Y
11:04 ?
但这样会在管理上比较麻烦~
如果后端的机器IP变了~~你可能需要把所有的机器hosts或配置文件改一边~~
Ajian
11:05 ?
嗯 这个会有这个麻烦 但这个更改远比你使用得少吧
你每次使用都会降低这个速度
当然你还可以自己内部做DNS
如果你要用域名没有问题 你内部做个DNS会比较好 你要知道 外部的DNS 还经常出问题呢 速度还是个很大的影响
大Y
11:08 ?
恩~~我们每段vlan都会做dns服务器:)
---------------
开始聊到vanrish和squid的区别了 varnish的优点和缺点
——————————————————————————————————
大Y
11:07 ?
我说这个和squit的对比~:)
Ajian
11:07 ?
对比做过 我肯定是喜欢vanrish才一直用它
大Y
11:08 ?
你觉得这个相比较好处在那~?
Ajian
11:10 ?
好处在于简单 而且速度快 当然这个只能对图片这些小文件来说 对于大文件 我觉得squid的磁盘缓存还是会好些 另外squid的缺点也让他性能不高 使用squid经常要重启 性能是越用越慢
而varnish 我用了很久也不需要去怎么维护他 如果你的网络结构不变的话
也可能我的squid使用得不好
大Y
11:11 ?
恩~我现在主要是想用这做图片什么的缓存~
11:12 ?
呵呵你实际测试过~大概性能能提升多少:)
Ajian
11:13 ?
提升 这样打个比喻吧 缓存的效果 如果在同样的访问量在 varnish 后端的WEB 是100K 流量 而squid后端是10M的流量
当然这个还在于squid的配置水平
但从这一点说明varnish是相当简单 对吧
Ajian
11:20 ?
你刚才说的性能问题 我现在用个实例说明下 你对比下你们的squid看下 有没有区别 我们现在一个cache组的总流量出口为最高80M 而后端总共流量不过2M
大Y
11:17 ?
对了对于前端反应上~~squit和var区别大么?
Ajian
11:20 ?
前端反应?
大Y
11:22 ?
恩~~就是用户响应这块~
我们比较关注用户响应~~这块~
Ajian
11:22 ?
个人觉得vanrish快
大Y
11:22 ?
比方命中率~
Ajian
11:23 ?
命中率 你应该从刚才我举的例子来看
当然这个你得看varnish的并发能力 这个我没有具体去压过 因为没有好的压力测试工具 有时间你可以做个詳細的对比测试
varnish是基于内存的 是直接读取 而squid是从硬盘读取 在工作正常的情况下 你想哪个会比较快?
原理来说 是varnish快 只要varnish 工作正常 而且能够承受够大的并发压力
大Y
11:25 ?
恩~我看中var也是因为它是基于内存的~
Ajian
11:26 ?
这个有优点 也有一个不好的缺点
一旦你需要更新 或者重启varnish的时候会造缓存丢失
大Y
11:27 ?
恩对后端压力大
Ajian
11:27 ?
如果你前面的cahce 很多 而且同时重启的话会对后端压力很大
所以一定尽量不要重启他 或者重启他的时候 错开时间 不要同时
你如果真的用上的话 还有一个课题必需要攻克的就是如何清理指定缓存的问题 我现在也在为这个为难 如果是用默认的hash还是可以解决
大Y
11:30 ?
我看它这个也支持PURGE来清理指定的缓存~
Ajian
11:30 ?
这个只能在默认hash的情况下
hash的规则 像我们站现在的 就加了很多 就无法使用 主要是不清楚他内部的具体机制
大Y
11:33 ?
默认hash我看说比较影响性能~~
Ajian
只能说默认的hash少了些东西 网站总有很多特殊的东西
他是最基本的 会少很多东西
如压缩的
---------------
新Varnish的功能
——————————————————————————————————
大Y
11:36 ?
我看到了2后支持RR和健康监测~~
呵呵~你做了这么?
Ajian
11:37 ?
这个我就用着
不过他的健康状态监测比较的傻 我只能把他当成备份的一种策略来做
大Y
11:39 ?
哦他监控检测发现失败后会剔除出去么?
Ajian
11:39 ?
健康状态监测很傻 在执行失败后会按你给他的服务器列表挨个跑一遍 直到遇到好的
他有个负载机制 还行
大Y
11:40 ?
失败一次后不会以后每次都是挨个走一遍?
Ajian
11:46 ?
失败后 会转到你设置的服务器列表 如你配置说如果第一次出错了 就找到哪个服务器 如果再失败了再去哪个服务器 这个就看你怎么设置了 你也可以让他重试几次 ,我现在的策略是 有一台比较差的服务器做备份机 所有出错的都会转给一台机器 减少重试次数
大Y
11:49 ?
哦~呵呵~~~
11:51 ?
你那有vcl呵呵能发我参考看看么:)
所以我决定还是写一个VCL的样本吧,这个VCL的样本把我原来用的几种情况的VCL都归到一个脚本中了,是经过生产情况的考验的。
环境:有四台WEB,
WEB1-3为提供 bbs.test.com 的后端服务器
WEB1-2为负载均衡根据服务器情况设置权重
WEB3为健康状态检测后的备份机 意思就是如果访问WEB1-2其中一台机器出现问题的问题那么转向WEB3服务器再访问
WEB4为theme.abc.com的后端服务器
backend web1 {
.host = "192.168.0.1";
.port = "80";
}
backend web2 {
.host = "192.168.0.2";
.port = "80";
}
backend web3 {
.host = "192.168.0.3";
.port = "80";
}
backend web4 {
.host = "192.168.0.4";
.port = "80";
}
#负载轮询机制
director default random {
{
.backend = web1;
.weight = 200; }
{
.backend = web2;
.weight = 300; }
}
#允许清理缓存控制
acl purge {
"localhost";
"127.0.0.1";
"192.168.0.0"/26;
}
sub vcl_recv {
if (req.request == "PURGE") {
if (!client.ip ~ purge) {
error 405 "Not allowed.";
}
elseif(req.url ~ "\.(php|cgi)($|\?)") {
pass;
}
else {
lookup;
}
}
#设置健康状态检测机制,这里实际是使用了一个备份功能
if((req.http.host ~"^(www.|bbs.)?test.com")&&(req.restarts == 0)) {
set req.backend = default;
} elseif(req.restarts == 1) {
set req.backend = web3;
}
#另外一个域名的配置
if(req.http.host ~"^(theme.|special.)?abc.com") {
set req.backend = web4;
}
#定义一些缓存的内容 PHP 不缓存
if (req.request != "GET" && req.request != "HEAD")
{
pipe;
}
elseif (req.url ~ "\.(php|cgi)($|\?)")
{
pass;
}
elseif (req.http.Authenticate || req.http.Authorization) {
pass;
}
lookup;
}
sub vcl_hit
{
if (req.request == "PURGE") {
set obj.ttl = 0s;
error 200 "Purged.";
}
if (!obj.cacheable)
{
pass;
}
if (obj.http.Vary)
{
unset obj.http.Vary;
}
}
sub vcl_miss
{
if (req.request == "PURGE") {
error 404 "Not in cache.";
}
}
#定义hash的值 这个跟清空缓存有很大的关系
#处理压缩情况的内容
sub vcl_hash {
set req.hash += req.url;
if (req.http.host) {
set req.hash += req.http.host;
} else {
set req.hash += server.ip;
}
if ( req.http.Accept-Encoding ){
if (req.url ~ "\.(jpg|jpeg|png|gif|rar|zip|gz|tgz|bz2|tbz|mp3|ogg|swf|exe|flv|avi|rmvb|rm|mpg|mpeg|pdf)$") {
} else {
set req.hash += req.http.Accept-Encoding;
}
}
hash;
}
sub vcl_fetch
{
#轮询机制的一部分,定义什么时候就restart
if ((obj.status == 500 || obj.status == 501 || obj.status == 502 || obj.status == 503) && req.restarts < 2) {
restart;
}
if (!obj.cacheable)
{
pass;
}
#决定哪些头不缓存
if (obj.http.Pragma ~ "no-cache" || obj.http.Cache-Control ~ "no-cache" || obj.http.Cache-Control ~ "private") {
pass;
}
if (obj.http.Set-Cookie)
{
pass;
}
#定义不同内容的缓存时间
if (req.request == "GET" && req.url ~ "\.(txt|js|css|html|htm)$") {
set obj.ttl = 600s;
}
else {
set obj.ttl = 10d;
}
}