在前端已经设置了缓存时间戳的情况下,后端数据库也已经更新,但是请求接口获取的数据仍然是老数据,排除了304重定向的问题,发现是服务器Varnish设置了缓存。
Varnish是一个http反向代理的缓存,高性能,轻量级,web应用程序加速器。
它通过缓存服务端的输出来减少你的网站/应用/API的加载时间,从而提高性能。
默认监听TCP80端口。
特点:
- 基础内存进行缓存,重启后数据消失;
- 利用虚拟内存方式,性能好;
- 支持设置0-60秒的精确缓存时间;
- VCL配置管理,比较灵活;
- 状态机设计巧妙,结构清晰;
- 后端服务器的负载均衡和健康检查 URL地址重写;
- 优雅的处理后端服务器宕机的问题;
过程:
- varnish从客户端接收请求 然后尝试从缓存中获取数据来响应客户端的请求 ;
- 如果varnish不能从缓存中获得数据来响应客户端;
- 它将转发请求到后端,获取响应同时缓存到本地 最后交付给客户端;
- 如果varnish已经缓存了某个响应,则请求直接从varnish的缓存中获取响应;
- 比从后端服务器去获取数据快的多;
bereq.
由varnish发往服务器的http请求;
beresp.
由服务器响应给varnish的响应报文;
resp.
由varnish响应给client相关;
obj.
只读的,存储在缓存空间中的缓存对象的属性;
Management管理进程:
编译VCL并应用新配置、监控varnish、初始化varnish并提供命令行接口CLI;
Child/Cache线程:
Acceptor:用于接收并连接请求;
Worker:用于处理并响应用户请求;
Object expiry:从缓存中清理过期的cache object;
cache-main:此线程只有一个,用于启动cache;
banluker:清理指定缓存;
expire:清理过期缓存
epoll:线程池管理器;
varnish定义其最大并发连接数是通过线程池模型实现的:
thread_pools:线程池个数,默认为2;
thread_pool_max:单线程池内允许启动的最大线程个数;
thread_pool_min:单线程池内允许启动的最小线程个数;
thread_pool_timeout:多于thread_pool_min的线程空闲此参数指定的时长后即被purge;
Log file:
varnish的日志,为保持其高性能,保持在一段共享内存空间中--Shared Memory Log,共享内存日志大小一般为90M,分为两部分:前一部分为计数器,后一部分为客户请求相关的数据,可以计算缓存命中率、分析请求首部、记录响应首部等;
vcl:Varnish Configuration Language
缓存策略配置工具;
基于“域”的简单编程语言,意为其所有代码都是写在各“{}”中,且作用范围仅为对应的“{}”,这些“域”称为varnish的状态引擎;
varnish定义其最大并发连接数:线程池模型:
thread_pools:线程池个数;默认为2;
thread_pool_max:单线程池内允许启动的最多线程个数;
thread_pool_min
thread_pool_timeout:多于thread_pool_min的线程空闲此参数指定的时长后即被purge;
实现负载均衡
1、varnish实现调度的方式:
round-robin:加权轮询,每个backend都有它的权重
random:随机
dns:基于DNS解析之后进行调度
2、使用varnish自动清理缓存
使用pruge
修剪缓存,能够让用户清理所指定的某个特定缓存对象,如果要移除多个缓存对象,对缓存某些内容基于正则方式进行清理的话,可以进行banning这种机制
Banning
通常可以移除指定的缓存对象或者进行匹配某些对象进行处理。
Varnish的缓存策略偏向保守型的(可以通过配置修改),默认缓存get请求和Head请求,不会缓存cookie和认证信息的请求,也不会缓存带有Set-cookie和有变化的头信息的响应。
Varnish会检查和请求响应中的Cache-control头信息,头信息中的一些选项控制缓存行为,当头信息有max-age=n,varnish以此n设置缓存时间,否则,取缓存默认时间default_ttl:120s。
Varnish子程序
vcl_recv:
接收到客户端请求后执行。返回结果有:
pass:直接去后端服务器请求资源,从后端拿到的数据不会被缓存;
pipe:直接去后端服务器请求资源,也不会被缓存,并且处理的请求不会被记录日志;
hash:进行hash计算检查缓存;
purge:在缓存中查找缓存对象并清除;
synth:合成http响应报文,通常是错误报文;
vcl_pass:
当一个subroutine执行return pass时,跳转到vcl_pass处理,把请求设置为pass模式,返回以下结果:
fetch:不会被缓存,直接响应给客户端;
synth:进入vcl_synth合成响应报文;
restart:从vcl_recv重新开始;
vcl_miss:
根据缓存命中情况来执行
fetch:获取服务器响应结果,缓存到本地;
pass:从后端获取到响应,但本地不缓存;
vcl_hit:
缓存命中执行。
deliver:直接将缓存结果返回给客户端;
pass:丢弃缓存,重新从后端获取数据;
vcl_fetch:
从后端服务器获取数据之后执行,通过beresp对象访问响应的数据。
deliver:将数据返回给客户端;
restart:重新开始这个请求;
vcl_deliver:
将响应结果返回给客户端,结束请求。
Varnish配置文件
# 1.定义一个健康监测
vcl 4.0; #指定版本
import directors; #加载后端的轮询模块
probe backend_healthcheck { #设置名为backend_healthcheck的健康监测
.url = "/index.html";
.window = 5; #窗口
.threshold = 2; #门槛
.interval = 3s;
.timeout = 1s;
}
# 后端服务器
backend web1 {
.host = "127.0.0.1";
.port = "80";
.probe = backend_healthcheck;
}
backend web2 {
.host = "127.0.0.1";
.port = "80";
.probe = backend_healthcheck;
}
# 设置后端集群事件
sub vcl_init {
new img_cluster = directors.round_robin();
img_cluster.add_backend(web1);
img_cluster.add_backend(web2);
}
acl purgers { # 定义可访问来源IP,权限控制
"127.0.0.1";
"172.17.0.0"/16;
}
# 定义引擎
sub vcl_recv {
if (req.method == "GET" && req.http.cookie) {
return(hash); //处理完recv 引擎,给下一个hash引擎处理
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "PURGE" &&
req.method != "DELETE") {
return (pipe); //除了上边的请求头部,通过通道直接扔后端的pass
}
# 定义index.php不经过缓存 直接后端请求
if (req.url ~ "index.php") {
return(pass);
}
# 定义删除缓存的方法
if (req.method == "PURGE") { //PURGE请求的处理的头部,清缓存
if (client.ip ~ purgers) {
return(purge);
}
}
# 为发往后端主机的请求添加X-Forward-For首部
if (req.http.X-Forward-For) {
set req.http.X-Forward-For = req.http.X-Forward-For + "," + client.ip;
} else {
set req.http.X-Forward-For = client.ip;
}
return(hash);
}
# 定义vcl_hash
sub vcl_hash {
hash_data(req.url);
}
# 自定义缓存文件的缓存时长,即TTL值
sub vcl_backend_response {
if (bereq.url ~ "\.(jpg|jpeg|gif|png)$") {
set beresp.ttl = 30d;
}
if (bereq.url ~ "\.(html|css|js)$") {
set beresp.ttl = 7d;
}
if (beresp.http.Set-Cookie) { //定义带Set-Cookie首部的后端响应不缓存,直接返回给客户端
set beresp.grace = 30m;
return(deliver);
}
}
# 定义vcl_deliver
sub vcl_deliver {
if (obj.hits > 0) { //为响应添加X-Cache首部,显示缓存是否命中
set resp.http.X-Cache = "HIT from " + server.ip;
} else {
set resp.http.X-Cache = "MISS";
}
unset resp.http.X-Powered-By; //取消显示php框架版本的header头
unset resp.http.Via; //取消显示varnish的header头
}
node-varnish的使用
npm install node-varnish
var Varnish = require('node-varnish');
var client = new Varnish.VarnishClient('127.0.0.1', MANAGEMENT_PORT[, secret]);
client.on('ready', function() {
client.run_cmd('purge obj.http.X == test', function(){});
});