本章概要
- 缓存的需求性
- Varnish介绍
- Varnish结构
- Varinsh配置
1、缓存的需求性
- 一般网站架构:
前端 javascript
后端 java,php 服务器端执行的程序 - 网站资源的动静分离:
静态资源:
图片服务器组:静态资源,单独存放在图片服务器
前后无关联,即无状态,可使用短连接调度算法,如wrr
前端内容组:html,css,js文件
在服务器端这些文件显示为静态,只需存放在web服务器(httpd或nginx服务器)上即可
动态资源:
后端业务处理层:.php文件
客户端请求某php动态资源,php服务器把程序执行后形成的动态资源发送给客户端 - 对于中小站点,一般会将静态和动态资源打包在一块
静态请求由nginx处理,把动态资源请求反代给php程序处理,同时,二者共享同一个目录或一个共享存储使得资源能够同步 - 应用程序版本变更
灰度发布:
使用排干模式逐步更换服务器,实现线上更换应用程序版本
金丝雀发布
先发布新版本给部分特殊用户(如vip)使用,如果没有问题在全部发布
蓝绿发布
生产环境和备用环境交替更换 - 数据存储:
mysql,nosql,mangodb
数据服务器,如果需求较大,需要做集群
对数据库做读写分离,需要读写分离器,并且对读写分离器做高可用
降低服务器压力
降低写压力:做分布式,一部分服务器只存储一部分数据
降低读压力:利用缓存 - 数据访问
访问日志:文件系统接口
访问数据库:数据库API接口
由于大部分人并没有能力通过API接口访问数据库,因此需要通过客户端工具与数据库进行交互 - 数据分类:
结构化数据:存储在关系型数据库
数据存储要求严格规范,牵一发而动全身,不能随意修改
半结构化数据:nosql,mangodb
数据存储并不规范
非结构化数据
靠文件接口访问,数据并不带元数据
注意:分布式存储对外提供文件系统接口称为分布式文件系统,如果不对外提供存储接口称为分布式块存储
根据数据类型存在对应的存储系统
结构化存储系统
半结构化存储系统
非结构化存储系统 - 数据热区
程序的运行具有局部性特征:
时间局部性:一个数据被访问过之后,可能很快会被再次访问到;
空间局部性:一个数据被访问时,其周边的数据也有可能被访问到
数据存在时间局部性和空间局部性,把用户访问次数较多的数据存放在性能好的服务器上,提升数据的转发和响应效率,提升用户体验 - 缓存服务器
把热区数据放置在缓存服务器上,缓存服务器能够承载来自前度80%-90%的访问量,降低后端服务器的压力,提升服务器性能
静态资源请求调度给缓存服务器,动态资源请求调度给动态资源服务器(php服务器)
缓存服务器能够提升数据命中率,基于url进行调度 - 缓存服务器里用户较远,提升用户访问体验,可以分不同区域使用CDN加速器,把数据缓存在CDN加速器上,使用户根据本区域的CDN加速访问服务器
- CDN加速器类型
自建CDN
第三方CDN服务 - 为了防止CDN加速单点失败问题,本地服务器需要做过载保护:限流、服务降级
限流:限流器,根据承载能力对访问量进行限制,超出限制的访问量进入排队等待状态
服务降级:关闭非重要功能的服务,只提供核心功能的服务 - 防止IDC机房整体出现问题,可以构建异地机房
异地机房作用:
数据同步
异地双活
2、varnish介绍
Varnish
- 缓存工作模式:
代理式缓存:(递归查询)
代理功能:如果缓存服务器本地拥有缓存,会先检索本地缓存数据,如果缓存命中(hit),则缓存服务器直接封装响应报文返回给客户端;如果本地没有缓存,由缓存服务器向上游服务器获取数据,根据上游服务器的提示,查看数据是否能够被缓存,如果可以缓存,则缓存到本地再响应给客户端,如果不可以缓存,则直接响应给客户端
旁路式缓存:(迭代查询) memcache
客户端向数据库查询数据,获取数据后,由用户自己判断是否存储在缓存服务器中,如果后续查询数据,客户端需要先去查询缓存服务器的缓存数据,如果缓存命中则直接返回给客户端,如果没有命中则需要客户端自行再去数据库查询数据
该模式的缓存依赖于客户端,客户端需要知道数据库服务器、缓存服务器的位置,还需要判断数据是否可以缓存,我们称这种客户端为smart client(智能客户端) - Varnish
代理式缓存
主要为http协议提供缓存 - 代理式缓存
多级缓存结构:
浏览器缓存:用户本地私有缓存
本地缓存未命中,客户端正向代理缓存:公共缓存
CDN加速器
正向代理缓存未命中,调度器之后的缓存服务器:公共缓存
缓存服务器未命中,后端服务器原始内容 - 版本
http1.0---http1.1---http2.0
缓存的构建
- 基于过期时间构建缓存 expire time
缺点:
刚缓存下来的数据,服务器端的原始数据有可能发生变化,在接下来的过期时间之内,数据是不准确的;
由于所在时区不同,服务器时间也不相同;
已经到达过期时间,但是服务器端的原始数据并没有发生改变,但是仍然需要去后端服务器获取数据,对缓存服务器造成更大的IO压力 - 基于条件式请求构建缓存
每一次客户端请求数据之前,代理(缓存)服务器检索本地缓存,无法确定缓存的有效性,向后端服务器询问原始内容是否发生变化
如果数据没有过期,后端服务器响应给代理(缓存)的报文只是一个没有过期的答案,报文中并不带有数据内容本身,这样就节省了带宽
如果数据过期,则后端服务器把新的数据发送给代理(缓存),代理(缓存)把新的内容代替旧的内容缓存在本地,然后再发送给客户端 - 条件式请求构建缓存的方式
If-Modified-Since
条件式请求模式中,代理(缓存)发送的请求报文中,报文头部信息中带有特殊的首部信息:If-Modified-Since,即询问服务器数据的时间戳是否和自己一致;
服务器的响应报文中:
304,Not Modified 即原始内容没有变化,响应报文只有头部信息,没有body实体部分
200 原始内容发生变化,响应报文有头部和body实体(即新的数据内容)
If-None-Match
由于基于时间戳对比精确度只到秒级,对于内容变化频率较多的网站:
使用另外一种条件式请求:If-None-Match,每一次服务器端响应代理(缓存)服务器内容时,不再做时间戳对比,而是根据文件内容的校验码进行对比,也就是说对文件内容进行指纹计算并生成扩展标记Etag,每一次代理服务器请求数据时,只对Etag标记进行对比
响应报文中:
304,Not Match 是指匹配,原始数据内容没有发生改变
200 是指不匹配,把新的数据北荣发送给地阿里服务器 - 结合两种方式使用,即过期时间+条件式请求
客户端发送请求,向代理服务器请求数据,代理服务器查询本地缓存,假设缓存没有命中,向原始服务器获取原始数据内容,原始服务器发送原始数据并告知代理服务器数据的缓存时间如2小时,代理获取原始内容后缓存在本地,该缓存在2小时之内有效。如果再有客户端请求该数据,在两个小时之内,代理服务器直接把缓存响应给客户端,不再到原始服务器获取内容
两小时之后,有可能原始服务器的内容并没有发生改变,缓存数据依然可以使用,因此,代理服务器并不会直接清理本地缓存,而是询问原始服务器数据内容是否发生改变,如果原始服务器回应数据没有发生改变,那么缓存数据的有效期再次更改为2小时,如果数据发生改变,则响应200响应码,发送新的数据给代理服务器 - Cache-Control
版本:http1.1
请求和响应报文中存在独特的首部:Cache-Control,用以控制缓存功能,以K/V格式定义缓存的使用方式
请求报文使用Cache-Control,是告诉原始服务器该如何响应自己,如:不能使用缓存响应,只能用原始内容响应
响应报文使用Cache-Control,是告诉缓存服务器缓存的方式,如:数据缓存时间,哪种数据能缓存哪种不能缓存
响应报文中,Cache-Control的作用就是告诉缓存服务器哪些是公共缓存数据,哪些是私有缓存数据
private 可被私有缓存服务器缓存
public 可以被公共和私有缓存服务器所缓存
缓存服务器分为公共缓存public和私有缓存private
公共缓存服务器:存储可以被众多客户端使用的缓存数据
私有缓存服务器:如本地浏览器的缓存,不可能共享给别人的缓存数据
Cache-Control在请求报文中的选项:
max-age 指定私有缓存时长
s-maxage 指定公共缓存时长
no-cache 表示可缓存,但不能直接用缓存响应客户端,即响应之前进行校验,询问服务器端是否能使用该缓存响应客户端
no-store 不允许存储响应内容于缓存中,即不能缓存
max-stale 最大允许过期多长时间的缓存响应客户端
缓存服务器缓存失效,去后端服务器获取新内容,但找不到后端服务器,使用过期缓存响应客户端
Cache-Control在响应报文中的选项:
private 可被私有缓存服务器缓存
public 可以被公共和私有缓存服务器所缓存
max-age 指定私有缓存时长
s-maxage 指定公共缓存时长
no-cache 表示可缓存,但不能直接用缓存响应客户端,即响应之前进行校验,询问服务器端是否能使用该缓存响应客户端
no-store 不允许存储响应内容于缓存中,即不能缓存
must-revalidate 类似于no-cache
3、varnish结构
- Varnish
开源代理服务器系统,缓存系统
存在多个版本,每个版本互不兼容 - 官网:varnish-cache.org
- squid和varnish相当于http和nginx
varnish组成
-
manager
负责配置文件分析、定义、加载,管理cacher
使用VCL配置语言写配置文件
由VCC编译器把用户使用VCL语言定义的配置文件转为c代码,调用c语言编译器,编译为共享模块,被多个cacher process使用
VCL主要定义缓存如何工作,如定义哪些能查缓存,哪些不能,以及哪些被阻止不让通过等
VCC varnish配置文件编译器
用来做代码转换,把用户定义的配置文件转为c代码,调用c语言编译器,编译为共享模块,被多个cacher process使用,而编译器中使用的这种配置语言叫做VCL
VCL varnish配置语言
varnishadm 配置语言使用工具
varnish监听6081和6082端口
6081端口是为了防止与http的80端口冲突
6082端口则是varnish的管理端口
VAC varnish程序控制台,也是varnish的交互工具,但企业版才有的功能 -
cacher
生成多线程处理工作
接收客户端请求的线程
处理客户端请求的线程
清理过期缓存的线程 -
shared memory log 单独划分的区域
用于存放统计数据和日志区域,固定大小为80-90m
由于大小固定,内容会被循环覆盖,无法长久保存日志,因此需要人为单独启动进程用以保存数据
注意:varnish也使用类似于netfilter框架的模式
netfilter的架构就是在整个网络流程的若干位置放置了一些检测点(HOOK),而在每个检测点上登记了一些处理函数进行处理。 -
缓存服务器中报文转发过程
当请求报文到达缓存服务器,先分析请求报文是否为标准的http请求报文,
如果不是,则用四层代理方式进行处理,打开一个管道pipe,直接以四层代理方式转发出去。
如果是,就用七层代理方式进行处理,分析请求报文中的请求方法,只缓存GET,HEAD方法;
接下来,判定是否可缓存的方法,分析是否可缓存请求,
如果不是可缓存请求,直接去后端服务器获取数据;
如果是可缓存请求,就查询缓存
如果命中缓存,从缓存中取数据,
如果不命中缓存,则也要直接去后端服务器获取数据
最后把获取到的数据或未获取到数据的状态响应给客户端
注意:每一个状态引擎都有自己的专用配置,我们称之为域专用配置 -
varnish4.0版本
前端 面向客户端
后端 面向后端服务器 -
varnish存在两类配置:
配置varnishd守护进程的工作方式,
如监听的地址、端口等,与缓存本身没有关系
配置缓存系统
vcl -
状态引擎:state engine
前端frontend:
vcl_recv
vcl_hash
vcl_hit
vcl_pass
vcl_miss
vcl_purge
vcl_pipe
vcl_deliver
vcl_synth
后端backend:
vcl_backend_fetch
vcl_backend_response
vcl_backend_error
vcl_init,vcl_fini
类似于awk中的begin、end,即在命令开始前先执行vcl_init,并在结束后执行vcl_fini
隐含循环效果 -
nginx缓存存放位置:
key,value方式存储
key 存储在内存中,通过keys_zone定义
value 在磁盘上,分级路由存放,通过对hash值1:1:1或1:2:2等方式取值设置缓存目录,进而存放缓存 -
支持两种缓存方式:
内存存储:
memory:内存中缓存,系统重启,缓存丢失,无法永久有效
申请内存:malloc 释放内存:free
jemalloc 能实现并发内存空间的申请或分配
磁盘存储:
磁盘存储:
整体作为“黑盒”,类似于单个大文件,以二进制格式存储,缓存无法被查看,也无法被重复使用,比如:重启varnish进程,所有缓存将会失效
因此,使用磁盘存储仅是因为其能够提供比内存更大的存储空间,在使用磁盘时推荐使用raid0,构建更好的磁盘阵列或使用固态硬盘,提升磁盘的IO
注意:缓存上线,需要先"热身"才能上线
热身:需要先将缓存服务器在线下运行一段时间,当缓存所需的大部分数据之后才能够正式上线使用,否则需要重新开始缓存数据,起不到使用缓存的作用
4、varnish配置
- 配置文件:
/etc/varnish/default.vcl
用以配置缓存系统的缓存工作机制
更改该文件需要重新加载才能生效
/etc/varnish/varnish.params
用以配置varnish守护进程的工作特性,定义varnish的环境变量
该文件更改后需要重启服务才能生效,但重启后缓存将会全部丢失,因此要做好规划 - 主程序文件
/usr/sbin/varnishd - varnish管理器
/usr/bin/varnishadm - 日志区域查看器,读取统计数据和日志信息
/usr/bin/varnishhist
/usr/bin/varnishlog
/usr/bin/varnishncsa
/usr/bin/varnishstat
/usr/bin/varnishtest
/usr/bin/varnishtop - 读取日志,并保存在其他文件
/usr/lib/systemd/system/varnishlog.service
保存日志格式:原始日志格式
/usr/lib/systemd/system/varnishncsa.service
保存日志格式:http配置文件中combind日志格式,即ncsa格式 - 重新加载配置文件
/usr/sbin/varnish_reload_vcl
注意:配置文件更改保存后不会立即生效,需要进行重载才能生效 - 配置文件参数详解:
配置文件参数解析:
vim /etc/varnish/varnish.params
# Varnish environment configuration description. This was derived from
# the old style sysconfig/defaults settings
# Set this to 1 to make systemd reload try to switch VCL without restart.
RELOAD_VCL=1 每次重启都会重新加载VCL
# Main configuration file. You probably want to change it.
VARNISH_VCL_CONF=/etc/varnish/default.vcl VCL配置文件路径
# Default address and port to bind to. Blank address means all IPv4
# and IPv6 interfaces, otherwise specify a host name, an IPv4 dotted
# quad, or an IPv6 address in brackets.
# VARNISH_LISTEN_ADDRESS=192.168.1.5
VARNISH_LISTEN_PORT=6081 varnish监听端口
# Admin interface listen address and port
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 varnish管理地址
VARNISH_ADMIN_LISTEN_PORT=6082 varnish管理接口
# Shared secret file for admin interface
VARNISH_SECRET_FILE=/etc/varnish/secret varnish管理接口登录的身份认证密钥
# Backend storage specification, see Storage Types in the varnishd(5)
# man page for details.
VARNISH_STORAGE="malloc,256M" 缓存存储系统,根据需求自定义缓存大小,但不推荐太大,容易产生碎片
# User and group for the varnishd worker processes
VARNISH_USER=varnish varnish用户
VARNISH_GROUP=varnish varnish组
# Other options, see the man page varnishd(1)
#DAEMON_OPTS="-p thread_pool_min=5 -p thread_pool_max=500 -p thread_pool_timeout=300"
- 使用磁盘存储,需要指定格式
示例:
-s [name=]kind[,options] # Backend storage specification
# -s malloc[,<size>]
# -s file [default: use /tmp]
# -s file,<dir_or_file>
# -s file,<dir_or_file>,<size>
# -s file,<dir_or_file>,<size>,<granularity>
# -s persist{experimental}
示例:指定磁盘存储格式
VARNISH_STORAGE="file,/var/lib/varnish/varnish.bin,20G"
要注意varnish用户对该目录具有写权限
- 由于配置文件中管理地址为本机,因此varnish管理接口只能通过本机访问:
两种方式:
短连接方式,一次请求,返回一次结果
长连接方式,交互方式 - varnishadm命令
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 进入VCL管理接口
-S 指定密钥文件
-T 指定ip地址和端口 - 相关指令:
示例:
ping [<timestamp>] 探测服务器存活性,如果回复pong,说明服务器存活
auth <response> 做认证
quit 退出
banner 输出欢迎信息
status 查看服务进程状态
start 启动varnish
stop 停止varnish
vcl.load <configname> <filename>
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>]...
ban.list
- 配置文件相关:
vcl.list
vcl.load:装载,加载并编译;
vcl.use:激活;
vcl.discard:删除;
vcl.show [-v] \:查看指定的配置文件的详细信息; - 运行时参数:
param.show -l:显示列表;
param.show \
param.set \ \ - 缓存存储:
storage.list - 后端服务器:
backend.list
示例:
添加后端服务器:
vim /etc/varnish/default.vcl
backend default {
.host = "192.168.32.130";
.port = "80";
}
添加后配置文件需要编译才能生效
varnish_reload_vcl
进入vcl管理接口查看:
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
查看vcl信息
vcl.list
200
available 0 boot
active 0 reload_2018-11-15T15:42:02
切换vcl,返回200状态码,说明切换成功
vcl.use reload_2018-11-15T15:42:02
200
在本地通过curl命令查看头部信息
curl -I 192.168.32.129:6081
-
vcl的语法格式:
(1) VCL files start with “vcl 4.0;”
(2) //, # and / foo / for comments; 注释信息
(3) Subroutines are declared with the sub keyword; 例如sub vcl_recv { ...}; 定义状态引擎使用sub
(4) No loops, state-limited variables(受限于引擎的内建变量); 不支持循环
(5) Terminating statements with a keyword for next action as argument of the return() function, i.e.: return(action);用于实现状态引擎转换;
(6) Domain-specific;
VCL, DSL -
The VCL Finite State Machine
(1) Each request is processed separately; 每一个请求被隔离单独处理
(2) Each request is independent from others at any given time; 每一个请求各自独立
(3) States are related, but isolated; 每个状态之间有关联性,但又是相互隔离的
(4) return(action); exits one state and instructs Varnish to proceed to the next state; 退出时,只能退出一个状态引擎
(5) Built-in VCL code is always present and appended below your own VCL; -
三类主要语法:
示例:
sub subroutine { 定义一个引擎
...
}
if CONDITION { if语句,支持单分支和双分支
...
} else {
...
}
return(), hash_data() 调用内减函数
- 操作符:
示例:
==, !=, ~, >, >=, <, <=
逻辑操作符:&&, ||, !
变量赋值:=
- 四类报文:
客户端发送给缓存服务器的请求 req
缓存服务器发送给后端服务器的请求 resp
后端服务器发送给缓存服务器的响应 bereq,即backend req
缓存服务器发送给客户端的响应 beresp,即backend resp - 缓存服务器能够修改的报文只有经过自己的两段报文:
缓存服务器发送给后端服务器的请求
缓存服务器响应给客户端的响应
举例:obj.hits是内建变量,用于保存某缓存项的从缓存中命中的次数;
if (obj.hits>0) {
set resp.http.X-Cache = "HIT via" + " " + server.ip;
} else {
set resp.http.X-Cache = "MISS from " + server.ip;
}
在c语言中,变量不能加引号,字符串则需要用引号
另外,该代码属于缓存服务器发送给客户端的信息,因此需要写在deliver配置段中
- 配置完成后,需要重新加载配置文件
varnish_reload_vcl
除了该命令之外还可以使用另外一种方法:
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 登录管理接口
vcl.load testconf1 default.vcl 重新加载配置文件并命名该vcl为testconf1
切换vcl,使该vcl生效
vcl.use testconf1
查看vcl状态
vcl.list
使用浏览器F12切换调试模式查看缓存状态
登录varnish管理接口
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
vcl.show testconf1 查看testconf1配置信息
vcl.show -v testconf1 查看testconf1配置详细信息
详细信息如下:
vcl 4.0;
#######################################################################
# Client side
sub vcl_recv {
if (req.method == "PRI") {
/* We do not support SPDY or HTTP/2.0 */
return (synth(405)); #不支持SPDY和http2.0,直接返回405状态码
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe); #如果不是http协议的标准方法,return (pipe)是指下一个状态引擎为vcl_pass,即转为四层代理
}
if (req.method != "GET" && req.method != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass); #http协议除了GET和HEAD方法,其他方法不能使用缓存,return (pass)是指下一个状态引擎为vcl_pass,即直接到后端服务器直接获取数据
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
return (pass); #客户端发送的请求报文首部能够包含认证信息和cookie信息,这些信息属于用户私密信息,因此这些信息不能被缓存,也就是说缓存中并没有这些信息,要直接去后端服务器。return (pass)是指下一个状态引擎为vcl_pass
}
return (hash); #如果不满足以上信息,即请求方法为GET,HEAD,也不包含认证和cookie信息,就可以查询缓存信息,下一个状态引擎为vcl_hash
}
- vcl_hash状态引擎定义:
说明:
sub vcl_hash {
hash_data(req.url); #根据url做hash运算,然后查询缓存。hash的内容可以自定义
if (req.http.host) { 如果请求报文中包含host信息
hash_data(req.http.host); 对host信息进行hash运算
} else {
hash_data(server.ip); 否则就对server.ip进行hash运算
}
return (lookup); 下一个状态引擎为lookup,即查询缓存
}
注意:根据host以及server.ip信息进行缓存,会降低缓存命中率,应该把这两项内容去掉
- 变量类型:
内建变量:
req.*:request,表示由客户端发来的请求报文相关;
req.http.*
req.http.User-Agent, req.http.Referer, ...
bereq.*:由varnish发往BE主机的httpd请求相关;
bereq.http.*
beresp.*:由BE主机响应给varnish的响应报文相关;
beresp.http.*
resp.*:由varnish响应给client相关;
obj.*:存储在缓存空间中的缓存对象的属性;只读;
注意:
bereq是指backend req
beresp是指backend resp
常用变量:
bereq.*, req.*:
bereq.http.HEADERS
bereq.request, req.request:请求方法;
bereq.url, req.url:请求的url;
bereq.proto,req.proto:请求的协议版本;
bereq.backend:指明要调用的后端主机;
req.http.Cookie:客户端的请求报文中Cookie首部的值;
req.http.User-Agent ~ "chrome"
beresp.*, resp.*:
beresp.http.HEADERS
beresp.status, resp.status:响应的状态码;
reresp.proto, resp.proto:协议版本;
beresp.backend.name:BE主机的主机名;
beresp.ttl:BE主机响应的内容的余下的可缓存时长;
obj.*
obj.hits:此对象从缓存中命中的次数;
obj.ttl:对象的ttl值,即余下的缓存时长
server.
server.ip:varnish主机的IP;
server.hostname:varnish主机的Hostname;
client.
client.ip:发请求至varnish主机的客户端IP;
用户自定义:
set
unset
示例1:强制对某类资源的请求不检查缓存:
vim /etc/varnish/default.vcl
vcl_recv {
if (req.url ~ "(?i)^/(login|admin)") { #即与登录验证有关的验证信息
return(pass); #此类信息属于私密信息,因此不查缓存,直接发送到后端服务器
}
}
注意:此代码必须写在vcl_recv代码段,因为一旦开始检查缓存,此代码就会失效
测试:
在后端服务器设置测试页
cd /var/www/html
mkdir login admin
vim login/index.html
login url
在客户端管理接口重新加载代码
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
vcl.load testconf3 default.vcl 加载配置文件
vcl.use testconf3 切换vcl
[root@centos7 ~]# curl -I 192.168.32.129:6081/login
X-Cache: Miss FROM 192.168.32.129 这里只贴出缓存命中情况
示例2:对于特定类型的资源,例如公开的图片等,取消其私有标识,并强行设定其可以由varnish缓存的时长; 定义在vcl_backend_response中;
if (beresp.http.cache-control !~ "s-maxage") { #后端服务器响应给缓存服务器的报文中cache-control不带有s-maxage(公共缓存时长)信息
if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") { #缓存服务器发送给后端服务器的请求报文中带有静态图片的请求
unset beresp.http.Set-Cookie; #取消后端服务器响应给缓存服务器报文中的Set-Cookie信息(服务器发送Set-Cookie信息给客户端,客户端下次访问时就会带上cookie信息。取消该变量,客户端的访问信息就不会带有cookie信息,也就能够缓存下来)
set beresp.ttl = 3600s; #由于匹配规则为不带有缓存时长(s-maxage),因此设置缓存的生存时长为3600s
}
}
注意:带有cookie信息都默认为私有信息,不进行缓存。在后端服务器发送响应报文时不让其在响应报文中添加set-cookie字段信息,则客户端就无法收到cookie信息,此需要在后端服务器响应时设置,即vcl_backend_response配置段
示例3:定义在vcl_recv中;
if (req.restarts == 0) { 限制在不重启的前提下
if (req.http.X-Fowarded-For) { 如果请求报文中带有X-Fowarded-For字段
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + "," + client.ip; 在X-Fowarded-For字段后加上client.ip字段
} else {
set req.http.X-Forwarded-For = client.ip; 否则,就直接在报文中添加client.ip字段
}
}
注意:
把真实的客户端ip地址传递给后端服务器,由于代理服务器的原因,后端服务器在日志中保存的ip地址为代理服务器的ip地址,可以在代理服务器上添加报文首部字段(如X-Fowarded-For)的方式把客户端ip地址加入请求报文中,并在后端服务器http日志格式中添加此字段以抓取客户端ip地址
由于存在多级代理服务器的原因,缓存服务器前端一般并不是真正的客户端,而是代理服务器,因此请求报文中可能已经被添加过此类报文首部字段,通过此方法获取的ip地址可能是代理服务器的ip地址,因此可以在获取到的报文首部字段(如X-Fowarded-For)后加上客户端的ip地址,这样一来就能获取到X-Fowarded-For字段中的客户端真实ip地址
另外,该代码之后并没有return,因此可以被多端代码引用
测试:
在后端服务器http日志格式中记录X-Forwarded-For字段信息
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{clientip}i\" %{X-Forwarded-For}i" combined
在客户端管理接口重新加载代码
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
vcl.load testconf3 default.vcl 加载配置文件
vcl.use testconf3 切换vcl
在客户端访问:
curl -I http://192.168.32.129:6081/login
查看后端服务器日志:
192.168.32.129 - - [15/Nov/2018:19:51:23 +0800] "HEAD /login HTTP/1.1" 301 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-" 192.168.32.128
注意:客户端ip地址为192.168.32.128
-
如果缓存过期时长刚刚获取新的时间,离缓存过期还有很长时间,但是后端服务器的数据内容却发生变化,图片等静态内容没有变化,但css,js文件由于版本不一致会导致网站内容排版错乱
这就需要手动清除老版本的css,js文件,当客户端再次请求时,不回命中缓存,而直接到后端服务器获取数据,然后新数据缓存到缓存服务器,这样就更新到新版本的内容 -
缓存对象的修剪:purge, ban
purge:一次只能修剪一个url
ban:一次性生效,可以临时清除某些缓存项
支持基于正则表达式模式,定义过滤器以拒绝对某些url所对应的缓存项查缓存的请求,从而使得缓存服务器必须到后端服务器去获取数据,然后缓存到缓存中并把原来的覆盖掉
配置purge操作:
(1) 能执行purge操作
sub vcl_purge {
return (synth(200,"Purged"));
}
(2) 何时执行purge操作
sub vcl_recv {
if (req.method == "PURGE") {
return(purge);
}
...
}
添加此类请求的访问控制法则:
acl purgers { 由于清理缓存操作危及网站缓存,因此定义acl规则urgers,只允许指定网段进行purge操作
"127.0.0.0"/8; 允许本机ip地址,注意字符串需要使用引号括起来,而掩码则需要放在引号外面
"192.168.32.129"/32; #允许指定ip地址,注意字符串需要使用引号括起来,而掩码则需要放在引号外面,32位掩码为限制本网段只有一个ip地址
}
#注意:在设置acl规则时,本机回环地址127.0.0.1和本机地址192.168.32.129 hash值并不一样,因此在本机访问127.0.0.1和在其他主机访问本机192.168.32.129并不相同,因此需要在acl规则中加上两个ip地址才能限制
另外,此段代码推荐写在default配置段
sub vcl_recv {
if (req.method == "PURGE") { #如果客户端请求方法为PURGE,这里PURGE为自定义名称
if (!client.ip ~ purgers) { #如果客户端ip地址不是acl规则中指定的网段
return(synth(405,"Purging not allowed for " + client.ip)); #拒绝进行purge操作,并发挥提示语
}
return(purge); 否则就允许puege操作
}
...
}
测试:
在客户端进行测试:
curl -X "PURGE" http://192.168.32.129:6081/index.html curl命令-X选项是指以指定方法访问网站页面,指定以PURGE方式获取进行测试
<title>405 Purging not allowed for 192.168.32.128</title> 由于客户端地址不满足acl规则,因此被拒绝
在本机进行测试:
curl -I 192.168.32.129:6081/index.html 清理后第一次访问,状态为MISS
X-Cache: Miss FROM 192.168.32.129
curl -I 192.168.32.129:6081/index.html 清理后第二次访问,状态为HIT
X-Cache: HIT via 192.168.32.129
执行清理操作后,第一次请求响应报文头部X-Cache处于MISS状态,会直接向后端服务器获取新内容,第二次请求时X-Cache处于HIT状态,说明是从缓存获取的数据内容
- Banning:
(1) varnishadm:
ban <field> <operator> <arg>
示例:
ban req.url ~ (?i)^/test[123].html$ 在varnish管理接口使用ban命令清除以test1,test2,test3开头,以.html结尾的缓存
查看ban规则:
ban.list
200
Present bans:
1542286682.822361 0 req.url ~ (?i)^/test[123].html$
测试:
在后端服务器准备test1-test10.html 10个文件
在客户端先对10个文件访问一次进行缓存
在varnish服务器使用ban命令清除test1-test3.html的缓存后
在客户端再次访问查看缓存命中状态
(2) 在配置文件中定义,使用ban()函数;
示例:
if (req.method == "BAN") {
ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
# Throw a synthetic page so the request won't go to the backend.
return(synth(200, "Ban added"));
}
测试:
curl -X BAN http://www.ilinux.io/test1.html
ban req.http.host==www.ilinux.io && req.url==/test1.html
- 如何设定使用多个后端主机:
如果主机内容不一样,可以进行动静分离
如果主机内容一样,可以进行负载均衡 - 实现动静分离:
示例:
backend default { 指定默认服务器名称以及ip和端口
.host = "172.16.100.6";
.port = "80";
}
backend appsrv { 指定app服务器名称以及ip和端口
.host = "172.16.100.7";
.port = "80";
}
#注意:此段代码要放在默认配置段default最上面
sub vcl_recv {
if (req.url ~ "(?i)\.php$") { 如果是php动态资源请求,把其调度给appsrv服务器
set req.backend_hint = appsrv;
} else { 如果是其他请求,则调度给default服务器
set req.backend_hint = default;
}
...
}
nginx: proxy_pass
haproxy: use_backend
测试:
加载配置文件:
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
vcl.load testconf6 default.vcl
vcl.use testconf6
在客户端分别访问
curl http://192.168.32.129:6081/index.html
curl http://192.168.32.129:6081/test.php
- 负载均衡:需要专门的模块才能实现
Director:
varnish module;
使用前需要导入:
import directors;
示例:
import directors; # load the directors 导入模块,要写在配置文件最上面
backend websrv1 { #定义后端服务器
.host = 192.168.32.130;
.port = 80;
}
backend websrv2 { #定义后端服务器
.host = 192.168.32.131;
.port = 80;
}
sub vcl_init { 该配置段要写在vcl_recv之前
new websrvs = directors.round_robin(); #定义组,组名websrvs为自定义,调用director模块内建的轮询调度算法,也可以使用random()函数即随机调度。另外轮询算法不支持权重,随机调度支持权重
websrvs.add_backend(websrv1); #把后端服务器websrv1添加到组中
websrvs.add_backend(websrv2); #把后端服务器websrv2添加到组中
}
sub vcl_recv {
# send all traffic to the bar director:
set req.backend_hint = websrvs.backend(); #调用GROUP_NAME组内的后端服务器
}
注意:这种方式是把服务器并组后进行调度
另外,负载均衡时,不能使用缓存,否则根据缓存调度,无法显示调度算法的效果;因此可以使用之前定义的不使用缓存的log和admin目录
创建测试页:
RS1: 创建login,admin目录,并在目录中创建10个测试页
cd /var/www/html
mkdir login
for i in {1..10};do echo test $i on RS1 > login/log$i.html;done
RS2: 创建login,admin目录,并在目录中创建10个测试页
cd /var/www/html
mkdir login
for i in {1..10};do echo test $i on RS1 > login/log$i.html;done
重新加载配置文件:
vcl.load testconf7 default.vcl
vcl.use testconf7
客户端测试:
while true do curl http://192.168.32.129/login/log1.html;sleep 0.5 ;done
- 基于cookie的session sticky:
示例:
sub vcl_init {
new h = directors.hash();
h.add_backend(one, 1); // backend 'one' with weight '1'
h.add_backend(two, 1); // backend 'two' with weight '1'
}
sub vcl_recv {
// pick a backend based on the cookie header of the client
set req.backend_hint = h.backend(req.http.cookie);
}
- 基于随机的调度方式,支持服务器权重:
示例:
sub vcl_init {
new websrvs = directors.random();
websrvs.add_backend(srv1,1);
websrvs.add_backend(srv2,2);
}
注意:负载均衡时,不能使用缓存,否则根据缓存调度,无法显示调度算法的效果
- 健康状态检测:
示例:
BE Health Check:
backend BE_NAME {
.host =
.port =
.probe = { 定义检测内容
.url= 定义检测url内容
.timeout= 定义超时时长
.interval= 隔多久进行检测
.window=
.threshold=
}
}
- State Changed:
OK -> Fail, Fail, Fail 连续三次失败就会被移除
ACTION: remove
Fail -> OK, OK 连续两次成功就会被添加
ACTION: add - .probe:定义健康状态检测方法;
.url:检测时要请求的URL,默认为”/";
.request:发出的具体请求;
.request =
"GET /.healthtest.html HTTP/1.1"
"Host: www.magedu.com"
"Connection: close"
.window:基于最近的多少次检查来判断其健康状态;
.threshold:最近.window中定义的这么次检查中至有.threshhold定义的次数是成功的;即成功阈值;
如.window=10,.threshold=7,意思是最近的10次检查中有7次以上检测为健康状态,服务器才算是处于健康状态
.interval:检测频度;
.timeout:超时时长;
.expected_response:期望的响应码,默认为200; - 健康状态检测的配置方式:
每个服务器单独定义健康状态检测方式十分不方便,因此可以定义一个健康状态检测方式,被多个后端服务器调用
示例:
(1) probe PB_NAME { }
backend NAME = {
.probe = PB_NAME;
...
}
(2) backend NAME {
.probe = {
...
}
}
示例:在vcl_init配置段之前写入该配置端,即在配置文件最上面
probe healthchk { #定义健康状态检测方式的名称为healthchk
.url = "/.healthcheck.html";
.window = 5; 检测最近的5次状态
.threshold = 4; 检测的5次中有4次为健康,服务器显示为健康
.interval = 2s;
.timeout = 1s;
}
backend default { #在default服务器组调用healthchk
.host = "10.1.0.68";
.port = "80";
.probe = healthchk;
}
backend appsrv { #在appsrv服务器组调用healthchk
.host = "10.1.0.69";
.port = "80";
.probe = healthchk;
}
注意:健康状态检测信息不要记录在日志中,并且只对特定页面进行检测
手动设定BE主机的状态:
sick:管理down;
healthy:管理up;
auto:probe auto;
测试:
varnish服务器重新加载配置文件:
vcl.load testconf9 default.vcl
vcl.use testconf9
客户端测试:
while true do curl http://192.168.32.129/login/log1.html;sleep 0.5 ;done
注意:也可以手动设定后端服务器状态查看健康检测的效果
- 设置后端的主机属性:
示例:
backend BE_NAME {
...
.connect_timeout = 0.5s;
.first_byte_timeout = 20s; #定义发送第一个字节超时时长
.between_bytes_timeout = 5s; #字节和字节之间的超时时长
.max_connections = 50;
}
- varnish的运行时参数:
线程模型:
cache-worker
cache-main
ban lurker
acceptor:
epoll/kqueue:
...
线程相关的参数:使用线程池机制管理线程;
在线程池内部,其每一个请求由一个线程来处理; 其worker线程的最大数决定了varnish的并发响应能力;
查看参数:在varnish管理接口中
param.show -l 查看所有参数
param.show 参数名 查看具体某一参数
thread_pools:Number of worker thread pools. 最好小于或等于CPU核心数量;
thread_pool_max:The maximum number of worker threads in each pool. 每线程池的最大线程数;默认为5000
thread_pool_min:The minimum number of worker threads in each pool. 额外意义为“最大空闲线程数”;
最大并发连接数 = thread_pools * thread_pool_max
thread_pool_timeout:Thread idle threshold. Threads in excess of thread_pool_min, which have been idle for at least this long, will be destroyed.
thread_pool_add_delay:Wait at least this long after creating a thread. 延时增加线程
thread_pool_destroy_delay:Wait this long after destroying a thread. 延迟销毁线程
Timer相关的参数:
send_timeout:Send timeout for client connections. If the HTTP response hasn't been transmitted in this many seconds the session is closed.
timeout_idle:Idle timeout for client connections.
timeout_req: Max time to receive clients request headers, measured from first non-white-space character to double CRNL.
cli_timeout:Timeout for the childs replies to CLI requests from the mgt_param.
设置方式:
vcl.param
param.set
param.set 参数 数量
永久有效的方法:
varnish.params
DEAMON_OPTS="-p PARAM1=VALUE -p PARAM2=VALUE"
注意:一个-p选项后跟一个参数,设置多个参数只需多个-p选项即可 - varnish日志区域:
shared memory log
计数器
日志信息
1、varnishstat - Varnish Cache statistics
-1
-1 -f FILED_NAME
-l:可用于-f选项指定的字段名称列表;
MAIN.cache_hit
MAIN.cache_miss
# varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss
显示指定参数的当前统计数据;
# varnishstat -l -f MAIN -f MEMPOOL
列出指定配置段的每个参数的意义;
2、varnishtop - Varnish log entry ranking
-1 Instead of a continously updated display, print the statistics once and exit.
-i taglist,可以同时使用多个-i选项,也可以一个选项跟上多个标签;
-I <[taglist:]regex>:对指定的标签的值基于regex进行过滤;
-x taglist:排除列表
-X <[taglist:]regex>:对指定的标签的值基于regex进行过滤,符合条件的予以排除;
3、varnishlog - Display Varnish logs
4、 varnishncsa - Display Varnish logs in Apache / NCSA combined log format
启动varnishncsa服务,自动保存日志
日志默认保存文件:/var/log/varnish/varnishncsa.log - 内建函数:
hash_data():指明哈希计算的数据;减少差异,以提升命中率;
regsub(str,regex,sub):把str中被regex第一次匹配到字符串替换为sub;主要用于URL Rewrite
regsuball(str,regex,sub):把str中被regex每一次匹配到字符串均替换为sub;
return():
ban(expression)
ban_url(regex):Bans所有的其URL可以被此处的regex匹配到的缓存对象;
synth(status,"STRING"):生成响应报文;
文章评论