首页->正文

Varnish安装配置和使用

2011-05-05 -Web开发 标签: 缓存 varnish

先引用Varnish官方网站宣传语:Varnish makes websites fly! (让网站飞?)

Varnish定义为一种网站加速的软件(其实就是反向代理),可以把整个网页内容缓存到内存(或文件)中,并设置不同类型的页面缓存不同时间(TTL),同时提供缓存的更新(purge)。
此外还可以设置多个后端服务器,支持后端服务器的健康检查,Header甚至url地址的重置修改,以及ESI(Edge Side Includes)功能(与nginx的ssi类似)

先介绍一下Varnish处理请求的主要处理方法和流程
1 vcl_recv,首先接收请求,判断是否要进一步处理,还是直接转发给后端(pass)等。 此过程中可以使用和请求相关的变量,例如客户端请求的url,ip,user-agent,cookie等,此过程中可以把不需缓存的地址,通过判断(相等、不相等、正则匹配等方法)转给后端,例如gif/png/jpg/css/js等静态文件;
2 vcl_fetch,当从后端服务器获取内容后会进入此阶段,除了可以使用客户端的请求变量,还可以使用从后端获取的信息(bersp),如后端返回的头信息,设置此信息的缓存时间TTL等;
3 vcl_miss 缓存未命中时中要做的处理
4 vcl_hit 缓存命中后做的处理
5 vcl_delever 发送给客户端前的处理
6 vcl_pass 交给后端服务器
7 vcl_hash 设置缓存的键值key

首次请求时过程如下:
recv->hash->miss->fetch->deliver
缓存后再次请求:
recv->hash->hit->deliver(fetch的过程没了,这就是我们要做的,把要缓存的页面保存下来)
直接交给后端pass的情况:
recv->hash->pass->fetch->deliver(直接从后端获取数据后发送给客户端,此时Varnish相当于一个中转站,只负责转发)

*详细请求过程可以参考./bin/varnishlog中的输出日志

下面具体介绍安装和配置
一、安装
1 下载源文件http://www.varnish-cache.org/releases,或者选择相应的编译版本直接安装(如直接安装方式以下步骤可跳过)
$ tar zxf varnish-2.1.5.tar.gz
$ cd varnish-2.1.5/

#假设安装在/data/app/varnish下,详细安装设置查看帮助./configure --help
$ ./configure --prefix=/data/app/varnish
$ make
$ make install

注意,如果配置中提示一个或者其中几个警告信息的话,是没有安装对应的支持库
configure: WARNING: xsltproc not found – not building documentation
checking for rst2man... no
checking for rst2man.py... no
configure: WARNING: rst2man not found – not building man pages
checking for clock_gettime in -lrt... yes
checking for dlopen in -ldl... yes
checking for library containing initscr... no
configure: WARNING: curses not found; some tools will not be built


直接安装对应扩展库即可,否则varnishhist varnishtop varnishsizes varnishstat这些工具不会安装
sudo apt-get install xsltproc  rst2man libncurses5-dev


二、配置
首先,修改Varnish的配置文件default.vcl,默认在安装目录的./etc/varnish/下,当然,在哪并不重要,可以在启动服务的时候指定配置文件(关键你要记得位置……)
#开头的是注释标记,.host表示的是后端服务器的地址或者域名,.port其对应的端口号(这里为了方便测试,Varnish和web服务器都位于一台机子上,我们用Varnish监听8080端口,暂时先不修改原web服务器的80端口)
backend default {
     .host = "127.0.0.1";
     .port = "80";
}

好了,可以运行Varnish了,
sudo ./sbin/varnishd -f /data/app/varnish/etc/varnish/default.vcl -s malloc,32M\
 -T 127.0.0.1:2000 -a 0.0.0.0:8080 -F

直接访问localhost:8080即可看到经过Varnish输出的页面了!怎么样,很简单吧。
通过Firebug或者curl -I查看http头信息:
Server: nginx
Content-Type: text/html
Last-Modified: Sat, 09 Apr 2011 15:51:38 GMT
Content-Encoding: gzip
Content-Length: 134
Date: Thu, 05 May 2011 14:15:59 GMT
X-Varnish: 1047954835 1047954830
Age: 7
Via: 1.1 varnish
Connection: keep-alive

可以看到多了三项X-Varnish、Age、Via

如何判断是否是经过缓存的呢?
首先可以看Age字段,如果是大于0的话,说明是从缓存命中的。其次查看X-Varnish字段,如果是两个数字例如X-Varnish: 1668515406 1668515405
代表的是Varnish两次请求id(req.xid):一个为发起请求的id,另外一个是获取缓存的id,而缓存未命中或者pass到后端的情况都是一个数值(当然你可以在deliver过程中修改这些头信息或者直接隐藏掉)

注意!
如果后端程序设置了类似这样的头信息Cache-Control: max-age=300
会重置默认的缓存时间TTL,当然你可以在配置文件中重置这个时间

下面看具体的配置文件,具体作用看注释
#设置后端服务器地址
backend default {
     .host = "127.0.0.1";
     .port = "80";
}

#允许刷新缓存的ip
acl purgeAllow {
     "localhost";
     "192.168.56.1";
}

sub vcl_recv {
     #刷新缓存设置
     if (req.request == "PURGE") {
          #判断是否允许ip
          if (!client.ip ~ purgeAllow) {
               error 405 "Not allowed.";
          }
          #去缓存中查找
          return (lookup);
     }
     
     #首次访问增加X-Forwarded-For头信息,方便后端程序
     #获取客户端ip
     if (req.restarts == 0) {
          #如果设置过此header则要再次附加上,用,隔开,如果
          #只有一层代理的话,就无需设置了
          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;
          }
     }

     #修正客户端的Accept-Encoding头信息,默认选用gzip方式
     #防止个别浏览器发送类似 deflate, gzip
     #当然后端如果只支持gzip的话.可以只设置gizp
     if (req.http.Accept-Encoding) {
          if (req.http.Accept-Encoding ~ "gzip") {
               set req.http.Accept-Encoding = "gzip";
          }
          #如果后端支持deflate方式,建议去掉,否则刷新缓存时
          #需特殊处理
          elsif (req.http.Accept-Encoding ~ "deflate") {
               set req.http.Accept-Encoding = "deflate";
          }
          #其他的压缩方式忽略
          else {
               remove req.http.Accept-Encoding;
          }
     }

     #静态文件和明确以.php结尾的url无需缓存, 建议把静态文件分离
     #至单独服务器下,减少动态应用服务器压力,同时降低arnishd的压力
     if(req.url ~ "\.(png|gif|jpg|css|js|php)$"){
          return (pass);
     }

     #非正规的请求直接转发给后端服务器
     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请求,如果你不想缓存POST的页面话
     if (req.request != "GET" && req.request != "HEAD") {
          return (pass);
     }
     #http认证的页面也pass
     if (req.http.Authorization) {
          return (pass);
     }
     
     return (lookup);
}

#管道?按官方文档说的是此模式下请求会原封不动的转交给后端知道连接关闭
sub vcl_pipe {
     return (pipe);
}

#交给后端服务器
sub vcl_pass {
    return (pass);
}

#缓存文件名的哈希处理
sub vcl_hash {
     #以url为hash
     set req.hash += req.url;
     #加上host
     if (req.http.host) {
          set req.hash += req.http.host;
     }
     #没有则取ip
     else {
          set req.hash += server.ip;
     }
     #支持压缩的要增加,防止发送给不支持压缩的浏览器压缩的内容
     if(req.http.Accept-Encoding){
          set req.hash += req.http.Accept-Encoding;
     }
     return (hash);
}

#缓存命中后的处理
sub vcl_hit {
     #如果不可缓存,直接交给后端
     if (!obj.cacheable) {
          return (pass);
     }
     #如果为更新缓存的请求,则设置ttl为0,并告知客户端处理结果
     if (req.request == "PURGE") {
          set obj.ttl = 0s;
          error 200 "Purged.";
     }
     
     return (deliver);
}

#缓存未命中
sub vcl_miss {
     #更新缓存的请求,提示缓存不存在Not in cache
     if (req.request == "PURGE") {
          error 404 "Not in cache.";
     }
     #既然缓存不存在了,就去后端取吧
     return (fetch);
}
# 
sub vcl_fetch {

     #是否开启ESI功能(仅做演示用,单独给/esi-test.html开启esi)
     if (req.url == "/esi-test.html") {
          esi;
     }

     #如果后端返回不可缓存,直接pass
     if (!beresp.cacheable) {
          return (pass);
     }
     
     #/article/下的url缓存30分钟(演示用)
     if (req.url ~ "^/article/\d+\.html$"){
          set beresp.ttl = 30m;
     }
     
     #/category/下的url缓存20分钟(演示用)
     if (req.url ~ "^/category/$"){
          set beresp.ttl = 20m;
     }
     
     #如果后端发送了set-cookie头信息(如session会话开启),则不缓存
     if (beresp.http.Set-Cookie) {
          return (pass);
     }
     
     return (deliver);
}

#发送给客户端
sub vcl_deliver {
     #去掉varnish中的一些头信息(如果你不想保留的话,建议保留Age,方便查看)
     #remove resp.http.X-Varnish;
     #remove resp.http.Via;
     return (deliver);
}

#定义错误页面的信息
sub vcl_error {
     #设置返回页面的类型及编码
     set obj.http.Content-Type = "text/html; charset=utf-8";
     #具体内容,可以更加自己的需要修改错误提示信息
     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>
          <pre>
          "}req.http.Host{"
          </pre>
          <hr>
          <p>Varnish cache server</p>
          </body>
          </html>
          "};
     return (deliver);
}


最后简单总结下Varnish
优点
.不同的页面设置不同的缓存时间,缓存更新快捷
缺点
.缓存不能持久保存,服务重启后缓存消失
.不支持https,本身不提供gzip输出(只是把后端服务器返回的压缩信息保存下来)
.Transfer-Encoding方式中的chunked消失了,替代的是Content-Length,看来Varnish是获取后端的数据后自己计算出了body长度

要考虑的问题
.后端动态应用的相应修改,例如对IP的获取,要通过http头信息中的x-forwarded-for获得,另外对cookie的读取不能直接在代码中调用,例如登录信息的读取可以改为js方式,防止页面缓存用户私有的信息(如用户的个人中心)
.直接pass给后端的请求会有一定的开销(代理服务器都有这个问题,前提是这个开销要小于缓存后的开销,否则还不如直接访问后端)
.不提供gzip等格式的压缩,而是从后端获取时更加Accept-Encoding来获取(即完全模拟浏览器的行为),所以很容易造成两种或多种格式的缓存文件,给缓存的更新带来麻烦

其他注意事项
.启动varnishd的时候如果选用-s file方式,每次会重新建立缓存文件,而原来的文件不会删除,因此注意清除这些遗留文件
.缓存的默认保存时间(time-to-live准确说应该是缓存的存活时间)default_ttl默认为120秒,当然你可以在启动varnish的时候指定为121秒,如:-t 121
.启动varnishd时注意-w min[,max[,timeout]],分别代表thread_pool_min、thread_pool_max、thread_pool_timeout,根据实际需求调节,当然你可以通过varnishadm随时调节
.写ACL配置文件时要注意,例如
acl purgeAllow {
        "localhost";
        "192.168.56.0"/24;
}

其中/24表示ip范围的要写到"之外,否则Varnish启动时就over了,也不报解析错误,会一直停留在第一阶段,一定要注意!(折腾了n次,去掉acl时才发现……)

.如何不停止服务重新加载配置文件
telnet 127.0.0.1 2000
vcl.load newconfig /data/app/varnish/etc/varnish/default.vcl
vcl.use newconfig
同时varnishlog中可以看到以下日志,表示加载配置文件成功
0 CLI          - Rd vcl.use newconfig


.查看编译为C语言后的VCL文件
./varnishd -C -f /data/app/varnish/etc/varnish/default.vcl


./varnish/bin/下的一些工具简单介绍
varnishadm
管理varnish,设置运行时的一些变量,加载配置文件等,和telnet到指定的管理地址功能一样
varnishlog
实时显示varnish的请求日志
varnishncsa
以Apache标准的格式combined输出日志
varnishreplay
从Varnish访问日志中获取请求,并重新请求,可以用来预热Varnish缓存
varnishstat
显示当前运行的一些概要统计,如运行了多长时间、接受和处理的请求数、缓存命中情况等等
varnishtest
通过编写脚本来测试Varnish
varnishtop
从Varnish访问日志中获取信息,用来显示类似请求页面、ip之类的排名情况
varnishhist&varnishsizes
实时显示日志中缓存命中情况,不重复的请求情况?文档很。。。有待发掘
以上命令具体参见手册http://www.varnish-cache.org/docs/2.1/reference/

附1,简单的测试
用apache自带的ab工具测试
ubutnu下可以通过
sudo apt-get install apache2-utils
安装
采用10000个请求,100个并发
ab -n 10000 -c 100 "http://....url...."

测试结果如下
静态页
Varnish每秒3410个
nginx每秒4095个
动态页
Varnish(缓存后)每秒3531个
nginx的纯动态页每秒722个
可见对动态页缓存后Varnish优势还是很大的,当然测试结果跟硬件,操作系统的优化,后端请求页面的大小,Varnish和nginx的参数配置关系都很大,应根据应用的实际情况做具体测试。

附2,运行状况检测Health checks
.probe = {
     #探测后端服务器的url
     .url = "/";
     #探测的间隔时间
     .interval = 5s;
     #探测多久算超时
     .timeout = 1 s;
     #确认服务器状态的次数(a sliding window?)
     .window = 5;
     #在确认服务器状态的次数时,检测够多少次后把后端标识为记作健康或不健康
     #也就是说当后端服务器恢复后,并不是马上被Varnish标识为可用,而是必须达到
     #这个设定数才标识为真正可用,具体处理流程可查看varnishlog
     .threshold = 3;
}

varnishlog中对应日志如下
0 Backend_health - default Still healthy 4--X-RH 5 3 5 0.000956 0.001645 HTTP/1.1 200 OK


相关链接
官方文档 http://www.varnish-cache.org/docs/
Varnish配置举例 http://www.varnish-cache.org/trac/wiki/VCLExamples
web加速 http://en.wikipedia.org/wiki/Web_accelerator
相关文章 http://kristianlyng.wordpress.com/tag/varnish/

@后记
原文标题为“Varnish-让网站飞”,后感觉过于文学化,还是技术化点比较大众化,改为了现在的“Varnish安装配置和使用”瞧!多通俗易懂!

下一篇 用php解析配置文件

上一篇 Windows下的Linux命令工具集

相关文章

nginx代理简单应用

php增加Last-Modified为何无效

Varnish3.0中对gzip的支持

配置Varnish支持多个后端域名

文章分类

开发小提示

  • 1:Mongodb中通过db.yourCollectionName. dataSize()查看某个文档的大小
  • 2:linux下用reset命令恢复查看二进制文件导致的命令行乱码
  • 3:查看MySQL表的索引情况show index from tableName
  • 更多...

交流

  • wangnow(a)126.com