Varnish 是一款高性能且开源的反向代理服务器和 HTTP 加速器,其采用全新的软件体系机构,和现在的硬件体系紧密配合,与传统的 squid 相比,varnish 具有性能更高、速度更快、管理更加方便等诸多优点。
什么是反向代理?
反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
在网络上常见的代理服务器有三种:
1、标准的代理缓冲服务器
一个标准的代理缓冲服务被用于缓存静态的网页(例如:html文件和图片文件等)到本地网络上的一台主机上---即代理服务器(传统代理)
优点:当被缓存的页面被第二次访问的时候,浏览器将直接从本地代理服务器那里请求数据而不再向原web站点请求数据。这样就节省了宝贵的网络带宽,而且提高了访问速度。
缺点:要想实现这种方式,必须在每一个内部主机的浏览器上明确指明代理服务器的IP地址和端口号。
2、透明代理缓冲服务器
透明代理缓冲服务和标准代理服务器的功能完全相同。但是,代理操作对客户端的浏览器是透明的(即不需指明代理服务器的IP和端口)。透明代理服务器阻断网络通信,并且过滤出访问外部的HTTP(80端口)流量。如果客户端的请求在本地有缓冲则将缓冲的数据直接发给用户,如果在本地没有缓冲则向远程web服务器发出请求,其余操作和标准的代理服务器完全相同。对于Linux操作系统来说,透明代理使用Iptables缓存及加速-03单-高性能缓存服务器Varnish.txt[2016/8/19 15:28:24]或者Ipchains实现。因为不需要对浏览器作任何设置,所以,透明代理对于ISP(Internet服务器提供商)特别有用。
3、反向代理缓冲服务器
反向代理是和前两种代理完全不同的一种代理服务。使用它可以降低原始WEB服务器的负载。反向代理服务器承担了对原始WEB服务器的静态页面的请求,防止原始服务器过载。它位于本地WEB服务器和Internet之间,处理所有对WEB服务器的请求,阻止了WEB服务器和Internet的直接通信。如果互联网用户请求的页面在代理服务器上有缓冲的话,代理服务器直接将缓冲内容发送给用户。如果没有缓冲则先向WEB服务器发出请求,取回数据,本地缓存后再发送给用户。这种方式通过降低了向WEB服务器的请求数从而降低了WEB服务器的负载。
Varnish 与一般服务器软件类似,分为 master 进程和 child 进程。Master 进程读入存储配置文件,调用合适的存储类型,然后创建 / 读入相应大小的缓存文件,接着 master 初始化管理该存储空间的结构体,然后 fork 并监控 child 进程。Child 进程在主线程的初始化的过程中,将前面打开的存储文件整个 mmap 到内存中,此时创建并初始化空闲结构体,挂到存储管理结构体,以待分配。Child 进程分配若干线程进行工作,主要包括一些管理线程和很多 worker 线程。
接着,开始真正的工作,varnish 的某个负责接收新 HTTP 连接线程开始等待用户,如果有新的 HTTP 连接过来,它总负责接收,然后唤醒某个等待中的线程,并把具体的处理过程交给它。Worker 线程读入 HTTP 请求的 URI,查找已有的 object,如果命中则直接返回并回复用户。如果没有命中,则需要将所请求的内容,从后端服务器中取过来,存到缓存中,然后再回复。
分配缓存的过程是这样的:它根据所读到 object 的大小,创建相应大小的缓存文件。为了读写方便,程序会把每个 object 的大小变为最接近其大小的内存页面倍数。然后从现有的空闲存储结构体中查找,找到最合适的大小的空闲存储块,分配给它。如果空闲块没有用完,就把多余的内存另外组成一个空闲存储块,挂到管理结构体上。如果缓存已满,就根据 LRU 机制,把最旧的 object 释放掉。
释放缓存的过程是这样的:有一个超时线程,检测缓存中所有 object 的生存期,如果超初设定的 TTL(Time To Live)没有被访问,就删除之,并且释放相应的结构体及存储内存。注意释放时会检查该存储内存块前面或后面的空闲内存块,如果前面或后面的空闲内存和该释放内存是连续的,就将它们合并成更大一块内存。
整个文件缓存的管理,没有考虑文件与内存的关系,实际上是将所有的 object 都考虑是在内存中,如果系统内存不足,系统会自动将其换到 swap 空间,而不需要 varnish 程序去控制。
varnish的安装可以根据具体的平台进行,既可以下载现有的rpm包进行安装也可以下载源码包进行编译安装。
示例配置在后面
VCL 简介
VCL(varnish configuration language)是 varnish 配置语言,其用来定义 varnish 的存取策略。VCL 语法比较简单,跟 C 和 Perl 比较相似。主要有以下几点:
块是由花括号分隔,语句以分号结束,使用‘ # ’符号可以添加注释。
VCL 使用指定运算符“=”、比较运算符“==”、逻辑运算符“!,&&,!!”等形式,还支持正则表达式和用“~”进行 ACL 匹配运算。
VCL 没有用户自己定义的变量,你可以在 backend、request 或 object 上设置变量值,采用 set 关键字进行设置。例如 set req.backend = director_employeeui;
两个字符串的连接,他们之间没有任何运算符。代码如清单 5 所示:
清单 5. 字符串连接代码
set req.http.X-hit = " hit" "it";
\”字符在 VCL 里没有特别的含义,这点与其他语言略有不同。
VCL 可以使用 set 关键字设置任何 HTTP 头,可以使用 remove 或是 unset 关键字移除 HTTP 头。
VCL 有 if/else 的判断语句,但是没有循环语句。
VCL backend
声明并初始化一个后端对像,backend 声明代码示例
backend www {
.host = "www.example.com";
.port = "9082";
}
后端对象的使用,backend 的使用代码示例
if (req.http.host ~ "^(www.)?example.com$") {
set req.backend = www;
}
VCL 后端的集合 director
VCL 可以把多个 backends 聚合成一个组,这些组被叫做 director,这样可以增强性能和弹力,当组里一个 backend 挂掉后,可以选择另一个健康的 backend。VCL 有多种 director,不同的 director 采用不同的算法选择 backend,主要有以下几种:
The random director
Random director 会根据所设置的权值(weight)来选择 backend,.retries 参数表示尝试找到一个 backend 的最大次数,.weight 参数表示权值
The round-robin director
Round-robin director 在选择 backend 时,会采用循环的方式依次选择。
The client director
Client director 根据 client.identity 来选择 backend,您可以设置 client.identity 的值为 session cookie 来标识 backend。
backend probes
VCL 可以设置 probe 来检测一个 backend 是否健康,定义一个 backend probes 的示例:
backend www {
.host = "www.example.com";
.port = "9082";
.probe = {
.url = "/test.jpg";// 哪个 url 需要 varnish 请求
.timeout = 1 s;// 等待多长时间超时
.interval = 5s// 检查的时间间隔
.window = 5;// 维持 5 个 sliding window 的结果
.threshold = 3;// 至少有三次 window 是成功的,就宣告 backend 健康
}
}
ACL
ACL 可创建一个客户端的访问控制列表,你可以使用 ACL 控制哪些客户端可以访问,哪些客户端禁止访问。定义 ACL 代码:
Acl local{
"localhost";
"192.0.2.0"/24;
!"192.0.2.23";// 除去该 IP
}
VCL 内置函数
vcl_recv 函数
用于接收和处理请求。当请求到达并成功接收后被调用,通过判断请求的数据来决定如何处理请求。例如如何响应、怎么响应、使用哪个后端服务器等。
此函数一般以如下几个关键字结束。
pass:表示进入 pass 模式,把请求控制权交给 vcl_pass 函数。
pipe:表示进入 pipe 模式,把请求控制权交给 vcl_pipe 函数。
lookup:表示进入 lookup 模式,把请求控制权交给 lookup 指令处理,在缓存中查找被请求的对象,并且根据查找的结果把控制权交给函数 vcl_hit 或函数 vcl_miss。
error code [reason]:表示返回“code”给客户端,并放弃处理该请求。“code”是错误标识,例如 200 和 405 等。“reason”是错误提示信息。
vcl_pipe 函数
此函数在进入 pipe 模式时被调用,用于将请求直接传递至后端主机,在请求和返回的内容没有改变的情况下,将不变的内容返回给客户端,直到这个连接被关闭。
此函数一般以如下几个关键字结束。
error code [reason]。
pipe。
vcl_pass 函数
此函数在进入 pass 模式时被调用,用于将请求直接传递至后端主机。后端主机在应答数据后将应答数据发送给客户端,但不进行任何缓存,在当前连接下每次都返回最新的内容。
此函数一般以如下几个关键字结束。
error code [reason]。
pass。
restart 重新启动流程,增加启动次数,如果重新启动次数高于 max_restarts 发出一个错误警告
vcl_hash
当您想把一个数据添加到 hash 上时,调用此函数。
此函数一般以如下几个关键字结束。
Hash。
vcl_hit 函数
在执行 lookup 指令后,在缓存中找到请求的内容后将自动调用该函数。
此函数一般以如下几个关键字结束。
deliver:表示将找到的内容发送给客户端,并把控制权交给函数 vcl_deliver。
error code [reason] 。
pass。
restart 重新启动流程,增加启动次数,如果重新启动次数高于 max_restarts 发出一个错误警告
vcl_miss 函数
在执行 lookup 指令后,在缓存中没有找到请求的内容时自动调用该方法。此函数可用于判断是否需要从后端服务器获取内容。
此函数一般以如下几个关键字结束。
fetch:表示从后端获取请求的内容,并把控制权交给 vcl_fetch 函数。
error code [reason] 。
pass。
vcl_fetch 函数
在后端主机更新缓存并且获取内容后调用该方法,接着,通过判断获取的内容来决定是将内容放入缓存,还是直接返回给客户端。
此函数一般以如下几个关键字结束。
error code [reason]。
pass。
deliver。
esi。
restart 重新启动流程,增加启动次数,如果重新启动次数高于 max_restarts 发出一个错误警告
vcl_deliver 函数
将在缓存中找到请求的内容发送给客户端前调用此方法。
此函数一般以如下几个关键字结束。
error code [reason]。
deliver。
restart 重新启动流程,增加启动次数,如果重新启动次数高于 max_restarts 发出一个错误警告
vcl_error
出现错误时调用此函数。
此函数一般以如下几个关键字结束。
deliver。
restart。
Varnish 处理 HTTP 请求的过程大致分为如下几个步骤。
Receive 状态(vcl_recv)。也就是请求处理的入口状态,根据 VCL 规则判断该请求应该 pass(vcl_pass)或是 pipe(vcl_pipe),还是进入 lookup(本地查询)。
Lookup 状态。进入该状态后,会在 hash 表中查找数据,若找到,则进入 hit(vcl_hit)状态,否则进入 miss(vcl_miss)状态。
Pass(vcl_pass)状态。在此状态下,会直接进入后端请求,即进入 fetch(vcl_fetch)状态
Fetch(vcl_fetch)状态。在 fetch 状态下,对请求进行后端获取,发送请求,获得数据,并根据设置进行本地存储。
Deliver(vcl_deliver)状态。将获取到的数据发给客户端,然后完成本次请求。
以上为一些基本的概念,接下来我们来配置一下(平台为rhel6.5.x86_64):
vim /etc/varnish/default.vcl
backend web1 {
.host = "172.25.5.10";
.port = "80";
}
配置 varnish 服务端口
VARNISH_LISTEN_PORT=80
为了测试显示更加清晰在vcl中添加如下代码
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT from web1 cache";
}
else {
set resp.http.X-Cache = "MISS from web1 cache";
}
return (deliver);
}
/etc/init.d/varnish restart
本机多次测试缓存命中
首次测试的结果
[root@vm10 ~]# curl 172.25.5.100
HTTP/1.1 200 OK
Server: Nginx (Red Hat)
Last-Modified: Mon, 10 Aug 2017 15:22:19 GMT
ETag: "1c13aa-16-4c7b4135e08a6"Content-Type: text/html; charset=UTF-8
Content-Length: 22
Accept-Ranges: bytes
Date: Fri, 24 Aug 2012 14:30:40 GMT
X-Varnish: 766434032
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: MISS from web1 cache #未命中
再次测试的结果:
[root@vm10 ~]# curl 172.25.5.100
HTTP/1.1 200 OK
Server: Nginx (Red Hat)
Last-Modified: Mon, 10 Aug 2017 15:23:10GMT
ETag: "1c13aa-16-4c7b4135e08a6"
Content-Type: text/html; charset=UTF-8
Content-Length: 22
Accept-Ranges: bytes
Date: Fri, 24 Aug 2017 15:23:10 GMT
X-Varnish: 766467033 766467032
Age: 14
Via: 1.1 varnish
Connection: keep-alive
X-Cache: HIT from westos cache #命中
通过 varnishadm 手动清除缓存
# varnishadm ban.url .*$
#清除所有
# varnishadm ban.url /index.html
#清除 index.html 页面缓存
# varnishadm ban.url /admin/$
#清除 admin 目录缓存
backend web1 {
.host = "172.25.5.10";
.port = "80";
}
backend web2 {
.host = "172.25.5.20";
.port = "80";
}
当访问 www.lockey.com 域名时从 web1 上取数据,访问 bbs.lockey.com 域名时到 web2 取数据,访问其他页面报错。
sub vcl_recv {
if (req.http.host ~ "^(www.)?lockey.com") {
set req.http.host = "www.lockey.com";
set req.backend = web1;
return (pass); #为了测试方便,不进行缓存。
} elsif (req.http.host ~ "^bbs.lockey.com") {
set req.backend = web2;
} else {error 404 "lockeycache";
}
}
定义负载均衡
director lb round-robin {
{ .backend = web1;}
{.backend = web2;}
}
上面采用rr算法,还有其他算法如hash,fallback,random等
首先系统要有 php 支持
unzip bansys.zip -d /var/www/html
vim /var/www/html/bansys/config.php #只保留如下设置,其余注释掉
array('172.25.5.100'),
'port' => '6082',
);
#varnish 群组定义
#对主机列表进行绑定
$VAR_CLUSTER = array(
'www.lockey.com' => $var_group1,
);
#varnish 版本2.x 和 3.x 推送命令不一样
$VAR_VERSION = "3";
?>
bansys 有两种工作模式,分别是:telnet 和 http 模式。
telnet 模式需要关闭 varnish 服务管理端口的验证,注释掉/etc/sysconfig/varnish 文件中的“-S $
{VARNISH_SECRET_FILE}”这行,重启 varnish 服务即可。
如果是http模式需要做如下设置:
vim /etc/varnish/default.vcl
acl lockey{
#设置访问控制
"127.0.0.1";
"172.25.5.0"/24;
}
sub vcl_recv {
if (req.request == "BAN") {
if (!client.ip ~ lockey) {
error 405 "Not allowed.";
}
ban("req.url ~ " + req.url);
error 200 "ban added";
}
}