varnish缓存代理配置

一、varnish原理

1.varnish简介
varnish缓存是web应用加速器,同时也作为http反向缓存代理。可以安装varnish在任何http的前端,同时配置它缓存内容。与传统的squid相比,varnish具有性能更高、速度更快、管理更加方便等诸多优点。有一部分企业已经在生产环境中使用其作为旧版本squid的替代方案,以在相同的服务器成本下提供更好的缓存效果,Varnish更是作为CDN缓存服务器的可选服务之一。
根据官网的介绍,Varnish的主要特性如下:
varnish官网

(1)缓存位置:可以使用内存也可以使用磁盘。如果使用磁盘,推荐SSD做RAID1;

(2)日志存储:日志也存储在内存中。存储策略:固定大小,循环使用;

(3)支持虚拟内存的使用;

(4)有精确的时间管理机制,即缓存的时间属性控制;

(5)状态引擎架构:在不同引擎上完成对不同缓存和代理数据进行处理。
可以通过特定的配置语言设计不同的控制语句,以决定数据在不同位置以不同方式缓存,在特定的地方对经过的报文进行特定规则的处理;

(6)缓存管理:以二叉堆格式管理缓存数据,做到数据的及时清理;

2.Varnish与Squid对比
(1)相同点

(1)都是反向代理服务器;

(2)都是开源软件;

(2)Varnish的优势

(1)Varnish的稳定性很高,两者在完成相同负荷的工作时,Squid服务器发生故障的几率要高于Varnish,因为使用Squid要经常重启;

(2)Varnish访问速度更快,因为采用了“Visual Page Cache”技术,所有缓存数据都直接从内存读取,而squid是从硬盘读取,因而Varnish在访问速度方面会更快;

(3)Varnish可以支持更多的并发连接,因为Varnish的TCP连接释放要比Squid快,因而在高并发连接情况下可以支持更多TCP连接;

(4)Varnish可以通过管理端口,使用正则表达式批量的清除部分缓存,而Squid做不到;
squid是单进程使用单核CPU,但Varnish是通过fork形式打开多进程来做处理,所以可以合理的使用所有核来处理相应的请求;

(3)Varnish的劣势

(1)varnish进程一旦Crash或者重启,缓存数据都会从内存中完全释放,此时所有请求都会发送到后端服务器,在高并发情况下,会给后端服务器造成很大压力;

(2)在varnish使用中如果单个url的请求通过HA/F5等负载均衡,则每次请求落在不同的varnish服务器中,造成请求都会被穿透到后端;
而且同样的请求在多台服务器上缓存,也会造成varnish的缓存的资源浪费,造成性能下降;

(4)Varnish劣势的解决方案

针对劣势一:在访问量很大的情况下推荐使用varnish的内存缓存方式启动,而且后面需要跟多台squid/nginx服务器。
主要为了防止前面的varnish服务、服务器被重启的情况下,大量请求穿透varnish,这样squid/nginx可以就担当第二层CACHE,而且也弥补了varnish缓存在内存中重启都会释放的问题;

针对劣势二:可以在负载均衡上做url哈希,让单个url请求固定请求到一台varnish服务器上;

3.使用varnish作为web代理缓存的原理
varnish是一个http反向代理的缓存。它从客户端接收请求,然后尝试从缓存中获取数据来响应客户端的请求。如果varnish不能从缓存中获得数据来响应客户端,它将转发请求到后端(backend servers),获取响应同时存储,最后交付给客户端。
如果varnish已经缓存了某个响应,它比传统的后端服务器的响应要快很多,所以需要尽可能是更多的请求直接从varnish的缓存中获取响应。
varnish决定是缓存内容或者是从后端服务器获取响应。后端服务器能通过http响应头中的Cache-Control来同步varnish缓存内容。在某些条件下varnish将不缓存内容,最常见的是使用cookie。当一个被标记有cookie的客户端web请求,varnish默认是不缓存。这些众多的varnish功能特点都是可以通过写vcl来改变的。
4.varnish的简单架构
Varnish分为management进程和child进程。

Management进程:对子进程进行管理,同时对VCL配置进行编译,并应用到不同的状态引擎;

Child进程:生成线程池,负责对用户请求进行处理,并通过hash查找返回用户结果。

5.varnish主要配置部分
配置主要分为:后端配置、ACL配置、probes配置、directors配置、核心子程序配置几大块。其中后端配置是必要的,在多台服务器中还会用到directors配置,核心子程序配置。

后端配置:给varnish添加反代服务器节点,最少配置一个;

ACL配置:给varnish添加访问控制列表,可以指定这些列表访问或禁止访问;

probes配置:给varnish添加探测后端服务器是否正常的规则,方便切换或禁止对应后端服务器;

directors配置:给varnish添加负载均衡模式管理多个后端服务器;

核心子程序配置:给varnish添加后端服务器切换、请求缓存、访问控制、错误处理等规则;

二、varnish相关参数

1.VCL中内置预设变量:变量(也叫object)
在这里插入图片描述

req:The request object,请求到达时可用的变量(客户端发送的请求对象);

bereq:The backend request object,向后端主机请求时可用的变量;

beresp:The backend response object,从后端主机获取内容时可用的变量(后端响应请求对象);

resp:The HTTP response object,对客户端响应时可用的变量(返回给客户端的响应对象);

obj:存储在内存中时对象属性相关的可用的变量(高速缓存对象,缓存后端响应请求内容);

预设变量是系统固定的,请求进入对应的vcl子程序后便生成,这些变量可以方便子程序提取,当然也可以自定义一些全局变量。

当前时间:
now:作用:返回当前时间戳;

客户端:(客户端基本信息)
client.ip:返回客户端IP地址;
注:原client.port已经弃用,如果要取客户端请求端口号使用std.port(client.ip),需要import std;才可以使用std;
client.identity:用于装载客户端标识码;

服务器:(服务器基本信息)
注:原server.port已经弃用,如果要取服务器端口号使用std.port(server.ip),需要import std;才可以使用std;
server.hostname:服务器主机名;
server.identity:服务器身份标识;
server.ip:返回服务器端IP地址;

req:(客户端发送的请求对象)
req:整个HTTP请求数据结构
req.backend_hint:指定请求后端节点,设置后bereq.backend才能获取后端节点配置数据;
req.can_gzip:客户端是否接受GZIP传输编码;
req.hash_always_miss:是否强制不命中高速缓存,如果设置为true,则高速缓存不会命中,一直会从后端获取新数据;
req.hash_ignore_busy:忽略缓存中忙碌的对象,多台缓存时可以避免死锁;
req.http:对应请求HTTP的header;
req.method:请求类型(如GET,POST);
req.proto:客户端使用的HTTP协议版本;
req.restarts:重新启动次数。默认最大值是4;
req.ttl:缓存有剩余时间;
req.url:请求的URL;
req.xid:唯一ID;

bereq:(发送到后端的请求对象,基于req对象);
bereq:整个后端请求后数据结构;
bereq.backend:所请求后端节点配置;
bereq.between_bytes_timeout:从后端每接收一个字节之间的等待时间(秒);
bereq.connect_timeout:连接后端等待时间(秒),最大等待时间;
bereq.first_byte_timeout:等待后端第一个字节时间(秒),最大等待时间;
bereq.http:对应发送到后端HTTP的header信息;
bereq.method:发送到后端的请求类型(如:GET,POST);
bereq.proto:发送到后端的请求的HTTP版本;
bereq.retries:相同请求重试计数;
bereq.uncacheable:无缓存这个请求;
bereq.url:发送到后端请求的URL;
bereq.xid:请求唯一ID;

beresp:(后端响应请求对象);

beresp:整个后端响应HTTP数据结构;
beresp.backend.ip:后端响应的IP;
beresp.backend.name:响应后端配置节点的name;
beresp.do_gunzip:默认为false,缓存前解压该对象;
beresp.do_gzip:默认为false,缓存前压缩该对象;
beresp.grace:设置当前对象缓存过期后可额外宽限时间,用于特殊请求加大缓存时间,当并发量巨大时,不易设置过大否则会堵塞缓存,一般可设置1m左右,当beresp.ttl=0s时该值无效;
beresp.http:对应的HTTP请求header;
beresp.keep:对象缓存后带保持时间;
beresp.proto:响应的HTTP版本;
beresp.reason:由服务器返回的HTTP状态信息;
beresp.status:由服务器返回的状态码;
beresp.storage_hint:指定保存的特定存储器;
beresp.ttl:该对象缓存的剩余时间,指定统一缓存剩余时间;
beresp.uncacheable:继承bereq.uncacheable,是否不缓存;

OBJ:(高速缓存对象,缓存后端响应请求内容);
obj.grace:该对象额外宽限时间;
obj.hits:缓存命中次数,计数器从1开始,当对象缓存该值为1,一般可以用于判断是否有缓存,当前该值大于0时则为有缓存;
obj.http:对应HTTP的header;
obj.proto:HTTP版本;
obj.reason:服务器返回的HTTP状态信息;
obj.status:服务器返回的状态码;
obj.ttl:该对象缓存剩余时间(秒);
obj.uncacheable:不缓存对象;

resp :(返回给客户端的响应对象);
resp:整个响应HTTP数据结构;
resp.http:对应HTTP的header;
resp.proto:编辑响应的HTTP协议版本;
resp.reason:将要返回的HTTP状态信息;
resq.status:将要返回的HTTP状态码;

存储:
storage.<name>.free_space:存储可用空间(字节数);
storage.<name>.used_space:存储已经使用空间(字节数);
storage.<name>.happy:存储健康状态;

2.特定功能性语句

ban(expression):清除指定对象缓存;
call(subroutine):调用子程序,如:call(name);
hash_data(input):生成hash键,用于制定hash键值生成结构,只能在vcl_hash子程序中使用。调用hash_data(input)后,即这个hash为当前页面的缓存hash键值,无需其它获取或操作,如:
sub vcl_hash{
    hash_data(client.ip);
    return(lookup);
}
注意:return(lookup);是默认返回值,所以可以不写;

new():创建一个vcl对象,只能在vcl_init子程序中使用;
return():结束当前子程序,并指定继续下一步动作;
如:return (ok); 每个子程序可指定的动作均有不同;

rollback():恢复HTTP头到原来状态,已经弃用,使用std.rollback()代替;
synthetic(STRING):合成器,用于自定义一个响应内容,比如当请求出错时,可以返回自定义404内容,而不只是默认头信息,只能在vcl_synth和vcl_backend_error子程序中使用;
如:
sub vcl_synth {
    #自定义内容
    synthetic ({"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="zh-cn">
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
   <title>error</title>
  </head>
  <body>
   <h1>Error</h1>
   <h3>这只是一个测试自定义响应异常内容</h3>
  </body>
</html>
  "});
  #只交付自定义内容
  return(deliver);

regsub(str,regex,sub):使用正则替换第一次出现的字符串,第一个参数为待处理字符串,第二个参数为正则表达式,第三个为替换为字符串;
regsuball(str,regex,sub):使用正则替换所有匹配字符串。参数与regsuball相同;
具体变量详见:
https://www.varnish-cache.org/docs/4.0/reference/vcl.html#reference-vcl

3.return语句
return语句是终止子程序并返回动作,所有动作都根据不同的vcl子程序限定来选用;
https://www.varnish-cache.org/docs/4.0/users-guide/vcl-built-in-subs.html
语法:return(action)

常用的动作:
abandon:放弃处理,并生成一个错误;

deliver:交付处理;

fetch:从后端取出响应对象;

hash:哈希缓存处理;

lookup:查找缓存对象;

ok:继续执行;

pass:进入pass非缓存模式;

pipe:进入pipe非缓存模式;

purge:清除缓存对象,构建响应;

restart:重新开始;

retry:重试后端处理;

synth(status code,reason):合成返回客户端状态信息;

4.varnish中内置子程序
注:varnish内置子程序均有自己限定的返回动作return,不同的动作将调用对应下一个子程序。
(1)vcl_recv子程序
开始处理请求,通过return(动作);选择varnish处理模式,默认进入hash缓存模式(即return(hash);),缓存时间为配置项default_ttl(默认为120秒)过期保持时间default_grace(默认为10秒)。该子程序一般用于模式选择,请求对象缓存及信息修改,后端节点修改,终止请求等操作。

可操作对象:(部分或全部值)
读:client,server,req,storage
写:client,req
返回值:
synth(status code,reason):定义响应内容;
pass进入pass模式,并进入vcl_pass子程序;
pipe进入pipe模式,并进入vcl_pipe子程序;
hash进入hash缓存模式,并进入vcl_hash子程序,默认返回值;
purge清除缓存等数据,子程序先从vcl_hash再到vcl_purge;

(2)vcl_pipe子程序
pipe模式处理,该模式主要用于直接取后端响应内容返回客户端,可定义响应内容返回客户端。该子程序一般用于需要及时且不作处理的后端信息,取出后端响应内容后直接交付到客户端不进入vcl_deliver子程序处理。

可操作对象:(部分或全部值)
读:client,server,bereq,req,storage
写:client,bereq,req
返回值:
synth(status code,reason):定义响应内容;
pipe继续pipe模式,进入后端vcl_backend_fetch子程序,默认返回值;

(3)vcl_pass子程序
pass模式处理,该模式类似hash缓存模式,仅不做缓存处理。

可操作对象:(部分或全部值)
读:client,server,req,storage
写:client,req
返回值:
synth(status code,reason):定义响应内容;
fetch继续pass模式,进入后端vcl_backend_fetch子程序,默认返回值;

(4)vcl_hit子程序
hash缓存模式时,存在hash缓存时调用,用于缓存处理,可放弃或修改缓存。

可操作对象:(部分或全部值)
读:client,server,obj,req,storage
写:client,req
返回值:
restart:重启请求。
deliver:交付缓存内容,进入vcl_deliver子程序处理,默认返回值;
synth(status code,reason):定义响应内容;

(5)vcl_miss子程序
hash缓存模式时,不存在hash缓存时调用,用于判断性的选择进入后端取响应内容,可以修改为pass模式。

可操作对象:(部分或全部值)
读:client,server,req,storage
写:client,req
返回值:
restart:重启请求。
synth(status code,reason):定义响应内容;
pass切换到pass模式,进入vcl_pass子程序;
fetch正常取后端内容再缓存,进入vcl_backend_fetch子程序,默认返回值;

(6)vcl_hash子程序
hash缓存模式,生成hash值作为缓存查找键名提取缓存内容,主要用于缓存hash键值处理,可使用hash_data(string)指定键值组成结构,可在同一个页面通过IP或cookie生成不同的缓存键值。

可操作对象:(部分或全部值)
读:client,server,req,storage
写:client,req
返回值:
lookup查找缓存对象,存在缓存进入vcl_hit子程序,不存在缓存进入vcl_miss子程序,当使用了purge清理模式时会进入vcl_purge子程序,默认返回值;

(7)vcl_purge子程序
清理模式,当查找到对应的缓存时清除并调用,用于请求方法清除缓存,并报告。

可操作对象:(部分或全部值)
读:client,server,req,storage
写:client,req
返回值:
synth(status code,reason):定义响应内容;
restart:重启请求;

(8)vcl_deliver子程序
客户端交付子程序,在vcl_backend_response子程序后调用(非pipe模式),或vcl_hit子程序后调用,可用于追加响应头信息,cookie等内容。

可操作对象:(部分或全部值)
读:client,server,req,resp,obj,storage
写:client,req,resp
返回值:
deliver:正常交付后端或缓存响应内容,默认返回值;
restart:重启请求;

(9)vcl_backend_fetch子程序
发送后端请求之前调用,可用于改变请求地址或其它信息,或放弃请求。

可操作对象:(部分或全部值)
读:server,bereq,storage
写:bereq
返回值:
fetch正常发送请求到到后端取出响应内容,进入vcl_backend_response子程序,默认返回值;
abandon放弃后端请求,并生成一个错误,进入vcl_backend_error子程序;

(10)vcl_backend_response子程序
后端响应后调用,可用于修改缓存时间及缓存相关信息。

可操作对象:(部分或全部值)
读:server,bereq,beresp,storage
写:bereq,beresp
返回值:
deliver:正常交付后端响应内容,进入vcl_deliver 子程序,默认返回值;
abandon:放弃后端请求,并生成一个错误,进入vcl_backend_error子程序;
retry:重试后端请求,重试计数器加1,当超过配置中max_retries值时会报错并进入vcl_backend_error子程序;

(11)vcl_backend_error子程序
后端处理失败调用,异常页面展示效果处理,可自定义错误响应内容,或修改beresp.status与beresp.http.Location重定向等。

可操作对象:(部分或全部值)
读:server,bereq,beresp,storage
写:bereq,beresp
返回值:
deliver:只交付sysnthetic(string)自定义内容,默认返回后端异常标准错误内容;
retry:重试后端请求,重试计数器加1,当超过配置中max_retries值时会报错并进入vcl_backend_error子程序;

(12)vcl_synth子程序
自定义响应内容。可以通过synthetic()和返回值synth调用,这里可以自定义异常显示内容,也可以修改resp.status与resp.http.Location重定向。

可操作对象:(部分或全部值)
读:client,server,req,resp,storage
写:req,resp
返回值:
deliver:只交付sysnthetic(string)自定义内容,默认返回sysnth异常指定状态码与错误内容;
restart:重启请求;

(13)vcl_init子程序
加载vcl时最先调用,用于初始化VMODs,该子程序不参与请求处理,仅在vcl加载时调用一次。

可操作对象:(部分或全部值)
读:server
写:无
返回值:
ok:正常返回,进入vcl_recv子程序,默认返回值;

(14)vcl_fini子程序
卸载当前vcl配置时调用,用于清理VMODs,该子程序不参与请求处理,仅在vcl正常丢弃后调用。

可操作对象:(部分或全部值)
读:server
写:无
返回值:
ok:正常返回,本次vcl将释放,默认返回值;

varnish子程序调用流程图,通过大部分子程序的return返回值进入下一步行动:
在这里插入图片描述
5.优雅模式(Garce mode)
Varnish中的请求合并
当几个客户端请求同一个页面时,varnish只发送一个请求到后端服务器,然后让其他几个请求挂起并等待返回结果;获得结果后,其它请求再复制后端的结果发送给客户端;但如果同时有数以千计的请求,那么这个等待队列将变得庞大,这将导致2类潜在问题:
惊群问题(thundering herd problem),即突然释放大量的线程去复制后端返回的结果,将导致负载急速上升;没有用户喜欢等待;故为了解决这类问题,可以配置varnish在缓存对象因超时失效后再保留一段时间,以给那些等待的请求返回过去的文件内容(stale content),配置案例如下:

sub vcl_recv {
if (! req.backend.healthy) {
set req.grace = 5m;
} else {
set req.grace = 15s;
}
}
sub vcl_fetch {
set beresp.grace = 30m;
}

以上配置表示varnish将会将失效的缓存对象再多保留30分钟,此值等于最大的req.grace值即可;而根据后端主机的健康状况,varnish可向前端请求分别提供5分钟内或15秒内的过期内容。

三、安装部署varnish

1.安装依赖关系的软件包

[root@varnish ~]# yum -y install autoconf automake libedit-devel libtool ncurses-devel pcre-devel 
[root@varnish ~]# yum -y install pkgconfig python-docutils python-sphinx

2.安装varnish
Varnish的官方网址:

http://varnish-cache.org,可以在这里下载最新版本的软件。

下载地址:https://www.varnish-cache.org/content/varnish-cache-403

Git下载:git clone https://github.com/varnish/Varnish-Cache /var/tmp/

把下载好的varnish压缩包传到root目录下解压,进入解压目录编译安装:

[root@varnish ~]# tar zxf varnish-4.1.11.tgz
[root@varnish ~]# cd varnish-4.1.11/
[root@varnish varnish-4.1.11]# ./configure && make && make install
注意:不指定安装路径,默认是安装在/usr/local目录下。

3.复制example.vcl文件(在编译安装目录下)到/usr/local/var/varnish/目录下

[root@varnish etc]# pwd
/root/varnish-4.1.11/etc
[root@varnish etc]# ls
builtin.vcl  example.vcl  Makefile  Makefile.am  Makefile.in
[root@varnish etc]# cp example.vcl /usr/local/var/varnish/default.vcl
四、varnish配置相关介绍

varnish配置基本上是编辑VCL(Varnish Configuration Language)文件,varnish有一套自定义VCL语法,启动时,会将配置文件编译为C语言再执行。
varnish4.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;
}
backend img1 {
  .host = "img1.lnmmp.com";
  .port = "80";
  .probe = backend_healthcheck;
}
backend img2 {
  .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只需要用匹配运算符 ~ 或 !~ 如:

sub 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);
   }
   set req.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)
($|\?)") {
     unset req.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完整配置实例

环境准备

varnish:192.168.229.175

web1:192.168.229.170

web2:192.168.229.152

1.配置web1、web2做为后端服务器
(1)yum安装并启动httpd服务

[root@web1 ~]# yum -y install httpd
[root@web1 ~]# systemctl start httpd

(2)编写测试页面index.html

[root@web1 ~]# cd /var/www/html/
[root@web1 html]# vim index.html
<p>web1</p>
<img src="兔子.png">
[root@web1 html]# ls
index.html  兔子.png

确保varnish服务器能正常访问web1、web2。
2.varnish缓存代理服务器配置vcl文件内容

[root@varnish varnish]# vim 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 web1 {
.host = "192.168.229.170";
.port = "80";
.first_byte_timeout = 9s;
.connect_timeout = 3s;
.between_bytes_timeout = 1s;
.probe = backend_healthcheck;
}
backend web2 {
.host = "192.168.229.152";
.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.229.0/24";
}
#vcl_init初始化子程序创建后端主机组;
sub vcl_init {
new web = directors.round_robin();
web.add_backend(web1);
web.add_backend(web2);
}
#请求入口,用于接收和处理请求。这里一般用作路由处理,判断是否读取缓存和指定该请求使用哪个后端;
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头为了安全;
unset resp.http.Server;
unset resp.http.X-Drupal-Cache;
unset resp.http.Via;
unset resp.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) {
set beresp.uncacheable = true;
}
#如请求php或jsp则不缓存;
if (bereq.url ~ "\.(php|jsp)(\?|$)") {
set beresp.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请求,你可能想设置这个参数在众所周知的http80端口。
例子:

-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.229.175:80
注意:如果你指定的是name,这个name必须能解析成一个IPv4或者IPv6的地址,如果你使用-f参数,你启动的时候可以在-f指定vcl文件。
默认的varnish使用100M的内存来缓存对象,如果你想缓存更多,可以使用-s参数。
:Varnish拥有大量的有用的命令行参数,建议查看其帮助。

[root@varnish ~]# /usr/local/sbin/varnishd -h

启动varnish

[root@varnish varnish]# /usr/local/sbin/varnishd -f /usr/local/var/varnish/default.vcl -s malloc,200M -a 0.0.0.0:80
[root@varnish varnish]# netstat -anpt | grep 80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      
25018/varnishd

现在varnish已经启动和运行,可以通过varnish访问Web应用程序。
打开火狐浏览器访问。
第一次访问
在这里插入图片描述
第二次访问
在这里插入图片描述
(3)varnish4配置手动清除缓存
通过vcl配置可以让客户端手动请求清楚缓存,以保证局部数据及时更新,而不用重启varnish服务器。
配置方法

#允许清除缓存IP集
acl purgers {
"127.0.0.1";
"localhost";
"192.168.229.0/24";
}
sub vcl_recv {
……
if (req.method == "PURGE") {
if (!client.ip ~ purgers) {
return (synth(405, "Not Allowed."));
}
return (purge); //清除缓存
}
……
}
sub vcl_purge {
return (synth(200,"success"));
}

打开火狐浏览器,随便进入一个缓存页面;
在这里插入图片描述
点击编辑和重发,修改请求类型为PURGE再点击发送;
在这里插入图片描述
在这里插入图片描述
查看返回状态,如果成功则成功清除缓存,可以按F5刷新页面,查看新内容。

六、varnish相关操作命令

1.varnishd启动

[root@varnish varnish]# /usr/local/sbin/varnishd -f /usr/local/var/varnish/default.vcl -s malloc,200M -T 127.0.0.1:2000 -a 0.0.0.0:80

#此处的80端口是varnish工作的监听端口;

-f /usr/local/varnish-3.0.2/etc/default.vcl
#这个–f选项指定varnishd使用哪个配置文件;

-s malloc,200M
#这个–s选项用来确定varnish使用的存储类型和存储容量,使用的是malloc类型(malloc是一个C函数,用于分配内存空间)200M定义多少内存;

-T 127.0.0.1:2000
#Varnish有一个基于文本的管理接口,启动它可以在不停止varnish的情况下来管理varnish。可以指定管理软件监听哪个接口。当然不能让所有人都能访问varnish管理接口,因为他们可以很轻松的通过访问varnish管理接口来获得root访问权限。推荐只让它监听本机端口;

-a 0.0.0.0:80
#varnish监听所有IP发给80端口的http请求,正常网站对外http访问端口为80;

2.varnishadm
管理平台,在这里可以通过help查看所有管理控制台上可执行的命令,比如清除指定url的缓存使用ban命令;

[root@varnish varnish]# yum -y install telnet
[root@varnish varnish]# telnet 127.0.0.1 2000
ban.url test.php  #清除该url缓存
ban.url .*  #清除所有的
[root@varnish varnish]# varnishadm -T 127.0.0.1:2000 ban "req.http.host ~ www.abc.com$ && req.url ~ /image/test.jpg"
#www.abc.com为被清除的域名;
#/image/test.jpg为被清除的url地址列表;

清除所有缓存
[root@varnish varnish]# varnishadm -T 127.0.0.1:2000 ban.url *$

清除image目录下所有缓存
[root@varnish varnish]# varnishadm -T 127.0.0.1:2000 ban.url test.php.url /images

3.varnishtop
这个工具用于读取共享内存的日志,适当使用一些过滤选项如-I,-i,-X和-x,可以连续不断地显示大部分普通日志。
varnishtop可以按照使用要求显示请求的内容、客户端、浏览器等一些其他日志里的信息。比如:
(1)查看客户端请求的url次数;

[root@varnish varnish]# varnishtop -i rxurl

(2)查看请求后端服务器的url次数;

[root@varnish varnish]# varnishtop -i txurl

(3)查看接收到的头信息中有多少次包含Accept-Encoding;

[root@varnish varnish]# varnishtop -i Rxheader -I Accept-Encoding

4.varnishhist
用于读取varnishd共享内存段的日志,并生成一个连续的柱状图。varnishhist用于显示最后N个请求的处理情况。
如果缓存命中则标记"|",如果缓存没有命中则标记"#"符号。

[root@varnish varnish]# varnishhist

5.varnishsizes
varnishsizes和varnishhist相似,可以查看服务对象的大致大小。

[root@varnish varnish]# varnishsizes

6.varnishstat
用于查看varnish计数丢失率、命中率、存储信息、创建线程、删除对象等。

[root@varnish varnish]# varnishstat

7.varnishlog
当您需要鉴定哪个URL被频繁的发送到后端服务器,您可以通过varnishlog对请求做一个全面的分析。

[root@varnish varnish]# varnishlog

8.varnishncsa
varnishncsa工具读取共享内存的日志,然后以apache/NCSA的格式显示出来。下面的选项可以用-w file file把日志写到一个文件里代替显示他们,如果不是用-a参数就会发生覆盖,如果varnishlog在写日志时,接收到一个SIGHUP信号,他会创建一个新的文件信号,老的文件可以移走。

[root@varnish varnish]# varnishncsa -w /var/lib/varnish/varnish.log  
#将varnishlog保存在一个文本文件里;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值