varnish作为一款优秀的反向代理服务器以及缓存服务器,已经越来越流行,本文就Varnish的基本使用以及如何与drupal合作使用,做一个简要清单。
安装
推荐使用系统自带的源安装,比如apt或者yum。
结构
/etc/varnish/ 存放varnish VCL配置文件
/etc/sysconfig/varnish 【CentOS】 存放varnish服务器运行的参数
/etc/default/varnish 【Ubuntu】 存放varnish服务器运行的参数
/usr/sbin/varnishd varnish服务器执行文件
/etc/init.d/varnish 运行程序
相关命令
这里列举一下比较有用的几个varnish管理命令
varnishadm 管理Varnish后端的工具 telnet也可以(下面详细介绍)
varnishhist 查看Varnish命中的工具 运行可以看到一张柱状描绘图,|表示缓存命中,#表示未命中,横向代表时间。 【非常有用】
varnishlog 实时显示varnish的请求日志
varnishncsa 以Apache标准的格式combined输出日志
varnishstat 查看状态、参数等,具体查阅百度。【非常有用】
varnishtop 类似top工具,查看varnish相关进程的资源、运行等状况。
varnishncsa 将Varnish的log以Apache的格式输出,varnishlog以原始方式显示Varnish的日志。我们知道varnish默认会把日志存放在内存中,如果我们要把日志存放起来,就需要你启动一个守护进程,把内存中的日志存放到文件中。
Varnish — (http)—> Backend servers
|
|–> (Daemon) varnishncsa/varnishlog === (write) ==> Log Files
管理varnish以及清除内存等操作虽然可以使用varnishadm,但是这里推荐使用telnet,一个交互的管理界面。
比如: telnet 127.0.0.1 6082
之后,输入help会显示所有可用命令。
help [command]
ping [timestamp]
status
start
stop
stats
vcl.load
vcl.inline
vcl.use
vcl.discard
vcl.list
vcl.show
param.show [-l] []
param.set
quit
purge.url
purge.hash
purge [&& ]...
purge.list |
重新加载Varnish配置文件
telnet 127.0.0.1 6082
vcl.load newconfig /data/app/varnish/etc/varnish/default.vcl
vcl.use newconfig |
注意:varnish 的CLI可能需要认证,最简单的办法就是在varnish启动的时候取掉相应的参数。
参考 https://www.varnish-cache.org/docs/trunk/reference/varnish-cli.html
Varnish的缓存方式
Malloc (malloc) 通过 malloc 获取内存,简单,速度快
Mmap file (file) 创建文件缓存
这个是varnish缓存的两种方式,可以在启动的时候通过参数指定。
Varnish处理流程
首次请求时过程如下:
recv->hash->miss->fetch->deliver
缓存后再次请求:
recv->hash->hit->deliver(fetch的过程没了,这就是我们要做的,把要缓存的页面保存下来)
直接交给后端pass的情况:
recv->hash->pass->fetch->deliver(直接从后端获取数据后发送给客户端,此时Varnish相当于一个中转站,只负责转发)
VCL以及基本对象
request 从客户端进来
responses 从后端服务器过来
object 存储在cache中
VCL语言
req 请求目标,当varnish接收到一个请求,这时req object就被创建了,你在vcl_recv中的大部分工作,都是在req object上展开的。
beresp 后端服务器返回的目标,它包含返回的头信息,你在vcl_fetch中的大部分工作都是在beresp object上开展的。
obj 被cache的目标,只读的目标被保存于内存中,obj.ttl的值可修改,其他的只能读。
VCL支持一下运算符
= 赋值运算符
== 对比
~ 匹配,在ACL中和正则表达式中都可以用
! 否定
&& 逻辑与
|| 逻辑或
Grace mode
如果后端需要很长时间来生成一个对象,这里有一个线程堆积的风险。为了避免这 种情况,你可以使用 Grace。他可以让 varnish 提供一个存在的版本,然后从后端生成新 的目标版本。
当同时有多个请求过来的时候,varnish只发送一个请求到后端服务器,在“set beresp.grace = 30m; ”时间内复制旧的请求结果给客户端。
Saint mode
有时候,服务器很古怪,他们发出随机错误,您需要通知 varnish 使用更加优雅的方式处理 它,这种方式叫神圣模式(saint mode)。Saint mode 允许您抛弃一个后端服务器或者另一 个尝试的后端服务器或者 cache 中服务陈旧的内容。
sub vcl_fetch {
if (beresp.status == 500) {
set beresp.saintmode = 10s;
restart;
}
set beresp.grace = 5m;
} |
Varnish代理
########
backend default {
.host = "192.168.0.12";
.port = "8080";
}
#现在添加一个新的backend服务器
backend test {
.host = "192.168.0.12";
.port = "8000";
}
#要定义特殊的url被发送到哪里
sub vcl_recv {
if (req.url ~ "^/abcd/") {
set req.backend = test;
} else {
set req.backend = default;
}
} |
Varnish负载均衡
可以把多台 backends 聚合成一个组,这些组被叫做directors。这样可以增强性能和弹力。您可以定义多个backends和多个group在同一个directors。
backend server1 {
.host = "192.168.0.1" ;
.port = "8080" ;
}
backend server2 {
.host = "192.168.0.2" ;
.port = "8080" ;
}
director drupal001 round-robin {
{ .backend = server1; }
{ .backend = server2; }
}
sub vcl_recv {
if (req.http.host !~ "www\.drupal001\.com$"){
error 404 "Unknown HostName!";
}
set req.backend = drupal001;
} |
健康检查
在之前的两个后端服务器上加上健康检查。
backend server1 {
.host = "192.168.0.1" ;
.port = "8000" ;
.probe = {
.url = "/"; #哪个 url需要varnish请求。
.interval = 5s; #检查的间隔时间。
.timeout = 1 s; #等待多长时间探针超时。
.window = 5; #维持5个sliding window的结果。
.threshold = 3; #至少有三次window是成功的,就宣告bachend健康。
}
} |
注意事项:
1. Varnish的不同版本的配置不同,必须查阅官方文档。
比如,在2.x 直接用ESI表示启用ESI,在3.x就不是了。
2. Varnish 2.x有Cookie的问题,当Cookie过大,会产生503错误,升级varnish或者减少set_cookie的操作。
3. 启动varnishd的时候如果选用-s file方式,每次会重新建立缓存文件,而原来的文件不会删除,因此注意清除这些遗留文件。
注释完整的一段VCL文件参考
#设置后端服务器地址
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 {"
HTTP ERROR
"};
return (deliver);
} |
官方文档
非常重要,必须参考。
https://www.varnish-cache.org/docs
官方示例 Example
https://www.varnish-cache.org/trac/wiki/VCLExamples
VCL2.x升级到3.0
https://www.varnish-cache.org/docs/3.0/installation/upgrade.html
WIKI
https://www.varnish-cache.org/trac/
Drupal on Varnish
https://www.varnish-cache.org/trac/wiki/VarnishAndDrupal
至此,基本的Varnish了解完毕,后面继续讨论ESI的使用,以及如何配合Drupal模块构建高性能网站。
待续…