Varnish是一款高性能、开源的反向代理服务器和缓存服务器。Varnish使用内存缓存文件来减少响应时间和网络带宽消耗。这个项目是由挪威的一家报纸Verdens Gang的网络分支起始的,其架构设计和开发总监Poul-Henning Kamp是FreeBSD核心的开发人员之一,最初项目的管理与基础设施及额外开发由挪威一家Linux咨询公司Linpro提供。

说到varnish,squid就不得不提及。squid算得上是古老的缓存服务器。由于varnish先进的设计理念,性能要比squid高上许多,varnish还可以通过端口进行管理,使用正则语句做到清除指定缓存的功能,这些squid都做不到。但是varnish在高并发的情况下,资源消耗较高,而且varnish服务进程一旦崩溃,重启,内存中的缓存数据将全部丢失。

1、Varnish架构

varnish是基于现代设备设计的服务项目,所以仅支持64位系统。Manager Process 负责处理请求任务,保证每个任务分配一个worker threads。所以varnish是一个重线程型服务。除此之外,manager process 包含接受CLI命令控制的功能,包括调整运行参数,vcl配置更新。初始化子进程Cacher Process,并按一定频率检测cacher在线与否。

Cacher Process 功能:

  • 监听客户端请求
  • 管理worker 线程
  • 存储缓存数据
  • 记录流量日志
  • 根据统计更新计数器数值

varnish使用工作空间减少每个线程需要请求或者修改内存时发生的争抢。varnish具有多个工作空间,最为重要的是  session 工作空间,用来维护session 相关数据。

在日志记录方面,Cacher process 使用VSL 机制来处理,这是一个共享内存空间,可以有效减少记录阻塞。日志空间分为两个部分,分别记录格式化的请求日志,以及计数器数值。可以通过varnish自带log工具进行查看,分析或者永久存储日志。

2、varnish的缓存存储机制( Storage Types):

2.1  malloc[,size]
调用malloc(),为缓存分配内存空间,此种方式不可避免地会产生碎片文件,额外占用内存

[,size]用于定义空间大小;重启后所有缓存项失效;

2.2   file[,path[,size[,granularity]]]
varnish创建一个文件用来存储缓存数据,然后将此文件映射到内存空间中,但是该文件并不会持久保存数据,重启后所有缓存项失效;

granularity  递增大小

2.3   persistent,path,size
持久文件存储,黑盒;重启后所有缓存项有效;但是处于实验阶段,问题较多;

2.4  MSE

Massive Storage Engine,在plus版可用,意味着收费。该模式设计的容量巨大可达100TB,磁盘性能要优于file模式。

总结:当内存空间不足以存储所有缓存数据时,应选择file 或 mse 存储。所以一般配置成file存储,当然付费的话使用mse更佳。

3、Varnish程序环境

本文主机环境为CentOS7.2,Varnish 版本 4.0

varnish的程序环境:
  /etc/varnish/varnish.params: 配置varnish服务进程的工作特性,例如监听的地址和端口,缓存机制;
  /etc/varnish/default.vcl:配置各Child/Cache线程的工作属性;
主程序:
  /usr/sbin/varnishd

CLI interface:
  /usr/bin/varnishadm
Shared Memory Log交互工具:
  /usr/bin/varnishhist
  /usr/bin/varnishlog
  /usr/bin/varnishncsa
  /usr/bin/varnishstat
  /usr/bin/varnishtop
测试工具程序:
  /usr/bin/varnishtest
VCL配置文件重载程序:
  /usr/sbin/varnish_reload_vcl
Systemd Unit File:
  /usr/lib/systemd/system/varnish.service   #varnish服务
  /usr/lib/systemd/system/varnishlog.service  #logger daemon
  /usr/lib/systemd/system/varnishncsa.service   #lgger daemon  in apache format

3.1  varnishd 主程序的选项:

systemd方式启动varnish 服务,主程序指定的配置文件为:/etc/varnish/varnish.params
  -a address[:port][,address[:port][...],默认为6081端口;    #对客户端开放的监听端口地址
  -T address[:port],默认为6082端口;                                #管理工具连接的端口地址
  -s [name=]type[,options],定义缓存存储机制;                   #可以多次定义此项
  -u user
  -g group
  -f config:VCL配置文件;
  -F:运行于前台;
  ...

线程相关的参数:
在线程池内部,其每一个请求由一个线程来处理; 其worker线程的最大数决定了varnish的并发响应能力;

thread_pools:Number of worker thread pools. 线程池数量,默认值为2,官方介绍2个线程池已足够用,再增加该数值没有提升效果;
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. 每个线程池保持最少线程的数量;额外意义为“最大空闲线程数”;默认100

所以我们经常需要调整的参数就是thread_pool_max,thread_pool_min
计算varnish最大并发连接数=thread_pools * thread_pool_max

thread_pool_timeout: 线程空闲时间,超过阈值则摧毁线程 
thread_pool_add_delay:创建一个新线程的延迟时间,默认值为0s
thread_pool_destroy_delay:摧毁一个线程的延迟时间,默认值为2s;

设置方式:

运行动态修改通过varniadm接口设置
命令:param.set

永久有效的方法:
运行时参数:/etc/varnish/varnish.params文件, DEAMON_OPTS

  -p param=value:设定运行参数及其值; 可重复使用多次;
  -r param[,param...]: 设定指定的参数为只读状态
  例如: DAEMON_OPTS="-p thread_pool_min=2 -p thread_pool_max=10000 -p thread_pool_timeout=300"

3.2  varnish管理工具

  用法:varnishadm -S /etc/varnish/secret -T [ADDRESS:]PORT

    指定了连接密钥,安装varnish时生成的。指明管理接口的端口地址,默认为127.0.0.1 可省略。

进入之后,输入help [command] 获取帮助

梳理常用指令

配置文件相关:
vcl.list :查看vcl 列表
vcl.load:装载,加载并编译;
vcl.use:激活;
vcl.discard:删除;
vcl.show [-v] <configname>:查看指定的配置文件的详细信息;-v 选项查看默认vcl代码

运行时参数:
param.show -l:显示列表;
param.show <PARAM>
param.set <PARAM> <VALUE>   设定参数

缓存存储:
storage.list

后端服务器:
backend.list

4、VCL 基础

Varnish Configuration Language (VCL) 是一种动态语言,用来描述请求处理和制定缓存策略。vcl配置内容由manager process 创建的VCC子进程转换成C语言代码,再经由gcc编译成共享对象,最后装载到cacher process中生效。

想要写好vcl配置,需要了解varnish内部报文的处理流程,其核心关键词是 finite state machine——有限状态引擎。下图为简单的处理流程:

图中椭圆中代表状态引擎。这些状态引擎被概念化后成为vcl中的子函数,以vcl_前缀开头,在引擎中,可以对每个请求中的http 首部或者其他各方面的内容进行检查或者修改操作。return(action)代码表示中断一个状态,其中action是vcl关键字,用来指向下一步去向哪个状态引擎。

每个请求都被单独分开处理;状态之间存在相关性,但彼此间互相隔离。

在下一步了解vcl 配置代码之前,先了解一下vcl背后的基础概念。当varnish处理一个请求时,首先要解析这个请求。从http 首部中分析出请求的方法类型,判断是否为有效的请求方法等等,当基础解析完成之后,依据第一个策略进行检查进而做出判断。vcl就是根据由各个策略组成的规则来进行各种动作。

上图可分为两个区域:前端frontend和后端backend

前端状态可分为四个阶段:

第一阶段:

vcl_recv     #接受客户端请求,进行判断

第二阶段:

vcl_hash   #进行hash计算,不进行判读处理,计算之后送往各个第三阶段状态引擎中

第三阶段:

vcl_hit        #缓存命中,到此处理

vcl_pass      #缓存跳过

vcl_miss      #缓存未命中

vcl_purge   #清理缓存

vcl_pipe       #对于无法识别的http首部请求直接送入管道,交由后端处理不再处理

第四阶段:

vcl_deliver: 大部分响应客户端的请求由此发送回去

vcl_synth:接受来自vcl_purge的任务,对于指定的缓存,进行删除处理

后端状态分为两阶段:

第一阶段:

    vcl_backend_fetch:接受来自前端状态vcl_pass或vcl_miss 的任务,向后端主机请求

第二阶段:

    vcl_backend_response:接受到后端返回正常状态报文,进行是否缓存检查,需要缓存的响应将其缓存,不需要则不缓存,最后送到vcl_deliver

    vcl_backend_error:后端主机错误,返回错误响应

除此之外还有两个特殊状态引擎:

vcl_init:在处理任何请求之前要执行的vcl代码:主要用于初始化VMODs;
vcl_fini:所有的请求都已经结束,在vcl配置被丢弃时调用;主要用于清理VMODs;

5、VCL语法

一个大前提:varnish 4.0版本开始,vcl拥有自己的默认规则,它不可移除,总是追加在自定义的规则之后。

(1) vcl配置文件以 vcl 4.0 开头;
(2) C语言注释风格://, # and /* foo */ ;
(3) 子函数使用sub关键字声明, 例如sub vcl_recv { ...};
(4) 无循环, state-limited variables(受限于引擎的内建变量);
(5) 使用return(action)中断引擎状态,指向下一步处理,action为关键字 ,例如: return(pass);
(6) 可动态装载;

5.1  三类主要语法:

sub subroutine {
...
}

if CONDITION {
...
} else {
...
}

return(), hash_data()

5.2 内建函数和关键字

函数:

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"):purge操作;

关键字:
  call subroutine, return(action),new,set,unset

下图为指定函数智能用于特定子函数中

  

操作符:
  ==, !=, ~, >, >=, <, <=
  逻辑操作符:&&, ||, !
  变量赋值:=

正则匹配:~

  (?i)  表示忽略大小写

同时注意匹配的规则如果是字符串需要"  " 引起

5.3  变量类型:

内建变量:

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.*, req.*:
bereq.http.HEADERS
bereq.request:请求方法;
bereq.url:请求的url;
bereq.proto:请求的协议版本;
bereq.backend:指明要调用的后端主机;

req.url:请求的url
req.http.Cookie:客户端的请求报文中Cookie首部的值;
req.http.User-Agent:浏览器类型

beresp.*, resp.*:
beresp.http.HEADERS
beresp.status:响应的状态码;
reresp.proto:协议版本;
beresp.backend.name:BE主机的主机名;
beresp.ttl:BE主机响应的内容的余下的可缓存时长;

obj.*
obj.hits:此对象从缓存中命中的次数;
obj.ttl:对象的ttl值

server.*
server.ip
server.hostname
client.*
client.ip

同时注意变量是受状态限制的,下图为可用表

用户自定义:
  set    variable=value    #定义变量
  unset   variable           #撤销定义的变量

6、vcl 配置示例

6.1  响应首部增加一个cache是否命中的字段X-cache

~]$ vi /etc/varnish/default.vcl

sub vcl_deliver {
  if ( obj.hits>0 ) {
    set resp.http.X-cache = " HIT via " + server.ip;
  }
   else {
    set resp.http.X-cache = " MISS via "+ server.ip;
   } }

~]$ varnish_reload_vcl           #重载vcl

或者 使用varnishadm 进入管理接口,使用如下命令

~]$ vcl.load   test1   default.vcl         #装载vcl,并指定一个命名test1

如果返回状态码200,则语法正确,编译通过

~]$ vcl.use  test1      #如下图所示vcl新配置已生效

使用curl命令测试,第一次无缓存,则未命中

第二次访问,已有缓存,则命中

6.2  强制对某类资源的请求不检查缓存:

设定访问/login 或 /admin 下的目录任何文件都不查询缓存

vcl_recv {
if (req.url ~ "(?i)^/(login|admin/)") {
return(pass);
}
}

6.3  对于特定类型的资源,例如公开的图片等,取消cookie,并强行设定其可以由varnish缓存的时长;

sub vcl_backend_response {
if (beresp.http.cache-control !~ "s-maxage") {
if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") {
unset beresp.http.Set-Cookie;
set beresp.ttl = 3600s;
}
}
}

6.4  缓存对象的修剪:purge, ban

(1) 能执行purge操作
sub vcl_purge {
return (synth(200,"Purged"));
}

(2) 何时执行purge操作
sub vcl_recv {
if (req.method == "PURGE") {
return(purge);
}
...
}

上面的定义比较简单,任何人都可以对cache做清理操作,下面则根据IP地址做出限制

添加此类请求的访问控制法则:
acl purgers {
"127.0.0.0"/8;
"10.1.0.0"/16;
}

sub vcl_recv {
if (req.method == "PURGE") {
if (!client.ip ~ purgers) {                      #这里正则匹配的时acl列表,不需要引号
return(synth(405,"Purging not allowed for " + client.ip));       #来自不属于acl定义的purgers组的purge请求则返回错误代码
}
return(purge);
}
...
}

6.5   设定使用多个后端主机

backend default {
  .host = "172.16.100.6";
  .port = "80";
}

backend appsrv {
  .host = "172.16.100.7";
  .port = "80";
}

sub vcl_recv {
  if (req.url ~ "(?i)\.php") {
    set req.backend_hint = appsrv;            #php资源转发至appsrv处理
  } else {
      set req.backend_hint = default;
   }
  ...
}

7、定义后端服务器组

7.1  定义后端服务器组

使用前需要在vcl配置中导入模块:
import director;

示例:
import directors; # load the directors

backend server1 {                 
.host =
.port =
}
backend server2 {
.host =
.port =
}

sub vcl_init {                    #在init 子函数中定义
new GROUP_NAME = directors.round_robin();         #创建组,并命名为GROUP_NAME,指定调度方法
GROUP_NAME.add_backend(server1);                    #为组添加服务器成员
GROUP_NAME.add_backend(server2);
}

sub vcl_recv {
# send all traffic to the bar director:
set req.backend_hint = GROUP_NAME.backend();  #组引用方法
}

7.2  后端主机健康检测机制

varnish可以对后端主机进行健康检测,动态进行移除或恢复后端主机调度列表

.probe:定义健康状态检测方法;
.url:检测时请求的URL,默认为"/";
.request:发出的具体请求;
.request =
"GET /.healthtest.html HTTP/1.1"
"Host: www.magedu.com"
"Connection: close"
.window:基于最近的多少次检查来判断其健康状态;
.threshhold:最近.window中定义的这么次检查中至有.threshhold定义的次数是成功的;
.interval:检测频度;
.timeout:超时时长;
.expected_response:期望的响应码,默认为200;

健康状态检测的配置方式:
(1) probe PB_NAME = { }
backend NAME = {
.probe = PB_NAME;
...
}

(2) backend NAME {
.probe = {
...
}
}

示例:
probe check {                               #probe   先定义好
.url = "/.healthcheck.html";
.window = 5;
.threshold = 4;
.interval = 2s;
.timeout = 1s;
}

backend default {
.host = "10.1.0.68";
.port = "80";
.probe = check;        #引用检测方式
}

backend appsrv {
.host = "10.1.0.69";
.port = "80";
.probe = check;
}

在varniadm 命令接口中查看检测状况

8、varnish日志

用来查看 shared memory log 日志工具

8.1  varnishstat - Varnish 缓存统计查看

默认为动态刷新显示方式

选项

-1  打印当前统计结果
-f FILED_NAME   显示指定字段的统计
-l:可用于-f选项指定的字段名称列表;

example:
# varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss

8.2  varnishtop - Varnish 日志 字段排名

默认动态更新

选项:

-1   打印当前排名
-i taglist  显示指定字段排名。可以同时使用多个-i选项,也可以一个选项跟上多个标签 ","分隔;
-I <[taglist:]regex>  基于正则显示字段
-x taglist:排除列表
-X <[taglist:]regex>  基于正则排除字段

8.3  varnishlog - Display Varnish logs

显示share memory 中的日志记录

8.4  varnishncsa - Display Varnish logs in Apache / NCSA combined log format

显示share memory 中的日志记录,apache日志形式

本文到此结束

学习varnish随笔的更多相关文章

  1. html标签学习入门 随笔

    Html学习入门    随笔1: HTML 标题 HTML 标题(Heading)是通过 <h1> - <h6> 等标签进行定义的. 标题仅用于标题文本  不应该被使用在加粗字 ...

  2. linux的学习记录随笔

    为什么学习linux 因为操作系统是一种介质,你要接触其中的东西,首先必须要有介质,而linux在服务器端是老大哥的地位,所以呢,学习linux吧. 学习的方式 可以看视频 imooc.百度传课.网易 ...

  3. 2015.8.1 bootstrap学习(个人每日学习的随笔,比较凌乱

    写在前面: 记录自己的学习中遇到的问题和解决办法.因为是每日晚上总结,可能只是随便一笔带过方便自己记忆.如有写的错误或者凌乱之处,请勿介意 1.<html lang="zh-hans& ...

  4. Java学习的随笔(一)对象概念、this指针、权限修饰符

    最近在看<Java编程思想>,下面按照最近看书的顺序梳理一下心得,由于是初次学习,大部分心得是摘抄自书中: 1. Java中,每个变量都是一个对象. 在创建时首先在内存的堆栈中创建一个该对 ...

  5. 19.10.11学习日记随笔 mysql事务隔离性

    一天的感悟 学习事务的处理方式,其中反想自己学过的flask 默认是开启事务的,flask_sqlalchemy每次在提交时都是需要commit,或者失败是需要rollback回滚操作的,其实pyth ...

  6. C# 学习的随笔【随时更新】

    1.结束自己 Application.Exit(); //这个东西有重载函数

  7. 学习java随笔第十一篇:java窗体程序

    要开java的窗体程序,就要下载开发窗体的工具. 这里我用的是的myeclipse,可以直接在网上下载安装即可. 我用的是10.0版本的,如果需要汉化的话,可以看一下这篇文章:myeclipse.10 ...

  8. 学习java随笔第十篇:java线程

    线程生命周期 线程的生命周期:新建状态.准备状态.运行状态.等待/阻塞状态.死亡状态 示意图: 定义.创建及运行线程 线程: package threadrun; //定义一个实现Runnable接口 ...

  9. 学习java随笔第九篇:java异常处理

    在java中的异常处理和c#中的异常处理是一样的都是用try-catch语句. 基本语法如下 try { //此处是可能出现异常的代码 } catch(Exception e) { //此处是如果发生 ...

随机推荐

  1. 冲刺一 (Day 2)

    冲刺一 (Day 2) 小组讨论结果 经过今天的小组会议,小组各成员决定先进一步探讨项目的需求.因为我们明白要砍倒一棵树,磨刀才是前期的重中重之重,实际中也有不少以为前期需求没做好而,在项目后期推翻重 ...

  2. FreeMark学习(三)

    沉淀的心   freemarker学习笔记--设计指导   <# ... > 中存放所有freemaker的内容,之外的内容全部原样输出.<@ ... /> 是函数调用两个定界 ...

  3. SQL语言基础

    主要学习链接1 http://www.cnblogs.com/anding/p/5281558.html 搜索学习链接2 http://www.cnblogs.com/libingql/p/41342 ...

  4. Jquery day01

    day01: 基础--选择器.属性和CSS.文档处理 day02: 高级--筛选.事件.效果.ajax jQuery介绍 JS类库 JavaScript 库封装了很多预定义的对象和实用函数.能帮助使用 ...

  5. Salt 安装方式(CentOS)

    安装前必备环境:2.6.6<Pythin<3.x 本文内容是参考 中国SaltStack用户组 里面 Salt中文手册 中的安装内容实践之后总结的. 初学者可用Ubuntu系统学习安装,因 ...

  6. Modified Least Square Method and Ransan Method to Fit Circle from Data

    In OpenCv, it only provide the function fitEllipse to fit Ellipse, but doesn't provide function to f ...

  7. 编译安装php

    ./configure --prefix=/usr/local/php \--with-libdir=lib64 \--enable-fpm \--with-fpm-user=roter \--wit ...

  8. unity5.0新功能-布料、动画系统

    原作者:只待苍霞 这一章讲一下布料系统, 这次的布料系统有很大的改良.Unity4中, 需要对SkinnedMeshRenderer使用SkinnedCloth, 或者对Cloth Renderer使 ...

  9. C6713的Boot mode

    2014年7月23日,终于把困扰我两个月的问题解决了,甚是嗨皮,所以做下记录,以供后人参考之用. 问题描述:我用的片子是TMS320C6713,通过EMIF总线连接的FLASH,此FLASH分为两部分 ...

  10. Java内部类,枚举

    模式: 模板模式: 简单工厂模式: 适配器模式:  interface ICellPhone  {   void sendMsg();  } class Android implements ICel ...