一、varnish定义
Varnish与一般服务器软件类似,分为master(management)进程和child(worker,主要做cache的工作)进程。master进程读入命令,进行一些初始化,然后fork并监控child进程。child进程分配若干线程进行工作,主要包括一些管理线程和很多woker线程。
在网站并发量过大时;无法通过向上或向外扩展来解决时;必须引入缓存来减小服务器的压力;而互联网在传输过程中三个关键点:客户端入口、传输中间路由、服务器端出口;相对于响应报文缓存可以解决第一公里问题;用于缓存到用户本地网络中;避免瞬间拥塞;减低因距离传输带来的延时。
判断缓存的有效性:根据文档命中率和字节命中率综合起来评估。
二、varnish引擎和存储
VCL用于让管理员定义缓存策略,而定义好的策略将由varnish的management进程分析、转换成C代码、编译成二进制程序并连接至child进程。varnish内部有几个所谓的状态(state),在这些状态上可以附加通过VCL定义的策略以完成相应的缓存处理机制,因此VCL也经常被称作“域专用”语言或状态引擎,“域专用”指的是有些数据仅出现于特定的状态中。
在VCL状态引擎中,状态之间具有相关性,但彼此间互相隔离,每个引擎使用return(x)来退出当前状态并指示varnish进入下一个状态。
状态引擎:子例程;函数;可以使用return()函数返回状态给varnish进程;
vcl_recv --> vcl_hash, vcl_pipe, vcl_pass
vcl_hash:vcl_hit, vcl_miss
vcl_pass:vcl_fetch
vcl_hit:vcl_deliver, vcl_pass
vcl_miss:vcl_fetch, vcl_pass
vcl_fetch:vcl_deliver
具体工作模式:
varnish存储
varnish支持多种不同类型的后端存储,这可以在varnishd启动时使用-s选项指定。后端存储的类型包括:
file:使用特定的文件存储全部的缓存数据,并通过操作系统的mmap()系统调用将整个缓存文件映射至内存区域(如果条件允许);
malloc:使用malloc()库调用在varnish启动时向操作系统申请指定大小的内存空间以存储缓存对象;
persistent(experimental):与file的功能相同,但可以持久存储数据(即重启varnish数据时不会被清除);仍处于测试期;
各参数对应的位置:
Variable | recv | fetch | pass | miss | hit | error | deliver | pipe | hash |
req.* | R/W | R/W | R/W | R/W | R/W | R/W | R/W | R/W | R/W |
bereq.* | R/W | R/W | R/W | R/W | |||||
obj.hits | R | R | |||||||
obj.ttl | R/W | R/W | |||||||
obj.grace | R/W | ||||||||
obj.* | R | R/W | |||||||
beresp.* | R/W | ||||||||
resp.* | R/W | R/W |
三、安装varnish及配置文件详解
2.X、3.X、4.X最新版本都是可以用的;安装直接使用yum即可。
[root@node1 ~]# ls anaconda-ks.cfg php-5.4.26.tar.bz2 xcache-3.1.0 install.log varnish-3.0.4-1.el6.x86_64.rpm xcache-3.1.0.tar.bz2 install.log.syslog varnish-libs-3.0.4-1.el6.x86_64.rpm php-5.4.26 varnish-libs-devel-3.0.4-1.el6.x86_64.rpm [root@node1 ~]# rpm -ivh varnish-*.rpm Preparing... ########################################### [100%] 1:varnish-libs ########################################### [ 33%] 2:varnish ########################################### [ 67%] 3:varnish-libs-devel ########################################### [100%] [root@node1 ~]# #这里安装的3.x版本的;且2.x与3.x的命令格式有不同;具体可以搜索下 [root@node1 varnish]# rpm -ql varnish /etc/logrotate.d/varnish /etc/rc.d/init.d/varnish /etc/rc.d/init.d/varnishlog /etc/rc.d/init.d/varnishncsa /etc/sysconfig/varnish /etc/varnish /etc/varnish/default.vcl
安装完成后直接查看配置文件:
[root@node1 ~]# vim /etc/sysconfig/varnish # Configuration file for varnish # # /etc/init.d/varnish expects the variable $DAEMON_OPTS to be set from this # shell script fragment. # # Maximum number of open files (for ulimit -n) NFILES=131072 允许打开的最大连接数 # Locked shared memory (for ulimit -l) # Default log size is 82MB + header MEMLOCK=82000 指定内存空间大小保存日志 # Maximum size of corefile (for ulimit -c). Default in Fedora is 0 # DAEMON_COREFILE_LIMIT="unlimited" 进程核心转储所使用的内存空间 # Set this to 1 to make init script reload try to switch vcl without restart. # To make this work, you need to set the following variables # explicit: VARNISH_VCL_CONF, VARNISH_ADMIN_LISTEN_ADDRESS, # VARNISH_ADMIN_LISTEN_PORT, VARNISH_SECRET_FILE, or in short, # use Alternative 3, Advanced configuration, below RELOAD_VCL=1 重新启动时是否重读VCL # This file contains 4 alternatives, please use only one. # content server on localhost:8080. Use a fixed-size cache file. # Listen on port 6081, administration on localhost:6082, and forward to # one content server selected by the vcl file, based on the request. Use a # fixed-size cache file. # #DAEMON_OPTS="-a :6081 \ # -T localhost:6082 \ # -f /etc/varnish/default.vcl \ # -u varnish -g varnish \ # -S /etc/varnish/secret \ # -s file,/var/lib/varnish/varnish_storage.bin,1G" ## Alternative 3, Advanced configuration # # See varnishd(1) for more information. # # # Main configuration file. You probably want to change it :) VARNISH_VCL_CONF=/etc/varnish/default.vcl 默认读取的VCL配置文件 # # # Default address and port to bind to # # Blank address means all IPv4 and IPv6 interfaces, otherwise specify # # a host name, an IPv4 dotted quad, or an IPv6 address in brackets. # VARNISH_LISTEN_ADDRESS= #VARNISH_LISTEN_PORT=6081 VARNISH_LISTEN_PORT=80 监听的端口 # # # Telnet admin interface listen address and port VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 管理接口监听的地址 VARNISH_ADMIN_LISTEN_PORT=6082 管理接口监听的端口 # # # Shared secret file for admin interface VARNISH_SECRET_FILE=/etc/varnish/secret 使用的密钥文件 # # # The minimum number of worker threads to start VARNISH_MIN_THREADS=1 最少线程数 # # # The Maximum number of worker threads to start VARNISH_MAX_THREADS=1000 最大线程数 # # # Idle timeout for worker threads VARNISH_THREAD_TIMEOUT=120 线程超时时长 # # # Cache file location VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin #基于文件存储时文件的路径 # # Cache file size: in bytes, optionally using k / M / G / T suffix, # # or in percentage of available disk space using the % suffix. VARNISH_STORAGE_SIZE=1G 存储空间大小 # # # Backend storage specification #VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" 存储类型 VARNISH_STORAGE="malloc,100M" # # # Default TTL used when the backend does not specify one VARNISH_TTL=120 超时时间 # # # DAEMON_OPTS is used by the init script. If you add or remove options, make # # sure you update this section, too. 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}" # ## Alternative 4, Do It Yourself. See varnishd(1) for more information. # # DAEMON_OPTS="" #只更改了存储类型和监听的端口
启动测试:
[root@node1 ~]# service varnish start Starting varnish HTTP accelerator: [ OK ] [root@node1 ~]# netstat -tunlp | grep 80 tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 13101/varnishd tcp 0 0 :::80 :::* LISTEN 13101/varnishd [root@node1 ~]#
varnish命令行工具
[root@node1 ~]# varnish varnishadm varnishlog varnishreplay varnishtest varnishd varnishncsa varnishsizes varnishtop varnishhist varnish_reload_vcl varnishstat #可以对每个命令进行命令帮助的获取 [root@node1 ~]# varnishadm -h varnishadm: invalid option -- 'h' usage: varnishadm [-t timeout] [-S secretfile] -T [address]:port command [...] [root@node1 ~]# varnishstat -h varnishstat: invalid option -- 'h' usage: varnishstat [-1lV] [-f field_list] [-n varnish_name] [-w delay] -1 # Print the statistics once and exit -f field_list # Comma separated list of fields to display. # If it starts with '^' it is used as an exclusion list -l # Lists the available fields to use with the -f option -n varnish_name # The varnishd instance to get logs from -V # Display the version number and exit -w delay # Wait delay seconds between updates. The default is 1. -x # Print statistics once as XML and exit. [root@node1 ~]#
登陆管理接口测试:
[root@node1 ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 200 ----------------------------- Varnish Cache CLI 1.0 ----------------------------- Linux,2.6.32-431.el6.x86_64,x86_64,-smalloc,-smalloc,-hcritbit varnish-3.0.4 revision 9f83e8f Type 'help' for command list. Type 'quit' to close CLI session. varnish> help 200 help [command] ping [timestamp] #测试服务器是否在线 auth response #认证响应 quit #退出 banner status #状态 start #启动 stop #停止 vcl.load#装载某指定文件编译成指定名称 vcl.inline vcl.use #指定使用编译的文件 vcl.discard #删除指定的文件 vcl.list #VCL列表 vcl.show #显示VCL信息 param.show [-l] [] #列出参数的信息 param.set #设置参数的值 panic.show panic.clear storage.list #后端存储信息 backend.list #后端服务器的列表及健康状态 backend.set_health matcher state ban.url #清理缓存的;正则表达式 ban [&& ]... ban.list #在url中定义的ban列表
四、参数详解
VCL的内置函数
regsub(str,regex,sub)/regsuball(str,regex,sub):这两个用于基于正则表达式搜索指定的字符串并将其替换为指定的字符串;但regsuball()可以将str中能够被regex匹配到的字符串统统替换为sub,regsub()只替换一次;
ban(expression)/ban_url(regex):Bans所有其URL能够由regex匹配的缓存对象;
purge:从缓存中挑选出某对象以及其相关变种一并删除,这可以通过HTTP协议的PURGE方法完成;
return():当某VCL域运行结束时将控制权返回给Varnish,并指示Varnish如何进行后续的动作;其可以返回的指令包括:lookup、pass、pipe、hit_for_pass、fetch、deliver和hash等;但某特定域可能仅能返回某些特定的指令,而非前面列出的全部指令;
return(restart):重新运行整个VCL,即重新从vcl_recv开始进行处理;每一次重启都会增加req.restarts变量中的值,而max_restarts参数则用于限定最大重启次数。
vcl_recv
vcl_recv是在Varnish完成对请求报文的解码为基本数据结构后第一个要执行的子例程,它通常有四个主要用途:
修改客户端数据以减少缓存对象差异性;比如删除URL中的www.等字符;
基于客户端数据选用缓存策略;比如仅缓存特定的URL请求、不缓存POST请求等;
为某web应用程序执行URL重写规则;
挑选合适的后端Web服务器;
可以使用下面的终止语句,即通过return()向Varnish返回的指示操作:
pass:绕过缓存,即不从缓存中查询内容或不将内容存储至缓存中;
pipe:不对客户端进行检查或做出任何操作,而是在客户端与后端服务器之间建立专用“管道”,并直接将数据在二者之间进行传送;此时,keep-alive连接中后续传送的数据也都将通过此管道进行直接传送,并不会出现在任何日志中;
lookup:在缓存中查找用户请求的对象,如果缓存中没有其请求的对象,后续操作很可能会将其请求的对象进行缓存;
error:由Varnish自己合成一个响应报文,一般是响应一个错误类信息、重定向类信息或负载均衡器返回的后端web服务器健康状态检查类信息;
Varnish默认的vcl_recv专门设计用来实现安全的缓存策略,它主要完成两种功能:
仅处理可以识别的HTTP方法,并且只缓存GET和HEAD方法;
不缓存任何用户特有的数据;
vcl_fetch
vcl_fetch则是根据服务器端的响应作出缓存决策。在任何VCL状态引擎中返回的pass操作都将由vcl_fetch进行后续处理。vcl_fetch中有许多可用的内置变量,比如最常用的用于定义某对象缓存时长的beresp.ttl变量。通过return()返回给varnish的操作指示有:
deliver:缓存此对象,并将其发送给客户端(经由vcl_deliver);
hit_for_pass:不缓存此对象,但可以导致后续对此对象的请求直接送达到vcl_pass进行处理;
restart:重启整个VCL,并增加重启计数;超出max_restarts限定的最大重启次数后将会返回错误信息;
error code [reason]:返回指定的错误代码给客户端并丢弃此请求;
其他参数可以参照:https://www.varnish-cache.org/docs/3.0/reference/index.html
五、具体实例
[root@node1 ~]# cd /etc/varnish/ [root@node1 varnish]# ls default.vcl default.vcl.bak secret [root@node1 varnish]# vim test.vcl #新建一个vcl文件;也可以使用默认的进行更改即可。 backend node2 { #定义后端主机 .host = "192.168.0.112"; .port = "80"; .probe = { .url = "/index.html"; .window = 5; .threshold = 2; .interval = 2s; } } backend node3 { .host = "192.168.0.113"; .port = "80"; .probe = { .url = "/index.html"; .window = 5; .threshold = 2; .interval = 2s; } } director webserver random { #定义组;类似nginx中的upstream { .backend = node2; .weight = 2; #权重 } { .backend = node3; .weight = 2; } } acl purgers { #acl访问控制列表 "127.0.0.1"; "192.168.0.0"/16; } sub vcl_recv { # set req.backend = webserver; #所有请求都代理到该组 if (req.url ~ "\.(html|htm|css|js)$") { #如果url为指定条件;则转到node2 set req.backend = node2; } else { set req.backend = node3; #否则到node3 } if (req.url ~ "nocache.html") { #如果请求url为指定条件;则不做缓存;直接pass return(pass); } if (req.request == "PURGE") { #请求方法为PURGE if(!client.ip ~ purgers) { #不符合ACL中的IP error 405 "Method not allowd."; #返回自定义错误代码及信息 } } # if (req.restarts == 0) { #重启次数为0; # if (req.http.x-forwarded-for) { #请求首部中含有x-forwarded-for # set req.http.X-Forwarded-For = #则设定 # req.http.X-Forwarded-For + ", " + client.ip; #原首部+客户端IP # } else { #如果原首部不存在 # set req.http.X-Forwarded-For = client.ip; #直接设置首部为客户端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 by default */ # return (pass); # } # if (req.http.Authorization || req.http.Cookie) { #请求如果是客户端认证信息或cookie;都不查缓存 /* Not cacheable by default */ # return (pass); # } return (lookup); } sub vcl_hit { #命中后 if(req.request == "PURGE") { purge; error 200 "Purged Success."; #返回自定义错误代码及信息 } } sub vcl_miss { #未命中 if(req.request == "PURGE") { purge; error 404 "Not in cache."; #返回自定义错误代码及信息 } } sub vcl_pass { #如果直接是pass中 if(req.request == "PURGE") { error 502 "Purged on a passed object."; #返回自定义错误代码及信息 } } sub vcl_fetch { #在获取的引擎中 if(req.url ~"\.(jpg|png|gif|jpeg)$") { #如果url为图片 set beresp.ttl = 7200s; #缓存指定时间 } if(req.url ~"\.(html|htm|css|js)$") { #如果url为静态页面 set beresp.ttl = 1200s; #缓存指定时间 } } sub vcl_deliver { #在deliver引擎中 if(obj.hits > 0) { #如命中 set resp.http.X-Cache = "HIT from " + server.ip; #则在响应报文添加对应信息 } else { set resp.http.X-Cache = "MISS"; #否则添加对应信息 } }
定义完成后进行编译并使用:
[root@node1 ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 200 ----------------------------- Varnish Cache CLI 1.0 ----------------------------- Linux,2.6.32-431.el6.x86_64,x86_64,-smalloc,-smalloc,-hcritbit varnish-3.0.4 revision 9f83e8f Type 'help' for command list. Type 'quit' to close CLI session. varnish> vcl.load test1 test.vcl 200 VCL compiled. varnish> vcl.use test1 200 varnish> backend.list 200 Backend name Refs Admin Probe default(127.0.0.1,,80) 2 probe Healthy (no probe) node2(192.168.0.112,,80) 1 probe Healthy 5/5 node3(192.168.0.113,,80) 1 probe Healthy 5/5 #可以看出状态和后端的列表
上述实例中;有很多功能是做了注释的;因为不能同时使用;主要是做了对不同资源进行的代理:
访问图片代理到node3上;静态页面代理到node2上;此处还可以做分组代理;可以根据需要做处理:
[root@node2 ~]# curl -I http://192.168.0.111 HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Sat, 26 Apr 2014 12:53:54 GMT ETag: "100102-2b-4f7f19324fe41" Content-Type: text/html; charset=UTF-8 Content-Length: 43 Accept-Ranges: bytes Date: Mon, 24 Mar 2014 00:21:09 GMT X-Varnish: 1655448565 1655448560 Age: 19 Via: 1.1 varnish Connection: keep-alive X-Cache: HIT from 192.168.0.111 #查看缓存已生效
六、param.show参数注意和varnishstat的参数含义
param
connect_timeout | 连接超时时间 |
fetch_chunksize | 获取文件时的chunksize的大小 |
lru_interval | 最近最少间隔时间清理过去缓存 |
nuke_limit | 一批清理缓存个数 |
shm_workspace | 共享内存大小 |
thread_pool_add_delay | 创建线程的时间间隔;默认2ms |
thread_pool_add_threshold | work线程创建时溢出的阈值 |
thread_pool_fail_delay | 创建线程失败后间隔多久再次创建 |
thread_pool_max | 每一个线程池内部最多可以容纳的线程数量 |
thread_pool_min | 线程的最小值;当线程池之间的线程不均衡时保证均衡性 |
thread_pool_purge_delay | 间隔多久清理空闲线程 |
thread_pool_stack | 工作线程的栈空间大小;32位系统需按需调整 |
thread_pool_timeout | 线程空闲阈值;达到后清理;结合thread_pool_min控制 |
thread_pool_workspace | 线程池的工作区大小 |
thread_pools | 线程池的个数;运行时调大立即生效;但是减小需重启 |
thread_stats_rate | 一次收集指定个数的线程信息 |
varnishstat:
client_conn | 客户端连接数 |
client_req | 客户端的请求数 |
cache_hit | 命中次数 |
cache_miss | 未命中次数 |
backend_conn | 对后端测试连接测试 |
n_wrk | 工作线程个数 |
n_wrk_create | 创建线程个数 |
n_backend | 后端个数 |
n_expired | 过期线程数 |
n_lru_moved | 基于LRU清理的线程 |
s_hdrbytes | 总体传输首部字节数 |
s_bodybytes | 总体传输实体字节数 |
其他具体参数可以查看其文档。
如有错误;恳请纠正。