Varnish Cache
Varnish 是一款开源的HTTP加速器和反向代理服务器,它的主要特点有:
(1)是基于内存缓存,重启后数据将消失。
(2)利用虚拟内存方式,io性能好。
(3)支持设置0~60秒内的精确缓存时间。
(4)VCL配置管理比较灵活。
(5)32位机器上缓存文件大小为最大2G。
(6)具有强大的管理功能,例如top,stat,admin,list等。
(7)状态机设计巧妙,结构清晰。
(8)利用二叉堆管理缓存文件,达到积极删除目的。
与Squid服务器相比,Varnish的优点包括:
1、稳定性很高。两者在完成相同负荷的工作时,Squid服务器发生故障的几率要高于Varnish,因为Squid需要经常重启。
2、访问速度更快。采用了“Visual Page Cache”技术,所有缓存数据都直接从内存读取;而Squid是从硬盘读取缓存数据,因此Varnish在访问速度方面会更快。
3、支持更多的并发连接。因为Varnish的TCP连接释放要比Squid快,所以在高并发连接情况下可以支持更多的TCP连接。
4、可以通过管理端口,使用正则表达式批量清除部分缓存,而Squid做不到。
缺点也有:
1、在高并发状态下CPU、I/O和内存等资源开销都高于Squid。
2、进程一旦挂起、崩溃或者重启,缓存数据都会从内存中完全释放,此时所有请求都会被发送到后端服务器,在高并发情况下,这会给后端服务器造成很大压力。
varnish 安装:
wget http://repo.varnish-cache.org/source/varnish-4.0.0.tar.gz
tar -zxvf varnish-4.0.0.tar.gz
cd varnish-4.0.0
./autogen.sh
./configure --prefix=/usr/local/varnish
varnish 运行:
varnishd -f default.vcl -s malloc,1024m -T 127.0.0.1:200 -a 0.0.0.0:80
其中,-f 指定VCL 配置文件,-s 指定内存分配类型和大小,-T 指定文本管理接口, -a 指定服务监听IP和端口;
VCL(varnish configuration language)是varnish配置语言,在执行VCL时,varnish会把其转换成二进制代码。
VCL语法特点
- 块是由花括号分隔,语句以分号结束,使用 ‘#‘ 添加注释;
- VCL支持以下运算符:=(赋值),==(对比),~(匹配),!(否定),&&(逻辑与),||(逻辑或);
- 在VCL中,有三个重要的数据结构req(client request)、beresp(backend response)和obj(cache object);
- 两个字符串的连接,它们之间没有任何运算符;
- VCL没有用户自定义的变量,但可以使用set关键字设置变量值,例如set beresp.ttl=5d;
- 使用set关键字设置http头,使用unset关键字移除http头,例如
set req.http.User-Agent = "unknown"; unset req.http.Range;
- VCL有if/else的条件语句,但没有循环语句;
- 支持include,加载配置文件,例如
include "foo.vcl";
- 支持import,加载模块,例如
-
import std; sub vcl_recv { std.log("foo"); }
定义backend
probe healthcheck { .url = "/test.jpg"; # 定义健康检查的页面 .interval = 6s; # 探测请求的发送周期,默认为5s .timeout = 0.3 s; # 每次探测请求的过期时长,默认为2s .window = 8; # 设定在判定后端主机健康状态时基于最近多少次的探测进行,默认为8 .threshold = 3; # 在.window中指定的次数中,至少有多少次是成功的才判定后端主机正健康运行,默认为3 .initial = 3; # Varnish启动时对后端主机至少需要多少次的成功探测,默认同.threshold }
backend default { .host = "127.0.0.1"; .port = "80"; .probe = healthcheck; .host_header = "www.example.com"; .connect_timeout = 60s; .first_byte_timeout = 60s; .between_bytes_timeout = 60s; .max_connections = 800; }
其中,host选项是必须显式赋值的,其它选项都可选;
VCL可以把多个backends聚合成一个组(director),当组里有一个backend挂掉后,可以把流量导入其他健康的backend;
director可以采用不同的算法选择backend:
- random,根据设置的权值(.weight参数)来选择backend;
- round-robin,轮询;
- client,根据client.identity来选择backend,可以设置client.identity的值为session cookie来标识backend;
director b2 random { .retries = 5; { // We can refer to named backends .backend = b1; .weight = 7; } { // Or define them inline .backend = { .host = "fs2"; } .weight = 3; } }
定义ACL(Access Control List)
ACL用于定义Varnish的访问控制机制。
acl localnetwork { "localhost"; # myself "192.0.2.0"/24; # and everyone on the local network ! "192.0.2.23"; # except for the dial-in router }
if (client.ip ~ localnetwork) { return (pipe); }
VCL 代码流程
VCL内置函数
- vcl_recv
用于接收和处理请求,当请求到达并成功接收后被调用,vcl_recv中的数据结构主要是req。 - vcl_fetch
获取backend响应后调用该方法,通过判断获取的内容来决定是将内容放入缓存,还是直接返回给客户端,vcl_fetch的主要数据结构是beresp; - vcl_pipe
将请求直接传递至backend; - vcl_pass
将请求直接传递至backend,并将backend的响应发送给客户端,但不进行任何缓存,在当前连接下每次都返回最新的内容; - vcl_hash
- vcl_deliver
将在缓存中找到的请求的内容发送给客户端前调用此方法; - vcl_hit
执行lookup指令后,在缓存中找到请求的内容将自动调用该函数; - vcl_miss
执行lookup指令后,在缓存中没有找到请求的内容将自动调用该函数,此函数可用于判断是否需要从backend获取内容; - vcl_error
出现错误时调用此函数;
VCL 动作(Actions)
- pass,把请求控制权交给vcl_pass函数;
- lookup,在缓存中查找被请求的对象,并根据查找的结果把控制权交给函数vcl_hit或vcl_miss;
- pipe,把请求控制权交给vcl_pipe函数;
- deliver,将在缓存中找到的内容发送给客户端,并把控制权交给函数vcl_deliver;
- esi,ESI-process,
- error code [reason],返回code给客户端,并放弃处理该请求;
VCL公共变量
请求到达varnish时:
req.backend 指定对应的后端主机
server.ip 表示服务器 IP
client.ip 表示客户端 IP
req.quest 只是请求的类型,例如 GET、HEAD 等
req.url 指定请求的地址
req.proto 表示客户端发起请求的 HTTP 协议版本
req.http.header 表示对应请求中的 HTTP 头部信息
req.restarts 表示重启次数,默认最大值为 4
varnish向backend发送请求时:
beresp.requset 指定请求类型,例如 GET、HEAD 等
beresp.url 表示请求地址
beresp.proto 表示客户端发起请求的 HTTP 协议版本
beresp.http.header 表示对应请求中 HTTP 头部信息
beresp.ttl 表示缓存的生存周期,cache 保留时间(s)
backend获取内容时:
obj.status 返回内容的请求状态码,例如 200、302、504 等
obj.cacheable 返回的内容是否可以缓存
obj.valid 是否有效的 HTTP 请求
obj.response 返回内容的请求状态信息
obj.proto 返回内容的 HTTP 版本
obj.ttl 返回内容的生存周期,也就是缓存时间,单位秒
obj.lastuse 返回上一次请求到现在的时间间隔,单位秒
响应客户端时:
resp.status 返回给客户端的 HTTP 代码状态
resp.proto 返回给客户端的 HTTP 协议版本
resp.http.header 返回给客户端的 HTTP 头部消息
resp.response 返回给客户端的 HTTP 头部状态