首页 > 代码库 > varnish实现的web缓存
varnish实现的web缓存
面对大量的PV访问,web服务器的缓存功能不容小觑,缓存在互联网中的地位已经举足轻重。那么好的缓存机制对web服务来说已经不可获取,varnish的缓存功能有目共睹,那么我们似乎没理由不了解一下基于varnish是如何让实现缓存的。
varnish的官方站点:https://www.varnish-cache.org/目前最新的版本是Varnish Cache 4.0.0
不一定最新的就是最好的,在实际生产环境中稳定最重要,我们这里使用的版本是varnish-3.0.4-1.el6.x86_64.rpm。
varnish的相关知识介绍:
一:varnish主要运行两个进程:Management进程和Child进程
Management:主要实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等,探测Child进程的健康状态。
Child进程:包含多种类型的线程
1:Acceptor线程:接收新的连接请求并响应
2:Worker线程:child进程会为每个会话启动一个worker线程
3:Expiry线程:从缓存中清理过期内容
4:backend communication 构建向后端服务器构建请求报文
5:log/stats 日志记录
6:Storage/hashing :hash表的存储 缓存就是数据的键值存储
二:连接Management的接口:
1:CLI interface
2:Telnet 已废弃
3:web 商业
三:varnish的缓存存储。
1:file:使用特定的文件存储全部的缓存数据。
2:malloc:使用malloc()库调用在varnish启动时向操作系统申请指定大小的内存空间以存储缓存对象
3:persistent(experimental):与file的功能相同,但可以持久存储数据(即重启varnish数据时不会被清除);仍处于测试期。
为varnishd指定使用的缓存类型时,-s选项可接受的参数格式如下:
1:malloc[,size]
2:file[,path[,size[,granularity]]]
3:persistent,path,size
三:varnish的工作原理图:
varnish的工作是靠几个状态引擎(state engine)也叫子进程来实现的,
varnish的子进程:
1:vcl_recv ---接收请求,并决定将后续操作交给谁 2:vcl_pipe ---在varnish不理解client请求的时候通过pipe直接转交后端server 3:vcl_pass ---在不缓存的请求资源的时候转交给pass 4:vcl_hash ---在缓存请求资源的时候叫给hash 5:vcl_hit ---varnish缓存命中请求时交给hit 6:vcl_miss ---varnish缓存未命中时交给miss 7:vcl_deliver ---从后端server获取到相应资源缓存到varnish,构建响应报文时交给deliver 8:vcl_fetch ---varnish本地没有请求资源的缓存,交给fetch去后端server获取资源时交给fetch去获取。
解说:我自己理解的,语言比较通俗:
当客户请求到达varnish服务器的时候,varnish的vcl_recv先检查自己是否可以理解请求,若不能理解,则直接通过管道vcl_pipe联系后端服务器去处理,若能理解,分两种情况:1:不缓存,则vcl_recv交给pass,pass在交给vcl_pass,cvl_pass在交给vcl_fetch让vcl_fetch去后端服务器获取相应的请求资源,获取到资源以后varnish不缓存,直接响应给客户端。2:缓存,则对请求的URL做hash,hash又分hit,miss, 若hit 则varnish直接利用本地缓存通过vcl_deliver构建响应报文响应给户端,若miss,则找vcl_fetch去后端获取,获取后缓存到本地,并通过vcl_deliver响应给客户端
通过以上介绍,大家应该对varnish有个大概的了解了,下面我们就通过具体配置来实现varnish的缓存控制机制。
(1):varnish的安装:
# rpm -ivh varnish-3.0.4-1.el6.x86_64.rpm varnish-libs-3.0.4-1.el6.x86_64.rpm varnish-docs-3.0.4-1.el6.x86_64.rpm
查看rmp包生成的文件:选几个用的着的:
c
# rpm -ql varnish /etc/rc.d/init.d/varnish ---服务脚本 /etc/sysconfig/varnish ---配置文件 /etc/varnish/default.vcl ---vcl的配置文件,也是实现缓存机制的配置文件 /usr/bin/varnishadm ---用户空间使用工具
如果我们要做web服务器的缓存及代理,还需要编辑下配置文件/etc/sysconfig/varnish
VARNISH_LISTEN_PORT=80 ---修改服务监听的端口 VARNISH_STORAGE="malloc,200M" ---修改缓存机制使用内存,并设定其大小
启动服务
# service varnish start
启动一个webserver,/etc/varnish/default.vcl默认代理的是本地的ip,我们自己编写个.vcl的配置文件叫test.vcl配置如下:
backend web1 { ---定义一个后端服务器叫web .host = "172.16.23.1"; ---后端服务器的ip .port = "80"; ---这个都懂 }
五:缓存代理的简单实现:
实验环境规划拓扑图:
开启varnish服务器的路由转发,并将web server的网关指向varnish的内网ip
测试是成功!
varnish的管理命令行管理工具是varnishadm
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 --- -S指定密钥文件的位置,-T指定登录的ip和端口,varnish默认的管理端口是6082,在/etc/sysconfig/varnish里有相关设定。
varnish> help ---获取帮助信息 200 help [command] ping [timestamp] auth response quit banner status ---显示状态 start stop vcl.load <configname> <filename> ---加载修改过的.vcl的配置文件 vcl.inline <configname> <quoted_VCLstring> vcl.use <configname> ---使用那个版本的.vcl的配置 vcl.discard <configname> vcl.list ---vcl列表 vcl.show <configname> ---查看 param.show [-l] [<param>] ---查看参数信息 param.set <param> <value> ---设定参数值 panic.show panic.clear storage.list ---显示缓存的相关信息 backend.list ---显示后端服务器的相关信息 backend.set_health matcher state ban.url <regexp> ban <field> <operator> <arg> [&& <field> <oper> <arg>]... ban.list
varnish> status ---查看状态 200 Child in state running varnish> vcl.list 200 available 0 boot ---available 表示可用的 active 2 reload_2014-04-23T01:03:06 ---active表示激活的 varnish> vcl.show reload_2014-04-23T01:03:06 ---查看名字叫做这个的.vcl的配置文件内容
代理成功,但是我们不知道我们的请求是否是缓存,所以我们需要在响应报文中添加一个首部来获取我们的请求是否是被缓存命中的
sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } }
varnish> vcl.load test1 test.vcl 200 VCL compiled. varnish> use test1 101 Unknown request. Type ‘help‘ for more info. varnish> vcl.use test1 200 varnish>
我们再次请求验证:
可以看到,缓存命中!
如果要看到是那个服务器命中的还可以在配置文件中附加一个变量server.ip
backend web1 { .host = "172.16.23.1"; .port = "80"; } sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT from"+" "+server.ip; } else { set resp.http.X-Cache = "MISS"; } }
注意:每次加载的时候test的版本都需要改变
varnish> vcl.load test3 test.vcl 200 VCL compiled. varnish> vcl.use test3 200
我们在看测试结果:
OK!结果很明显!
如果我们要对某些请求不做缓存处理我们则需要在vcl_recv中做URL的过滤匹配:
sub vcl_recv { if (req.url ~ "test.html") { return(pass); } return(lookup); }
varnish> vcl.load test4 test.vcl 200 VCL compiled. varnish> vcl.use test4 200
看看效果:
OK!可以看到varnish不在缓存对test.html URL的请求。
几个简单的例子我们可以看到对缓存控制的配置基本都是在.vcl的配置文件中做配置。而且需要用到一些内置的变量。还有内置的函数:例如return()那么我们就对VCL,以及内置函数,内置变量做 简单介绍:
VCL:VCL:Varnish Configuration Language (VCL)是varnish配置缓存策略的工具,它是一种基于“域”(domain specific)的简单编程语言 varnish就是用VCL编写对缓存的处理机制的。vcl文件需要用 VCL compiler 调用C compiler编译成二进制的格式后才能由varnish调用结果
VCL语法
VCL的设计参考了C和Perl语言,因此,对有着C或Perl编程经验者来说,其非常易于理解。其基本语法说明如下:
(1)//、#或/* comment */用于注释
(2)sub $name 定义函数
(3)不支持循环,有内置变量
(4)使用终止语句,没有返回值
(5)域专用
(6)操作符:=(赋值)、==(等值比较)、~(模式匹配)、!(取反)、&&(逻辑与)、||(逻辑或)
vcl的内置函数:
vcl_recv 函数
用于接收和处理请求。当请求到达并成功接收后被调用,通过判断请求的数据来决定如何处理请求。例如如何响应、怎么响应、使用哪个后端服务器等。
此函数一般以如下几个关键字结束。
pass:表示进入 pass 模式,把请求控制权交给 vcl_pass 函数。
pipe:表示进入 pipe 模式,把请求控制权交给 vcl_pipe 函数。
lookup:表示进入 lookup 模式,把请求控制权交给 lookup 指令处理,在缓存中查找被请求的对象, 并且根据查找的结果把控制权交给函数 vcl_hit 或函数 vcl_miss。
error code [reason]:表示返回“code”给客户端,并放弃处理该请求。“code”是错误标识,例 如 200 和 405 等。“reason”是错误提示信息。
vcl_pipe 函数
此函数在进入 pipe 模式时被调用,用于将请求直接传递至后端主机,在请求和返回的内容没有改变的 情况下,将不变的内容返回给客户端,直到这个连接被关闭。
此函数一般以如下几个关键字结束。
error code [reason]。
pipe。
vcl_pass 函数
此函数在进入 pass 模式时被调用,用于将请求直接传递至后端主机。后端主机在应答数据后将应答 数据发送给客户端,但不进行任何缓存,在当前连接下每次都返回最新的内容。
此函数一般以如下几个关键字结束。
error code [reason]。
pass。
restart 重新启动流程,增加启动次数,如果重新启动次数高于 max_restarts 发出一个错误警告
vcl_hash
当您想把一个数据添加到 hash 上时,调用此函数。
此函数一般以如下几个关键字结束。
Hash。
vcl_hit 函数
在执行 lookup 指令后,在缓存中找到请求的内容后将自动调用该函数。
此函数一般以如下几个关键字结束。
deliver:表示将找到的内容发送给客户端,并把控制权交给函数 vcl_deliver。
error code [reason] 。
pass。
restart 重新启动流程,增加启动次数,如果重新启动次数高于 max_restarts 发出一个错误警告
vcl_miss 函数
在执行 lookup 指令后,在缓存中没有找到请求的内容时自动调用该方法。此函数可用于判断是否需要从后端服务器获取内容。
此函数一般以如下几个关键字结束。
fetch:表示从后端获取请求的内容,并把控制权交给 vcl_fetch 函数。
error code [reason] 。
pass。
vcl_fetch 函数
在后端主机更新缓存并且获取内容后调用该方法,接着,通过判断获取的内容来决定是将内容放入缓存,还是直接返回给客户端。
此函数一般以如下几个关键字结束。
error code [reason]。
pass。
deliver。
esi。
restart 重新启动流程,增加启动次数,如果重新启动次数高于 max_restarts 发出一个错误警告
vcl_deliver 函数
将在缓存中找到请求的内容发送给客户端前调用此方法。
此函数一般以如下几个关键字结束。
error code [reason]。
deliver。
restart 重新启动流程,增加启动次数,如果重新启动次数高于 max_restarts 发出一个错误警告
vcl_error
出现错误时调用此函数。
此函数一般以如下几个关键字结束。
deliver。
restart。
VCL 内置公共变量
VCL 内置的公共变量可以用在不同的 VCL 函数中,下面根据使用的不同阶段进行介绍
当请求到达时,可以使用的公共变量表 1 所示
表 1. 请求到达时可用公共变量
公共变量名 | 含义 |
---|---|
req.backend | 指定对应的后端主机 |
server.ip | 表示服务器 IP |
client.ip | 表示客户端 IP |
req.quest | 只是请求的类型,例如 GET、HEAD 等 |
req.url | 指定请求的地址 |
req.proto | 表示客户端发起请求的 HTTP 协议版本 |
req.http.header | 表示对应请求中的 HTTP 头部信息 |
req.restarts | 表示重启次数,默认最大值为 4 |
Varnish 在向后端主机请求时,可是用的公共变量如表 2 所示
表 2. 向后端主机请求时可用公共变量
公共变量名 | 含义 |
---|---|
beresp.requset | 指定请求类型,例如 GET、HEAD 等 |
beresp.url | 表示请求地址 |
beresp.proto | 表示客户端发起请求的 HTTP 协议版本 |
beresp.http.header | 表示对应请求中 HTTP 头部信息 |
beresp.ttl | 表示缓存的生存周期,cache 保留时间(s) |
从 cache 或是后端主机获取内容后,可以使用的公共变量如表 3 所示
表 3. 后端主机获取内容时可使用公共变量
公共变量名 | 含义 |
---|---|
obj.status | 返回内容的请求状态码,例如 200、302、504 等 |
obj.cacheable | 返回的内容是否可以缓存 |
obj.valid | 是否有效的 HTTP 请求 |
obj.response | 返回内容的请求状态信息 |
obj.proto | 返回内容的 HTTP 版本 |
obj.ttl | 返回内容的生存周期,也就是缓存时间,单位秒 |
obj.lastuse | 返回上一次请求到现在的时间间隔,单位秒 |
对客户端应答时,可以使用的公共变量如表 4 所示
表 4. 对客户端相应时可使用公共变量
公共变量名称 | 含义 |
---|---|
resp.status | 返回给客户端的 HTTP 代码状态 |
resp.proto | 返回给客户端的 HTTP 协议版本 |
resp.http.header | 返回给客户端的 HTTP 头部消息 |
resp.response | 返回给客户端的 HTTP 头部状态 |
varnish,作为缓存服务器,不是所有资源都要缓存的。缓存也是有选择的缓存,那么那些资源可以缓存,那些资源不需要缓存?2:如果缓存,缓存多久?3:如果我们不想要某个缓存资源了但是却没有超出我们定义的缓存有效时间,如何手动清除?4:如果对某一资源,我们的后端服务器上多个的,怎么定义多个服务器为一组?5:如果我们的资源是分类的,如何定义不同的请求到不同的服务器上?总结下:
不缓存的资源:
1:用户的敏感信息例如认证信息不缓存
2:带cookie信息的用户请求不需要缓存,有时候反而会降低缓存命中,因为,带来cookie,hash计算的时候就根据url+cookie计算的
3:服务器端给发送的响应报文带有set-cookie信息不缓存
实现配置:请求的不缓存是在vcl_recv中配置的,服务器端的是在vcl_fetch中配置的
sub vcl_recv { if (req.http.Authorization || req.http.Cookie) { return (pass); } if (!(req.url ~ "wp-(login|admin)")) { unset req.http.cookie; } } sub vcl_fetch { if (!(req.url ~ "wp-(login|admin)")) { unset beresp.http.set-cookie; } }
服务器组的代理缓存配置:是通过directo
backend web1 { .host = "www.a.com"; .port = "80"; } director webservers random { ---random调度方法 .retries = 5; ---表示尝试找到一个 backend 的最大次数 { .backend = web1; .weight = 2; } { .backend = { .host = "www.b.org"; .port = "80"; } .weight = 3; } }
后端服务器的健康状态监测:VCL 可以设置 probe 来检测一个 backend 是否健康
backend web1 { .host = "www.a.com"; .port = "80"; .probe = { .url = "/test.jpg";// 哪个 url 需要 varnish 请求 .timeout = 1 s;// 等待多长时间超时 .interval = 5s// 检查的时间间隔 .window = 5;// 维持 5 个 sliding window 的结果 .threshold = 3;// 至少有三次 window 是成功的,就宣告 backend 健康 } }
对于资源缓时间的设定我们是在/etc/sysconfig/varnish配置文件中设定的
VARNISH_THREAD_TIMEOUT=120
对于缓存资源未到期,但是我们需要清理,是靠 purgers参数来实现手动清理的:
安全起见,我们需要对purgers进行acl的访问控制:purgers的手动清理实现配置:
acl purgers { “127.0.0.1”; “192.168.0.0”/24; } sub vcl_recv { if (req.request == “PURGE”) { if (!client.ip ~ purgers) { error 405 “Method not allowed”; } return (lookup); } } sub vcl_hit { if (req.request == “PURGE”) { purge; error 200 “Purged”; } } sub vcl_miss { if (req.request == “PURGE”) { purge; error 404 “Not in cache”; } } sub vcl_pass { if (req.request == “PURGE”) { error 502 “PURGE on a passed object”; } }
对不同资源的请求发往不同服务器的设定:
sub vcl_recv { if (req.url ~ "\.(jpj)$") { set req.backend = web2; } else{ set req.backend = web1; } }
ok,varnish的简单使用就先介绍到这里。如有不足之处,多多赐教。
本文出自 “欢迎大家来做客” 博客,请务必保留此出处http://aperson.blog.51cto.com/3480661/1408150