标签:varnish原理
二、安装varnish
1、安装依赖关系的软件包(注:使用centos在线yum源)
[root@varnish ~]# yum -y install autoconfautomake libedit-devel libtool ncurses-devel pcre-devel pkgconfigpython-docutils python-sphinx
2、安装varnish
Varnish的官方网址为http://varnish-cache.org,可以在这里下载最新版本的软件。
下载地址:https://www.varnish-cache.org/content/varnish-cache-403
注意:Varnish网站有时会被墙。
Git 下载:gitclone https://github.com/varnish/Varnish-Cache /var/tmp/
解压,进入解压目录编译安装:
[root@varnish ~]# tar zxfvarnish-4.0.3.tar.gz
[root@varnish ~]# cdvarnish-4.0.3/
[root@varnishvarnish-4.0.3]# export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
注:
./autogen.sh
如果从Git库下载的安装包时才需要运行,用于生成configure编译文件。
配置:
[root@varnishvarnish-4.0.3]# ./configure
注:不指定安装路径,默认是安装在/usr/local目录下
编译、安装
[root@varnishvarnish-4.0.3]# make && make install
复制 vcl 文件(在编译安装目录下),如果安装目录里没有 default.vcl 文件。
复制到安装目录的/usr/local/var/varnish/目录下(当然并无必需要求在哪个目录,因为正式启动时还得指定这个文件的目录)
[root@varnishvarnish-4.0.3]# cp etc/example.vcl /usr/local/var/varnish/default.vcl
三、varnish实例解析:
varnish 配置基本上是编辑 VCL(Varnish Configuration Language) 文件,varnish有一套自定义 VCL 语法,启动时,会将配置文件编译为C语言,再执行。
varnish 4.0开始,每个VCL文件必须在开始行声明它的版本“vcl 4.0;”
块(子程序)由大括号分隔,语句用分号结束。所有的关键字及预设子程序名都是全小写。
注释:支持 //或 # 多行时还可以使用 /*.. */
1、后端服务器地址池配置及后端服务器健康检查
varnish有"后端"或者"源"服务器的概念。backend server提供给varnish加速的内容。实际上就是给varnish添加可供访问的web服务器,如果有多台web服务器时,可添加多个backend块。
1)后端服务器定义:
命令:backend。这个定义为最基本的反向入口定义,用于varnish连接对应的服务器,如果没有定义或定义错误则用户无法访问正常页面。
语法格式:
backend name{
.attribute = "value";
}
说明:backend 是定义后端关键字,name 是当前后端节点的别名,多个后端节点时,name 名不能重复,否则覆盖。花括号里面定义当前节点相关的属性(键=值)。除默认节点外其它节点定义后必需有调用,否则varnish无法启动。后端是否正常可以通过std.healthy(backend)判断。
支持运算符:
= (赋值运算)
== (相等比较)
~ (匹配,可以使用正则表达式,或访问控制列表)
!~ (不匹配,可以使用正则表达式,或访问控制列表)
! (非)
&& (逻辑与)
|| (逻辑或)
属性列表:
.host="xxx.xxx.xxx.xxx"; //要转向主机(即后端主机)的IP或域名,必填键/值对。
.port="8080"; //主机连接端口号或协议名(HTTP等),默认80
.host_header=‘‘; //请示主机头追加内容
.connect_timeout=1s; //连接后端的超时时间
.first_byte_timeout=5s; //等待从后端返回的第一个字节时间
.between_bytes_timeout=2s; //每接收一个字节之间等待时间
.probe=probe_name; //监控后端主机的状态,指定外部监控name或者内部直接添加
.max_connections=200; //设置最大并发连接数,超过这个数后连接就会失败
例:(下面两个例子结果是一样的,但第二个例子中更适用于集群,可以方便批量修改)
backend web{
.host="192.168.31.83";
.port="80";
.probe={ //直接追加监控块.probe是一个的参数
.url="/";
.timeout=2s;
}
}
或
probe web_probe{ //监控必需定义在前面,否则后端调用找不到监控块。
.url="/";
.timeout=2s;
}
backend web{
.host="192.168.31.83";
.port="80";
.probe=web_probe; //调用外部共用监控块
}
2)监视器定义:
命令:probe 。监控可以循环访问指定的地址,通过响应时间判定服务器是否空闲或正常。这类命令非常适用于集群中某些节点服务器崩溃或负载过重,而禁止访问这台节点服务器。
语法格式:
probe name{
.attribute = "value";
}
说明:probe 是定义监控关键字,name 是当前监控点的别名,多个监控节点时,name 名不能重复,否则覆盖。花括号里面定义当前节点相关的属性(键=值)。
没有必填属性,因为默认值就可以正常执行操作。
属性列表:
.url="/"; //指定监控入口URL地址,默认为"/"
.request=""; //指定监控请求入口地址,比 .url 优先级高。
.expected_response="200"; //请求响应代码,默认是 200
.timeout=2s; //请求超时时间。
.interval=5s; //每次轮询请求间隔时间,默认为 5s 。
.initial=-1; //初始启动时以.window轮询次数中几次良好后续才能使用这个后端服务器节点,默认为 -1 ,则轮询完 .window 所有次数良好判定为正常。
.window=8; //指定多少轮询次数,用于判定服务器正常,默认是 8。
.threshold=3; //必须多少次轮询正常才算该后端节点服务器正常,默认是 3。
例:创建健康监测,定义健康检查名称为backend_healthcheck
probe
backend_healthcheck {
.url = "/";
.timeout = 1s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
在上面的例子中varnish将每5s检测后端,超时设为1s。每个检测将会发送get /的请求。如果5个检测中大于3个是成功,varnish就认为后端是健康的,反之,后端就有问题了。
3)集群负载均衡directors:
varnish可以定义多个后端,也可以将几个后端放在一个后端集群里面已达到负载均衡的目的。
你也可以将几个后端组成一组后端。这个组被叫做Directors。可以提高性能和弹性。
directors是varnish负载均衡模块,使用前必需引入directors模块,directors模块主要包含:round_robin,random,hash,fallback负载均衡模式。
round_robin : 循环依次逐个选择后端服务器。
random :随机选择后端服务器,可设置每个后端权重增加随机率。
hash : 通过散列随机选择对应的后端服务器且保持选择对应关系,下次则直接找对应的后端服务器。
Fallback:后备
注意:random,hash 有权重值设置,用于提高随机率。每个后端最好都配置监控器(后端服务器正常监测)以便directors自动屏蔽不正常后端而不进入均衡列队中。
这些操作需要你载入VMOD(varnish module),然后在vcl_init中调用这个VMOD。
import directors; # load the directors
backend web1 {
.host = "192.168.0.10";
.port = "80";
.probe =backend_healthcheck;
}
backend web2 {
.host = "192.168.0.11";
.port = "80";
.probe = backend_healthcheck;
}
//
初始化处理
sub vcl_init { //调用vcl_init初始化子程序创建后端主机组,即directors
new web_cluster = directors.round_robin();//使用new关键字创建drector对象,使用
round_robin算法
web_cluster.add_backend(web1); //添加后端服务器节点
web_cluster.add_backend(web2);
}
//
开始处理请求
sub vcl_recv { //调用vcl_recv子程序,用于接收和处理请求
set req.backend_hint = web_cluster.backend(); //选取后端
}
说明:
set命令是设置变量
unset命令是删除变量
web_cluster.add_backend( backend , real); 添加后端服务器节点,backend 为后端配置别名,real为权重值,随机率计算公式:100 * (当前权重 / 总权重)。
req.backend_hint是varnish的预定义变量,作用是指定请求后端节点
vcl对象需要使用new关键字创建,所有可创建对象都是内定的,使用前必需import,所有new操作只能在vcl_init子程序中。
扩展:varnish将不同的url发送到不同的后端server
import directors; # load the directors
backend web1 {
.host = "192.168.0.10";
.port = "80";
.probe =backend_healthcheck;
}
backend web2 {
.host = "192.168.0.11";
.port = "80";
.probe = backend_healthcheck;
}
backendimg1 {
.host = "img1.lnmmp.com";
.port = "80";
.probe = backend_healthcheck;
}
backendimg2 {
.host = "img2.lnmmp.com";
.port = "80";
.probe = backend_healthcheck;
}
//
初始化处理
sub vcl_init { //调用vcl_init初始化子程序创建后端主机组,即directors
new web_cluster = directors.round_robin();//使用new关键字创建drector对象,使用
round_robin算法
web_cluster.add_backend(web1); //添加后端服务器节点
web_cluster.add_backend(web2);
new img_cluster =directors.random();
img_cluster.add_backend(img1,2); //添加后端服务器节点,并且设置权重值
img_cluster.add_backend(img2,5);
}
//根据不同的访问域名,分发至不同的后端主机组
sub vcl_recv {
if (req.http.host ~ "(?i)^(www.)?benet.com$") {
set req.http.host = "www.benet.com";
set req.backend_hint = web_cluster.backend(); //选取后端
} elsif (req.http.host ~ "(?i)^images.benet.com$"){
set req.backend_hint = img_cluster.backend();
}
}
说明:中的i就是忽略大小写的意思。(?i)表示开启忽略大小写,而(?-i)表示关闭忽略大小写
4)访问控制列表(ACL):
创建一个地址列表,用于后面的判断,可以是域名或IP集合。这个可以用于指定某些地址请求入口,防止恶意请求等。
语法格式:
acl purgers {
"127.0.0.1";
"localhost";
“192.168.134.0/24”
!"192.168.134.1";
}
说明:acl 是访问列表关键字(必需小写),name 是该列表的别名用于调用,花括号内部是地址集。
注意:如果列表中包含了无法解析的主机地址,它会匹配任何地址。
如果不想让它匹配可以在前添加一个 ! 符号,如上面 !"192.168.134.1";
使用ACL只需要用匹配运算符 ~ 或 !~ 如:
s
ub vcl_recv {
if (req.method == "PURGE") { //
PURGE请求的处理
if (client.ip ~
purgers) {
return(purge);
} else {
return(synth(403, "Access denied."));
}
}
}
5)缓存规则配置:
sub vcl_recv {
// PURGE请求的处理
if(req.method == "PURGE") {
if(!client.ip ~ purgers) {
return (synth(405, "Not Allowed."));
}
return (purge);
}
setreq.backend_hint = web.backend();
//将php、asp等动态内容访问请求直接发给后端服务器,不缓存。
if(req.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {
return (pass);
}
//将非GET和HEAD访问请求直接发给后端服务器,不缓存。例如POST请求。
if(req.method != "GET" && req.method != "HEAD") {
return (pass);
}
//如果varnish看到header中有‘Authorization‘头,它将pass请求。
if(req.http.Authorization) {
return (pass);
}
//带cookie首部的GET请求也缓存
if(req.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)"){
unsetreq.http.cookie;
return (hash);
}
说明:默认情况,varnish不缓存从后端响应的http头中带有Set-Cookie的对象。如果客户端发送的请求带有Cookie header,varnish将忽略缓存,直接将请求传递到后端。
//为发往后端主机的请求添加X-Forward-For首部,首次访问增加X-Forwarded-For 头信息,方便后端程序获取客户端ip,而不是varnish地址
if (req.restarts == 0) {
if (req.http.x-forwarded-for) {//如果设置过此header则要再次附加上用逗号隔开
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", "+ client.ip;
} else {//如果只有一层代理的话,就无需设置了
set req.http.X-Forwarded-For = client.ip;
}
}
说明:X-Forwarded-For是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段
子程序:
子程序是一种类似C的函数,但是程序没有调用参数,子程序以 sub 关键字定义。在VCL里子程序是用于管理程序。
注意:所有VCL内置的程序都是以 vcl_ 开头,并已经预置好,在VCL文件中只要声明对应的内置子程序,都会在对应的流程中调用。
三、varnish完整配置实例
1、拓扑环境
Varnish:192.168.31.250
Web01:192.168.31.83
Web02:192.168.31.141
配置web01、web02做为后端服务器(过程略)
确保varnish服务器能正常访问web01、web02
Varnish缓存代理服务器配置:
2、vcl文件配置内容:
[root@varnish ~]# cat /usr/local/var/varnish/default.vcl
#使用varnish版本4的格式.
vcl 4.0;
#加载后端负载均衡模块
import directors;
#加载std模块
import std;
#创建名为backend_healthcheck的健康检查策略
probe backend_healthcheck {
.url="/";
.interval = 5s;
.timeout = 1s;
.window = 5;
.threshold = 3;
}
#定义后端服务器
backend web_app_01 {
.host= "192.168.31.83";
.port= "80";
.first_byte_timeout = 9s;
.connect_timeout = 3s;
.between_bytes_timeout = 1s;
.probe = backend_healthcheck;
}
backend web_app_02 {
.host= "192.168.31.141";
.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.31.0/24";
}
#vcl_init初始化子程序创建后端主机组
sub vcl_init {
newweb = 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.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {
return (pass);
}
#具有身份验证的请求跳到pass
if(req.http.Authorization) {
return (pass);
}
if(req.http.Accept-Encoding) {
if (req.url ~"\.(bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)$"){
unset req.http.Accept-Encoding;
}elseif (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
}elseif (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);
}
# 把真实客户端IP传递给后端服务器后端服务器日志使用X-Forwarded-For来接收
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);
}
# 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);
}
# 返回给用户的前一个事件通常用于添加或删除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头为了安全
unsetresp.http.Server;
unsetresp.http.X-Drupal-Cache;
unsetresp.http.Via;
unsetresp.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) {
setberesp.uncacheable = true;
}
#如请求php或jsp 则不缓存
if(bereq.url ~ "\.(php|jsp)(\?|$)") {
setberesp.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);
}
3、启动varnish
当启动varnish时有两个重要的参数你必须设置: 一个是处理http请求的tcp监听端口,另一个是处理真实请求的后端server
注:如果你使用操作系统自带的包管理工具安装的varnish,你将在下面的文件找到启动参数:
Red Hat, Centos: /etc/sysconfig/varnish
1):‘-a‘ 参数定义了varnish监听在哪个地址,并用该地址处理http请求,你可能想设置这个参数在众所周知的http 80端口.
例子:
-a :80
-a localhost:80
-a 192.168.1.100:8080
-a ‘[fe80::1]:80‘
-a ‘0.0.0.0:8080,[::]:8081‘
如果你的webserver和varnish运行在同一台机器,你必须换一个监听地址.
2):‘-f‘ VCL-file or ‘-b‘ backend
-f添加vcl文件,-b定义后端server
varnish需要知道从哪里找到这个需要缓存的http server.你可以用-b参数指定,或者帮把它放在vcl文件中,然后使用-f参数指定.
在启动的时候使用-b是一个快捷的方式.
-b 192.168.1.2:80
注意:如果你指定的是name,这个name必须能解析成一个IPv4或者IPv6的地址
如果你使用-f参数,你启动的时候可以在-f指定vcl文件。
默认的varnish使用100M的内存来缓存对象,如果你想缓存更多,可以使用-s参数.
注:Varnish拥有大量的有用的命令行参数,建议查看其帮助
[root@varnish ~]# /usr/local/sbin/varnishd -h
启动varnish
[root@varnish ~]# /usr/local/sbin/varnishd -f/usr/local/var/varnish/default.vcl -s malloc,200M -a 0.0.0.0:80
[root@varnish ~]# netstat-anpt | grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 6173/varnishd
2)现在,varnish已经启动和运行,你可以通过varnish访问您的Web应用程序。
打开火狐浏览器
第一次访问
第二次访问
3)varnish4配置手动清除缓存
varnish4通过vcl配置清楚缓存
通过vcl配置可以让客户端手动请求清楚缓存,以保证局部数据及时更新,而不用重启varnish服务器。
配置方法:
#允许清除缓存IP集
acl purgers {
"127.0.0.1";
"localhost";
"192.168.31.0/24";
}
sub vcl_recv {
……
if (req.method == "PURGE") {
if (!client.ip ~ purgers) {
return (synth(405, "NotAllowed."));
}
return (purge); //清除缓存
}
……
}
sub vcl_purge {
return(synth(200,"success"));
}
打开火狐浏览器,随便进入一个缓存页面,如下图所示。
点击编辑和重发,修改请求类型为 PURGE 再点击 发送
查看返回状态,如果成功则成功清除缓存,可以按 F5 刷新页面,查看新内容。
Varnish工作流程
1)、varnish从客户端接收请求后,由vcl_recv状态引擎处理,不能识别的请求将会通过参数pipe交给vcl_pipe状态引擎,需要查找缓存的
请求通过lookup参数将会交给vcl_hash状态引擎,无需缓存的数据通过参数pass将会交给 vcl_pass状态引擎;
2)、vcl_hash状态引擎在接收到请求后会从缓存中查找数据,查询结果有两种,一种是hit缓存命中,另一种是miss缓存未命中;
3)、vcl_hit状态引擎将命中的缓存数据通过参数deliver交给vcl_deliver状态引擎,vcl_deliver状态引擎将数据处理后,最终返回给客户端;
4)、vcl_miss状态引擎将未命中的结果参数fetch交给vcl_fetch状态引擎,vcl_fetch状态引擎将会从数据库中查找数据;
5)、vcl_fetch状态引擎将从数据库中查询到的结果,返回给vcl_deliver状态引擎;
6)、vcl_deliver状态引擎将结果返回给master进程,最终返回给客户端;
本文出自 “13110271” 博客,请务必保留此出处http://13120271.blog.51cto.com/13110271/1950626
标签:varnish原理
原文地址:http://13120271.blog.51cto.com/13110271/1950626