Linux服务篇--varnish缓存

本章概要

  • 缓存的需求性
  • 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
        隐含循环效果

Linux服务篇--varnish缓存_第1张图片

  • 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[,]
                             #   -s file  [default: use /tmp]
                             #   -s file,
                             #   -s file,,
                             #   -s file,,,
                             #   -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 []    探测服务器存活性,如果回复pong,说明服务器存活
auth        做认证
quit        退出
banner      输出欢迎信息
status      查看服务进程状态
start       启动varnish
stop        停止varnish
vcl.load  
vcl.inline  
vcl.use             切换vcl
vcl.discard         删除vcl
vcl.list          				列出正在使用的vcl
param.show [-l] []       查看参数
param.set         服务器内部线程的配置参数
panic.show       内部出现故障,获取排障信息
panic.clear
storage.list     列出使用的存储系统,是磁盘还是内存
vcl.show [-v]         列出vcl
backend.list []     列出后端服务器
backend.set_health  
ban    [&&   ]...
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方式获取进行测试
	405 Purging not allowed for 192.168.32.128   由于客户端地址不满足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
    示例:
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"):生成响应报文;  

你可能感兴趣的:(Linux服务篇--varnish缓存)