千家信息网

Web架构:varnish缓存代理服务器超详细剖析

发表于:2025-02-06 作者:千家信息网编辑
千家信息网最后更新 2025年02月06日,小生博客:http://xsboke.blog.51cto.com-------谢谢您的参考,如有疑问,欢迎交流目录varnishi简介varnish配置组成---------------------
千家信息网最后更新 2025年02月06日Web架构:varnish缓存代理服务器超详细剖析

小生博客:http://xsboke.blog.51cto.com

-------谢谢您的参考,如有疑问,欢迎交流


目录

  • varnishi简介

  • varnish配置组成

-------------------------------------- vcl内置预设变量

-------------------------------------- 功能语句与对象

-------------------------------------- varnish中内置子程序

-------------------------------------- varnish缓存模式和子程序的关系

  • varnish的安装

  • varnish实例配置解析

  • 启动varnish

  • varnish acl配置解析



一、 Varnish简介

1. 作用

  • Web应用加速器,同时作为http反向缓存代理

2. 特点

  • Varnish可以使用内存也可以使用硬盘进行数据缓存

  • 支持虚拟内存的使用

  • 有精确的时间管理机制

  • 状态引擎架构:通过特定的配置语言设计不同的语句

  • 以二叉堆格式管理缓存数据

3. Varnish的优势

  • Varnish访问速度快,因为采用了"Visual Page Cache"技术,在读取数据时直接从内存中读取

  • Varnish支持更多的并发连接,因为varnishTCP连接比squid

  • Varnish通过管理端口,使用正则表达式批量的清除部分缓存

4. Varnish的劣势

  • 进程一旦crash或重启,缓存的数据将从内存中完全释放

  • 在多台varnish实现负载均衡时,每次请求都会落到不同的varnish服务器中,造成url请求可能会穿透到后端

    1)劣势解决方案

    A、varnish的后端添加squid/nignx代理,这样防止了当varnish缓存被清空时,瞬间大量的请求发往web服务器

    B、在负载均衡上做url哈西,让单个url请求固定请求到一台varnish服务器上

5. Varnish的组成

1) Management进程(管理进程)

child进程进行管理,同时对vcl配置进行编译

2) Child进程(子进程)

生成线程池,负责处理用户请求

6. Varnish配置组成

l 后端配置:指定后端服务器

l ACL配置:为varnish添加访问控制列表,用于规则设置

l Probes配置:实现后端服务器的健康检查

l Directors配置:为varnish添加集群

l 核心子进程:为后端服务器、缓存、访问控制、错误处理等功能添

一、 arnish配置简介

1. vcl内置预设变量

  • 这些变量一般用于设置各个阶段的对象值

  • 预设变量是系统固定的,请求进入vcl子程序后便生成,这些变量可以方便子进程进行提取或者自定义

  • 格式一般为:阶段 .对象运算符值

1)阶段

  • Req:处理客户端发送的请求时使用

  • Bereq:处理varinish向后端服务器发送的请求时使用

  • Beresp:处理后端服务器响应时使用,用于varnish未缓存前

  • Resp:处理返回给客户端的响应时使用

  • Obj:处理存储在内存中的对象时使用

2)对象

3) 运算符

2. 功能语句与对象

l 一般功能语句都用于匹配对象,就是对某个对象实现什么操作

l 格式为:功能语句(对象)

1)功能语句

  • ban():清除指定对象缓存

  • call():调用子程序

  • hash_data():生成hash键值,只能在vcl_hash子程序中使用

  • new():创建一个vcl对象,只能在vcl_init子程序中使用

  • return():结束当前子程序并执行下一步动作

  • rollback():恢复http头到原来的状态,现在使用std.rollback()代替。

  • .synthetic():合成器,用于自定义一个响应内容,只能在vcl_synthvcl_backend_error子程序中使用

  • regsub(待处理字符,正则表达式,替换为的字符):使用正则替换第一次出现的字符串

  • regsuball(待处理字符,正则表达式,替换为的字符):使用正则替换所有字符串

2)return的常用动作

语法:returnaction

  • abandon:放弃处理,并生成一个错误。

  • deliver:交付处理

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

  • hash:哈希缓存处理

  • lookup:从缓存中查找应答数据并返回,如果查找不到,则调用pass函数,从后端服务器调用数据。

  • ok:继续执行

  • pass:绕过缓存,直接向后端服务器调用数据

  • pipe:建立客户端和后端服务器之间的直接连接,从后端服务器调用数据

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

  • restart:重新开始

  • retry:重试后端处理

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

3. varnish中内置子程序

  • 子进程也叫状态引擎,每一个状态引擎均有自己限定的返回动作 return (动作); 不同的动作将调用对应下一个状态引擎。

  • 我们可以把一个请求分为多个阶段,每个阶段都会调用不同的状态引擎去操作,这样,我们只要编写出相应的状态引擎,就可以控制每个请求阶段。

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

  • 每个内置子程序都需要通过关键字sub进行定义

1) vcl_recv子程序

2) vcl_pipe子程序

3) vcl_pass子程序

4) vc_hit子程序

5) vcl_miss子进程

6) vcl_hash子进程

7) acl_purge子进程

8) vcl_deliver子进程

9) vcl_backend--fetch子程序

10) vcl_backend_response子程序

11) vcl_backend_error子程序

12) vcl_synth子程序

13) vcl_init子进程

14) acl_fini子进程

4. varnish缓存模式和子程序的关系

l varnish的配置文件,就是通过各种子程序组成的,当varnish运行时,也是通过子程序的配置进行相应的操作

l 子程序的关系如下图

1) 右上角的两个图代表:加载vcl时执行vcl_init子程序,卸载vcl时执行vcl_fini子程序

2) vcl_recv调用 hash函数时

进入该状态后,会通过vcl_hash子程序,根据请求的url或其他信息生成hash键值,

然后查找hash键值相同的缓存数据,若找到,则进入val_hit状态,否则进入vcl_miss状态

3) vcl_recv调用pass 函数时

  • vcl_recv调用pass 函数时,pass将当前请求直接转发到后端服务器。而后续的请求仍然通过varnish处理。

  • pass varnish)通常只处理静态页面。即只在GET HEAD 类型的请求中时才适合调用pass函数。另外,需要注意的一点是,pass模式不能处理POST请求,为什么呢?因为POST请求一般是发送数据给服务器,需要服务器接收数据,并处理数据,反馈数据。是动态的,不作缓存

4) vcl_recv判断需要调用 pipe 函数时

  • vcl_recv判断需要调用 pipe 函数时,varnish会在客户端和服务器之间建立一条直接的连接,之后客户端的所有请求都直接发送给服务器,绕过varnish,不再由varnish检查请求,直到连接断开。

  • 类型是POST时用pipe,举个例子,当客户端在请求一个视频文件时,或者一个大的文档,如.zip .tar 文件,就需要用pipe模式,这些大的文件是不被缓存在varnish中的。


5) vcl_recv指定purge模式时

Purge模式用于清除缓存

5. 优雅模式garce mode

1) 请求合并

  • 当几个客户端请求同一个页面的时候,varnish只发送一个请求到后端服务器,然后让其他几个请求挂起并等待返回结果

2) 问题

  • 如果数以千计或更多的这种请求同时出现,那么这个等待队列将变得庞大,这将导致2类潜在问题:惊群问题(thundering herd problem),即突然释放大量的线程去复制后端返回的结果,将导致负载急速上升;没有用户喜欢等待;

3) 解决问题

  • 配置varnish在缓存对象因超时失效后再保留一段时间,以给那些等待的请求返回过去的文件内容(stale content)

1
2
3
4
5
6
7
8
9
10
11
12
13
14


案例:
Sub vcl_recv {
If (! req.backend.healthy) { 判断如果不健康
set req.grace = 5m;varnish向前端提供5分钟的过期内容
}
else { 如果健康
set req.grace = 15s;varnish向前段提供15秒的过期内容
}
}

sub vcl_fetch {
set beresp.grace = 30m;将失效的缓存对象再多保留30分钟
}

三、 varnish的安装

1. 下载varnish压缩包

有两个地方可下载

1) 通过Varnish的官方网址http://varnish-cache.org,可以在这里下载最新版本的软件。

但是有时候varnish的官网会被墙

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

但是在安装时需要先使用./autogen.sh生成configure编译配置文件

2. Varnish的安装

首先安装依赖包

配置varnish

编译并安装

拷贝vcl文件

官方提供的vcl配置文件没有提示太多的配置信息,在生产环境中还是需要自己进行配置

四、 Varnish VCL实例配置解析

拓扑环境

五、 启动varnish

六、 varnish vcl配置解析

Varnish有自己的编程语法vclvarnish启动时,会将配置文件编译为C语言,然后再执行

1. 后端服务器地址池配置及后端服务器健康检查

1) 后端服务器定义,用于varnish连接指定的后端服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20


例:将监控块定义在后端服务器中
backend web{
.host="192.168.31.83";指定后端服务器的IP
.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) 监视器定义

1
2
3
4
5
6
7
8


例:创建健康监测,定义健康检查名称为backend_healthcheck
probe backend_healthcheck { 创建名为backend_healthcheck的健康检查
.url = "/";监控入口地址为/的
.timeout = 1s;请求超时时间
.interval = 5s;每次轮询间隔5秒
.window = 5;轮询5次
.threshold = 3;必须有3次轮询正常才算该节点正常
}

3) 负载均衡群集directors

  • 负载均衡群集需要directors模块的支持,import directors

  • Directors负载均衡支持的算法:

使用randomhash 必须配置权重值,用于提高随机率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36


例:

加载directors模块
import 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_hintvarnish的预定义变量,作用是指定请求后端节点

  • vcl对象需要使用new关键字创建,所有可创建对象都是内定的,使用前必需import,所有new操作只能在vcl_init子程序中。

2. 访问控制列表(acl

  • 创建一个地址列表,用于后面的判断

  • 如果列表中包含了无法解析的主机地址,它会匹配任何地址。

  • 不想匹配的IP,在前面加个!即可

3. 缓存规则设置

说明:

  • X-Forwarded-For 是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段

  • 要想后端服务器记载客户端的真实IP,只在varnish中设置不行,还需要修改后端web服务器的配置(这里是apache做后端web服务器):

修改框中的变量,指定为varnish中设置的变量

七、 varnish将不同的url发送到不同的后端server

AutoIt Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58


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;
}
定义后端服务器web1和web2


backend img1 {
.host = "img1.lnmmp.com";
.port = "80";
.probe = backend_healthcheck;
}
backend img2 {
.host = "img2.lnmmp.com";
.port = "80";
.probe = backend_healthcheck;
}
定义后端服务器img1和img2



//初始化处理
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$") {
如果请求头为www.benet.com或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)表示关闭忽略大小写


0