共计 5175 个字符,预计需要花费 13 分钟才能阅读完成。
简介
Varnish 是一款高性能、开源的缓存反向代理服务器。它从客户端接受请求,并尝试从缓存中响应请求,如果无法从缓存中提供响应,Varnish 向后端服务器发起请求,获取响应,将响应存储在缓存中,然后把响应发送给客户端。如果 Varnish 能够从 Cache 中响应一个请求,所消耗的时间是微秒级别的,这个响应速度比直接从 HTTP 服务器响应请求的速度要快两个数量级,缓存命中率越高,网站的访问速度就越快。
主要特性
- 缓存位置:可以使用内存也可以使用磁盘,如果要使用磁盘的话推荐 SSD 做 RAID1;
- 日志存储:日志也存储在内存中,存储策略:固定大小,循环使用;
- 支持虚拟内存的使用;
- 有精确的时间管理机制,即缓存的时间属性控制;
- 状态引擎架构:在不同的引擎上完成对不同的缓存和代理数据进行处理,可以通过特定的配置语言设计不同的控制语句,以决定数据在不同位置以不同方式缓存;
- 缓存管理:以二叉堆格式管理缓存数据,做到数据的及时清理。
系统架构
Varnish 主要有两个进程:Management 进程与 Child 进程(也称为 Cache 进程)。
- Management 进程:主要对子进程进行管理,实现应用新的配置、编译 VCL、监控 varnish、初始化 varnish 以及提供一个命令行接口等;Management 进程会每隔几秒钟探测一下 Child 进程以判断其是否正常运行,如果在指定的时长内未得到 Child 进程的回应,Management 将会重启此 Child 进程。
- Child 进程:生成线程池,负责对用户请求进行处理,并通过 hash 查找返回用户结果。
Child 进程包含多种类型的线程,常见的有:
- Accept 线程:接收新的连接请求并响应;
- Worker 线程:child 进程会为每个会话启动一个 worker 线程,因此在高并发的场景中可能会出现数百个 worker 线程甚至更多;
- Object Expiry 线程:从缓存中清理过期内容;
- Commad line 线程 : 管理接口;
- Storage/hashing 线程:缓存存储;
- Log/stats 线程:日志管理线程;
- Backend Communication 线程:管理后端主机线程。
日志
为了与系统的其它部分进行交互,Child 进程使用可以通过文件系统接口进行访问的共享内存日志(shared memory log),因此如果某线程需要记录信息,其仅需要持有一个锁,而后向共享内存中的某内存区域写入数据,再释放持有的锁即可;需要注意,为了减少竞争,每个 worker 线程都使用了日志数据缓存。
共享内存日志大小一般为 90M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。Varnish 提供了多个不同的工具,如 varnishlog、varnishncsa 或 varnishstat 等来分析共享内存日志中的信息并能够以指定的方式进行显示。
算法
Varnish 的 Director 支持的挑选方法中主要有 round-robin(轮询)和 random(随机)两种。其中,round-robin 类型没有任何参数,只需要为其指定各后端主机即可,并在某后端主机故障时不再将其视作挑选对象;random 方法随机从可用后端主机中进行挑选,每一个后端主机都需要一个.weight 参数指定其权重,同时还可以使用.retires 参数来设定查找一个健康后端主机时的尝试次数。
Varnish2.1.0 后,random 挑选方法又多了两种变化形式 client 和 hash。client 类型的 Director 使用 client.identity 作为挑选因子,这意味着 client.identity 相同的请求都将被发送至同一个后端主机;client.identity 默认为 cliet.ip,但也可以在 VCL 中将其修改为所需要的标识符。类似地,hash 类型的 Director 使用 hash 数据作为挑选因子,这意味着对同一个 URL 的请求将被发往同一个后端主机,其常用于多级缓存的场景中。无论是 client 还 hash,当其倾向于使用后端主机不可用时将会重新挑选新的后端其机。
VCL 工具
Varnish Configuration Language(VCL),Varnish 配置缓存策略的工具,它是一种基于“域”(domain specific) 的简单编程语言,可以使用运算符包括“=、==、!、&&”等,支持使用正则表达式进行字符串匹配,允许用户使用 set 自定义变量,支持 if 判断语句,也有内置的函数和变量等。VCL 策略在启用前,会由 management 进程将其转换为 C 代码,而后再由 gcc 编译器将 C 代码编译成二进制程序,编译完成后 management 负责将其连接至 varnish 实例,即 child 进程。
后端存储
Varnish 支持多种不同类型的后端存储,这可以在 varnishd 启动时使用 - s 选项指定。后端存储的类型包括:
- file:使用特定的文件存储全部的缓存数据,并通过操作系统的 mmap() 系统调用,将整个缓存文件映射至内存区域 ( 如果条件允许);
- malloc:使用 malloc() 库调用在 varnish 启动时向操作系统申请指定大小的内存空间以存储缓存对象;
- persistent(experimental):与 file 的功能相同,但可以持久存储数据 (即重启 varnish 数据时不会被清除),但仍处于测试阶段。
Varnish 无法追踪某缓存对象是否存入了缓存文件,也就无从得知磁盘上的缓存文件是否可用,因此 file 存储方法在 varnish 停止或重启时会清除数据;而 persistent 方法的出现对此有了一个弥补,但 persistent 仍处于测试阶段,其仅适用于有着巨大缓存空间的场景。
选择使用合适的存储方式有助于提升系统性。从经验的角度来看,建议在内存空间足以存储所有的缓存对象时使用 malloc 的方法,而 file 存储有着更好的性能表现。需要注意的是,varnishd 实际上使用的空间比使用 - s 选项指定的缓存空间更大,一般说来,其需要为每个缓存对象多使用差不多 1K 左右的存储空间,这意味着,对于 100 万个缓存对象的场景来说,其使用的缓存空间将超出指定大小 1G 左右。另外,为了保存数据结构等,varnish 自身也会占去不小的内存空间。
为 varnishd 指定使用的缓存类型时,-s 选项可接受的参数格式如下:
- malloc [size] 或 file [path [size [granularity]]] 或 persistent path size {experimental}
VCL 内置函数
- vcl_recv 函数:用于接收和处理请求。当请求到达并成功接收后被调用,通过判断请求的数据来决定如何处理请求。此函数一般以如下几个关键字结束:
- pass:进入 pass 模式,把请求控制权交给 vcl_pass 函数;
- pipe:进入 pipe 模式,把请求控制权交给 vcl_pipe 函数;
- error code [reason]:返回 code 给客户端并放弃处理该请求;code 是错误标识,例如 200、405 等;reason 是错误提示信息。
- vcl_pipe 函数:此函数在进入 pipe 模式时被调用,用于将请求直接传递至后端主机,在请求和返回的内容没有改变的情况下,将不变的内容返回给客户端,直到这个链接关闭。此函数一般以如下几个关键字结束:
- error code [reason]
- pipe
- vcl_pass 函数:此函数在进入 pass 模式时被调用,用于将请求直接传递至后端主机,后端主机应答数据后送给客户端,但不进行任何缓存,在当前连接下每次都返回最新的内容。此函数一般以如下几个关键字结束:
- error code [reason]
- pass
- vcl_hash:表示在缓存里查找被请求的对象,并且根据查找的结果把控制权交给函数 vcl_hit 或者函数 vcl_miss
- vcl_hit 函数:在执行 vcl_hash 后,如果在缓存中找到请求的内容,将自动调用该函数。此函数一般以如下几个关键字结束:
- deliver:表示将找到的内容发送给客户端,并把控制权交给函数 vcl_deliver
- error code [reason]
- pass
- vcl_miss 函数:在执行 val_hash 后,如果没有在缓存中找到请求的内容时自动调用该方法,此函数可以用于判断是否需要从后端服务器取内容。此函数一般以如下几个关键字结束:
- fetch:表示从后端获取请求的内容,并把控制权交给 vcl_fetch 函数
- error code [reason]
- pass
- vcl_fetch 函数:在从后端主机更新缓存并且获取内容后调用该方法,接着,通过判断获取的内容来决定是否将内容放入缓存,还是直接返回给客户端。此函数一般以如下几个关键字结束:
- error code [reason]
- pass
- deliver
- vcl_deliver 函数:在缓存中找到请求的内容后,发送给客户端前调用此方法。此函数一般以如下几个关键字结束:
- error code [reason]
- deliver
- vcl_timeout 函数:此函数在缓存内容到期前调用。一般以如下几个关键字结束:
- discard:表示从缓存中清除该内容。
- fetch
- vcl_discard 函数:在缓存内容到期后或缓存空间不够时,自动调用该方法。此函数一般以如下几个关键字结束:
- keep:表示将内容继续保留在缓存中
- discard
以下是 VCL 处理流程图,通过下图可以更清楚 Varnish 的工作过程:
Varnish 处理 HTTP 请求 的过程分为以下几个步骤:
- Receive 状态,也就是请求处理的入口状态,根据 VCL 规则判断该请求应该 Pass 或 Pipe,或者进入 Lookup(本地查询);
- PIPE 状态,不可缓存数据,直接管道后端处理;
- Lookup 状态,进入此状态后,会在 hash 表中查找数据,若找到,则进入 Hit 状态,否则进入 miss 状态;
- Pass 状态,在此状态下,会进入后端请求,即进入 Fetch 状态;
- Fetch 状态,在 Fetch 状态下,对请求进行后端获取,发送请求,获得数据,并进行本地存储;
- Deliver 状态,将获取到的数据发送给客户端,然后完成本次请求。
VCL 内置公用变量
- 当请求到达后,可以使用的公用变量如下所示:
- req.backend:指定对应的后端主机
- server.ip:表示服务器端 IP
- client.ip:表示客户端 IP
- req.request:指定请求的类型,例如 GET、HEAD、POST 等
- req.url:指定请求的地址
- req.proto:表示客户端发起请求的 HTTP 协议版本
- req.http.header:表示对应请求中的 http 头部信息
- req.restarts:表示请求重启的次数,默认最大值为 4
- Varnish 在向后端主机请求时,可以使用的公用变量如下所示:
- beresp.request:指定请求的类型,例如 GET、HEAD 等
- beresp.url:指定请求的地址
- beresp.proto:表示客户端发起请求的 HTTP 协议版本
- beresp.http.header:表示对应请求中的 http 头部信息
- beresp.ttl:表示缓存的生存周期,也就是 cache 保留多长时间,单位是秒
- 从 cache 或者后端主机获取内容后,可以使用的公用变量如下所示:
- obj.status:返回内容的请求状态代码,例如 200、302、504 等
- obj.cacheable:返回的内容是否可以缓存,也就是说,如果 HTTP 返回是 200、203、300、301、302、404、410 等,并且有非 0 的生存期,则可以缓存
- obj.valid:表示是否是有效的 HTTP 应答
- obj.response:返回内容的请求状态信息
- obj.proto:返回内容的 HTTP 协议版本
- obj.ttl:返回内容的生存周期,也就是缓存时间,单位是秒
- obj.lastuse:返回上一次请求到现在的间隔时间,单位是秒
- 对客户端应答时,可以使用的公用变量如下所示:
- resp.status:返回客户端的 HTTP 状态代码
- resp.proto:返回客户端的 HTTP 协议版本
- resp.http.header:返回客户端的 HTTP 头部信息
- resp.response:返回客户端的 HTTP 状态信息
如果想要了解更多请查阅 Varnish 官方文档 查看文档。
缓存服务器 Varnish 概念篇 https://www.linuxidc.com/Linux/2014-05/101389.htm
Varnish 缓存实现动静分离 https://www.linuxidc.com/Linux/2016-11/137152.htm
Linux 中 Varnish 基础应用 https://www.linuxidc.com/Linux/2016-08/134025.htm
Varnish 的详细介绍 :请点这里
Varnish 的下载地址 :请点这里
: