1. varnish原理
1)Varnish 简介:
varnish 缓存是 web 应用加速器,同时也作为 http 反向缓存代理。你可以安装 varnish 在任何http 的前端,同时配置它缓存内容。与传统的 squid 相比,varnish 具有性能更高、速度更快、管理更加方便等诸多优点。有一部分企业已经在生产环境中使用其作为旧版本的 squid的替代方案,以在相同的服务器成本下提供更好的缓存效果,Varnish 更是作为 CDN 缓存服务器的可选服务之一。
根据官网的介绍,Varnish 的主要特性如下:https://www.varnish-cache.org/
- 缓存位置:可以使用内存也可以使用磁盘。如果要使用磁盘的话推荐 SSD 做 RAID1
- 日志存储:日志也存储在内存中。存储策略:固定大小,循环使用
- 支持虚拟内存的使用。
- 有精确的时间管理机制,即缓存的时间属性控制。
- 状态引擎架构:在不同的引擎上完成对不同的缓存和代理数据进行处理。可以通过特定的配置语言设计不同的控制语句,以决定数据在不同位置以不同方式缓存,在特定的地方对经过的报文进行特定规则的处理。
- 缓存管理:以二叉堆格式管理缓存数据,做到数据的及时清理
2)Varnish 与 Squid 的对比
相同点:
都是一个反向代理服务器;
都是开源软件;
Varnish 的优势:
- Varnish 的稳定性很高,两者在完成相同负荷的工作时,Squid 服务器发生故障的几率要高于 Varnish,因为使用 Squid 要经常重启;
- Varnish 访问速度更快,因为采用了“Visual Page Cache”技术,所有缓存数据都直接从内存读取,而 squid 是从硬盘读取,因而 Varnish 在访问速度方面会更快;
- Varnish 可以支持更多的并发连接,因为 Varnish 的 TCP 连接释放要比 Squid 快,因而在高并发连接情况下可以支持更多 TCP 连接;
- Varnish 可以通过管理端口,使用正则表达式批量的清除部分缓存,而 Squid 是做不到的;squid 属于是单进程使用单核 CPU,但 Varnish 是通过 fork 形式打开多进程来做处理,所以可以合理的使用所有核来处理相应的请求;
Varnish 的劣势:
- varnish 进程一旦 Crash 或者重启,缓存数据都会从内存中完全释放,此时所有请求都会发送到后端服务器,在高并发情况下,会给后端服务器造成很大压力;
- 在 varnish 使用中如果单个 url 的请求通过 HA/F5 等负载均衡,则每次请求落在不同的varnish 服务器中,造成请求都会被穿透到后端;而且同样的请求在多台服务器上缓存,也会造成 varnish 的缓存的资源浪费,造成性能下降;
Varnish 劣势的解决方案:
- 针对劣势一:在访问量很大的情况下推荐使用 varnish 的内存缓存方式启动,而且后面需要跟多台 squid/nginx 服务器。主要为了防止前面的 varnish 服 务、服务器被重启的情况下,大量请求穿透 varnish,这样 squid/nginx 可以就担当第二层 CACHE,而且也弥补了 varnish 缓存在内存中重启都会释放的问题;
- 针对劣势二:可以在负载均衡上做 url 哈希,让单个 url 请求固定请求到一台 varnish 服务器
3)使用 varnish 作为 web 代理缓存的原理
varnish 是一个 http 反向代理的缓存。它从客户端接收请求然后尝试从缓存中获取数据来响应客户端的请求,如果 varnish 不能从缓存中获得数据来响应客户端,它将转发请求到后端(backend servers),获取响应同时存储,最后交付给客户端。如果 varnish 已经缓存了某个响应,它比你传统的后端服务器的响应要快很多,所以你需要尽可能是更多的请求直接从 varnish 的缓存中获取响应。varnish 决定是缓存内容或者是从后端服务器获取响应。后端服务器能通过 http 响应头中的Cache-Control 来同步 varnish 缓存内容。在某些条件下 varnish 将不缓存内容,最常见的是使用 cookie。
4)简单架构:
Varnish 分为 management 进程和 child 进程;
- Management 进程:对子进程进行管理,同时对 VCL 配置进行编译,并应用到不同的状态引擎。
- Child 进程:生成线程池,负责对用户请求进行处理,并通过 hash 查找返回用户
5)vcl中内置预设变量
req:The request object,请求到达时可用的变量(客户端发送的请求对象)
bereq:The backend request object,向后端主机请求时可用的变量
beresp:The backend response object,从后端主机获取内容时可用的变量(后端响应请求对象)
resp:The HTTP response object,对客户端响应时可用的变量(返回给客户端的响应对象)
2. varnish初体验
1)安装varnish
25 yum -y install libtool ncurses-devel pcre-devel libxslt pkgconfig groff python-imaging libedit-devel python-docutils
26 cd varnish/ #这个路径下存放着varnish4.19的包
27 gunzip varnish-4.1.9.tgz
28 ls
29 tar -xf varnish-4.1.9.tar
30 cd varnish-4.1.9/
31 ./configure --prefix=/usr/local/varnish
--enable-developer-warnings
--enable-debugging-symbols
--enable-dependency-tracking
32 make && make install
33 ln -s /usr/local/varnish/bin/* /usr/bin/
34 ln -s /usr/local/varnish/sbin/* /usr/sbin/
35 cd /usr/local/varnish/share/doc/varnish/
36 cp example.vcl /usr/local/varnish/default.vcl
2) varnish缓存初体验(4版本varnish)
服务器规划:
varnish: 192.168.1.129
httpd:192.168.1.127
客户端:windows浏览器
varnish修改配置文件如下
[root@localhost ~]# vim /usr/local/varnish/default.vcl
16 backend default {
17 .host = "192.168.1.127";
18 .port = "80";
19 }
启动varnish
[root@localhost ~]# varnishd -f /usr/local/varnish/default.vcl -a 0.0.0.0:80
[root@localhost ~]# netstat -anput | grep varnish
tcp 0 0 127.0.0.1:46573 0.0.0.0:* LISTEN 67838/varnishd
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 67838/varnishd
tcp6 0 0 ::1:40495 :::* LISTEN 67838/varnishd
varnish启动参数
-a 绑定ip和端口
-f 配置文件
-T varnish的telnet管理地址和端口
varnish启动日志
[root@localhost ~]# varnishlog
会阻塞,类似于tailf
http服务器开启服务并动态监控访问日志
[root@localhost ~]# tailf /var/log/httpd/access_log
结果和结论
用Windows浏览器访问varnish多次,查看varnish和httpd的访问日志,可以发现,Windows浏览器访问varnish很多次,在varnish日志中可以体现出来,第一次是Miss,之后是hit,但是在httpd日志中只能看到一次或者两次varnish的访问日志,说明,客户访问的时候,varnish是拿缓存的东西给客户端
3)代理web集群
服务器规划
varnish: 192.168.1.129
httpd1: 192.168.1.127
httpd2: 192.168.1.128
客户端: Windows浏览器
修改varnish配置文件
16 backend web1 {
17 .host = "192.168.1.127";
18 .port = "80";
19 .probe = {
20 .url = "/";
21 .timeout = 1s;
22 .interval = 5s;
23 .window = 5;
24 .threshold = 3;
25 }
26 }
27 backend web2 {
28 .host = "192.168.1.128";
29 .port = "80";
30 .probe = {
31 .url = "/";
32 .timeout = 1s;
33 .interval = 5s;
34 .window = 5; #维持5个滑动窗口的结果
35 .threshold = 3; #至少有三个欢动窗口是成功的就认为后端节点健康
36 }
37 }
38
39 import directors; #导入集群负载均衡模块
40
41 sub vcl_init {
42 new bar = directors.round_robin(); #varnish的调度算法
43 bar.add_backend(web1);
44 bar.add_backend(web2);
45 }
46 sub vcl_recv {
47 # Happens before we check if we have this in cache already.
48 #
49 # Typically you clean up the request here, removing cookies you don't need,
50 # rewriting the request, etc.
51 set req.backend_hint = bar.backend(); #此行为添加内容 表示自动屏蔽后端不正常的节点
52 }
varnish调度算法:
round_robin:轮询
fallback:选择正常的服务器
random:随机选择后端服务器
hash:会话保持
varnish检查语法
[root@localhost ~]# varnishd -C -f /usr/local/varnish/default.vcl
未保存而是完整输出配置文件内容则为正常
重启varnish
[root@localhost ~]# netstat -anput | grep varnish
tcp 0 0 127.0.0.1:46573 0.0.0.0:* LISTEN 67838/varnishd
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 67838/varnishd
tcp6 0 0 ::1:40495 :::* LISTEN 67838/varnishd
[root@localhost ~]# killall -s QUIT varnishd
[root@localhost ~]# varnishd -f /usr/local/varnish/default.vcl -a 0.0.0.0:80
varnish启动参数
-f 指定vcl配置文件
-a 监听的ip地址和端口
-T 提供一个管理接口的地址和端口
-s 指定varnish缓存内容的存放方式、路径、大小
-n 指定varnish的工作目录
-S 指定访问管理端口用到的安全认证文件的路径
-p 指定服务器参数,用于优化varnish
两台web服务器正常启动
windows浏览器访问varnish,查看轮询结果。因为有缓存的缘故,查看的时候只能看到第一台web服务器的内容,第二天的暂时看不到,要等待一段时间才可以看得到,但是集群代理的设置没有问题
关于滑动窗口的文章请看
https://blog.csdn.net/a133900029/article/details/80599193?from=groupmessage
3. varnish流程图解读
1) 2版本varnish
1)VCL内置子进程说明:
- vcl_recv:开始处理请求
- vcl_hash:hash缓存模式,将请求形成hash值,并以此作为键名提取缓存内容
- vcl_hit:命中缓存时候进入该程序
- vcl_miss:未命中缓存时进入此程序
- vcl_pass: pass模式,对请求不做缓存处理
- vcl_fetch:发送请求到后端服务器,并获取响应内容
- vcl_deliver:交付缓存内容或后端响应内容给客户端
- vcl_pipe:pipe模式,建立管道,请求被直接发送到backend,后端和客户端之间的后继数据不进行处理,只是简单传递,直到一方关闭连接,一般用于出错的时候,比如,客户端访问的页面不存在
2)流程图整体说明
a. vcl_recv—>vcl_pipe
客户端请求内容错误,varnish直接建立管道,不修改来自客户端的数据,直接将请求数据转交给后端的服务器进行处理
b. vcl_recv—>vcl_pass—>vcl_fetch—>vcl_deliver
接收请求,但是可以明显判断出来内容不在缓存中,比如密码或者cookie这类的内容,那么进入vcl_pass进程,表示不查找缓存而直接去后端抓取内容,转交给vcl_fetch进程,由vcl_fetch进程请求后端服务器并获取响应内容
c. vcl_recv—>vcl_hash—>vcl_hit—>vcl_deliver
接收请求后,判断请求内容为可以缓存的内容,通过vcl_hash将请求进行hash处理形成hash值,并把这个hash值作为键名用于提取缓存内容,检索之后发现有缓存,缓存命中,进入vcl_hit程序,之后进入vcl_deliver程序,把缓存的内容发送给客户端
d. vcl_recv—>vcl_hash—>vcl_hit—>vcl_pass—>vcl_fetch—>vcl_deliver
接收请求后,判断请求内容为可以缓存的内容,通过vcl_hash将请求进行hash处理形成hash值,并把这个hash值作为键名用于提取缓存内容,检索之后发现有缓存,缓存命中,进入vcl_hit程序,但是经过判断,发现这个缓存的东西已经不合时宜了(比如支付宝付款二维码,作为图片来说是静态内容,但是不需要缓存,需要去后端获取新的二维码),这个时候进进入vcl_pass程序,再经过vcl_fetch程序向后端请求内容,最后把响应的内容通过vcl_deliver返回给客户端
e. vcl_recv—>vcl_hash—>vcl_miss—>vcl_fetch—>vcl_deliver
查找之后发现未命中缓存,进入vcl_miss模式,然后去后端服务器抓取数据,最后返回给客户端
f. vcl_recv—>vcl_hash—>vcl_miss—>vcl_pass
发现没有缓存,也不去后端fetch,因为数据已经不需要了,就当做异常,抛给vcl_pass,且不往后端传递(举例:淘宝某款女装已经下架,数据没有了,缓存也没有了,这个时候用户访问的时候返回一个报错页面,不去后端抓取数据)
2) 4版本varnish的变化
4版本新增程序
- vcl_purge 清理模式,当查找到对应的缓存时清除并调用,用于请求方法清除缓存,并报告
- vcl_backend_fetch 和vcl_fetch基本相同 向后端发送请求之前调用,可用于改变请求地址或其它信息,或放弃请求
- vcl_backend_reponse 后端响应后调用,可用于修改缓存时间及缓存相关信息
- vcl_backend_error 后端处理失败调用,异常页面展示效果处理,可自定义错误响应内容
- vcl_synth 自定义响应内容
- vcl_init 加载vcl时最先调用,用于初始化
- vcl_fini 用于卸载当前vcl时调用,不参与请求处理,仅在 vcl 正常丢弃后调用
新增优雅模式
当几个客户端请求同一个页面的时候,varnish 只发送一个请求到后端服务器,然后让其他几个请求挂起并等待返回结果;获得结果后,其它请求再复制后端的结果发送给客户端;但如果同时有数以千计的请求,那么这个等待队列将变得庞大,这将导致 2 类潜在问题:惊群问题(thundering herd problem),即突然释放大量的线程去复制后端返回的结果,将导致负载急速上升;没有用户喜欢等待;故为了解决这类问题,可以配置 varnish 在缓存对象因超时失效后再保留一段时间,以给那些等待的请求返回过去的文件内容(stale content),配置案例如下:
sub vcl_recv {
if (! req.backend.healthy) {
set req.grace = 5m;
} else {set req.grace = 15s;
}
4版本流程图
4. vcl多主机实例(2版本varnish)
varnish支持运算符:
= (赋值运算)
== (相等比较)
~ (匹配,可以使用正则表达式,或访问控制列表)
!~ (不匹配,可以使用正则表达式,或访问控制列表)
! (非)
&& (逻辑与)
|| (逻辑或)
另外:
set 表示设置变量
unset 表示删除变量
服务器规划
vcl配置如下
# This is a basic VCL configuration file for varnish. See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# Default backend definition. Set this to point to your content
# server.
#
backend webserver1 {
.host = "192.168.1.127";
.port = "80";
}
backend webserver2 {
.host = "192.168.1.128";
.port = "80";
}
backend webserver3 {
.host = "192.168.1.132";
.port = "80";
}
backend webserver4 {
.host = "192.168.1.131";
.port = "80";
}
director webserver random {
{.backend = webserver1; .weight = 5;}
{.backend = webserver2; .weight = 8;}
{.backend = webserver3; .weight = 5;}
{.backend = webserver4; .weight = 10;}
}
acl purge { #acl叫做访问控制列表 purge是这个列表的名字
"localhost";
"127.0.0.1";
"192.168.1.132"/24;
}
sub vcl_recv {
if (req.request == "PURGE"){ #如果请求是PURGE
if (!client.ip ~ purge) { #且ip不是acl列表的
error 405 "NOT ALLOWED."; # 返回405 不允许
}
elseif (req.url ~ "\.(php|cgi)($|\?)") {
return (pass);
}
else {
return (lookup);
}
}
if ((req.http.host ~"^(www.|bbs.)?han.com") && (req.restarts == 0)){
set req.backend = webserver; #如果请求的主机是... 并且是第一次访问,则去集群
}elseif (req.restarts == 1) { #如果不是第一次访问,就去webserver3
set req.backend = webserver3;
}
if (req.http.host ~ "^(img.|images.)?han.com") {
set req.backend = webserver4;
}
if (req.request != "GET" && req.request != "HEAD") {
return (pipe); #如果请求方式不是get也不是head,则认为是异常,进入pipe
}
elseif (req.url ~"\.(cgi|php)($|\?)") {
return (pass); #请求以cgi或者php结尾,则认为是动态请求,pass
}
elseif (req.http.Authenticate || req.http.Authorization) {
return (pass); #如果请求时认证或者授权信息,pass,去后端
}
return (lookup); #其他的请求进入lookup,寻找缓存
}
sub vcl_hit {
if (req.request == "PURGE") {
set obj.ttl = 0s;#命中缓存的情况下,如果请求中包含PURGE,设置缓存时间为0,也就是清除缓存
error 200 "Purged."; #返回信息为200 purged
}
if (!obj.cacheable) { #如果不在缓存中,pass
return (pass);
}
if (obj.http.Vary) { #如果是有变化的数据,就取消变化
unset obj.http.Vary;
}
}
sub vcl_miss {
if (req.request == "PURGE") {
error 404 "Not in cache.";
}
}
sub vcl_hash {
set req.hash += req.url;
if (req.http.host) {
set req.hash += req.http.host;
}else{
set req.hash += server.ip;
}
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|jpeg|png|gif|rar|zip|gz|tgz|bz2|mp3)$") {
}else { #如果是类似的静态数据的编码格式,就进行hash处理,作为缓存
set req.hash += req.http.Accept-Encoding;
}
}
return (hash); #其他的直接hash
}
sub vcl_fetch {
if (!beresp.cacheable) {
return (pass);
}
if (beresp.http.Set-Cookie) {
return (pass);
}
if (beresp.status == 500 || beresp.status == 501|| beresp.status == 502 || beresp.status == 503 || beresp.status == 504 || beresp.status == 404) {
return (restart);
}
if (beresp.http.Pragma ~ "no-cache" || beresp.http.Cache-Control ~"no-cache" || beresp.http.Cache-Control ~ "private") {
return (pass); #http头部有哪些数据的时候就不缓存,Pragma和Cache-Control是头部的两个字段
}
if (req.request == "GET" && req.url ~ "\.(css|js|html|htm)$") {
set beresp.ttl = 300s;
}
if (req.request == "GET" && req.url ~ "\.(gif|jpg|jpeg|bmp|ico|png)$") {
set beresp.ttl = 3600s;
}
if (req.request == "GET" && req.url ~ "\.(mp3|mp4|avi|wav|rmvb)$") {
set beresp.ttl = 10d;
}
return (deliver);
}
sub vcl_deliver {
if (obj.hits >0) {
set resp.http.X-Cache = "HIT www.han.com";
}else {
set resp.http.X-Cache = "MISS www.han.com";
}
return (deliver);
}
varnish检测语法
# varnishd -C -f /usr/local/varnish/default.vcl
varnish启动
# varnishd -f /usr/local/varnish/default.vcl -a 0.0.0.0:80
四台web主机启动服务并清空防火墙,静待测试
在webserver1上进行测试,结果如下
[root@localhost ~]# curl -I http://192.168.1.136
HTTP/1.1 200 OK
Server: Apache/2.4.6 (CentOS)
Last-Modified: Thu, 11 Apr 2019 12:18:14 GMT
ETag: "4-586402e3389fc"
Content-Type: text/html; charset=UTF-8
Content-Length: 4
Date: Fri, 12 Apr 2019 11:10:08 GMT
X-Varnish: 1414415100
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: MISS www.han.com
[root@localhost ~]# curl -I http://192.168.1.136
HTTP/1.1 200 OK
Server: Apache/2.4.6 (CentOS)
Last-Modified: Thu, 11 Apr 2019 12:18:14 GMT
ETag: "4-586402e3389fc"
Content-Type: text/html; charset=UTF-8
Content-Length: 4
Date: Fri, 12 Apr 2019 11:10:40 GMT
X-Varnish: 1414415101 1414415100
Age: 33
Via: 1.1 varnish
Connection: keep-alive
X-Cache: HIT www.han.com
[root@localhost ~]# curl -X PURGE http://192.168.1.136
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>200 Purged.</title>
</head>
<body>
<h1>Error 200 Purged.</h1>
<p>Purged.</p>
<h3>Guru Meditation:</h3>
<p>XID: 1414415102</p>
<hr>
<address>
<a href="http://www.varnish-cache.org/">Varnish cache server</a>
</address>
</body>
</html>
5. varnish多主机实例(4版本varnish)
varnish配置文件如下(注释版)
vcl 4.0;
import directors; #加载集群负载均衡模块
import std; #加载std模块
#创建名为 backend_healthcheck 的健康检查策略
probe backend_healthcheck {
.url="/";
.interval = 5s;
.timeout = 1s;
.window = 5;
.threshold = 3;
}
#定义后端服务器
backend web_app_01 {
.host = "192.168.1.111";
.port = "80";
.first_byte_timeout = 9s; #等待从后端返回的第一个字节时间
.connect_timeout = 3s;
.between_bytes_timeout = 1s; #每接收一个字节之间等待时间
.probe = backend_healthcheck;
}
backend web_app_02 {
.host = "192.168.1.112";
.port = "80";
.first_byte_timeout = 9s;
.connect_timeout = 3s;
.between_bytes_timeout = 1s;
.probe = backend_healthcheck;
}
#定义允许清理缓存的ip
acl purgers {
"127.0.0.1";
"localhost";
"192.168.1.0/24";
}
#vcl_init 初始化子程序创建,后端主机组
sub vcl_init {
new web = directors.round_robin();
web.add_backend(web_app_01);
web.add_backend(web_app_02);
}
#请求入口,用于接收和处理请求。这里一般用作路由处理,判断是否读取缓存和指定该请求使用哪个后端
sub vcl_recv {
#将请求指定使用 web 后端集群 .在集群名后加上 .backend()
set req.backend_hint = web.backend();
# 匹配清理缓存的请求
if (req.method == "PURGE") {
if (!client.ip ~ purgers) {
return (synth(405, "Not Allowed."));
}
#是的话就执行清理
return (purge);
}
#如果不是正常请求,就直接穿透
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "PATCH" &&
req.method != "DELETE") {
return (pipe);
}
#如果不是GET和HEAD就跳到pass
if (req.method != "GET" &&
req.method != "HEAD") {
return (pass);
}
#具有身份认证的跳转到pass
if (req.http.Authorization) {
return (pass);
}
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "gzip") { #如果在请求头的Accept-Encoding字段可以匹配到gzip
set req.http.Accept-Encoding = "gzip"; #说明客户端可以接收的压缩格式为gzip,那么就把请求头的该字段直接设置为gzip
}
elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
}
else {
unset req.http.Accept-Encoding;
}
}
if (req.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
unset req.http.cookie; #如果是类似的静态文件,就去除cookie
return (hash);
}
#把真是客户端ip传递给后端服务器 后端服务器日志使用X-Forwarded-For来接收
if (req.restarts == 0) { #如果第一次访问
if (req.http.X-Forwarded-For) { #如果头部有x-Forwarded-For,就把这个字段设置为该字段+,+client.ip
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip; #如果没有这个字段,直接把这个字段设置成客户端ip
}
}
return (hash);
}
# hash事件(缓存事件)
sub vcl_hash {
hash_data(req.url); #默认hash请求的url
if (req.http.host) { #如果有请求的servername,那么将其hash
hash_data(req.http.host);
} else { #如果没有请求的servername,将服务器ip进行hash
hash_data(server.ip);
}
return (lookup);
}
#缓存命中时间
sub vcl_hit {
if (req.method == "PURGE") {
return (synth(200, "Purged."));
}
return (deliver);
}
#缓存未命中事件
sub vcl_miss {
if (req.method == "PURGE") {
return (synth(404, "Purged."));
}
return (fetch);
}
#返回给用户的前一个事件 通常用户添加或删除header头部
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
set resp.http.X-Cache-Hits = obj.hits;
} else {
set resp.http.X-Cache = "MISS";
}
#取消显示php框架版本的header头
unset resp.http.X-Powered-By;
#取消显示 web 软件版本、Via(来自 varnish)等 header 头 为了安全
unset resp.http.Server;
unset resp.http.X-Drupal-Cache;
unset resp.http.Via;
unset resp.http.Link;
unset resp.http.X-Varnish;
#显示请求经理restarts事件的次数
set resp.http.xx_restarts_count = req.restarts;
#显示该资源缓存的时间单位秒
set resp.http.xx_Age = resp.http.Age;
#显示该资源命中的次数
set resp.http.hit_count = obj.hits;
#取消显示 Age 为了不和 CDN 冲突
unset resp.http.Age;
#返回给用户
return (deliver);
}
#pass事件
sub vcl_pass {
return (fetch);
}
#处理对后端返回结果的时间(设置缓存、移除cookie信息,设置header头等)在fetch事件后会自动调用
sub vcl_backend_response {
#开启 grace 模式 表示当后端全挂掉后 即使缓存资源已过期(超过缓存时间) 也会把该资源返回给用户 资源最大有效时间为5分钟
set beresp.grace = 5m;
#后端返回如下错误状态码 则不缓存
if (beresp.status == 499 || beresp.status == 404 || beresp.status == 502) {
set beresp.uncacheable = true;
}
#如果请求php或jsp则不缓存
if (bereq.url ~ "\.(php|jsp)(\?|$)") {
set beresp.uncacheable = true;
} else { //自定义缓存文件的缓存时长,即 TTL 值
if (bereq.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico)($|\?)") {
set beresp.ttl = 15m;
unset beresp.http.Set-Cookie;
} elseif (bereq.url ~ "\.(gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
set beresp.ttl = 30m;
unset beresp.http.Set-Cookie;
} else {
set beresp.ttl = 10m;
unset beresp.http.Set-Cookie;
}
}
#返回给用户
return (deliver);
}
sub vcl_purge {
return (synth(200,"success"));
}
sub vcl_backend_error {
if (beresp.status == 500 ||
beresp.status == 501 ||
beresp.status == 502 ||
beresp.status == 503 ||
beresp.status == 504){
return (retry);
}
}
sub vcl_fini {
return (ok);
}
varnish检测语法并启动,另外两个web服务器启动并清空防火墙,检测最后效果
[root@localhost ~]# curl -I http://192.168.1.136
HTTP/1.1 200 OK
Date: Sat, 13 Apr 2019 03:20:48 GMT
Last-Modified: Thu, 11 Apr 2019 12:18:14 GMT
ETag: "4-586402e3389fc"
Content-Length: 4
Content-Type: text/html; charset=UTF-8
X-Cache: MISS
xx_restarts_count: 0
xx_Age: 0
hit_count: 0
Accept-Ranges: bytes
Connection: keep-alive
[root@localhost ~]# curl -I http://192.168.1.136
HTTP/1.1 200 OK
Date: Sat, 13 Apr 2019 03:20:48 GMT
Last-Modified: Thu, 11 Apr 2019 12:18:14 GMT
ETag: "4-586402e3389fc"
Content-Length: 4
Content-Type: text/html; charset=UTF-8
X-Cache: HIT
X-Cache-Hits: 1
xx_restarts_count: 0
xx_Age: 5
hit_count: 1
Accept-Ranges: bytes
Connection: keep-alive
第一次MISS 第二次HIT
[root@localhost ~]# curl -I -X PURGE http://192.168.1.136
HTTP/1.1 200 success
Date: Sat, 13 Apr 2019 03:25:54 GMT
Server: Varnish
X-Varnish: 2
Content-Type: text/html; charset=utf-8
Retry-After: 5
Content-Length: 239
Accept-Ranges: bytes
Connection: keep-alive
varnish配置文件(纯净版)
vcl 4.0;
import directors;
import std;
probe backend_healthcheck {
.url="/";
.interval = 5s;
.timeout = 1s;
.window = 5;
.threshold = 3;
}
backend web_app_01 {
.host = "192.168.1.111";
.port = "80";
.first_byte_timeout = 9s;
.connect_timeout = 3s;
.between_bytes_timeout = 1s;
.probe = backend_healthcheck;
}
backend web_app_02 {
.host = "192.168.1.112";
.port = "80";
.first_byte_timeout = 9s;
.connect_timeout = 3s;
.between_bytes_timeout = 1s;
.probe = backend_healthcheck;
}
acl purgers {
"127.0.0.1";
"localhost";
"192.168.1.0/24";
}
sub vcl_init {
new web = directors.round_robin();
web.add_backend(web_app_01);
web.add_backend(web_app_02);
}
sub vcl_recv {
set req.backend_hint = web.backend();
if (req.method == "PURGE") {
if (!client.ip ~ purgers) {
return (synth(405, "Not Allowed."));
}
return (purge);
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "PATCH" &&
req.method != "DELETE") {
return (pipe);
}
if (req.method != "GET" &&
req.method != "HEAD") {
return (pass);
}
if (req.http.Authorization) {
return (pass);
}
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
}
elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
}
else {
unset req.http.Accept-Encoding;
}
}
if (req.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
unset req.http.cookie;
return (hash);
}
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;
}
}
return (hash);
}
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
return (lookup);
}
sub vcl_hit {
if (req.method == "PURGE") {
return (synth(200, "Purged."));
}
return (deliver);
}
sub vcl_miss {
if (req.method == "PURGE") {
return (synth(404, "Purged."));
}
return (fetch);
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
set resp.http.X-Cache-Hits = obj.hits;
} else {
set resp.http.X-Cache = "MISS";
}
unset resp.http.X-Powered-By;
unset resp.http.Server;
unset resp.http.X-Drupal-Cache;
unset resp.http.Via;
unset resp.http.Link;
unset resp.http.X-Varnish;
set resp.http.xx_restarts_count = req.restarts;
set resp.http.xx_Age = resp.http.Age;
set resp.http.hit_count = obj.hits;
#取消显示 Age 为了不和 CDN 冲突
unset resp.http.Age;
return (deliver);
}
sub vcl_pass {
return (fetch);
}
sub vcl_backend_response {
set beresp.grace = 5m;
if (beresp.status == 499 || beresp.status == 404 || beresp.status == 502) {
set beresp.uncacheable = true;
}
if (bereq.url ~ "\.(php|jsp)(\?|$)") {
set beresp.uncacheable = true;
} else {
if (bereq.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico)($|\?)") {
set beresp.ttl = 15m;
unset beresp.http.Set-Cookie;
} elseif (bereq.url ~ "\.(gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
set beresp.ttl = 30m;
unset beresp.http.Set-Cookie;
} else {
set beresp.ttl = 10m;
unset beresp.http.Set-Cookie;
}
}
#返回给用户
return (deliver);
}
sub vcl_purge {
return (synth(200,"success"));
}
sub vcl_backend_error {
if (beresp.status == 500 ||
beresp.status == 501 ||
beresp.status == 502 ||
beresp.status == 503 ||
beresp.status == 504){
return (retry);
}
}
sub vcl_fini {
return (ok);
}