首页 > 代码库 > Varnish
Varnish
varnish
凡是位于速度相差较大的两种硬件之间,用于协调两者数据传输速度差异的结构,均可称之为Cache。
varnish是一款开源的高性能http加速器。
varnish架构:
varnish也和传统的服务器软件一样,也有master进程和child进程,master进程负责加载配置文件,调用合适的存储类型,创建/读入相应大小的缓存文件,进行一些初始化后,fork并监控child进程,child进程分配若干线程进行工作,主要包括一些管理线程和很多worker线程;
接着,开始真正的工作,varnish的某个负责接收新HTTP连接线程开始等待用户,如果有新的HTTP连接过来,它总负责接收,然后唤醒某个等待中的线程,并把具体的处理过程交给它。worker线程读入HTTP请求的URI,查找已有的object,如果命中则直接返回并回复用户。如果没有命中,则需要将请求的内容从后端服务器中取过来,存到缓存中,然后再回复。
分配缓存的过程:根据多读到的object的大小,创建响应大小的缓存文件。为了读写方便,程序会把每个object的大小变为最接近其大小的内存页面倍数。然后从现有的空闲存储结构体中查找,找到最合适大小的空间存储块,分配给它。如果空闲块没有用完,就把多余的内存另外组成一个空闲存储块,挂到管理结构体上,如果缓存已满,就根据LRU机制,把最旧的object释放掉。
释放缓存的过程是这样的:有一个超时线程,检测缓存中所有 object 的生存期,如果超出设定的 TTL(Time To Live)没有被访问,就删除之,并且释放相应的结构体及存储内存。注意释放时会检查该存储内存块前面或后面的空闲内存块,如果前面或后面的空闲内存和该释放内存是连续的,就将它们合并成更大一块内存。
整个文件缓存的管理,没有考虑文件与内存的关系,实际上是将所有的 object 都考虑是在内存中,如果系统内存不足,系统会自动将其换到 swap 空间,而不需要varnish 程序去控制。
热区数据的局部性:当一个数据频繁的被访问,那么他周围的数据也有可能被访问;
时效性:缓存时间过期;
缓存空间耗尽(LRU算法淘汰);
数据是否能缓存:
私有数据:private #不应该缓存;
公共数据:public #可缓存;
浏览器自己的缓存是私有缓存(private cache);
缓存方式:
1、 过期时间缓存:
Expires:过期时长(绝对时间控制,HTTP/1.0):如果客户端与服务器双方时区不一致,则有可能导致缓存一直无法生效。
2、 Cache-Control:s-maxage=N / Cache-Control:maxage=N(相对时间控制,在HTTP/1.1才支持)
用户请求某资源后,服务器返回时会在response headers头部增加一个首部,其值为用户请求的资源可在本地缓存的缓存时长,如果用户再次请求同样的资源,用户Agent会先检查本地缓存资源是否过期,如果否,则直接从本地加载,如果过期,则重新向服务器发送请求;
3、 条件式请求:
HTTP/1.1才引入的功能:
Last Modified/If-Modified-Since(基于时间戳进行缓存控制)
用户请求某资源,服务器返回是加上一个Last Modified首部,其值为该资源的最后一次修改时间;用户Agent将其资源缓存在本地,如果用户再次访问该资源,Agent会向服务器发送If-Modified-Since其值为此资源的最后一次修改时间,服务端会对比本地资源的最后一次修改时间与客户端发来的时间是否一致;如果一致,则标识该资源没有改变,服务端会以304的响应码响应给客户端,原因短语为 Not Modified,而用户Agent则直接从缓存中回应给客户端,响应码为200。可以保证内容的新鲜度,但效率低下。
Etag/If-None-Match(基于内容做缓存控制)
将资源进行校验,将校验值保存为Etag首部的值,通过对比Etag首部的值来判断缓存是否失效;
基于过期时间与内容进行缓存控制:
在用户首次请求是,服务器返回给客户端一个资源过期时间以及资源的效验以后的一个结果值,保存为Etag首部的值,在缓存有效时长内,用户可直接从本地缓存中加载数据,如果本地缓存过期,则向服务器发起请求,附加一个If-None-Match首部,其值为本地该请求资源的效验码,body为空,服务器判断本地资源是否发生变化,如果资源未改变,则返回304响应码,原因短语为 Not Modified,并且再此为该资源设置缓存过期时长;如果资源发生变化,则以200响应码回应客户端,body部分为请求资源;
请求报文用于通知缓存服务如何使用缓存响应请求:
cache-request-directive=
|"no-cache":不要用缓存响应;
|"no-store" :不能缓存;
|"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 <">],可缓存,但响应给客户端之前需要去原始服务器revalidation(校验);
|"no-store" ,不允许存储响应内容于缓存中;
|"no-transform"
|"must-revalidate"
|"proxy-revalidate"
|"max-age" "=" delta-seconds :对公共和私有都有效;
|"s-maxage" "=" delta-seconds:通常只对公共缓存有效;
|cache-extension
varnish的架构图:
程序架构:
Manager进程:
master进程负责加载配置文件,调用合适的存储类型,创建/读入相应大小的缓存文件,进行一些初始化后,fork并监控child进程,
Cacher进程,包含多种类型的线程:
accept,worker, expiry, ...
sharedmemory log:
统计数据:计数器;
日志区域:日志记录;
varnishlog,varnishncsa, varnishstat...
日志直接保存在内存中,默认大小90M,分两段,一部分统计数据,一部分日志
配置接口:VCL
VarnishConfiguration Language,
vclcomplier --> c complier --> shared object
VCC Precess
varnish的配置文件编辑完成后,要通过VCC-Precess调用C-Compiler将配置文件编译成二进制格式,编译后的配置文件称为共享对象(share object),此共享对象可以被各cache-precess所共享;
varnish的存储机制:
1、 malloc,[size]
内存存储,[,size]用于定义空间大小,重启后所有缓存项失效;
2、 file,[path[,size[,granularity]]]
文件存储,黑盒,重启后所有缓存项失效;
注:不论是内存存储或是文件存储,重启后都会失效;
3、 persistent,path,size
文件存储,黑盒,重启后所有缓存项有效,实验;
配置文件:
/etc/varnish/varnish.params:定义varnish的工作特性;
/etc/varnish/default.vcl:定义缓存代理机制;
varnish程序的选项:
程序选项:/etc/varnish/varnish.params文件
-aaddress[:port][,address[:port][...],默认为6081端口;
-T address[:port],默认为6082端口;
-s [name=]type[,options],定义缓存存储机制;
-u user
-g group
-f config:VCL配置文件;
-F:运行于前台;
...
行时参数:/etc/varnish/varnish.params文件,DEAMON_OPTS
DAEMON_OPTS=”-pthread_pool_min=10 -pthread_pool_max=500 -p thread_pool_timeout=300 -p thread_pools=4”
-pparam=value:设定运行参数及其值;可重复使用多次;
-rparam[,param...]: 设定指定的参数为只读状态;
重载vcl配置文件:
~ ]#varnish_reload_vcl
varnish的管控接口的使用
varnishadm -S 预共享密钥的路径 -T 指定连接的IP及端口
交互式命令:
help:获取帮助;
ping:检测varnish是否在线;
backend.list:列出后端主机;
storage.list:列出缓存路径;
vcl.list:显示使用的vcl文件;
vcl.load <configname> <filename>:装载,加载并编译;
vcl.use <configname>:激活;
vcl.discard<configname>:删除某配置文件;
vcl.show[-v] <configname>:查看指定的配置文件的详细信息;
varnish的状态引擎:
VCL:
“域”专有类型的配置语言;
stateengine:状态引擎;
VCL有多个状态引擎,状态之间存在相关性,但状态引擎彼此间互相隔离;每个状态引擎可使用return(x)指明关联至哪个下一级引擎;每个状态引擎对应于vcl文件中的一个配置段,即为subroutine
两个特殊的引擎:
vcl_init:在处理任何请求之前要执行的vcl代码:主要用于初始化VMODs;
vcl_fini:所有的请求都已经结束,在vcl配置被丢弃时调用;主要用于清理VMODs;
三类主要语法:
subsubroutine {
...
}
ifCONDITION {
...
}else {
...
}
return(),hash_data()
varnish中的变量:
变量类型:
内建变量:
req.*:request,表示由客户端发来的请求报文相关;
req.http.*
req.http.User-Agent, req.http.Referer,...
bereq.*:由varnish发往BE主机的httpd请求相关;
bereq.http.*
beresp.*:由BE主机响应给varnish的响应报文相关;
beresp.http.*
resp.*:由varnish响应给client相关;
obj.*:存储在缓存空间中的缓存对象的属性;只读;
常用变量:
bereq.*,req.*:
bereq.http.HEADERS
bereq.request:请求方法;
bereq.url:请求的url;
bereq.proto:请求的协议版本;
bereq.backend:指明要调用的后端主机;
req.http.Cookie:客户端的请求报文中Cookie首部的值;
req.http.User-Agent~ "chrome"
beresp.*,resp.*:
beresp.http.HEADERS
beresp.status:响应的状态码;
reresp.proto:协议版本;
beresp.backend.name:BE主机的主机名;
beresp.ttl:BE主机响应的内容的余下的可缓存时长;
obj.*
obj.hits:此对象从缓存中命中的次数;
obj.ttl:对象的ttl值
server.*
server.ip
server.hostname
client.*
client.ip
用户自定义:
set:自定义首部;
unset:删除某个首部;
varnish中变量的使用范围:
示例1:强制对某类资源的请求不检查缓存:
sub vcl_recv {
if(req.url ~ "(?i)^/(login|admin)") {
return(pass);
}
}
示例2:对于特定类型的资源,例如公开的图片等,取消其私有标识,并强行设定其可以由varnish缓存的时长;
subvcl_backend_response {
if (beresp.http.cache-control !~ "s-maxage") {
if(bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") {
unsetberesp.http.Set-Cookie;
setberesp.ttl = 3600s;
}
}
}
示例3:判断请求报文中是否有X-Forwarded-For首部,如果有,则在原有值的后面加上Client ip;否则设置X-Forwarded-For的值为client ip。
if(req.restarts == 0) {
if(req.http.X-Fowarded-For) {
setreq.http.X-Forwarded-For = req.http.X-Forwarded-For + "," +client.ip;
}else {
setreq.http.X-Forwarded-For = client.ip;
}
}
缓存对象的修剪:purge, ban
(1)能执行purge操作
subvcl_purge {
return(synth(200,"Purged"));
}
(2)何时执行purge操作
subvcl_recv {
if(req.method == "PURGE") {
return(purge);
}
...
}
添加此类请求的访问控制法则:
aclpurgers {
"127.0.0.0"/8;
"10.1.0.0"/16;
}
subvcl_recv {
if(req.method == "PURGE") {
if(!client.ip ~ purgers) {
return(synth(405,"Purgingnot allowed for " + client.ip));
}
return(purge);
}
...
}
如何设定使用多个后端主机:
backenddefault {
.host ="172.16.100.6";
.port= "80";
}
backendappsrv {
.host= "172.16.100.7";
.port= "80";
}
subvcl_recv {
if(req.url ~ "(?i)\.php$") {
setreq.backend_hint = appsrv;
}else {
setreq.backend_hint = default;
}
...
}
Director:
varnishmodule;
使用前需要导入:
importdirectors;
示例:
importdirectors; # load the directors
backendserver1 {
.host=
.port=
}
backendserver2 {
.host=
.port=
}
subvcl_init {
newGROUP_NAME = directors.round_robin();
GROUP_NAME.add_backend(server1);
GROUP_NAME.add_backend(server2);
}
subvcl_recv {
#send all traffic to the bar director:
setreq.backend_hint = GROUP_NAME.backend();
}
BEHealth Check:
backendBE_NAME {
.host=
.port=
.probe= {
.url=
.timeout=
.interval=
.window=
.threshold=
}
}
.probe:定义健康状态检测方法;
.url:检测时请求的URL,默认为”/";
.request:发出的具体请求;
.request=
"GET/.healthtest.html HTTP/1.1"
"Host:www.magedu.com"
"Connection:close"
.window:基于最近的多少次检查来判断其健康状态;
.threshhold:最近.window中定义的这么次检查中至有.threshhold定义的次数是成功的;
.interval:检测频度;
.timeout:超时时长;
.expected_response:期望的响应码,默认为200;
健康状态检测的配置方式:
(1)probe PB_NAME = { }
backend NAME = {
.probe= PB_NAME;
...
}
(2)backend NAME {
.probe= {
...
}
}
示例:
probecheck {
.url= "/.healthcheck.html";
.window= 5;
.threshold= 4;
.interval= 2s;
.timeout= 1s;
}
backenddefault {
.host= "10.1.0.68";
.port= "80";
.probe= check;
}
backendappsrv {
.host= "10.1.0.69";
.port= "80";
.probe= check;
}
varnish的运行时参数:
线程模型:
cache-worker
cache-main
banlurker
acceptor:
epoll/kqueue:
...
线程相关的参数:
在线程池内部,其每一个请求由一个线程来处理; 其worker线程的最大数决定了varnish的并发响应能力;
thread_pools:Number ofworker thread pools. 最好小于或等于CPU核心数量;
thread_pool_max:The maximumnumber of worker threads in each pool. 每线程池的最大线程数;
thread_pool_min:The minimumnumber of worker threads in each pool. 额外意义为“最大空闲线程数”;
最大并发连接数=thread_pools * thread_pool_max
thread_pool_timeout:Thread idlethreshold. Threads in excess ofthread_pool_min, which have been idle for at least this long, will bedestroyed.
thread_pool_add_delay:Wait at leastthis long after creating a thread.
thread_pool_destroy_delay:Wait thislong after destroying a thread.
设置方式:
vcl.param
param.set
永久有效的方法:
varnish.params
DEAMON_OPTS="-pPARAM1=VALUE -p PARAM2=VALUE"
varnish日志区域:
shared memory log
计数器
日志信息
1、varnishstat -Varnish Cache statistics
-1
-1-f FILED_NAME
-l:可用于-f选项指定的字段名称列表;
MAIN.cache_hit
MAIN.cache_miss
#varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss
#varnishstat -l -f MAIN -f MEMPOOL
2、varnishtop -Varnish log entry ranking
-1:显示一遍就退出;
-i taglist,可以同时使用多个-i选项,也可以一个选项跟上多个标签;
-I<[taglist:]regex>:可以基于正则表达式匹配内容,匹配的是指定首部的值,而非首部名称;
-xtaglist:排除列表
-X <[taglist:]regex>:基于正则表达式进行匹配内容排除,匹配的也是首部的值,而非首部名称;
3、varnishlog -Display Varnish logs
4、 varnishncsa - Display Varnish logs in Apache / NCSA combined logformat
显示combined格式的日志;(类似与Apache的日志记录格式);
也可以启动 varnishncsa服务来让其自动记录日志,默认保存位置为/var/log/varnish/varnishncsa.log
内建函数:
hash_data():指明哈希计算的数据;减少差异,以提升命中率;
regsub(str,regex,sub):把str中被regex第一次匹配到字符串替换为sub;主要用于URLRewrite
regsuball(str,regex,sub):把str中被regex每一次匹配到字符串均替换为sub;
return():
ban(expression)
ban_url(regex):Bans所有的其URL可以被此处的regex匹配到的缓存对象;
synth(status,"STRING"):purge操作;
Varnish