关于varnish缓存
前言:本文将以varnish4.X版本为基础,进行学习。
缓存的概念
熟悉一些名词:
-
时间局部性:一个数据被访问过之后,可能很快会被再次访问到;
-
空间局部性:一个数据被访问时,其周边的数据也有可能被访问到
-
数据缓存:例如MySQL到web应用服务器之间的缓存,服务器缓存的资源是数据缓存
-
页面缓存:接入层和应用层中间的缓存,服务器缓存的是可缓存的页面,这层就是缓存层
-
缓存命中率
:hit/(hit+miss),一般高于30%命中率则是正向收益,好的设计系统可以达到80%到95%以上 -
字节命中率:按照数据的字节大小来计算命中率
-
请求命中率:按照请求的数量来计算命中率
-
代理式缓存:客户端访问缓存服务器,缓存服务器没有命中缓存时到后端服务器请求数据,此时它作为反向代理服务器工作,这种类型的缓存服务器叫做代理式缓存
-
旁挂式缓存:客户端亲自去查询数据库,并且将数据复制给缓存服务器一份,下次先去找缓存服务器,如果没有命中则再去数据库服务器查询,此时这种工作方式的缓存叫做旁挂式缓存,这个客户端叫做胖客户端(smart client)
-
private cache:私有缓存,用户代理附带的本地缓存机制
-
public cache:公共缓存,反向代理服务器的缓存功能
-
CND:Content Delivery Network 内容投递系统
-
GSLB:全网均衡调度
-
缓存有效性判断机制
:- 过期时间
- 条件式验证
- Last-Modified/If-Modified-Since:基于文件的修改时间戳来判别
- Etag/If-None-Match:基于文件的校验码来判别
“过期时间验证”缓存是否失效颗粒度太大,如果页面刚刚缓存应用服务器发生了变化,结果客户端拿到的就是过期数据;从而加入了条件式验证缓存的失效性,每次客户端请求到达缓存服务器,缓存服务器都要拿本地的数据和应用服务器的数据比较时间戳,如果时间戳发生了变化则缓存新的数据;这样虽然粒度小了,但是还是会有问题,如果应用服务器在同一秒页面数据变化了三次,而缓存服务器拿到的是第一份数据,这样还是会发生数据失效的问题;从而又引入了Etag(扩展标记)来标记唯一的页面数据。此时虽然解决了数据失效性的问题,但是每次客户端的请求都要去后端服务器做比较,对缓存和应用服务器都是不小的压力,我们不得不采取折中的解决方案就是“过期时间验证+条件式验证”,将不经常变动的页面做过期时间验证,变动频繁的采用条件式验证。
请求报文用于通知缓存服务如何使用缓存响应请求:
cache-request-directive =
"no-cache" 不能使用缓存系统中的缓存响应我,必须先去应用服务器做缓存验证
"no-store" 不能使用缓存系统中的缓存响应我,必须去应用服务器请求响应我
"max-age" "=" delta-seconds
"max-stale" [ "=" delta-seconds ]
"min-fresh" "=" delta-seconds
"no-transform"
"only-if-cached"
cache-extension
响应报文用于通知缓存服务器如何存储上级服务器响应的内容:
cache-response-directive =
"public" 所有缓存系统都可以缓存
"private" [ "=" <"> 1#field-name <"> ] 仅能够被私有缓存所缓存
"no-cache" [ "=" <"> 1#field-name <"> ],可缓存,但响应给客户端之前需要revalidation,即必须发出条件式请求进行缓存有效性验正
"no-store" ,不允许存储响应内容于缓存中
"no-transform" 不能转换格式
"must-revalidate" 必须重新验证
"proxy-revalidate"
"max-age" "=" delta-seconds 私有缓存最大缓存时长
"s-maxage" "=" delta-seconds 公共缓存最大缓存时长
cache-extension
Web Page Cache解决方案:squid和varnish,它们的关系就像Apache和Nginx
一、varnish缓存
1. 简介
Varnish cache,是一套高性能的开源反向网站缓存服务器(reverse proxy server)、HTTP加速器 ,很多门户网站已经部署了varnish,并且性能要比squid高上许多,甚至比squid还稳定,且效率更高,资源占用更少。
特点:
- Varnish可以使用内存也可以使用硬盘进行数据缓存
- 支持虚拟内存的使用
- 有精确的时间管理机制
- 状态引擎架构:通过特定的配置语言设计不同的语句
- 以二叉堆格式管理缓存数据
优势:
- 稳定性强:将worker单独分开;
- 速度快:采用“page Cache”技术,所有缓存数据直接从内存读取;
- 支持多并发;
- 通过管理端口,使用正则批量清除部分缓存;或者通过页面清;
- 通过fork打开多进程处理
劣势:
- 进程一旦crash或重启,缓存的数据将从内存中完全释放
- 在多台varnish实现负载均衡时,每次请求都会落到不同的varnish服务器中,造成url请求可能会穿透到后
- 劣势解决方案
- a. 在varnish的后端添加squid/nignx代理,这样防止了当varnish缓存被清空时,瞬间大量的请求发往web服务器
- b. 在负载均衡上做url哈西,让单个url请求固定请求到一台varnish服务器上
2. 总体结构
如上图所示,主要进程:Management、Cacher。
2.1 两个主进程
2.1.1 Management进程
Management进程主要实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等。Management进程会每隔几秒钟探测一下Child进程以判断其是否正常运行,如果在指定的时长内未得到Child进程的回应,Management将会重启此Child进程。
Manager管理的接口:
- CLI interface :命令行接口
- Telnet interface :telnet接口
- Web interface :Web管理接口
2.1.2 Child/Cacher进程
进程包括多个线程:
- Accept 线程:接收新的连接请求并响应
- Worker 线程:child进程会为每个会话启动一个worker线程,因此,在高并发的场景中可能会出现数百个worker线程甚至更多;
- Object Expiry 线程:从缓存中清理过期内容;
- Commad line 线程 : 管理接口
- Storage/hashing 线程 :缓存存储
- Log/stats 线程:日志管理线程
- Backend Communication 线程:管理后端主机线程
- Varnish依赖“工作区(workspace)”以降低线程在申请或修改内存时出现竞争的可能性。在varnish内部有多种不同的工作区,其中最关键的当属用于管理会话数据的session工作区。
2.2 Varnish的日志收集
为了与系统的其它部分进行交互,Child进程使用了可以通过文件系统接口进行访问的共享内存日志(shared memory log),因此,如果某线程需要记录信息,其仅需要持有一个锁,而后向共享内存中的某内存区域写入数据,再释放持有的锁即可。而为了减少竞争,每个worker线程都使用了日志数据缓存。共享内存日志大小一般为90M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。
varnish提供了多个不同的工具如varnishlog、varnishncsa或varnishstat等
来分析共享内存日志中的信息并能够以指定的方式进行显示。
2.3 VCL—varnish配置缓存策略的工具
Varnish Configuration Language(VCL)是varnish配置缓存策略的工具,它是一种基于域(domain specific)的简单编程语言,它支持有限的算术运算和逻辑运算操作、允许使用正则表达式进行字符串匹配、允许用户使用set自定义变量、支持if判断语句,也有内置的函数和变量等。
使用VCL编写的缓存策略通常保存至.vcl文件中(默认文件:$varnish_home/default.vcl
),其需要编译成二进制的格式后才能由varnish调用,即编写的.vcl文件由VCL compiler来编译,VCL compiler调用C compiler来编译后由management来读取生效(读取是及时的),编译后management让各Child进程来应用生效(因为编译成sharedobject为各子进程各读取了一份)。事实上,整个缓存策略就是由几个特定的子程序如vcl_recv、vcl_fetch等组成,不同的子程序在不同的时间里执行,比如一个子程序在接到请求时执行,另一个子程序在接收到后端服务器传送的文件时执行。如果没有事先为某个位置自定义子例程,varnish将会执行默认的定义。
VCL策略在启用前,会由management进程将其转换为C代码,而后再由gcc编译器将C代码编译成二进制程序。编译完成后,management负责将其连接至varnish实例,即child进程。正是由于编译工作在child进程之外完成,它避免了装载错误格式VCL的风险。因此,varnish修改配置的开销非常小,其可以同时保有几份尚在引用的旧版本配置,也能够让新的配置即刻生效。编译后的旧版本配置通常在varnish重启时才会被丢弃,如果需要手动清理,则可以使用varnishadm的vcl.discard命令完成。
二、Varnish的工作原理(more)
1. VCL内置函数与状态引擎的概念
每个请求都被单独处理,处理过程将处在不同的状态中进行,退出一种状态就会转入下一个状态。
状态之间存在相关性,但彼此间互相隔离,下面的图中可以清楚的看出状态的转换,以及不同状态所要经过的处理函数。
- 4.0
- 椭圆形部分称为varnish的
状态节点
,又称为vcl状态引擎
,还有种叫法为varnish的vcl内置函数
(子程序)。 - 红色的线条:没有查询缓存/缓存中数据过期/缓存中没有数据 情况下的流程
- 橙黄色的线条:直接将匹配数据通过内置函数vcl_pipe送往后台主机的流程
- 绿色的线条:查询缓存数据命中且数据没有过期/经后台主机返回的数据经被缓存后返回给客户流程
- 蓝色的线条:数据没有命中缓存向后台主机发出查询的流程
- 黑色的线条:数据从后台主机返回给varnish缓存的流程
状态引擎
是被概念化的VCL内置函数
或叫VCL子程序
,以vcl_前缀开头,在引擎中,可以对每个请求中的http 首部或者其他各方面的内容进行检查或者修改操作。return(action)代码表示中断一个状态,其中action是vcl关键字,用来指向下一步去向哪个状态引擎。
状态引擎
即在VCL子程序
被调用时,按照VCL子程序
中自定义的规则策略做出的阶段性动作。
2. VCL处理流程
VCL背后的基础概念:varnish开始处理一个请求时,首先需要分析HTTP请求本身,比如从首部获取请求方法、验正其是否为一个合法的HTT请求等。当这些基本分析结束后就需要依据第一个决策来进行检查进而做出判断,即varnish是否从缓存中查找请求的资源。这个决定的实现则需要由VCL来完成,简单来说,要由vcl_recv方法来完成。如果管理员没有自定义vcl_recv函数,varnish将会执行默认的vcl_recv函数。然而,即便管理员自定义了vcl_recv,但如果没有为自定义的vcl_recv函数指定其终止操作(terminating),其仍将执行默认的vcl_recv函数。事实上,varnish官方强烈建议让varnish执行默认的vcl_recv以便处理自定义vcl_recv函数中的可能出现的漏洞。
VCL处理流程就是根据由各个策略组成的规则来进行各种动作。
2.1 内置函数的作用
结合上节内容,VCL处理流程可分两个区域:
- 前端 frontend
- 后端 backend
frontend区域
阶段1
vcl_recv:用于接受和处理客户端请求的状态引擎。当请求到达并成功接收后被调用,通过判断请求的数据来决定如何处理请求。例如如何响应、怎么响应、使用哪个后端服务器等。
阶段2
vcl_hash:进行hash计算,不进行判读处理,计算之后送往各个第三阶段状态引擎中
阶段3
vcl_hit:从缓存中查找到缓存对象时要执行的操作;
vcl_miss:从缓存中款查找到缓存对象时要执行的操作;
vcl_pass:用于将请求直接传递至后端主机,后端主机在应答数据后将应答数据发送给客户端,跳过缓存。
vcl_purge:清理缓存
vcl_pipe:对于无法理解的用户请求,将请求直接发往后端主机;
阶段4
vcl_deliver:将用户请求的内容响应给客户端时用;
vcl_synth:接受来自vcl_purge的任务,对于指定的缓存,进行删除处理
backend区域
阶段1
vcl_backend_fetch:接受来自前端状态vcl_pass或vcl_miss 的任务,向后端主机请求
阶段2
vcl_backend_response:接受到后端返回正常状态报文,进行是否缓存检查,需要缓存的响应将其缓存,不需要则不缓存,最后送到vcl_deliver
vcl_backend_error:后端主机错误,返回错误响应
两个特殊状态引擎(4.0版本)
vcl_init:在处理任何请求之前要执行的vcl代码:主要用于初始化VMODs;
vcl_fini:所有的请求都已经结束,在vcl配置被丢弃时调用;主要用于清理VMODs;
2.2. 常见的状态引擎之间的处理流程
如果缓存命中:
用户请求–>vcl_recv–>vcl_hash–>vcl_hit–>vcl_deliver–>响应给用户
如果缓存未命中:
用户请求–>vcl_recv–>vcl_hash–>vcl_miss–>vcl_backend_fetch–>后端服务器接受请求发送响应报文–>vcl_backend_response–>vcl_deliver
或:
用户请求–>vcl_recv–>vcl_hash–>vcl_miss–>vcl_pass–>vcl_backend_fetch–>后端服务器接受请求发送响应报文–>vcl_backend_response–>vcl_deliver–>响应给用户
如果不能从缓存中进行响应
用户请求–>vcl_recv–>vcl_hash–>vcl_pass–>vcl_backend_fetch–>后端服务器接受请求发送响应报文–>vcl_backend_response–>vcl_deliver–>响应给用户
如果进行缓存修剪
用户请求–>vcl_recv–>vcl_hash–>vcl_purge–>vcl_synth–>返回给用户
如果请求报文无法理解
用户请求–>vcl_recv–>vcl_hash–>vcl_pipe–>交给后端服务器
三、varnish程序环境(more)
Varnish程序的组成部分,大致分以下几类:
- varnish的程序配置
/etc/varnish/varnish.params: 配置varnish服务进程的工作特性,例如监听的地址和端口,缓存机制;
/etc/varnish/default.vcl:配置各Child/Cache线程的工作属性;编写缓存策略的核心配置,通过官网学习最新的Varnish子进程的使用。 - 主程序
/usr/sbin/varnishd - CLI interface
/usr/bin/varnishadm:登录管理程序 - VCL配置文件重载程序
/usr/sbin/varnish_reload_vcl - Shared Memory Log交互工具
/usr/bin/varnishhist
/usr/bin/varnishlog
/usr/bin/varnishncsa
/usr/bin/varnishstat
/usr/bin/varnishtop - 测试工具程序
/usr/bin/varnishtest - Systemd Unit File
varnish服务:/usr/lib/systemd/system/varnish.service
logger daemon:/usr/lib/systemd/system/varnishlog.service
lgger daemon in apache format:/usr/lib/systemd/system/varnishncsa.service
1. 配置文件
1.1 缓存策略配置文件
/etc/varnish/default.vcl
涉及知识点将在下篇文章VCL配置语言中整理
1.2 varnish.params
/etc/varnish/varnish.params
默认配置如下:
# 将其设置为1以使systemd重新加载尝试切换VCL而不重新启动。
RELOAD_VCL=1
# 指定配置文件
VARNISH_VCL_CONF=/etc/varnish/default.vcl
# varnish服务监听的IP地址,默认为所有地址
VARNISH_LISTEN_ADDRESS="服务的IP地址"
# 监听的端口默认为6081
VARNISH_LISTEN_PORT=6081
# 接受管理程序监听的IP
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
# 管理端口
VARNISH_ADMIN_LISTEN_PORT=6082
#t 密钥文件的路径
VARNISH_SECRET_FILE=/etc/varnish/secre
# 使用的缓存机制
VARNISH_STORAGE="malloc,256M"
# 使用varnish用户运行varnishi服务
VARNISH_USER=varnish
VARNISH_GROUP=varnish
# 设置线程池中最小线程和最大线程数及线程空闲时间
# thread_pools:工作线程数,最好小于或等于CPU核心数量
# thread_pool_max:每线程池的最大线程数
# thread_pool_min:最大空闲线程数
# thread_pool_timeout:空闲超过多长时间被清除
# thread_pool_add_delay:生成线程之前等待的时间
# thread_pool_destroy_delay:清除超出最大空闲线程数的线程之前等待的时间
# DAEMON_OPTS="-p thread_pool_min=5 -p thread_pool_max=500 -p thread_pool_timeout=300"
2. 管理工具(more)
2.1 varnishd
-s [name=]type[,options]
:定义缓存数据的存储方式malloc[,size]
:内存存储,[,size]用于定义空间大小;重启后所有缓存项失效file[,path[,size[,granularity]]]
:磁盘文件存储,黑盒;重启后所有缓存项失效persistent,path,size
:文件存储,黑盒;重启后所有缓存项有效;实验阶段,不建议使用
-a address[:port][,address[:port][...]
:服务监听端口,默认为6081端口-T address[:port]
:管理服务监听端口,默认为6082端口-f config
:VCL配置文件-F
:运行于前台-p param=value
:设定运行参数及其值; 可重复使用多次-r param[,param...]
: 设定指定的参数为只读状态
RPM安装 启动方式:
#/usr/lib/systemd/system/varnish.service #system启动项设置,可参考
systemctl restart varnish
2.2 varnishadm
- 登录管理系统
varnishadm -S /etc/varnish/secret -T address[:port]
help [<command>] 获取帮助
ping [<timestamp>] 测试服务器
auth <response>
quit 退出cli
banner
status 显示状态
start 启动
stop 停止
vcl.load <configname> <filename> 加载VCL配置文件
vcl.inline <configname> <quoted_VCLstring>
vcl.use <configname> 激活VCL配置文件
vcl.discard <configname> 删除VCL配置
vcl.list 列出VCL配置
param.show [-l] [<param>] 列出当前运行的参数
param.set <param> <value> 运行参数临时调整
panic.show
panic.clear
storage.list 列出数据存储信息
vcl.show [-v] <configname> 列出VCL详细配置
backend.list [<backend_expression>] 列出后端服务器
backend.set_health <backend_expression> <state>
ban <field> <operator> <arg> [&& <field> <oper> <arg>]...
2.3 日志交换工具
2.3.1 varnishstat
- 显示指定参数的当前统计数据
varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss
- 列出指定配置段的每个参数的意义
varnishstat -l -f MAIN -f MEMPOOL
2.3.2 varnishtop
-1
:打印统计信息一次并退出,而不是持续更新的显示-i taglist
:可以同时使用多个-i选项,也可以一个选项跟上多个标签-I <[taglist:]regex>
:对指定的标签的值基于regex进行过滤-x taglist
:排除列表-X <[taglist:]regex>
:对指定的标签的值基于regex进行过滤,符合条件的予以排除
[sleepy↓]