Varnish4.0缓存代理配置

Varnish4.0缓存代理配置

一、Varnish原理
1、Varnish简介

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

根据官网的介绍,Varnish 的主要特性如下:https://www.varnish-cache.org/

  • 缓存位置:

可以使用内存也可以使用磁盘。如果要使用磁盘的话推荐 SSD 做 RAID1

  • 日志存储:

日志也存储在内存中。存储策略:固定大小,循环使用支持虚拟内存的使用

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

  • 状态引擎架构:

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

  • 缓存管理:

以二叉堆格式管理缓存数据,做到数据的及时清理

2、Varnish 与 与 Squid 的对比

相同点:

都是一个反向代理服务器

都是开源软件

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

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

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

Varnish 可以通过管理端口,使用正则表达式批量的清除部分缓存,而 Squid 是做不到的;
squid 属于是单进程使用单核 CPU,但 Varnish 是通过 fork 形式打开多进程来做处理,所以可以合理的使用所有核来处理相应的请求
Varnish 的劣势:
varnish 进程一旦 Crash 或者重启,缓存数据都会从内存中完全释放,此时所有请求都会发送到后端服务器,在高并发情况下,会给后端服务器造成很大压力

在 varnish 使用中如果单个 url 的请求通过 HA/F5 等负载均衡,则每次请求落在不同的varnish 服务器中,造成请求都会被穿透到后端;而且同样的请求在多台服务器上缓存,也会造成 varnish 的缓存的资源浪费,造成性能下降
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 分为 management 进程和 child 进程

Management 进程:

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

Child 进程:

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

5、varnish 主要配置部分

varnish 配置主要分为:

  • 后端配置
  • ACL 配置
  • probes 配置
  • directors 配置
  • 核心子程序配置

其中后端配置是必要的,在多台服务器中还会用到 directors 配置,核心子程序配置

后端配置:

即给 varnish 添加反代服务器节点,最少配置一个

ACL 配置:

即给 varnish 添加访问控制列表,可以指定这些列表访问或禁止访问

probes 配置:

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

directors 配置:

即给 varnish 添加负载均衡模式管理多个后端服务器

核心子程序配置:

即给 varnish 添加后端服务器切换,请求缓存,访问控制,错误处理等规则

6、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.port 已经弃用,如果要取客户端请求端口号使用std.port(client.ip), importstd;才可以使用 std

client.ip:返回客户端 IP 地址

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:(发送到后端的请求对象,基于 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:存储健康状态
7、特定功能性语句
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="ContentType"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
8、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) 合成返回客户端状态信息
9、varnish 中内置子程序

注:varnish 内置子程序均有自己限定的返回动作 return (动作); 不同的动作将调用对应下一个子程序

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

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

可操作对象:(部分或全部值)

读:client,server,bereq,req,storage

写:client,bereq,req

返回值:

synth(status code,reason); 定义响应内容

pipe 继续 pipe 模式,进入后端 vcl_backend_fetch 子程序,默认返回值

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

可操作对象:(部分或全部值)
读:client,server,req,storage

写:client,req

返回值:

synth(status code,reason); 定义响应内容

fetch 继续 pass 模式,进入后端 vcl_backend_fetch 子程序,默认返回值

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

可操作对象:(部分或全部值)

读:client,server,obj,req,storage

写:client,req

返回值:

restart 重启请求

deliver 交付缓存内容,进入 vcl_deliver 子程序处理,默认返回值

synth(status code,reason); 定义响应内容

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

可操作对象:(部分或全部值)

读:client,server,req,storage

写:client,req

返回值:

restart 重启请求

synth(status code,reason); 定义响应内容

pass 切换到 pass 模式,进入 vcl_pass 子程序

fetch 正常取后端内容再缓存,进入 vcl_backend_fetch 子程序,默认返回值

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

可操作对象:(部分或全部值)

读:client,server,req,storage

写:client,req

返回值:

lookup 查找缓存对象,存在缓存进入 vcl_hit 子程序,不存在缓存进入 vcl_miss 子程序,当使用了 purge 清理模式时会进入 vcl_purge 子程序,默认返回值

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

可操作对象:(部分或全部值)

读:client,server,req,storage

写:client,req

返回值:

synth(status code,reason); 定义响应内容

restart 重启请求

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

可操作对象:(部分或全部值)

读:client,server,req,resp,obj,storage

写:client,req,resp

返回值:

deliver 正常交付后端或缓存响应内容,默认返回值

restart 重启请求

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

可操作对象:(部分或全部值)

读:server,bereq,storage

写:bereq

返回值:

fetch 正常发送请求到到后端取出响应内容,进入 vcl_backend_response 子程序,默认返回值

abandon 放弃后端请求,并生成一个错误,进入 vcl_backend_error 子程序

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

可操作对象:(部分或全部值)

读:server,bereq,beresp,storage

写:bereq,beresp

返回值:

deliver 正常交付后端响应内容,进入 vcl_deliver 子程序,默认返回值

abandon 放弃后端请求,并生成一个错误,进入 vcl_backend_error 子程序

retry 重试后端请求,重试计数器加 1,当超过配置中 max_retries 值时会报错并进入vcl_backend_error 子程序

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

可操作对象:(部分或全部值)

读:server,bereq,beresp,storage

写:bereq,beresp

返回值:

deliver 只交付 sysnthetic(string) 自定义内容,默认返回后端异常标准错误内容

retry 重试后端请求,重试计数器加 1,当超过配置中 max_retries 值时会报错并进入vcl_backend_error 子程序

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

可操作对象:(部分或全部值)

读:client,server,req,resp,storage

写:req,resp

返回值:

deliver 只交付 sysnthetic(string) 自定义内容,默认返回 sysnth 异常指定状态码与错误内容

restart 重启请求

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

可操作对象:(部分或全部值)

读:server

写:无

返回值:

ok 正常返回,进入 vcl_recv 子程序,默认返回值

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

可操作对象:(部分或全部值)

读:server

写:无

返回值:

ok 正常返回,本次 vcl 将释放,默认返回值

varnish 子程序调用流程图,通过大部分子程序的 return 返回值进入下一步行动:

10、优雅模式(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 pkgconfig python-docutils python-sphinx
2、安装 varnish

安装包

提取码:h71d

varnish 的官方网址为 http://varnish-cache.org,可以在这里下载最新版本的软件

注意:Varnish 网站有时会被墙

解压,进入解压目录编译安装:

[root@varinsh ~]# tar zxf varnish-4.0.3.tar.gz 
[root@varinsh ~]# cd varnish-4.0.3/
[root@varinsh varnish-4.0.3]# export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
[root@varinsh varnish-4.0.3]# ./configure && make && make install
# 不指定安装路径,默认是安装在/usr/local 目录下

注:

./autogen.sh
如果从 Git 库下载的安装包时才需要运行,用于生成 configure 编译文件

复制 vcl 文件(在编译安装目录下),如果安装目录里没有 default.vcl 文件

复制到安装目录的/usr/local/var/varnish/目录下(当然并无必需要求在哪个目录,因为正式启动时还得指定这个文件的目录)

[root@varinsh varnish-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;
}
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 完整配置实例
1、拓扑环境
varnish192.168.1.20
web01192.168.1.30
web02192.168.1.40

配置 web01、web02 做为后端服务器(过程略)

确保 varnish 服务器能正常访问 web01、web02

Varnish 缓存代理服务器配置:

2、vcl文件配置内容
[root@varinsh ~]# cat /usr/local/var/varnish/default.vcl 
vcl 4.0;
import directors;
import std;
probe  backend_healthcheck {
        .url = "/";
        .timeout = 1s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
}
backend web1 {
    .host = "192.168.1.30";
    .port = "80";
    .probe = backend_healthcheck;
}
backend web2 {
    .host = "192.168.1.40";
    .port = "80";
    .probe = backend_healthcheck;
}
acl purgers {
        "127.0.0.1";
        "localhost";
        "192.168.1.0/24";
        !"192.168.1.40";
}
sub vcl_init {  
        new web_cluster = directors.round_robin();  
        web_cluster.add_backend(web1);  
        web_cluster.add_backend(web2);
}
sub vcl_recv {
        set req.backend_hint = web_cluster.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);
                }
        if (req.method != "GET" && req.method != "HEAD"){
                return (pass);  

        }
        if (req.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)"){
                return (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|sw f|flv)($|\?)") {
                unset req.http.cookie; 
                return (hash); 
        }
        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);
}
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);
}
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";
        }
        unset resp.http.X-Powered-By; 
        unset resp.http.Server; 
        unset resp.http.X-Drupal-Cache; 
        unset resp.http.Via; 
        unset resp.http.Link; 
        unset resp.http.X-Varnish; 
        set resp.http.xx_restarts_count = req.restarts; 
        set resp.http.xx_Age = resp.http.Age; 
        set resp.http.hit_count = obj.hits; 
        unset resp.http.Age;
        return (deliver);
}
sub vcl_pass {
        return (fetch); 
}
sub vcl_backend_response {
        set beresp.grace = 5m; 
        if (beresp.status == 499 || beresp.status == 404 || beresp.status == 502) {
                set beresp.uncacheable = true; 
        }
        if (bereq.url ~ "\.(php|jsp)(\?|$)") {
                set beresp.uncacheable = true; 
        } else {
                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’

‘-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’

-f 添加 vcl 文件,-b 定义后端 serve

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@varinsh ~]# /usr/local/sbin/varnishd -h

启动 varnish

[root@varinsh ~]# varnishd -f /usr/local/var/varnish/default.vcl -s malloc,200M -a 0.0.0.0:80[root@varinsh ~]# netstat -anput | grep 80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      13968/varnishd
3)访问

现在,varnish 已经启动和运行,你可以通过 varnish 访问您的 Web 应用程序

第一次访问

第二次访问

4)清除缓存再次访问

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值