本章概要
静态资源:
图片服务器组:静态资源,单独存放在图片服务器
前后无关联,即无状态,可使用短连接调度算法,如wrr
前端内容组:html,css,js文件
在服务器端这些文件显示为静态,只需存放在web服务器(httpd或nginx服务器)上即可
动态资源:
后端业务处理层:.php文件
客户端请求某php动态资源,php服务器把程序执行后形成的动态资源发送给客户端
Varnish
缓存的构建
varnish组成
vim /etc/varnish/varnish.params
# Varnish environment configuration description. This was derived from
# the old style sysconfig/defaults settings
# Set this to 1 to make systemd reload try to switch VCL without restart.
RELOAD_VCL=1 每次重启都会重新加载VCL
# 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=192.168.1.5
VARNISH_LISTEN_PORT=6081 varnish监听端口
# Admin interface listen address and port
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 varnish管理地址
VARNISH_ADMIN_LISTEN_PORT=6082 varnish管理接口
# Shared secret file for admin interface
VARNISH_SECRET_FILE=/etc/varnish/secret varnish管理接口登录的身份认证密钥
# Backend storage specification, see Storage Types in the varnishd(5)
# man page for details.
VARNISH_STORAGE="malloc,256M" 缓存存储系统,根据需求自定义缓存大小,但不推荐太大,容易产生碎片
# User and group for the varnishd worker processes
VARNISH_USER=varnish varnish用户
VARNISH_GROUP=varnish varnish组
# Other options, see the man page varnishd(1)
#DAEMON_OPTS="-p thread_pool_min=5 -p thread_pool_max=500 -p thread_pool_timeout=300"
-s [name=]kind[,options] # Backend storage specification
# -s malloc[,]
# -s file [default: use /tmp]
# -s file,
# -s file,,
# -s file,,,
# -s persist{experimental}
示例:指定磁盘存储格式
VARNISH_STORAGE="file,/var/lib/varnish/varnish.bin,20G"
要注意varnish用户对该目录具有写权限
ping [] 探测服务器存活性,如果回复pong,说明服务器存活
auth 做认证
quit 退出
banner 输出欢迎信息
status 查看服务进程状态
start 启动varnish
stop 停止varnish
vcl.load
vcl.inline
vcl.use 切换vcl
vcl.discard 删除vcl
vcl.list 列出正在使用的vcl
param.show [-l] [] 查看参数
param.set 服务器内部线程的配置参数
panic.show 内部出现故障,获取排障信息
panic.clear
storage.list 列出使用的存储系统,是磁盘还是内存
vcl.show [-v] 列出vcl
backend.list [] 列出后端服务器
backend.set_health
ban [&& ]...
ban.list
添加后端服务器:
vim /etc/varnish/default.vcl
backend default {
.host = "192.168.32.130";
.port = "80";
}
添加后配置文件需要编译才能生效
varnish_reload_vcl
进入vcl管理接口查看:
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
查看vcl信息
vcl.list
200
available 0 boot
active 0 reload_2018-11-15T15:42:02
切换vcl,返回200状态码,说明切换成功
vcl.use reload_2018-11-15T15:42:02
200
在本地通过curl命令查看头部信息
curl -I 192.168.32.129:6081
vcl的语法格式:
(1) VCL files start with “vcl 4.0;”
(2) //, # and /* foo */ for comments; 注释信息
(3) Subroutines are declared with the sub keyword; 例如sub vcl_recv { …}; 定义状态引擎使用sub
(4) No loops, state-limited variables(受限于引擎的内建变量); 不支持循环
(5) Terminating statements with a keyword for next action as argument of the return() function, i.e.: return(action);用于实现状态引擎转换;
(6) Domain-specific;
VCL, DSL
The VCL Finite State Machine
(1) Each request is processed separately; 每一个请求被隔离单独处理
(2) Each request is independent from others at any given time; 每一个请求各自独立
(3) States are related, but isolated; 每个状态之间有关联性,但又是相互隔离的
(4) return(action); exits one state and instructs Varnish to proceed to the next state; 退出时,只能退出一个状态引擎
(5) Built-in VCL code is always present and appended below your own VCL;
三类主要语法:
sub subroutine { 定义一个引擎
...
}
if CONDITION { if语句,支持单分支和双分支
...
} else {
...
}
return(), hash_data() 调用内减函数
==, !=, ~, >, >=, <, <=
逻辑操作符:&&, ||, !
变量赋值:=
if (obj.hits>0) {
set resp.http.X-Cache = "HIT via" + " " + server.ip;
} else {
set resp.http.X-Cache = "MISS from " + server.ip;
}
在c语言中,变量不能加引号,字符串则需要用引号
另外,该代码属于缓存服务器发送给客户端的信息,因此需要写在deliver配置段中
vcl 4.0;
#######################################################################
# Client side
sub vcl_recv {
if (req.method == "PRI") {
/* We do not support SPDY or HTTP/2.0 */
return (synth(405)); #不支持SPDY和http2.0,直接返回405状态码
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe); #如果不是http协议的标准方法,return (pipe)是指下一个状态引擎为vcl_pass,即转为四层代理
}
if (req.method != "GET" && req.method != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass); #http协议除了GET和HEAD方法,其他方法不能使用缓存,return (pass)是指下一个状态引擎为vcl_pass,即直接到后端服务器直接获取数据
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
return (pass); #客户端发送的请求报文首部能够包含认证信息和cookie信息,这些信息属于用户私密信息,因此这些信息不能被缓存,也就是说缓存中并没有这些信息,要直接去后端服务器。return (pass)是指下一个状态引擎为vcl_pass
}
return (hash); #如果不满足以上信息,即请求方法为GET,HEAD,也不包含认证和cookie信息,就可以查询缓存信息,下一个状态引擎为vcl_hash
}
sub vcl_hash {
hash_data(req.url); #根据url做hash运算,然后查询缓存。hash的内容可以自定义
if (req.http.host) { 如果请求报文中包含host信息
hash_data(req.http.host); 对host信息进行hash运算
} else {
hash_data(server.ip); 否则就对server.ip进行hash运算
}
return (lookup); 下一个状态引擎为lookup,即查询缓存
}
注意:根据host以及server.ip信息进行缓存,会降低缓存命中率,应该把这两项内容去掉
vim /etc/varnish/default.vcl
vcl_recv {
if (req.url ~ "(?i)^/(login|admin)") { #即与登录验证有关的验证信息
return(pass); #此类信息属于私密信息,因此不查缓存,直接发送到后端服务器
}
}
注意:此代码必须写在vcl_recv代码段,因为一旦开始检查缓存,此代码就会失效
测试:
在后端服务器设置测试页
cd /var/www/html
mkdir login admin
vim login/index.html
login url
在客户端管理接口重新加载代码
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
vcl.load testconf3 default.vcl 加载配置文件
vcl.use testconf3 切换vcl
[root@centos7 ~]# curl -I 192.168.32.129:6081/login
X-Cache: Miss FROM 192.168.32.129 这里只贴出缓存命中情况
示例2:对于特定类型的资源,例如公开的图片等,取消其私有标识,并强行设定其可以由varnish缓存的时长; 定义在vcl_backend_response中;
if (beresp.http.cache-control !~ "s-maxage") { #后端服务器响应给缓存服务器的报文中cache-control不带有s-maxage(公共缓存时长)信息
if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") { #缓存服务器发送给后端服务器的请求报文中带有静态图片的请求
unset beresp.http.Set-Cookie; #取消后端服务器响应给缓存服务器报文中的Set-Cookie信息(服务器发送Set-Cookie信息给客户端,客户端下次访问时就会带上cookie信息。取消该变量,客户端的访问信息就不会带有cookie信息,也就能够缓存下来)
set beresp.ttl = 3600s; #由于匹配规则为不带有缓存时长(s-maxage),因此设置缓存的生存时长为3600s
}
}
注意:带有cookie信息都默认为私有信息,不进行缓存。在后端服务器发送响应报文时不让其在响应报文中添加set-cookie字段信息,则客户端就无法收到cookie信息,此需要在后端服务器响应时设置,即vcl_backend_response配置段
示例3:定义在vcl_recv中;
if (req.restarts == 0) { 限制在不重启的前提下
if (req.http.X-Fowarded-For) { 如果请求报文中带有X-Fowarded-For字段
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + "," + client.ip; 在X-Fowarded-For字段后加上client.ip字段
} else {
set req.http.X-Forwarded-For = client.ip; 否则,就直接在报文中添加client.ip字段
}
}
注意:
把真实的客户端ip地址传递给后端服务器,由于代理服务器的原因,后端服务器在日志中保存的ip地址为代理服务器的ip地址,可以在代理服务器上添加报文首部字段(如X-Fowarded-For)的方式把客户端ip地址加入请求报文中,并在后端服务器http日志格式中添加此字段以抓取客户端ip地址
由于存在多级代理服务器的原因,缓存服务器前端一般并不是真正的客户端,而是代理服务器,因此请求报文中可能已经被添加过此类报文首部字段,通过此方法获取的ip地址可能是代理服务器的ip地址,因此可以在获取到的报文首部字段(如X-Fowarded-For)后加上客户端的ip地址,这样一来就能获取到X-Fowarded-For字段中的客户端真实ip地址
另外,该代码之后并没有return,因此可以被多端代码引用
测试:
在后端服务器http日志格式中记录X-Forwarded-For字段信息
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{clientip}i\" %{X-Forwarded-For}i" combined
在客户端管理接口重新加载代码
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
vcl.load testconf3 default.vcl 加载配置文件
vcl.use testconf3 切换vcl
在客户端访问:
curl -I http://192.168.32.129:6081/login
查看后端服务器日志:
192.168.32.129 - - [15/Nov/2018:19:51:23 +0800] "HEAD /login HTTP/1.1" 301 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-" 192.168.32.128
注意:客户端ip地址为192.168.32.128
如果缓存过期时长刚刚获取新的时间,离缓存过期还有很长时间,但是后端服务器的数据内容却发生变化,图片等静态内容没有变化,但css,js文件由于版本不一致会导致网站内容排版错乱
这就需要手动清除老版本的css,js文件,当客户端再次请求时,不回命中缓存,而直接到后端服务器获取数据,然后新数据缓存到缓存服务器,这样就更新到新版本的内容
缓存对象的修剪:purge, ban
purge:一次只能修剪一个url
ban:一次性生效,可以临时清除某些缓存项
支持基于正则表达式模式,定义过滤器以拒绝对某些url所对应的缓存项查缓存的请求,从而使得缓存服务器必须到后端服务器去获取数据,然后缓存到缓存中并把原来的覆盖掉
配置purge操作:
(1) 能执行purge操作
sub vcl_purge {
return (synth(200,"Purged"));
}
(2) 何时执行purge操作
sub vcl_recv {
if (req.method == "PURGE") {
return(purge);
}
...
}
添加此类请求的访问控制法则:
acl purgers { 由于清理缓存操作危及网站缓存,因此定义acl规则urgers,只允许指定网段进行purge操作
"127.0.0.0"/8; 允许本机ip地址,注意字符串需要使用引号括起来,而掩码则需要放在引号外面
"192.168.32.129"/32; #允许指定ip地址,注意字符串需要使用引号括起来,而掩码则需要放在引号外面,32位掩码为限制本网段只有一个ip地址
}
#注意:在设置acl规则时,本机回环地址127.0.0.1和本机地址192.168.32.129 hash值并不一样,因此在本机访问127.0.0.1和在其他主机访问本机192.168.32.129并不相同,因此需要在acl规则中加上两个ip地址才能限制
另外,此段代码推荐写在default配置段
sub vcl_recv {
if (req.method == "PURGE") { #如果客户端请求方法为PURGE,这里PURGE为自定义名称
if (!client.ip ~ purgers) { #如果客户端ip地址不是acl规则中指定的网段
return(synth(405,"Purging not allowed for " + client.ip)); #拒绝进行purge操作,并发挥提示语
}
return(purge); 否则就允许puege操作
}
...
}
测试:
在客户端进行测试:
curl -X "PURGE" http://192.168.32.129:6081/index.html curl命令-X选项是指以指定方法访问网站页面,指定以PURGE方式获取进行测试
405 Purging not allowed for 192.168.32.128 由于客户端地址不满足acl规则,因此被拒绝
在本机进行测试:
curl -I 192.168.32.129:6081/index.html 清理后第一次访问,状态为MISS
X-Cache: Miss FROM 192.168.32.129
curl -I 192.168.32.129:6081/index.html 清理后第二次访问,状态为HIT
X-Cache: HIT via 192.168.32.129
执行清理操作后,第一次请求响应报文头部X-Cache处于MISS状态,会直接向后端服务器获取新内容,第二次请求时X-Cache处于HIT状态,说明是从缓存获取的数据内容
ban req.url ~ (?i)^/test[123].html$ 在varnish管理接口使用ban命令清除以test1,test2,test3开头,以.html结尾的缓存
查看ban规则:
ban.list
200
Present bans:
1542286682.822361 0 req.url ~ (?i)^/test[123].html$
测试:
在后端服务器准备test1-test10.html 10个文件
在客户端先对10个文件访问一次进行缓存
在varnish服务器使用ban命令清除test1-test3.html的缓存后
在客户端再次访问查看缓存命中状态
(2) 在配置文件中定义,使用ban()函数;
示例:
if (req.method == "BAN") {
ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
# Throw a synthetic page so the request won't go to the backend.
return(synth(200, "Ban added"));
}
测试:
curl -X BAN http://www.ilinux.io/test1.html
ban req.http.host==www.ilinux.io && req.url==/test1.html
backend default { 指定默认服务器名称以及ip和端口
.host = "172.16.100.6";
.port = "80";
}
backend appsrv { 指定app服务器名称以及ip和端口
.host = "172.16.100.7";
.port = "80";
}
#注意:此段代码要放在默认配置段default最上面
sub vcl_recv {
if (req.url ~ "(?i)\.php$") { 如果是php动态资源请求,把其调度给appsrv服务器
set req.backend_hint = appsrv;
} else { 如果是其他请求,则调度给default服务器
set req.backend_hint = default;
}
...
}
nginx: proxy_pass
haproxy: use_backend
测试:
加载配置文件:
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
vcl.load testconf6 default.vcl
vcl.use testconf6
在客户端分别访问
curl http://192.168.32.129:6081/index.html
curl http://192.168.32.129:6081/test.php
import directors; # load the directors 导入模块,要写在配置文件最上面
backend websrv1 { #定义后端服务器
.host = 192.168.32.130;
.port = 80;
}
backend websrv2 { #定义后端服务器
.host = 192.168.32.131;
.port = 80;
}
sub vcl_init { 该配置段要写在vcl_recv之前
new websrvs = directors.round_robin(); #定义组,组名websrvs为自定义,调用director模块内建的轮询调度算法,也可以使用random()函数即随机调度。另外轮询算法不支持权重,随机调度支持权重
websrvs.add_backend(websrv1); #把后端服务器websrv1添加到组中
websrvs.add_backend(websrv2); #把后端服务器websrv2添加到组中
}
sub vcl_recv {
# send all traffic to the bar director:
set req.backend_hint = websrvs.backend(); #调用GROUP_NAME组内的后端服务器
}
注意:这种方式是把服务器并组后进行调度
另外,负载均衡时,不能使用缓存,否则根据缓存调度,无法显示调度算法的效果;因此可以使用之前定义的不使用缓存的log和admin目录
创建测试页:
RS1: 创建login,admin目录,并在目录中创建10个测试页
cd /var/www/html
mkdir login
for i in {1..10};do echo test $i on RS1 > login/log$i.html;done
RS2: 创建login,admin目录,并在目录中创建10个测试页
cd /var/www/html
mkdir login
for i in {1..10};do echo test $i on RS1 > login/log$i.html;done
重新加载配置文件:
vcl.load testconf7 default.vcl
vcl.use testconf7
客户端测试:
while true do curl http://192.168.32.129/login/log1.html;sleep 0.5 ;done
sub vcl_init {
new h = directors.hash();
h.add_backend(one, 1); // backend 'one' with weight '1'
h.add_backend(two, 1); // backend 'two' with weight '1'
}
sub vcl_recv {
// pick a backend based on the cookie header of the client
set req.backend_hint = h.backend(req.http.cookie);
}
sub vcl_init {
new websrvs = directors.random();
websrvs.add_backend(srv1,1);
websrvs.add_backend(srv2,2);
}
注意:负载均衡时,不能使用缓存,否则根据缓存调度,无法显示调度算法的效果
BE Health Check:
backend BE_NAME {
.host =
.port =
.probe = { 定义检测内容
.url= 定义检测url内容
.timeout= 定义超时时长
.interval= 隔多久进行检测
.window=
.threshold=
}
}
(1) probe PB_NAME { }
backend NAME = {
.probe = PB_NAME;
...
}
(2) backend NAME {
.probe = {
...
}
}
示例:在vcl_init配置段之前写入该配置端,即在配置文件最上面
probe healthchk { #定义健康状态检测方式的名称为healthchk
.url = "/.healthcheck.html";
.window = 5; 检测最近的5次状态
.threshold = 4; 检测的5次中有4次为健康,服务器显示为健康
.interval = 2s;
.timeout = 1s;
}
backend default { #在default服务器组调用healthchk
.host = "10.1.0.68";
.port = "80";
.probe = healthchk;
}
backend appsrv { #在appsrv服务器组调用healthchk
.host = "10.1.0.69";
.port = "80";
.probe = healthchk;
}
注意:健康状态检测信息不要记录在日志中,并且只对特定页面进行检测
手动设定BE主机的状态:
sick:管理down;
healthy:管理up;
auto:probe auto;
测试:
varnish服务器重新加载配置文件:
vcl.load testconf9 default.vcl
vcl.use testconf9
客户端测试:
while true do curl http://192.168.32.129/login/log1.html;sleep 0.5 ;done
注意:也可以手动设定后端服务器状态查看健康检测的效果
backend BE_NAME {
...
.connect_timeout = 0.5s;
.first_byte_timeout = 20s; #定义发送第一个字节超时时长
.between_bytes_timeout = 5s; #字节和字节之间的超时时长
.max_connections = 50;
}
线程模型:
cache-worker
cache-main
ban lurker
acceptor:
epoll/kqueue:
...
线程相关的参数:使用线程池机制管理线程;
在线程池内部,其每一个请求由一个线程来处理; 其worker线程的最大数决定了varnish的并发响应能力;
查看参数:在varnish管理接口中
param.show -l 查看所有参数
param.show 参数名 查看具体某一参数
thread_pools:Number of worker thread pools. 最好小于或等于CPU核心数量;
thread_pool_max:The maximum number of worker threads in each pool. 每线程池的最大线程数;默认为5000
thread_pool_min:The minimum number of worker threads in each pool. 额外意义为“最大空闲线程数”;
最大并发连接数 = thread_pools * thread_pool_max
thread_pool_timeout:Thread idle threshold. Threads in excess of thread_pool_min, which have been idle for at least this long, will be destroyed.
thread_pool_add_delay:Wait at least this long after creating a thread. 延时增加线程
thread_pool_destroy_delay:Wait this long after destroying a thread. 延迟销毁线程
Timer相关的参数:
send_timeout:Send timeout for client connections. If the HTTP response hasn't been transmitted in this many seconds the session is closed.
timeout_idle:Idle timeout for client connections.
timeout_req: Max time to receive clients request headers, measured from first non-white-space character to double CRNL.
cli_timeout:Timeout for the childs replies to CLI requests from the mgt_param.
设置方式:
vcl.param
param.set
param.set 参数 数量
永久有效的方法:
varnish.params
DEAMON_OPTS="-p PARAM1=VALUE -p PARAM2=VALUE"
注意:一个-p选项后跟一个参数,设置多个参数只需多个-p选项即可
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 Instead of a continously updated display, print the statistics once and exit.
-i taglist,可以同时使用多个-i选项,也可以一个选项跟上多个标签;
-I <[taglist:]regex\>:对指定的标签的值基于regex进行过滤;
-x taglist:排除列表
-X <[taglist:]regex\>:对指定的标签的值基于regex进行过滤,符合条件的予以排除;
3、varnishlog - Display Varnish logs
4、 varnishncsa - Display Varnish logs in Apache / NCSA combined log format
启动varnishncsa服务,自动保存日志
日志默认保存文件:/var/log/varnish/varnishncsa.log
hash_data():指明哈希计算的数据;减少差异,以提升命中率;
regsub(str,regex,sub):把str中被regex第一次匹配到字符串替换为sub;主要用于URL Rewrite
regsuball(str,regex,sub):把str中被regex每一次匹配到字符串均替换为sub;
return():
ban(expression)
ban_url(regex):Bans所有的其URL可以被此处的regex匹配到的缓存对象;
synth(status,"STRING"):生成响应报文;