一、Web缓存
1.web缓存
web缓存是可以自动保存常见文档副本的HTTP设备。当Web请求抵达缓存时,如果本地有"已缓存的"副本,可以从本地的存储设备而不是原始服务器设备中提取这个文档。
通过key-value键值方式缓存,key中保存了URL路径,value中保存了web内容,其均使用hash格式,算法保证能在海量数据中快速命中缓存内容
(1)缓存优点
1) 减少冗余的数据传输,节省带宽
2) 缓解网络瓶颈问题,无需耕宇宽带就能更快的加载页面
3) 降低对原始服务器的要求,服务器可以更快的响应避免过载
4) 降低了距离时延
(2)相关术语
缓存命中 |
cache hit,可以用已有的副本为某些到达缓存的请求提供服务 |
缓存未命中 |
cache miss,无缓存命中副本,被转向后端原始服务器 |
HTTP再验证 |
revalidation,新鲜度检测,缓存副本和原始服务器上的数据要不时的进行检测是否为最新 |
命中率 |
cache hit rate,缓存提供服务的请求所占的比例,命中率在0-1之间,通常用百分数描述 |
注意:
1) 命中率可以分为:文档命中率(从文档个数进行衡量)、字节命中率(从内容大小进行衡量)
2) 缓存对象由其生命周期,需要定期清理
3)当缓存空间耗尽,会采用LRU(最近最少使用算法)进行覆盖
4)不是所有的数据都能够缓存,如首部Authorization、Cookie、Vary:accept-encoding= … ...
(3)缓存处理的步骤:
接收请求 -->解析请求(提取请求的URL及各种首部) --> 查询缓存 --> 新鲜度检测 --> 创建响应报文--> 发送响应 --> 记录日志
2.HTTP首部缓存控制机制
Cache-Control: no-store禁止缓存对响应进行复制
Cache-Control: no-cache可以存储在本地缓存区域中,只是在于原始服务器进行新鲜度在验证之前缓存不能提供给客户端使用
Cache-Control:must-revalidate严格遵循过期信息,在实现没和服务器再验证不提供对象旧的副本。新鲜度检测失败返回504
Cache-Control: max-age读秒,仅适用于公共缓存
Expries日期首部到响应中,指明具体时间,不推荐使用
不添加过期信息,让缓存确定自己的过期日期
(1)新鲜度检测机制
在HTTP协议的报文首部会对web作出首部定义,通过首部的报文对比来做新鲜度检测。HTTP/1.0 Expires定义的是绝对时间,当达到指定的日期时间的时候,缓存失效;HTTP/1.1 Cache-Control:max-age=#定义的是相对时间,记秒,已解决HTTP/1.0协议在解决全球性数据缓存检测时各地区时差产生的问题
实例:
HTTP/1.0 Expires:Thu, 04 Jun 2015 23:38:18 GMT
HTTP/1.1Cache-Control:max-age=600
(2)有效性再验正:revalidate
如果原始内容未改变,则仅响应首部(不附带body部分),响应码304(Not Modified)
如果原始内容发生改变,则正常响应,响应码200;
如果原始内容消失,则响应404,此时缓存中的cacheobject也应该被删除;
说明:
相关条件式请求首部:If-Modified-Since、If-Unmodified-Since、If-Match、If-None-Match:
If-Modified-Sicce:<date> |
如果从指定日期之后文档被修改了,就执行请求的方法。可以与Last-Modified(最后被修改)服务器响应首部配合使用,只有在内容被修改后与已缓存版本有所不同时候采取获取内容 |
If-None_Match:<tags> |
标签,如果一缓存标签与服务器文档中的标签不同,If-None_Match首部就会执行请求的方法 |
HTTP缓存报文实例:
Cache-Control = "Cache-Control" ":"1#cache-directive
cache-directive = cache-request-directive
| cache-response-directive
cache-request-directive =
"no-cache"
| "no-store" (backup)
| "max-age" "="delta-seconds
| "max-stale" ["=" delta-seconds ]
| "min-fresh" "="delta-seconds
| "no-transform"
| "only-if-cached"
| cache-extension
cache-response-directive =
"public"
| "private" [ "="<"> 1#field-name <"> ]
| "no-cache" [ "="<"> 1#field-name <"> ]
| "no-store"
| "no-transform"
| "must-revalidate"
| "proxy-revalidate"
| "max-age" "="delta-seconds
| "s-maxage" "="delta-seconds
| cache-extension
3.常见的缓存服务开源解决方案
软件 |
存储模式 |
性能 |
配置复杂度 |
purge效率 |
共享存储 |
squid |
硬盘 |
较高 |
简单 |
低 |
可以并联,但是配置复杂 |
varnish |
硬盘/内存 |
高 |
比较简单 |
低 |
不能 |
说明:
Squid,很古老的反向代理软件,拥有传统代理、身份验证、流量管理等高级功能,但是配置太复杂。优势在于完整的庞大的cache技术资料。Squid在大规模负载均衡场景下很稳定
Varnish是新兴的一个软件,设计简单,基于内存缓存,重启后数据将消失。
(1)varnish对比squid的优点
1)varnish稳定性很高,两者在完成相同负荷的工作时,squid服务器发生故障的几率要高于varnish,因为squid要经常重启
2)varnish访问速度更快,其采用了"Visual PageCache"技术,所有缓存数据都直接从内存中读取,而squid是从硬盘读取,因而varnish在访问速度方面会更快
3)varnish可支持更多并发连接,因为varnish的TCP连接释放要比squid快,因而在高并发连接情况下可以支持更多TCP连接
4)varnish可以通过管理端口,使用正则表达式批量的清除部分缓存,而squid是做不到的。
5)squid属于单进程使用单核CPU,但Varnish是通过fork形式打开多进程来做处理,所以是合理的使用所有核来处理相应的请求
(2)varnish对比squid的缺点
1)varnish进程一旦Hang、Crash或者重启,缓存数据都会从内存中完全释放,此时所有请求都会发送到后端服务器,在高并发情况下,会给后端服务器造成很大的压力
2)在varnish使用中,如果单个vrl的请求通过HA/F5,每次请求不同的varnish服务器时,被请求的varnish服务器都会被穿透到后端,而同样的请求会在多台服务器上缓存,也会造成varnish的缓存资源浪费,也会造成性能下降
二、Varnish缓存概述
1.varnish程序架构
varnish是高性能开源反向代理服务器,http加速器。官方网址:www.varnish-cache.org。版本:v2,v3,v4
架构图:
主要运行两个进程:Management进程和Child(Cache)进程
(1) Management管理进程
主要实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等。Management进程会定期探测Child进程。若在指定时长未得到Child进程回应,会重启此Child进程。
配置文件是VCL编程语言格式,定义缓存工作功能的,VCL编译器需要调用C编译器编译后成varnish能加载的格式才能使用
(2) Child(Cache)进程
Child进程包含多种类型的线程,主要用于用户请求管理和缓存管理
Acceptor:接收器,接收新的连接请求并响应;
Worker:处理用户请求
Expiry:从缓存中清理过期缓存内容;
cache-worker线程
cache-main线程:此线程只有一个,用于启动caceh;
ban luker:缓存清理线程
acceptor:接收线程
epoll:线程池管理器
expire:清理过期缓存
注意:
Varnish依赖“工作区(workspace)”以降低线程在申请或修改内存时出现竞争的可能性。在varnish内部有多种不同的工作区,其中最关键的当属用于管理会话数据的session工作区。
(3) VCL和C编译器
VCL编写的缓存策略保存至.vcl文件中,其需要编译成二进制的格式后才能由varnish调用。
VCL策略在启用前,会由management进程将其转换为C代码,而后再由gcc编译器将C代码编译成二进制程序。编译完成后,management负责将其连接至varnish实例,即child进程。
(4) 调用接口
CLIInterface(命令行接口)、telent Interface(文本协议不安全)、web Interface(商业)
(5) varnish log file日志
1) 共享内存日志(shared memory log):日志保存在内存中的共享空间,大小一般为90M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。如果某线程需要记录信息,其仅需要持有一个锁,而后向共享内存中的某内存区域写入数据,再释放持有的锁
2) varnish log在内存中实现日志保存,默认不能长久保存,会循环保存
3) varnish提供多个工具如varnishlog、varnishncsa或varnishstat等来分析共享内存日志中的信息并以指定的方式进行显示。
2.VCL:配置缓存策略工具
Varnish Configuration Language (VCL):
(1)基于“域”(domain specific)的简单编程语言
(2)支持有限的算术运算和逻辑运算操作
(3)允许使用正则表达式进行字符串匹配
(4)允许用户使用set自定义变量
(5)支持if判断语句
(6)支持内置的函数和变量等。
3.varnish存储类型
(1)file:单个文件;不支持持久机制
使用特定的文件存储全部的缓存数据,并通过操作系统的mmap()系统调用将整个缓存文件映射至内存区域
(2)malloc:内存方式
使用malloc()库调用在varnish启动时向操作系统申请指定大小的内存空间以存储缓存对象;
(3)persistent(experimental):基于文件的持久存储
与file的功能相同,但可以持久存储数据(即重启varnish数据时不会被清除);仍处于测试期;
注意:
1) file存储方法在varnish停止或重启时会清除数据:varnish无法追踪某缓存对象是否存入了缓存文件
2) persistent仅适用于有巨大缓存空间的场景,目前尚无法有效处理要缓存对象总体大小超出缓存空间的情况,
3) 建议在内存空间足以存储所有的缓存对象时使用malloc的方法,反之,file存储将有着更好的性能的表现。
4) 对于100万个缓存对象的场景来说,其使用的缓存空间将超出指定大小1G左右。另外,为了保存数据结构等,varnish自身也会占去不小的内存空间。
三、安装启动varnish服务
1.安装相关程序包
[[email protected] ~]#yum install -y gcc varnish-3.0.4-1.el6.x86_64.rpm varnish-docs-3.0.4-1.el6.x86_64.rpmvarnish-libs-3.0.4-1.el6.x86_64.rpm varnish-libs-devel-3.0.4-1.el6.x86_64.rpm
说明:
1) gcc编辑器是varnish编译val语言所需的依赖组件
2) varnish提供程序的主包、varnish-libs提供程序相关的库,必须安装
3) varnish-libs-devel提供二次开发所依赖的库文件、varnish-docs提供varnish帮助文档,可以视情况安装
2.配置varnish启动时脚本配置文件/etc/sysconfig/varnish
[[email protected] ~]# vim /etc/sysconfig/varnish
NFILES=131072 |
打开的最大文件数 |
MEMLOCK=82000 |
锁定共享内存大小 |
NPROCS="unlimited" |
最大线程数,无限制 |
RELOAD_VCL=1 |
自动重新装载缓存策略,1表示脚本启动自动重新装载缓存策略配置文件 |
VARNISH_VCL_CONF=/etc/varnish/default.vcl |
默认的缓存策略配置文件 |
VARNISH_LISTEN_PORT=6081 |
默认监听端口,一般改为80端口 |
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 |
管理员management进程管理地址 |
VARNISH_ADMIN_LISTEN_PORT=6082 |
管理员management进程管理端口 |
VARNISH_SECRET_FILE=/etc/varnish/secret |
启动装载的密钥文件位置,域共享密钥 |
VARNISH_MIN_THREADS=50 |
最少线程,启动时最骚启动的空闲进程 |
VARNISH_MAX_THREADS=1000 |
最大启动进程,最多不能超过5000个并发 |
VARNISH_THREAD_TIMEOUT=120 |
线程超时时长 |
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin |
缓存文件存放文件(二进制文件) |
VARNISH_STORAGE_SIZE=1G |
缓存大小 |
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" |
缓存类型和大小 |
VARNISH_TTL=120 |
当后端主机不是指定的时候使用超时时长 |
DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \ -f ${VARNISH_VCL_CONF} \ -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \ -t ${VARNISH_TTL} \ -w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \ -u varnish -g varnish \ -S ${VARNISH_SECRET_FILE} \ -s ${VARNISH_STORAGE}" |
注意:
以下配置必须修改:VARNISH_LISTEN_PORT=80,一般改为80端口,响应http请求,做为web缓存
VARNISH_STORAGE,一般在大规模图片缓存时候可以为file,但时硬件上最好是固态硬盘
3.启动服务
[[email protected]~]# service varnish start
四、varnish命令行管理工具
1.varnishadm:登录命令行终端
命令语法:varnishadm[-t timeout] [-S secret_file] [-T address:port] [-n name] [command [...]]
[[email protected]~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
varnish> help
vcl.list |
查看配置文件 |
vcl.load |
重新编译加载vcl配置,需要指明自定义版本名称,和需要编译的文件default.vcl位置(相对) |
vcl.use |
使用编译的配置文件,指明自定义版本名称 |
vcl.show |
显示指定的配置文件详细信息,指明自定义版本名称 |
vcl.discard |
手动清理版本,否则旧版本配置信息会在varnish重启后丢弃 |
2.varnish的param(参数)查看及设置
param.show[-l] [param]
param.set[param] [value]
varnish通过线程池模型定义其最大并发连接数:
thread_pools |
线程池个数;默认为2; |
thread_pool_max |
单个线程池内允许启动的最多线程个数; |
thread_pool_min |
启动时要启动的线程数; |
thread_pool_purge_delay |
清理线程的时间长度,最小不能100毫秒 |
thread_pool_stack |
工作线程栈,默认为-1,unlimited,不做限制 |
thread_pool_timeout |
超时时长,多于thread_pool_min的线程空闲此参数指定的时长后即被purge |
thread_pool_wokerspace |
工作空间,默认使用内存65536字节 |
3.varnishtop: 内存日志区域查看工具
-Iregexp: 仅显示被模式匹配到的条目
-Xregexp:仅显示不被模式匹配到的条目
-C:忽略字符大小写;
-d:显示已有日志;
varnish>varnishtopUser-Agent
显示结果实例:
RxHeader User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/43.0.2357.81 Safari/537.36
说明:
RxHeader:称为tag,基于tag过滤,可使用-i或-x选项;
User-Agent起始的内容:称为日志信息,可使用-I或-X选项进行过滤;
4.varnishstat:varnish数据统计工具
-f field, field, …:指明显示的字段
-l:列出所有可用字段
-x:xml输出格式
-j:json输出格式
五、VCL状态引擎制定缓存策略
1.varnish状态引擎
varnish内部有几个所谓的状态(state),在这些状态上可以附加通过VCL定义的策略以完成相应的缓存处理机制,因此VCL也经常被称作“域专用”语言或状态引擎,“域专用”指的是有些数据仅出现于特定的状态中。
状态之间具有相关性,但彼此间互相隔离,每个引擎使用return(x)来退出当前状态并指示varnish进入下一个状态。
(1)状态引擎分类
vcl_recv、vcl_hash、vcl_hit、vcl_miss、vcl_fetch、vcl_deliver、vcl_pipe、vcl_pass、vcl_error
vcl_init |
在装载vcl,用其处理任何请求之前 |
vcl_recv |
请求被接入,但在其被分析、处理完成之前;是否服务此请求、如何服务、使用哪个后端主机为其服务 |
vcl_pipe |
方法不理解 |
vcl_pass |
不查询缓存 |
vcl_hash |
查找缓存,调用内置hash_data()函数,决定对什么内容hash计算,如后端存在虚拟主机,但用ip访问不能命中 |
vcl_hit |
命中 |
vcl_miss |
未命中 |
vcl_fetch |
在后端服务器取内容,其能让可缓存或不缓存。从后端主机收到响应报文之前会被调用,返回的值可以为deliver、error code [reason]、hit_for_pass、restart |
vcl_deliver |
传送 |
vcl_error |
错误处理机制 |
(2)状态引擎工作流程(v3):
varnish先对HTTP请求本身进本分析,再由vcl_recv方法完成决策(是否在缓存中查询请求额资源)
vcl_recv--> vcl_hash --> vcl_hit --> vcl_deliver
vcl_recv--> vcl_hash --> vcl_miss --> vcl_fetch --> vcl_deliver
vcl_recv--> vcl_pass --> vcl_fetch --> vcl_deliver
vcl_recv--> vcl_pipe
(3)vcl_recv引擎
vcl_recv是在Varnish完成对请求报文的解码为基本数据结构后第一个要执行的子例程,它通常有四个主要用途:
1)修改客户端数据以减少缓存对象差异性;比如删除URL中的www.等字符;
2)基于客户端数据选用缓存策略;比如仅缓存特定的URL请求、不缓存POST请求等;
3)为某web应用程序执行URL重写规则;
4)挑选合适的后端Web服务器;
可以通过return()向Varnish返回的指示操作:
pass:绕过缓存,即不从缓存中查询内容或不将内容存储至缓存中;
pipe:不对客户端进行检查或做出任何操作,而是在客户端与后端服务器之间建立专用“管道”,并直接将数据在二者之间进行传送;此时,keep-alive连接中后续传送的数据也都将通过此管道进行直接传送,并不会出现在任何日志中;
lookup:在缓存中查找用户请求的对象,如果缓存中没有其请求的对象,后续操作很可能会将其请求的对象进行缓存;
error:由Varnish自己合成一个响应报文,一般是响应一个错误类信息、重定向类信息或负载均衡器返回的后端web服务器健康状态检查类信息;
注意:
1) Varnish默认的vcl_recv专门设计用来实现安全的缓存策略,它主要完成两种功能:
a)仅处理可以识别的HTTP方法,并且只缓存GET和HEAD方法;
b)不缓存任何用户特有的数据;
2) 一般自定义vcl_recv中不要使用return()终止语句,而是再由默认vcl_recv进行处理,并由其做出相应的处理决策。
#sub vcl_recv {
# if (req.restarts == 0) {
# 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 isweird. */
# return (pipe);
# }
# if (req.request != "GET"&& req.request != "HEAD") {
# /* We only deal with GET and HEAD bydefault */
# return (pass);
# }
# if (req.http.Authorization ||req.http.Cookie) {
# /* Not cacheable by default */
# return (pass);
# }
# return (lookup);
#}
(3)vcl_fetch引擎
vcl_fetch是根据服务器端的响应作出缓存决策。在任何VCL状态引擎中返回pass操作都将由vcl_fetch进行后续处理。默认的vcl_fetch放弃了缓存任何使用了Set-Cookie首部的响应。通过return()返回给varnish的操作指示有:
1)deliver:缓存此对象,并将其发送给客户端(经由vcl_deliver);
2)hit_for_pass:不缓存此对象,但可以导致后续对此对象的请求直接送达到vcl_pass进行处理;
3)restart:重启整个VCL,并增加重启计数;超出max_restarts限定的最大重启次数后将会返回错误信息;
4)errorcode [reason]:返回指定的错误代码给客户端并丢弃此请求;
2.VCL编程语言语法
(1) //, #, /* */用于注释;会被编译器忽略;
(2) sub $name定义函数,不接受参数;VCL内部的数据传递只能隐藏在HTTP首部内部进行
(3) 不支持循环语句;
(4)有众多内置的变量,变量的可调用位置与state engine有密切相关性;
(5)支持终止语句,return(action);没有返回值;
(6) "域"专用语言,只能用在特定的域上;
(7) 支持众多操作符:=(赋值)、==(等值比较)、~(模式匹配)、!(取反)、&&(逻辑与)、||(逻辑或)
(8) 一般为条件判断语句:单分支、双分支、多分支
3.varnish内置函数
(1)regsub(str,regex,sub)
regsuball(str,regex,sub):这两个用于基于正则表达式搜索指定的字符串并将其替换为指定的字符串;但regsuball()可以将str中能够被regex匹配到的字符串统统替换为sub,regsub()只替换一次;
(2)ban(expression):
ban_url(regex):Bans所有其URL能够由regex匹配的缓存对象;
(3)purge:
从缓存中挑选出某对象以及其相关变种一并删除,这可以通过HTTP协议的PURGE方法完成;
(4)hash_data(str):
return():当某VCL域运行结束时将控制权返回给Varnish,并指示Varnish如何进行后续的动作;其可以返回的指令包括:lookup、pass、pipe、hit_for_pass、fetch、deliver和hash等;但某特定域可能仅能返回某些特定的指令
return(restart)重新运行整个VCL,即重新从vcl_recv开始进行处理;每一次重启都会增加req.restarts变量中的值,而max_restarts参数则用于限定最大重启次数。
4.常用变量
(1)在任何状态引擎中均可
.host后端主机的主机名或者IP
.port后端主机的服务名或者端口号
(2)用于处理请求阶段
client.ip客户端ip
server.hostname缓存服务器主机名
server.ip缓存服务器ip
server.port缓存服务器端口
req.request:请求方法
req.url:请求的URL
req.proto:请求的HTTP协议版本
req.backend:指定用于服务此次请求的后端主机
req.backend.healthy:后端主机的健康状态
req.http.HEADER:调用request报文中http协议的指定的HEADER首部
req.can_gzip:客户端是否能结束gzip压缩格式响应内容
req.restarts:此请求被重启的次数
req.grace:宽限期响应
(3)varnish向backend主机发起请求前可用的变量
bereq.request:后端主机请求方法
bereq.url:后端主机请求路径
bereq.proto:后端主机请求http协议
bereq.http.HEADER:后端主机HTTP首部
bereq.connect.timeout:等待后端建立连接的超时时长
(4)backend主机的响应报文到达本主机(varnish)后,将其放置在cache中之前可用的变量
beresp.do_stream:流式响应(接收一个响应一个)
beresp.do_gzip:是否压缩后存储
beresp.do_gunzip:是否先解压缩再存储
beresp.http.HEADER:后端主机响应http首部
beresp.proto:后端主机响应http协议
beresp.status:后端主机响应状态码
beresp.response:响应的原因短语
beresp.ttl:响应对象剩余的响应时长,单位为秒
beresp.backend.name指明此响应报文来源的后端主机的名称
beresp.backend.ip指明此响应报文来源的后端主机的ip地址
beresp.backend.port指明此响应报文来源的后端主机的端口号
beresp.storage强制varnish存储在指定的缓存后端
(5)缓存对象(从后端主机取出内容)存入cache之后可用变量
obj.proto:缓存对象响应时的协议
obj.status:缓存对象响应时的状态码
obj.response:缓存对象响应时的原因短语
obj.ttl:缓存对象响应时的生存时长
obj.hits:缓存对象响应时的命中次数
obj.http.HEADER:缓存对象响应时的的http首部
(6)在决定对请求的键做hash计算时可用变量
req.hash:指明将什么键当成hash缓存的键
(7)在为客户端准备响应报文时可用的变量
resp.proto响应的协议
resp.status响应的状态码
resp.response响应的响应原因短语
resp.http.HEADER响应的
变量的可用位置:
官方文档:https://www.varnish-cache.org/docs/4.0/reference/vcl.html#varnish-configuration-language
5.使用案例概述
(1)变量赋值:set name=value
实例:定义在vcl_deliver中,向响应给客户端的报文添加一个自定义首部X-Cache;
if (obj.hits>0) {
set resp.http.X-Cache ="HIT";
} else {
set resp.http.X-Cahce ="MISS";
}
(2)支持虚拟主机:
if(req.http.host == "www.magedu.com"){
}
(3)强制对某资源的请求,不检查缓存;
实例:对URL以/admin和/login结尾不做缓存:
if(req.url ~ "(?i)^/login" || req.url ~ "(?i)^/admin") {
return(pass);
}
(4)对特定类型的资源取消其私有的cookie标识,并强行设定其可以varnish缓存的时长:
vcl_backend_response
if(beresp.http.cache-control !~ "s-maxage") {
if(bereq.url ~ "(?i)\.jpg$") {
setberesp.ttl = 3600s;
unsetberesp.http.Set-Cookie;
}
if(bereq.url ~ "(?i)\.css$") {
setberesp.ttl = 600s;
unsetberesp.http.Set-Cookie;
}
}
官方配置示例:https://www.varnish-cache.org/trac/wiki/VCLExamples
六、varnish后端主机健康监测和负载均衡
1.backend server的定义
backend name {
.attribute ="value";
}
.host |
后端主机的IP; |
.port |
后端主机监听的PORT; |
.probe |
对后端做健康状态检测; |
.max_connections |
并连接最大数量; |
2.后端主机的健康状态检测方式
probe name {
.attribute= "value";
}
.url: |
判定BE健康与否要请求的url; |
.expected_response |
期望响应状态码;默认为200; |
实例一:
backendwebsrv1 {
.host = "172.16.100.68";
.port = "80";
.probe = {
.url = "/test1.html";
}
}
backendwebsrv2 {
.host = "172.16.100.69";
.port = "80";
.probe = {
.url = "/test1.html";
}
}
subvcl_recv {
if (req.url ~"(?i)\.(jpg|png|gif)$") {
set req.backend_hint = websrv1;
} else {
set req.backend_hint = websrv2;
}
}
实例二:
importdirectors;
subvcl_init {
new mycluster = directors.round_robin();
mycluster.add_backend(websrv1);
mycluster.add_backend(websrv2);
}
vcl_recv{
setreq.backend_hint = mycluster.backend();
}
负载均衡算法:fallback,random, round_robin, hash
七、实际案例
1.移除单个缓存对象
purge用于清理缓存中的某特定对象及其变种(variants),因此,在有着明确要修剪的缓存对象时可以使用此种方式。HTTP协议的PURGE方法可以实现purge功能,不过,其仅能用于vcl_hit和vcl_miss中,它会释放内存工作并移除指定缓存对象的所有Vary:-变种,并等待下一个针对此内容的客户端请求到达时刷新此内容。另外,其一般要与return(restart)一起使用。下面是个在VCL中配置的示例。
aclpurgers {
"127.0.0.1";
"192.168.0.0"/24;
}
subvcl_recv {
if(req.request == "PURGE") {
if(!client.ip ~ purgers) {
error405 "Method not allowed";
}
return(lookup);
}
}
subvcl_hit {
if(req.request == "PURGE") {
purge;
error200 "Purged";
}
}
subvcl_miss {
if(req.request == "PURGE") {
purge;
error404 "Not in cache";
}
}
subvcl_pass {
if(req.request == "PURGE") {
error502 "PURGE on a passed object";
}
}
客户端在发起HTTP请求时,只需要为所请求的URL使用PURGE方法即可,其命令使用方式如下:
#curl -I -X PURGE http://varniship/path/to/someurl
2.生产环境案例实例
aclpurge {
"localhost";
"127.0.0.1";
"10.1.0.0"/16;
"192.168.0.0"/16;
}
subvcl_hash {
hash_data(req.url);
return (hash);
}
subvcl_recv {
set req.backend = shopweb;
# set req.grace = 4h;
if (req.request == "PURGE") {
if (!client.ip ~ purge) {
error 405 "Not allowed.";
}
return(lookup);
}
if (req.request == "REPURGE") {
if (!client.ip ~ purge) {
error 405 "Not allowed.";
}
ban("req.http.host == " +req.http.host + " && req.url ~ " + req.url);
error 200 "Ban OK";
}
if (req.restarts == 0) {
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 bydefault */
return (pass);
}
if (req.http.Authorization) {
/* Not cacheable by default */
return (pass);
}
if ( req.url == "/Heartbeat.html" ){
return (pipe);
}
if ( req.url == "/" ) {
return (pipe);
}
if ( req.url == "/index.jsp" ) {
return (pipe);
}
if (req.http.Cookie ~ "dper=") {
return (pass);
}
if (req.http.Cookie ~ "sqltrace="){
return (pass);
}
if (req.http.Cookie ~"errortrace=") {
return (pass);
}
# if ( req.request == "GET"&& req.url ~ "req.url ~ "^/shop/[0-9]+$" ) {
if ( req.url ~ "^/shop/[0-9]+$" ||req.url ~ "^/shop/[0-9]?.*" ) {
return (lookup);
}
if ( req.url ~"^/shop/(\d{1,})/editmember" || req.url ~"^/shop/(\d{1,})/map" || req.url ~"^/shop/(\d+)/dish-([^/]+)" ) {
return (lookup);
}
return (pass);
# return (lookup);
}
subvcl_pipe {
return (pipe);
}
subvcl_pass {
return (pass);
}
subvcl_hit {
if (req.request == "PURGE") {
purge;
error 200 "Purged.";
}
return (deliver);
}
subvcl_miss {
if (req.request == "PURGE") {
error 404 "Not in cache.";
}
# if (object needs ESI processing) {
# unset bereq.http.accept-encoding;
# }
return (fetch);
}
subvcl_fetch {
set beresp.ttl = 3600s;
set beresp.http.expires = beresp.ttl;
#set beresp.grace = 4h;
# if (object needs ESI processing) {
# set beresp.do_esi = true;
# set beresp.do_gzip = true;
# }
if ( req.url ~ "^/shop/[0-9]+$" ||req.url ~ "^/shop/[0-9]?.*" ) {
set beresp.ttl = 4h;
}
if ( req.url ~"^/shop/(\d{1,})/editmember" || req.url ~"^/shop/(\d{1,})/map" || req.url ~"^/shop/(\d+)/dish-([^/]+)" ) {
set beresp.ttl = 24h;
}
if (beresp.status != 200){
return (hit_for_pass);
}
return (deliver);
}
subvcl_deliver {
if (obj.hits > 0){
set resp.http.X-Cache = "HIT";
}
else {
set resp.http.X-Cache = "MISS";
}
set resp.http.X-Powered-By = "Cache on" + server.ip;
set resp.http.X-Age = resp.http.Age;
return (deliver);
}
subvcl_error {
set obj.http.Content-Type = "text/html;charset=utf-8";
set obj.http.Retry-After = "5";
synthetic {""} + obj.status +" " + obj.response + {""};
return (deliver);
}
subvcl_init {
return (ok);
}
subvcl_fini {
return (ok);
}