一、varnish简介
varnish是一款高性能且开源的反向代理服务器和HTTP加速器,其采用全新的软件设置体系机构,和现的的硬件体系紧密配合,与传递的squid相比,varnish相比,varnish具有性能更高、速度更快、管理更加方便等诸多优点。
二、 varnish软件体系架构
varnish主要运行两个进程:Management进程和Child进程。
Management进程主要实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等。Management进程会每隔几秒钟探测一下Child进程以判断其是否正常运行,如果在指定的时长内未得到Child进程的回应,Management将会重启此Child进程。
Child进程包含多种类型的线程,常见的如:
Acceptor线程:接收新的连接请求并响应;
Worker线程:child进程会为每个会话启动一个worker线程,因此,在高并发的场景中可能会出现数百个worker线程甚至更多;
Expiry线程:从缓存中清理过期内容;
三、 VCL
VCL是VarnishConfiguration Language,即Varnish配置语言,它提供了一个控制Varnish工作的接口,用于编写缓存策略配置工具,它是一个基于“域”的简单编程语言,“域”在一情况下称为varnish的状态引擎。
下图来自varnish官方,可以情况的看到每一个状态的转换。
3.1 VCL状态引擎
vcl_recv
| 请求被接入,但在其被分析、处理完成之前,决定选择一下步的策略动作 | | 不经由varnish直接将请求发往后端主机的时候调用,请求和内容不做任何改变,如同为客户端和backend建立一个管道 | | 将请求直接发给backend,而不是用缓存中的数据响应客户端 | | 对URL进行hash,可以自定义hash键 | | 在缓存中找到对象时执行的动作 | | 未在缓存中找到对象时执行的动作 | | 从后端主机获取内容,并判断是否缓冲此内容,然后响应给客户端 | | 响应客户端时执行的动作 | vcl_error | 在varnishi上合成错误响应页时,调用此函数 |
四、常用变量
4.1 在任何状态引擎中均可使用
.now:获取当前系统当前时间
.host:后端主机的IP地址或主机名
.port:后端主机的端口号或服务名
4.2 用于处理请求阶段:(vcl_recv,vcl_hash,vcl_pass,vcl_pipe)
client.ip:客户端的IP地址
server.hostname:varnish缓存服务器的主机名
server.ip:varnish缓存服务器的IP地址
server.port:varnish缓存服务器的端口
req.request:请求方法(例:“GET”,“HEAD”)
req.url: 请求的URL
req.proto: HTTP协议版本
req.backend: 用于服务此次请求的后端主机;
req.backend.healthy: 后端主机健康状态;
req.http.HEADER: 引用请求报文中指定的首部; (如:req.http.host)
req.can_gzip:客户端是否能够接受gzip压缩格式的响应内容;
req.restarts: 此请求被重启的次数;(如重定向之后就要进行restart)
4.3 varnish向backend主机发起请求前可用的变量
bereq.request: 请求方法
bereq.url
bereq.proto
bereq.http.HEADER
bereq.connect_timeout: 等待与backend建立连接的超时时长
4.4 backend主机的响应报文到达本主机(varnish)后,将其放置于cache中之前可用的变量
beresp.do_stream: 流式响应;
beresp.do_gzip:是否压缩之后再存入缓存;默认为false
beresp.do_gunzip:当收到后端服务器的压缩报文时,是否解压之后在存入缓存,默认为false
beresp.http.HEADER:
beresp.proto:
beresp.status:响应状态码
beresp.response:响应时的原因短语
beresp.ttl:响应对象剩余的生存时长,单位为second;
beresp.backend.name: 此响应报文来源backend名称;
beresp.backend.ip:此响应报文来源backend IP地址
beresp.backend.port:此响应报文来源backend 端口
beresp.storage
4.5 缓存对象存入cache之后可用的变量
obj.proto:响应时的协议
obj.status:响应时的状态码
obj.response:响应时的原因短语
obj.ttl
obj.hits:缓存对象被用于当着响应时的次数
obj.http.HEADER
4.6 在决定对请求键做hash计算时可用的变量
req.hash:指明把什么当着hash键来查询缓存的键
4.7 在为客户端准备响应报文时可用的变量
resp.proto
resp.status
resp.response
resp.http.HEADER
4.8 快速记忆表
五、安装varnish
5.1 安装varnish
1
2
3
4
5
| varnish-3.0.6-1.el6.x86_64.rpm
varnish-docs-3.0.6-1.el6.x86_64.rpm
varnish-libs-3.0.6-1.el6.x86_64.rpm
rpm -ivh varnish-3.0.6-1.el6.x86_64.rpmvarnish-libs-3.0.6-1.el6.x86_64.rpm varnish-docs-3.0.6-1.el6.x86_64.rpm
#可能会依赖于gcc
|
5.2 查看相应生成的文件
1
2
3
4
5
| /etc/rc.d/init.d/varnishlog #把内存中的日志读入到日志文件
/etc/rc.d/init.d/varnishncsa
/etc/rc.d/init.d/varnish #服务脚本程序
/etc/sysconfig/varnish #varnish配置文件
/etc/varnish/default.vcl #varnish策略文件
|
5.3 /etc/sysconfig/varnish配置文件解释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| [iyunv@node-02 ~]# egrep -v "#|^$"/etc/sysconfig/varnish
NFILES=131072 #所能打开的文件数,会自动调整
MEMLOCK=82000 #内存锁空间
NPROCS="unlimited" #单个用户或进程所能运行的线程数
RELOAD_VCL=1 #是否自动装载缓存策略文件,当使用脚本启动时varish会自动的去装载配置文件
VARNISH_VCL_CONF=/etc/varnish/default.vcl #默认读取的缓存策略文件路径
VARNISH_LISTEN_PORT=6081 #varnish监听的端口,如http的80端口
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 #用于CLI的管理的地址
VARNISH_ADMIN_LISTEN_PORT=6082 #用于CLI的管理的端口
VARNISH_SECRET_FILE=/etc/varnish/secret #密码文件路径
VARNISH_MIN_THREADS=50 #启动的最小线程数
VARNISH_MAX_THREADS=1000 #最多的线程数,即为最大的并发数,不能超出5000,过了就不维定
VARNISH_THREAD_TIMEOUT=120 #空闲线程空闲超时时间
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin #varnish缓存文件,即为缓存类型为文件
VARNISH_STORAGE_SIZE=1G #缓存大小
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" #存储类型
VARNISH_TTL=120
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}"
|
5.4 解析默认的defaults.vcl文件中的vcl_recv中的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| sub vcl_recv {
if (req.restarts == 0) { #重启次数为0,即为第一次访问
if(req.http.x-forwarded-for) { #“req.http.x-forwarded-for”的值为空或为NULL,即为真
set req.http.X-Forwarded-For =
req.http.X-Forwarded-For+ ", " + client.ip; #当“X-Forwarded-For”首部信息中有值,
#就在其值后加上一个“,”和客户端的IP地址;用于在后端服务器记录真实的客户端的IP地址,
}else {
set req.http.X-Forwarded-For = client.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); #不是标准的HTTP请求方法,直接送往后端服务器
}
if (req.request != "GET" && req.request !="HEAD") {
/* We only deal with GET and HEAD by default */
return (pass); #不是“GET”和“HEAD”就不查询缓存,直接送往后端服务器
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
return (pass); #当有认证或cookie时不查询缓存,直接送往后端服务器
}
return (lookup); #任何一个函数在遇到return时就会返回,以下都不在执行;return (lookup)将将由vcl_hash
}
|
5.5 解析默认的defaults.vcl中的vcl_hash
1
2
3
4
5
6
7
8
9
| subvcl_hash {
hash_data(req.url); #获取请求的URL
if (req.http.host) { #如果请求的host不空
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
return (hash); #重新回到hash从hash计算
}
|
5.6 解析默认的defaults.vcl中的vcl_fetch
1
2
3
4
5
6
7
8
9
10
11
12
13
| sub vcl_fetch {
if (beresp.ttl <= 0s || #beresp后端主机的响应报文收到放到缓存之前
beresp.http.Set-Cookie ||
beresp.http.Set-Cookie ||
beresp.http.Vary == "*") {
/*
* Mark as"Hit-For-Pass" for the next 2 minutes
*/
set beresp.ttl = 120 s;
return (hit_for_pass);
}
return (deliver);
}
|
六、varnish实验
6.1 实验拓扑
6.2 实验规划
(1)实现动静分离,Web-01用于解析php,Web-01和Web-02都解析静态的页面
(2)缓存静态内容,只缓存GET和HEAD方法,不缓存带有cookie的内容,URL中包含静态资源的,不缓存动态资源的请求
(3)增加一个HTTP首部信息,显示是否命中
(4)设置清理缓存PURGE
(5)实现后端主机的健康检查
6.3 VCL文件defaluts.vcl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
| [iyunv@node-02 varnish]# egrep -v"#|^$" default.vcl
probe healthcheck { #定义健康检测方法
.url = "/index.html";
.interval = 60s;
.timeout = 0.3 s;
.window = 8;
.threshold = 3;
.initial = 3;
.expected_response = 200;
}
backend PHP { #定义后端服务器
.host = "192.168.9.181";
.port = "80";
.probe = healthcheck;
}
backend web {
.host = "192.168.9.182";
.port = "80";
.probe = healthcheck;
}
director svrs random { #定义服务器组
{.backend = PHP; .weight = 1;}
{.backend = web; .weight = 1;}
}
acl purgers { #定义purger的访问控制列表
"127.0.0.1";
"192.168.9.0"/24;
}
subvcl_recv {
if (req.restarts == 0) {
if (req.http.x-forwarded-for) {
setreq.http.X-Forwarded-For =
req.http.X-Forwarded-For + ", "+ client.ip;
} else {
setreq.http.X-Forwarded-For = client.ip;
}
}
#如果是PURGE方法,且不是ACL中的,直接返回错误,如果是ACL中的将继续
if (req.request == "PURGE") {
if (!client.ip ~ purgers) {
error 405 "Method not allowd";
}
return(lookup);
}
#非指定的HTTP请求访求,将直接访问后端服务器
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "PURGE" &&
req.request !="DELETE") {
return (pipe);
}
#如果不是GET、HEAD方法,就不查缓存
if (req.request != "GET" && req.request !="HEAD") {
return (pass);
}
#如果是认证和cookie则不查缓存
if (req.http.Authorization || req.http.Cookie) {
return (pass);
}
#如果访问php页面,就不查缓存,将会直接指向后端服务器处理请求
if (req.url ~ "\.php($|\?)") {
setreq.backend = PHP;
return(pass);
}
#指定后端响应的服务器组
set req.backend = svrs;
return (lookup);
}
subvcl_pass {
#如果PURGE被送至此次将会被pass
if (req.request == "PURGE") {
error502 "PURGE on a passed object";
}
return (pass);
}
subvcl_hit {
#purge函数就是移除这个obj对应的所有变量缓存
if (req.request == "PURGE") {
purge;
error200 "Purged";
}
return (deliver);
}
subvcl_miss {
#直接通过vcl_error返回客户端
if (req.request == "PURGE") {
purge;
error404 "Not in cache";
}
return (fetch);
}
sub vcl_fetch{
#如果响应的ttl小于0秒或者使用了cookie或Very,则不缓存直接返回给客户端
if (beresp.ttl <= 0s ||
beresp.http.Set-Cookie ||
beresp.http.Vary == "*") {
set beresp.ttl = 120 s;
return (hit_for_pass);
}
#对于GET方法与请求的不同静态资源使用不同的缓存时长
if (req.request == "GET") {
if(req.url ~ "\.(css|js|html|htm)") {
setberesp.ttl = 10m;
}
elseif(req.url ~ "\.(gif|jpg|jpeg|png)") {
setberesp.ttl = 30m;
}
elseif(req.url ~ "\.ico") {
setberesp.ttl = 30d;
}
}
return (deliver);
}
subvcl_deliver {
#返回客户端之前,添加一个HTTP首部信息
if (obj.hits>0) {
setresp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
return (deliver);
}
|
6.4 修改/etc/sysconfig/varnish文件
1
2
3
| VARNISH_LISTEN_PORT=80
VARNISH_MEM_SIZE=32M
VARNISH_STORAGE="malloc,${VARNISH_MEM_SIZE}"
|
6.5 启动varnish
1
2
3
4
5
| [iyunv@node-02 ~]# service varnish start
Starting Varnish Cache: [ OK ]
[iyunv@node-02 ~]# ss -tanp |grep"80"
LISTEN 0 128 :::80 :::* users:(("varnishd",4134,8))
LISTEN 0 128 *:80 *:* users:(("varnishd",4134,7))
|
6.6 Web服务页面显示信息
Web服务器的配置域名是www.c.org,需要配置DNS服务器或更改hosts文件。
1
2
3
4
5
6
| [iyunv@node-01 html]# cat index.htmlindex.php
<h1>Html Static Web-01</h1>
<h1>Real Server node-01</h1>
[iyunv@node-02 html]# cat index.htmlindex.php
<h1>Html Static Web-02</h1>
<h1>Real Server node-02</h1>
|
6.7 访问静态页面
第一次访问时未命令中缓存,因为缓存中根据就没有缓存到有内容。
当再次刷新浏览器是查看响应报文首部信息已经是“HIT”
6.8 访问动态内容
当访问后缀是php页面时,varnish不会缓存内容,所以不断的刷新都会是“MISS”
6.9 purge清理缓存
当更新了后端的Web服务器的内容时,就需要清理一些缓存信息,让用户访问的页面内容是最新的,使用curl命令通过-X选项传递一个HTTP首部信息。需要在hosts文件中添加一条www.c.org的解析,因为varnish缓存是根据用户请求的URL作为键的,当你使用IP地址又是一个新的键了,所以是无法清理缓存中的信息。
curl -I -X PURGE http://varniship/path/to/someurl 使用crul访问www.c.org/index.html页面,查看请求的响应首部信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| [iyunv@node-02 ~]# curl -I www.c.org/index.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 13 Jun 2015 05:41:09GMT
ETag: "120632-1c-5185fabf370f0"
Content-Type: text/html; charset=UTF-8
Content-Length: 28
Accept-Ranges: bytes
Date: Sun, 14 Jun 2015 12:09:40 GMT
X-Varnish: 1227901809
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: MISS #第一次请求未命中
[iyunv@node-02 ~]# curl -I www.c.org/index.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 13 Jun 2015 05:41:09GMT
ETag: "120632-1c-5185fabf370f0"
Content-Type: text/html; charset=UTF-8
Content-Length: 28
Accept-Ranges: bytes
Date: Sun, 14 Jun 2015 12:09:43 GMT
X-Varnish: 1227901810 1227901809
Age: 2
Via: 1.1 varnish
Connection: keep-alive
X-Cache: HIT #再次请求命中
|
清理缓存index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| [iyunv@node-02 ~]# curl -I -X PURGE www.c.org/index.html
HTTP/1.1 200 Purged #第一次清理成功
Server: Varnish
Content-Type: text/html; charset=utf-8
Retry-After: 5
Content-Length: 380
Accept-Ranges: bytes
Date: Sun, 14 Jun 2015 12:14:04 GMT
X-Varnish: 1227901815
Age: 0
Via: 1.1 varnish
Connection: close
X-Cache: MISS
[iyunv@node-02 ~]# curl -I -X PURGE www.c.org/index.html
HTTP/1.1 404 Not in cache #第二次清理告知没有缓存
Server: Varnish
Content-Type: text/html; charset=utf-8
Retry-After: 5
Content-Length: 398
Accept-Ranges: bytes
Date: Sun, 14 Jun 2015 12:14:07 GMT
X-Varnish: 1227901816
Age: 0
Via: 1.1 varnish
Connection: close
X-Cache: MISS
|
小结:
varnish是一个非常优秀强大的缓存服务器,它已经在大型的生产环境中得到了广泛的运用,已经经得起考验,不同的业务要根据不同的业务进行分析,一个好的缓存服务器能命中80%的内容,这样对后端服务器的访问的压力是有多么的重要是你可以想一想的,现在的互联网产业是严重的依赖于缓存服务器。
|