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. NK3C程序资源占用分析

    1.程序放在一个Tomcat下最低配置推荐:最大堆:768M,最大PermGen:160M(-Xmx768m -XX:MaxPermSize=160m) 2.机器最低配置推荐:最小内存2G 3.正式运 ...

  2. 【转】sql server获取数据库名,表明,表结构

    1.查询数据库中的所有数据库名: SELECT Name FROM Master..SysDatabases ORDER BY Name 2.查询某个数据库中所有的表名: SELECT Name FR ...

  3. 敏捷软件开发 VS. 传统软件工程

    敏捷软件开发 VS. 传统软件工程 软件工程这一术语1968年被提出,之后美国软件工程专家巴利·玻姆对十多年间研究软件工程的专家学者们提出的一些准则与信条,于1983年对提出软件工程的七条基本定理,将 ...

  4. XidianOJ 1195 Industry of Orz Pandas

    --正文 贪心 排序好慢慢找就好 #include <iostream> #include <cstring> #include <cstdio> #include ...

  5. Java:多态(向上转型)

    先来看一段代码: class BaseClass{          public int book = 6;          public void base(){         System. ...

  6. 利用BeautifulSoup抓取新浪网页新闻的内容

    第一次写的小爬虫,python确实功能很强大,二十来行的代码抓取内容并存储为一个txt文本 直接上代码 #coding = 'utf-8' import requests from bs4 impor ...

  7. Math类中的BigDecimal

    如果我们编译运行下面这个程序会看到什么? public class Test {    public static void main(String args[]) {                 ...

  8. 使用hex6x 进行十六进制转换

    接触DSP两年多,虽然烧写Flash的操作都没问题,但是要是问起来为什么这么做的,就有点自惭形秽了.所以花些时间,查阅一下资料,整理一下. 1.先看看BurnFlash都需要什么东西. XXX.out ...

  9. js获取IP和MAC地址

    1.IP 百度一下有很多 可以用这个 <script src="http://pv.sohu.com/cityjson?ie=utf-8"></script> ...

  10. asp.net LINQ数据访问技术from where select order by子句

    using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.UI ...