Varnish

一、Varnish简介

     Varnish是一款高性能的开源HTTP加速器,挪威最大的在线报纸 Verdens Gang 使用3台Varnish代替了原来的12台Squid,性能比以前更好。

Varnish 的作者Poul-Henning Kamp是FreeBSD的内核开发者之一,他认为现在的计算机比起1975年已经复杂许多。在1975年时,储存媒介只有两种:内存与硬盘。但现在计算机系统的内存除了主存外,还包括了CPU内的L1、L2,甚至有L3快取。硬盘上也有自己的快取装置,因此Squid Cache自行处理物件替换的架构不可能得知这些情况而做到最佳化,但操作系统可以得知这些情况,所以这部份的工作应该交给操作系统处理,这就是 Varnish cache设计架构。

varnish项目是2006年发布的第一个版本0.9.距今已经八年多了,此文档之前也提过varnish还不稳定,那是2007年时候编写的,经过varnish开发团队和网友们的辛苦耕耘,现在的varnish已经很健壮。很多门户网站已经部署了varnish,并且反应都很好,甚至反应比squid还稳定,且效率更高,资源占用更少。相信在反向代理,web加速方面,varnish已经有足够能力代替squid。

严格意义上说,Varnish是一个高性能的反向代理软件,只不过与其出色的缓存功能相比,企业更愿意使用其搭建缓存服务器。

    引用:百度百科

二、Varnish系统架构

1) varnish主要运行两个进程:Management进程和Child进程(也叫Cache进程)。

Management进程主要实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等。Management进程会每隔几秒钟探测一下Child进程以判断其是否正常运行,如果在指定的时长内未得到Child进程的回应,Management将会重启此Child进程。

 

Child进程包含多种类型的线程,常见的如:

Acceptor线程:接收新的连接请求并响应;

Worker线程:child进程会为每个会话启动一个worker线程,因此,在高并发的场景中可能会出现数百个worker线程甚至更多;

Expiry线程:从缓存中清理过期内容;

Varnish依赖“工作区(workspace)”以降低线程在申请或修改内存时出现竞争的可能性。在varnish内部有多种不同的工作区,其中最关键的当属用于管理会话数据的session工作区。

2) varnish日志

为了与系统的其它部分进行交互,Child进程使用了可以通过文件系统接口进行访问的共享内存日志(shared memory log),因此,如果某线程需要记录信息,其仅需要持有一个锁,而后向共享内存中的某内存区域写入数据,再释放持有的锁即可。而为了减少竞争,每个worker线程都使用了日志数据缓存。

共享内存日志大小一般为90M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。varnish提供了多个不同的工具如varnishlog、varnishncsa或varnishstat等来分析共享内存日志中的信息并能够以指定的方式进行显示。

3) 

Varnish Configuration Language (VCL)是varnish配置缓存策略的工具,它是一种基于“域”(domain specific)的简单编程语言,它支持有限的算术运算和逻辑运算操作、允许使用正则表达式进行字符串匹配、允许用户使用set自定义变量、支持if判断语句,也有内置的函数和变量等。使用VCL编写的缓存策略通常保存至.vcl文件中,其需要编译成二进制的格式后才能由varnish调用。事实上,整个缓存策略就是由几个特定的子例程如vcl_recv、vcl_fetch等组成,它们分别在不同的位置(或时间)执行,如果没有事先为某个位置自定义子例程,varnish将会执行默认的定义。

VCL策略在启用前,会由management进程将其转换为C代码,而后再由gcc编译器将C代码编译成二进制程序。编译完成后,management负责将其连接至varnish实例,即child进程。正是由于编译工作在child进程之外完成,它避免了装载错误格式VCL的风险。因此,varnish修改配置的开销非常小,其可以同时保有几份尚在引用的旧版本配置,也能够让新的配置即刻生效。编译后的旧版本配置通常在varnish重启时才会被丢弃,如果需要手动清理,则可以使用varnishadm的vcl.discard命令完成。

4) varnish的后端存储

varnish支持多种不同类型的后端存储,这可以在varnishd启动时使用-s选项指定。后端存储的类型包括:

(1)file:使用特定的文件存储全部的缓存数据,并通过操作系统的mmap()系统调用将整个缓存文件映射至内存区域(如果条件允许);

(2)malloc:使用malloc()库调用在varnish启动时向操作系统申请指定大小的内存空间以存储缓存对象;

(3)persistent(experimental):与file的功能相同,但可以持久存储数据(即重启varnish数据时不会被清除);仍处于测试期;

varnish无法追踪某缓存对象是否存入了缓存文件,从而也就无从得知磁盘上的缓存文件是否可用,因此,file存储方法在varnish停止或重启时会清除数据。而persistent方法的出现对此有了一个弥补,但persistent仍处于测试阶段,例如目前尚无法有效处理要缓存对象总体大小超出缓存空间的情况,所以,其仅适用于有着巨大缓存空间的场景。

选择使用合适的存储方式有助于提升系统性,从经验的角度来看,建议在内存空间足以存储所有的缓存对象时使用malloc的方法,反之,file存储将有着更好的性能的表现。然而,需要注意的是,varnishd实际上使用的空间比使用-s选项指定的缓存空间更大,一般说来,其需要为每个缓存对象多使用差不多1K左右的存储空间,这意味着,对于100万个缓存对象的场景来说,其使用的缓存空间将超出指定大小1G左右。另外,为了保存数据结构等,varnish自身也会占去不小的内存空间。

 

为varnishd指定使用的缓存类型时,-s选项可接受的参数格式如下:

malloc[,size] 或file[,path[,size[,granularity]]] 或 persistent,path,size {experimental}

 

三、Varnish 安装

Varnish的官方网站https://www.varnish-cache.org/

方法1:

# yum install -y gcc

# ls

varnish-3.0.6-1.el6.x86_64.rpm

varnish-docs-3.0.6-1.el6.x86_64.rpm

varnish-libs-3.0.6-1.el6.x86_64.rpm

# rpm -ivh varnish-*

方法2:

# rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-3.0.el6.rpm

# yum install varnish -y

# rpm -ql varnish

/etc/varnish/default.vcl             # vcl的主配置文件

/usr/sbin/varnishd

/etc/sysconfig/varnish                # varnish的主配置文件

 

# vim /etc/sysconfig/varnish

NFILES=131072                                 # 最大打开文件数65536*2{ulimit -n}

MEMLOCK=82000                                 # 锁定使用多大的共享内存来保存日志信息,默认为82M

NPROCS="unlimited"                            # 最大的线程数,默认无限制

# DAEMON_COREFILE_LIMIT="unlimited"          # 进程核心转储所使用的内存空间,unlimited表示无上限

RELOAD_VCL=1                                   # 设置为1可以使用restart自动加载vcl配置文件

VARNISH_VCL_CONF=/etc/varnish/default.vcl   # 定义vcl配置文件

VARNISH_LISTEN_PORT=6081                    # 定义varnish服务监听端口,一般改为80端口

VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1      # 定义允许进行进程管理地址

VARNISH_ADMIN_LISTEN_PORT=6082              # 定义管理进程监听端口

VARNISH_SECRET_FILE=/etc/varnish/secret     # 定义秘钥和签名认证文件

VARNISH_MIN_THREADS=50                      # 定义varnish启动时最小工作线程数

VARNISH_MAX_THREADS=1000                    # 定义varnish启动时最大工作线程数

VARNISH_THREAD_TIMEOUT=120                  # 定义varnish线程响应超时时间

VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin   # 定义varnish的缓存文件

VARNISH_STORAGE_SIZE=1G                     # 定义缓存使用文件大小,单位:k/M/G/T

VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" # 使用文件缓存

VARNISH_STORAGE_SHM=64M  # 定义缓存使用内存的大小

# VARNISH_STORAGE="malloc,${VARNISH_STORAGE_SHM}"              # 使用内存作为缓存;malloc

VARNISH_TTL=120                 # 如果后端服务器没有设置数据缓存多长时间,则默认为120秒

DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \        # 变量调用

             -f ${VARNISH_VCL_CONF} \

             -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \

             -t ${VARNISH_TTL} \

             -w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \

             -u varnish -g varnish \

             -S ${VARNISH_SECRET_FILE} \ #前端和后端在通信的过程中实现加密和签名的密钥文件,防止别人篡改数据的。

             -s ${VARNISH_STORAGE}"

 

配置好varnish 后应该指明 后端的服务器是谁; 启动后端的web服务器;

# cp /etc/varnish/default.vcl{,.bak}

# vim /etc/varnish/default.vcl

修改为:

backend default {                             #定义后端默认的服务器

.host = "172.16.8.101";             # 定义后端的主机地址;

.port = "80";                             # 定义后端主机的端口;

}

启动服务:

# service varnish start

进行访问: 就可以访问到后端node1主机上了;

wKiom1V0LjWTovF_AAB--IypYPk958.jpg 

注意:因为是缓存服务器,如果重启配置文件的话,那么缓存中的所有数据就会丢失,如果每一次都因为修改配置文件而让缓存丢失,那么会对缓存服务器造成巨大的损害;该怎么解决这个办法呢,下面来为大家说明;

这种方法可以不重启就让策略生效:

应该重新通知装载设备文件让它生效:varnishadm 专用的命令接口来连接varnish;

# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082

-S: 指明密钥文件;密钥文件要与服务器启动时一样;

-T:指明要连接的端口;

 Varnish Cache CLI 1.0

varnish> help  获取到所有可以使用的命令;

varnish> ping                              # 测试ping命令

varnish> status                            # 查看状态信息

varnish> vcl.list                               # 列出所有的配置

varnish> backend.list                           #查看配置文件的配置信息

varnish> storage.list                           #查看储存列表信息

 

让修改的配置生效:

# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082

varnish> vcl.load test2 default.vcl                        # 加载编译配置 test2为自定义的配置名;

varnish> vcl.use test2                                     # 使用配置,需指定配置名

varnish> vcl.list                                          # 列出所有的配置

现在处于激活状态的是 test1

 

四、Varnish 状态引擎以及工作原理说明(state engine)

wKiom1V0LePAAKl0AAGIXQ1il8w308.jpg 

VCL用于让管理员定义缓存策略,而定义好的策略将由varnish的management进程分析、转换成C代码、编译成二进制程序并连接至child进程。varnish内部有几个所谓的状态(state),在这些状态上可以附加通过VCL定义的策略以完成相应的缓存处理机制,因此VCL也经常被称作“域专用”语言或状态引擎,“域专用”指的是有些数据仅出现于特定的状态中。

1) VCL状态引擎:

# vim /etc/varnish/default.vcl 

在VCL状态引擎中,状态之间具有相关性,但彼此间互相隔离,每个引擎使用return(x)来退出当前状态并指示varnish进入下一个状态。

vcl_recv:用于接收到用户的请求

在vcl_hit引擎中可以调用return(pipe)指令和调用return(lookup)指令和调用return(pass)指令。

如果不检查缓存;

调用的是return(pipe)指令,然后由vcl_pipe引擎直接交给后端服务器进行处理

如果是检查缓存;

调用return(lookup)指令,检查缓存,看缓存是否命中,需自行定义如何

检查缓存

调用return(pass)指令,则将请求送给vcl_pass进行处理

 

vcl_pipe:用于把用户的请求接进来,然后建立一个管道直接交给后端服务器

    在vcl_pipe引擎中可以调用return(pipe)指令

    调用return(pipe)指令则建立一个与后端服务器的管道

 

vcl_hash:用于自行定义其它缓存的机制

    在vcl_hash引擎中可以调用return(hash)指令

    调用return(hash)指令,则通过hash键值对进行判断,是否命中

 

vcl_hit:用于表示缓存命中

在vcl_hit引擎中可以调用return(pass)指令和调用return(delive)指令

    如果是调用return(pass)指令,则将请求送给vcl_pass进行处理

    如果是调用return(delive)指令,则从缓存中直接取出后由vcl_deliver返回给用户

 

vcl_miss:用于表示缓存未命中

在vcl_miss引擎中可以调用return(pass)指令和调用return(fetch)指令

    如果是调用return(pass)指令,则将请求送给vcl_pass进行处理

    如果是调用return(fetch)指令,则将请求送给vcl_fetch进行处理

 

vcl_pass:用于给命中引擎和未命中引擎提供处理机制

在vcl_pass引擎中可以调用return(fetch)指令

    调用return(fetch)指令,则将请求送给vcl_fetch进行处理

 

vcl_fetch:用于到后端服务器去取数据

在vcl_fetch引擎中可以调用return(delive)指令和调用return(pass)指令

    如果是调用return(delive)指令,则把后端取的数据保存在缓存中

    如果是调用return(pass)指令,则不把后端取的数据保存在缓存中

 

vcl_deliver:用于从缓存中取数据返回给用户

vcl_error:用于由varnish直接构建错误响应报文

 

五、Varnish 示例

5.1)移除单个缓存对象

purge用于清理缓存中的某特定对象及其变种(variants),因此,在有着明确要修剪的缓存对象时可以使用此种方式。HTTP协议的PURGE方法可以实现purge功能,不过,其仅能用于vcl_hit和vcl_miss中,

它会释放内存工作并移除指定缓存对象的所有Vary:-变种,并等待下一个针对此内容的客户端请求到达时刷新此内容。另外,其一般要与return(restart)一起使用。下面是个在VCL中配置的示例。

 

要定义在 状态引擎之外;

acl purgers {

"127.0.0.1";

"192.168.0.0"/24;                # 掩码不用引起来;

}

来自于上面这些地址才允许做purge; 修剪缓存;

 

启用默认vcl_recv默认配置时 使用的方式;

sub vcl_recv {

if (req.request == "PURGE") {

if (!client.ip ~ purgers) {

error 405 "Method not allowed";

}

return (lookup);#允许查缓存;

}

}

sub vcl_hit {

if (req.request == "PURGE") {

purge;                                # varnish 内置的方法,直接做purge操作;

error 200 "Purged";            # 操作完成后,告诉它完成;

}

}

sub vcl_miss {

if (req.request == "PURGE") {

purge;

error 404 "Not in cache";

}

}

sub vcl_pass {

if (req.request == "PURGE") {

error 502 "PURGE on a passed object";

}

return (pass);

}

装载配置文件,让其生效:

# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082

varnish> vcl.load t2 default.vcl

varnish> vcl.ues t2

 

客户端在发起HTTP请求时,只需要为所请求的URL使用PURGE方法即可,其命令使用方式如下:

# curl -I -X PURGE http://172.16.8.100/index.html                        # 手动清理对象:

# curl -I http://172.16.8.100/index.html                                        # 缓存中没有对象;不可以可以被命中

# curl -I http://172.16.8.100/index.html                                        # 第二次缓存中有对象;可以被命中

 

5.2) 强制缓存未命中

在vcl_recv中使用return(pass)能够强制到上游服务器取得请求的内容,但这也会导致无法将其缓存。使用purge会移除旧的缓存对象,但如果上游服务器宕机而无法取得新版本的内容时,此内容将无法再响应给客户端。使用req.has_always_miss=ture,可以让Varnish在缓存中搜寻相应的内容但却总是回应“未命中”,于是vcl_miss将后续地负责启动vcl_fetch从上游服务器取得新内容,并以新内容缓存覆盖旧内容。此时,如果上游服务器宕机或未响应,旧的内容将保持原状,并能够继续服务于那些未使用req.has_always_miss=true的客户端,直到其过期失效或由其它方法移除。

 

5.3) 实现动静分离

实现动静分离:

# vim /etc/varnish/default.vcl

backend appsrv {                                        # 定义动态页面访问的主机

 .host = "172.16.100.7";

 .port = "80";

}

backend static {                                            # 定义静态页面访问的主机

 .host = "172.16.100.8";

 .port = "80";

}

if (req.url ~ "\.php$" ) {                                    # 如果是以.php结尾的页面则送到appsrv定义的主机

set req.backend = appsrv;

} else {

set req.backend = static;                                # 其他的页面全被交给静态页面的主机

}

# 让配置生效:

varnish> vcl.load t4 ./default.vcl

varnish> vcl.use t4

 

5.4) 健康状态检查:演示示例:

Varnish可以检测后端主机的健康状态,在判定后端主机失效时能自动将其从可用后端主机列表中移除,而一旦其重新变得可用还可以自动将其设定为可用。为了避免误判,Varnish在探测后端主机的健康状态发生转变时(比如某次探测时某后端主机突然成为不可用状态),通常需要连续执行几次探测均为新状态才将其标记为转换后的状态。

# vim /etc/varnish/default.vcl

probe chk {

.url = "/index.html";                    # 探测后端主机健康状态时请求的URL,此处是index.html的文件

.window =5;                                # 设定在判定后端主机健康状态时基于最近多少次的探测进行,默认是8;

.threshold = 3;

#在.window中指定的次数中,至少有多少次是成功的才判定后端主机正健康运行;默认是3;

.interval = 3s;                    # 探测请求的发送周期,默认为5秒;

.timeout = 1s;                    # 每次探测请求的过期时长,默认为2秒;这里设定为1秒;

 

backend appsrv {            # 定义动态页面访问的主机

 .host = "172.16.100.7";

 .port = "80";

.probe = chk;            # 调用健康状态检测;

}    

backend static {                # 定义静态页面访问的主机

 .host = "172.16.100.8";

 .port = "80";

.probe = chk;            # 调用健康状态检测;

}

 

让新配置生效

varnish> vcl.load t5 ./default.vcl

varnish> vcl.use t5

查看状态信息:要进入到vcl 模式下!

varnish> backend.list                # 列出当前的所有后端主机,以及健康状态机制;

 

注意:后端主机都应该关闭对后端主机状态检查的日志信息;

 

5.5) Varnish 使用多台后端主机

Varnish中可以使用director指令将一个或多个近似的后端主机定义为一个逻辑组,并可以指定的调度方式(也叫挑选方法)来轮流将请求发送至这些主机上。不同的director可以使用同一个后端主机,而某director也可以使用“匿名”后端主机(在director中直接进行定义)。每个director都必须有其专用名,且在定义后必须在VCL中进行调用,VCL中任何可以指定后端主机的位置均可以按需将其替换为调用某已定义的director。

backend appsrv {

.host = "172.16.100.7";

 .port = "80";

 .probe = chk;                    # 调用状态检测;

}

backend static {

.host = "172.16.8.101";

.port = "80";

 .probe = chk;                # 调用状态检测;

}

director mysrvs random {

.retries = 3;

{

.backend = appsrv;

.weight = 1;

}

{

.backend = static;

.weight = 1;

}

}

注:director支持的挑选方法中比较简单的有round-robin和random两种

定义的director  必须被调用;

if (req.url ~ "\.php$" ) {

set req.backend = appsrv;所有动态内容只有appsrv 这台服务器响应;

}  else {

set req.backend = mysrvs;静态内容 上面定义的2台服务器都可以进行响应;

}

 

varnish的线程模型:

cache-worker线程

cache-main线程:此线程只有一个,用于启动cache;

ban luker: 实现缓存清理;

acceptor:

epoll:线程池管理器

expire: 清理过期缓存;

 

varnish定义其最大并发连接数; 线程池模型;

thread_pools:线程池的个数,默认为2;

thread_pool_max:单线程池内允许启动的最多线程个数;

thread_pool_min:允许启动的最少线程个数;

thread_pool_timeout: 多于thread_pool_min的线程空闲此参数指定的时长后即被purge;

 

六、Varnish 的命令行工具

varnishadm命令

命令语法:varnishadm [-t timeout] [-S secret_file] [-T address:port] [-n name] [command [...]]

通过命令行的方式连接至varnishd进行管理操作的工具,指定要连接的varnish实例的方法有两种:

-n name ―― 连接至名称为“name”的实例;

-T address:port ―― 连接至指定套接字上的实例;Varnishadm命令:

-S: 指明密钥文件;密钥文件要与服务器启动时一样;

例:# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082

 

varnishtop: 内存日志区域查看工具;

-I regexp: 仅显示被模式匹配到的条目;

-X regexp: 仅显示不被模式匹配的条目

-C:忽略字符大小写;

-d: 显示已有的日志;

 

varnishstat: varnish缓存的统计数据;从内容日志区域中来获取数据的;

-f field,field, ...

-l: 列出所有可用字段

-x: xml输出格式

-j: json输出格式;


你可能感兴趣的:(varnish)