Varnish是一款高性能的开源HTTP加速器,挪威最大的在线报纸 Verdens Gang 使用3台Varnish代替了原来的12台Squid,性能比以前更好。作者Poul-Henning Kamp是FreeBSD的内核开发者之一,他认为现在的计算机比起1975年已经复杂许多。在1975年时,储存媒介只有两种:内存与硬盘。但现在计算机系统的内存除了主存外,还包括了CPU内的L1、L2,甚至有L3快取。硬盘上也有自己的快取装置,因此Squid Cache自行处理物件替换的架构不可能得知这些情况而做到最佳化,但操作系统可以得知这些情况,所以这部份的工作应该交给操作系统处理,这就是 Varnish cache设计架构。
Varnish项目是2006年发布的第一个版本0.9.距今已经有十多年了,此文档之前也提过varnish还不稳定,那是2007年时候编写的,经过varnish开发团队和网友们的辛苦耕耘,现在的varnish已经很健壮。很多门户网站已经部署了varnish,并且反应都很好,甚至反应比squid还稳定,且效率更高,资源占用更少。相信在反向代理,web加速方面,varnish已经有足够能力代替squid。
Varnish与一般服务器软件类似,分为master(management)进程和child(worker,主要做cache的工作)进程。master进程读入命令,进行一些初始化,然后fork并监控child进程。child进程分配若干线程进行工作,主要包括一些管理线程和很多woker线程。
针对文件缓存部分,master读入存储配置(-s file[,path[,size[,granularity]]] ),调用合适的存储类型,然后创建/读入相应大小的缓存大文件。接着,master初始化管理该存储空间的结构体。这些变量都是全局变量,在fork以后会被child进程所继承(包括文件描述符)。
在child进程主线程初始化过程中,将前面打开的存储大文件整个mmap到内存中(如果超出系统的虚拟内存,mmap失败,进程会减少原来的配置mmap大小,然后继续mmap),此时创建并初始化空闲存储结构体,挂到存储管理结构体,以待分配。
接着,真正的工作开始,Varnish的某个负责接受新HTTP连接的线程开始等待用户,如果有新的HTTP连接过来,它总负责接收,然后叫醒某个等待中的线程,并把具体的处理过程交给它。Worker线程读入HTTP请求的URI,查找已有的object,如果命中则直接返回并回复用户。如果没有命中,则需要将所请求的内容,从后端服务器中取过来,存到缓存中,然后再回复。
分配缓存的过程是这样的:它根据所读到object的大小,创建相应大小的缓存文件。为了读写方便,程序会把每个object的大小变为最接近其大小的内存页面倍数。然后从现有的空闲存储结构体中查找,找到最合适的大小的空闲存储块,分配给它。如果空闲块没有用完,就把多余的内存另外组成一个空闲存储块,挂到管理结构体上。如果缓存已满,就根据LRU机制,把最旧的object释放掉。
释放缓存的过程是这样的:有一个超时线程,检测缓存中所有object的生存期,如果超初设定的TTL(Time To Live)没有被访问,就删除之,并且释放相应的结构体及存储内存。注意释放时会检查该存储内存块前面或后面的空闲内存块,如果前面或后面的空闲内存和该释放内存是连续的,就将它们合并成更大一块内存。
整个文件缓存的管理,没有考虑文件与内存的关系,实际上是将所有的object都考虑是在内存中,如果系统内存不足,系统会自动将其换到swap空间,而不需要varnish程序去控制。
Varnish特点与Squid的对比
Varnish特点:
基于内存缓存,重启后数据将消失。
利用虚拟内存方式,I/O性能好。
支持设置0~60秒内的精确缓存时间。
VCL(全称varnish config language,这是Varnish自己领域的特定语言)配置管理比较灵活。
32位机器上缓存文件大小为最大2G。
具有强大的管理功能,例如top,stat,admin,list等。
状态机设计巧妙,结构清晰。
利用二叉堆管理缓存文件,达到积极删除目的。
Varnish与Squid的对比:
相同点:
都是一个反向代理服务器;
都是开源软件;
Varnish相较于Squid的优点:
Varnish的稳定性很高,两者在完成相同负荷的工作时,Squid服务器发生故障的几率要高于Varnish,因为使用Squid要经常重启;
Varnish访问速度更快,Varnish采用了“Visual Page Cache”技术,所有缓存数据都直接从内存读取,而Squid是从硬盘读取,因此Varnish在访问速度方面会更快;
Varnish可以支持更多的并发连接,因为Varnish的TCP连接释放要比Squid快,所以在高并发连接情况下可以支持更多TCP连接;
Varnish可以通过管理端口,使用正则表达式批量的清除部分缓存,而Squid是做不到的;
Squid属于是单进程使用单核CPU,但Varnish是通过fork形式打开多进程来做处理,所以是合理的使用所有核来处理相应的请求;
Varnish相较于Squid的缺点:
Varnish在高并发状态下CPU、I/O和内存等资源开销都高于Squid;
Varnish进程一旦Hang(挂起)、Crash(崩溃)或者重启,缓存数据都会从内存中完全释放,此时所有请求都会发送到后端服务器,在高并发情况下,会给后端服务器造成很大压力;
在Varnish使用中如果单个url的请求通过HA/F5(负载均衡)每次请求不同的varnish服务器中,被请求varnish服务器都会被穿透到后端,而且同样的请求会在多台服务器上缓存,也会造成Varnish的缓存的资源浪费,也会造成性能下降。
实验环境
Web1 192.168.1.3 CentOS 7 Nginx+PHP
Web2 192.168.1.4 CentOS 7 Nginx+PHP
Web3 192.168.1.5 CentOS 7 Nginx+PHP
Varnish官网地址:http://varnish-cache.org/
Yum 安装方式,https://packagecloud.io/varnishcache
curl -s https://packagecloud.io/install/repositories/varnishcache/varnish64/script.rpm.sh | sudo bash
yum -y install varnish
varnishd -V 查看版本信息
/etc/varnish varnish工作目录
/etc/varnish/default.vcl 默认配置各Child/Cache线程的缓存策略
/usr/bin/varnishadm varnish内置的管理工具,端口默认为 6082
/usr/bin/varnishhist Shared Memory Log交互工具
/usr/bin/varnishlog
/usr/bin/varnishncsa
/usr/bin/varnishstat
/usr/bin/varnishtop
/usr/bin/varnishtest 内置测试工具程序
/usr/lib/systemd/system/varnish.service varnish服务启动脚本
/usr/lib/systemd/system/varnishncsa.service 日志持久服务启动脚本
/usr/sbin/varnishreload VCL配置文件重载程序
pass 表示进入pass模式,将请求交给vcl_pass 函数
pipe 表示进入pipe模式,将请求交个vcl_pipe函数
error code[reason] 表示返回'code'给客户端并放弃请求, 'code'是错误标示例如200 405等,'reason'是错误原因
error code[reason] 同vcl_recv
pipe
error code[reason] 同vcl_recv
pass
vcl_hit函数:在执行lookup后,如果在缓存中找到对象,该函数将会被自动调用,该函数有以下几个返回值 =>
deliver 表示找到内容发送给客户端,把控制权交给vcl_deliver函数
error code[reason] 同vcl_recv
pass
fetch 表示从后端获取内容,并把控制权交给vcl_fetch函数
error code[reason] 同vcl_recv
pass
error code[reason] 同vcl_recv
pass
deliver
error code[reason] 同vcl_recv
deliver
discard 表示中缓存中清除该内容
fetch
keep 表示在内容中保留该缓存
discard
Receive状态:请求处理的入口状态,根据VCL规则判断该请求应该在Pass或Pipe,还是进入Lookup(到本地缓存中查询)
Lookup状态:进入此状态后,会在hash表中查询数据,如果找到则进入Hit状态,否则进入Miss状态
Pass状态:在此状态下会进入后端获取内容,既进入Fetch状态
Fetch状态:从后端获取请求,发送请求,获得数据,并存储到本地
Deliver状态:将获取的数据发送给客户端,然后完成本次请求
请求到达后可以使用的内置公用变量:
req.backend => 指定对应的后端主机
server.ip => 表示服务器端IP
client.ip => 表示客户端IP
req.request => 指定请求的类型,如GET POST PUT DELETE
req.url => 知道请求的URL
req.proto => 表示客户端发起请求的HTTP协议版本
req.http.header => 表示请求中的头部信息
req.restarts => 表示请求重启的次数,默认最大值为4
beresp.request => 指定请求的类型,如GET POST
beresp.url => 指定请求的地址
beresp.proto => 指定请求的协议版本号
beresp.http.header => 对应请求的HTTP头信息
beresp.ttl => 表示缓存的周期,单位秒
obj.status => 表示返回的状态码,如200 404 500 等
obj.cacheable => 表示返回的内容是否可以缓存
obj.valid => 表示是否是有效的HTTP应答
obj.response => 表示应答的状态信息
obj.ttl => 表示返回内容的生存周期,单位秒
obj.lastuse => 表示上次请求到现在的间隔,单位秒
resp.status => 表示返回给客户端的状态码
resp.proto => 表示返回给客户端的HTTP协议版本
resp.http.header => 表示返回给客户端的头信息
resp.response => 表示返回给客户端的状态信息
每个后端服务器当前探测的健康状态探测方法通过.probe进行设定,其结果可由req.backend.healthy变量获取,也可通过varnishlog中的Backend_health查看或varnishadm的debug.health查看。
.probe中的探测指令常用的有:
(1) .url:探测后端主机健康状态时请求的URL,默认为“/”;
(2) .request: 探测后端主机健康状态时所请求内容的详细格式,定义后,它会替换.url指定的探测方式;比如:
.request =
"GET /.healthtest.html HTTP/1.1"
"Host: www.myweb.com"
"Connection: close";
(3) .window:设定在判定后端主机健康状态时基于最近多少次的探测进行,默认是8;
(4) .threshold:在.window中指定的次数中,至少有多少次是成功的才判定后端主机正健康运行;默认是3;
(5) .initial:Varnish启动时对后端主机至少需要多少次的成功探测,默认同.threshold;
(6) .expected_response:期望后端主机响应的状态码,默认为200;
(7) .interval:探测请求的发送周期,默认为5秒;
(8) .timeout:每次探测请求的过期时长,默认为2秒;
设置后台健康状态检查
vim /etc/varnish/health_check.vcl
probe chk {
.interval = 5s; 每隔1秒钟尝试一次
.timeout = 3s; 每次探测请求的过期时长
.window = 10; 最多尝试10次,判断采样的样本
.threshold = 8; 如果采样10次,如果有8次是错误的,就认为是失败的,上线也是一样
.request =
"GET /.test.html HTTP/1.1" 测试根目录下的 .test.html ,这个文件必须存在且能够访问
"Host: www.myweb.com"
"Connection: close"
"Accept-Encoding: foo/bar";
}
后端服务器地址池配置
vim /etc/varnish/backends.vcl
import directors;
include "health_check.vcl";
backend web1 {
.host = "192.168.1.3";
.port = "80";
.probe = chk;
}
backend web2 {
.host = "192.168.1.4";
.port = "80";
.probe = chk;
}
backend web3 {
.host = "192.168.1.5";
.port = "80";
.probe = chk;
}
负载均衡池
sub vcl_init {
new web = directors.round_robin(); 以轮询方式调度
web.add_backend(web1);
web.add_backend(web2);
web.add_backend(web3);
}
缓存规则主配置
vim /etc/varnish/default.vcl
vcl 4.1;
import std;
include "backends.vcl";
acl purge_ip { 定义acl访问控制,只允许以下网段或主机执行purgers操作
"127.0.0.1";
"192.168.0.0"/16;
}
sub vcl_recv {
if (req.method == "PURGE"){ 如果请求方法是PURGE,并且客户端IP在上面定义的网段内的就允许执行PURGE操作,否则生成一个错误页面返回给用户
if (!client.ip ~ purge_ip) {
return(synth(405,"Purging not allowed for "+client.ip));
}
return(purge);
}
if (req.http.host ~ "^(www.)?myweb.com") {
set req.http.host = "www.myweb.com";
set req.backend_hint = web.backend();
}
}
sub vcl_purge{
return (synth(200,"success"));
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT cache" + " " + server.ip;
}
else {
set resp.http.X-Cache = "Miss cache";
}
}
sub vcl_pass {
if (req.method == "PURGE") {
return (synth(502, "PURGE on a passed object."));
}
}
为了方便测试,将 varnish 默认启动端口 6081 更改为 80
编辑 vim /usr/lib/systemd/system/varnish.service
[Unit]
Description=Varnish Cache, a high-performance HTTP accelerator
After=network-online.target
[Service]
Type=forking
KillMode=process
Maximum number of open files (for ulimit -n)
LimitNOFILE=131072
Locked shared memory - should suffice to lock the shared memory log
(varnishd -l argument)
Default log size is 80MB vsl + 1M vsm + header -> 82MB
unit is bytes
LimitMEMLOCK=85983232
Enable this to avoid "fork failed" on reload.
TasksMax=infinity
Maximum size of the corefile.
LimitCORE=infinity
ExecStart=/usr/sbin/varnishd -a :6081 -f /etc/varnish/default.vcl -s malloc,256m
ExecStart=/usr/sbin/varnishd -a :80 -f /etc/varnish/default.vcl -s malloc,256m 修改默认监听端口为 :80
ExecReload=/usr/sbin/varnishreload
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl restart varnish
网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。
加入交流群
请使用微信扫一扫!