Varnish 代理缓存服务器

Varnish之于squid就好比nginx之于apache,是一款轻量级的代理缓存服务器,在CDN(内容分发网络)中广为使用。


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



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选项可接受的参数格式如下:

malloc[,size]

file[,path[,size[,granularity]]]

persistent,path,size {experimental}

varnishd实际上使用的空间比使用-s选项指定的缓存空间更大,varnish为了保存数据结构,也要占去不少的内存空间


Varnish与Squid的对比

说到Varnish,不能不提Squid,Squid是一个高性能的代理缓存服务器,它和varnish之间有诸多的异同点,这里分析如下:

下面是他们之间的相同点:
(1)都是一个反向代理服务器,
(2)都是开源软件,
下面是它们的不同点,也是Varnish的优点:
(1)Varnish的稳定性很高,两者在完成相同负荷的工作时,Squid服务器发生故障的几率要高于Varnish,因为使用Squid要经常重启。
(2)Varnish访问速度更快,Varnish采用了“Visual Page Cache”技术,所有缓存数据都直接从内存读取,而squid是从硬盘读取,因而Varnish在访问速度方面会更快。
(3)Varnish可以支持更多的并发连接,因为Varnish的TCP连接释放要比Squid快。因而在高并发连接情况下可以支持更多TCP连接。
(4)Varnish可以通过管理端口,使用正则表达式批量的清除部分缓存,而Squid是做不到的。
当然,与传统的Squid相比,Varnish也是有缺点的,列举如下:
(1)varnish在高并发状态下CPU、IO、内存等资源开销都高于Squid。
(2)varnish进程一旦Hang、Crash或者重启,缓存数据都会从内存中完全释放,此时所有请求都会发送到后端服务器,在高并发情况下,会给后端服务器造成很大压力。




Vcl处理流程图


varnish运行机制及管理优化_varnish




Varnish支持的算法

Varnishdirector支持的挑选方法中比较简单的有round-robinrandom两种。其中,round-robin类型没有任何参数,只需要为其指定各后端主机即可,挑选方式为轮叫,并在某后端主机故障时不再将其视作挑选对象;random方法随机从可用后端主机中进行挑选,每一个后端主机都需要一个.weight参数以指定其权重,同时还可以director级别使用.retires参数来设定查找一个健康后端主机时的尝试次数。



安装配置,官方提供的方法

https://www.varnish-cache.org/installation/redhat


# rpm --nosignature -i http://repo.varnish-cache.org/redhat/varnish-3.0/el6/noarch
/varnish-release-3.0-1.el6.noarch.rpm
# yum -y install varnish


也可以到这里下载,下载时要下载同一个版本的安装

http://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/

下载完整的一套

varnish-3.0.3-1.el6.x86_64.rpm 必须的

varnish-libs-3.0.3-1.el6.x86_64.rpm 必须的

varnish-libs-devel-3.0.3-1.el6.x86_64.rpm 不必须

varnish-docs-3.0.3-1.el6.x86_64.rpm 不必须


# rpm -ql varnish
/etc/rc.d/init.d/varnish  #服务脚本
/etc/rc.d/init.d/varnishlog   #启动日志功能,将缓存存放到日志文件-----------
-----------------------------------------/var/log/varnish/varnish.log中。
/etc/sysconfig/varnish    #运行时的参数,varnish服务自身的配置
/etc/varnish/   #存放vcl文件, vcl文件可以定义多个


配置文件注释


# cat varnish | grep -v -e ^# -e ^$
NFILES=131072   #最多可以打开多少个套接字文件,套接字65536,前段后端各一个
MEMLOCK=82000  #内存中保存日志的空间大小82M
NPROCS="unlimited"  #打开的线无上限
RELOAD_VCL=1  #每一次重启,重新编译并载入vcl,不用重启就能编译,reload
#DAEMON_OPTS="-a :6081 \ #传递给启动时的选项,默认监听的端口,应设为80,避免冲突
#-T localhost:6082 \# 通过此端口连进去可以设置新的vcl
# -f /etc/varnish/default.vcl \#默认加载的vcl文件
#-u varnish -g varnish \#用户和组
# -S /etc/varnish/secret \#密钥文件,如果没有,任何人都可以连接到服务器
#-s file,/var/lib/varnish/varnish_storage.bin,1G"VARNISH_VCL_CONF=
/etc/varnish/default.vcl
#缓存文件存储位置
VARNISH_LISTEN_PORT=6081
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082
VARNISH_SECRET_FILE=/etc/varnish/secret
VARNISH_MIN_THREADS=50
VARNISH_MAX_THREADS=1000
VARNISH_THREAD_TIMEOUT=120
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin
VARNISH_STORAGE_SIZE=1G
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"
缓存存在文件中,可以修改为存在内存中
VARNISH_STORAGE_SIZE=1G
VARNISH_STORAGE="malloc,${VARNISH_STORAGE_SIZE}"
VARNISH_TTL=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}"
# mv varnish varnish.bak
# cat varnish.bak | grep -v -e ^# -e ^$ > varnish
# vim varnish
NFILES=131072
MEMLOCK=82000
NPROCS="unlimited"
RELOAD_VCL=1
VARNISH_VCL_CONF=/etc/varnish/default.vcl
VARNISH_LISTEN_PORT=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_MAX_THREADS=1000
VARNISH_THREAD_TIMEOUT=120
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin
VARNISH_STORAGE_SIZE=1G
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"
VARNISH_TTL=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管理工具


# service varnish start
# ss -tanlp | grep varnish
LISTEN     0      128                      :::80                      :::*      users:(("varnishd",30208,8))
LISTEN     0      10                127.0.0.1:6082                     *:*      users:(("varnishd",30207,7))
# varnishadm -h
varnishadm: invalid option -- 'h'
usage: varnishadm [-n ident] [-t timeout] [-S secretfile] -T [address]:port command [...]
-n is mutually exlusive with -S and -T
# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
varnish> help
200 
help [command]
ping [timestamp]   # 测试varnish是否工作正常
auth response
quit
Banner  #进入varnish控制器时显示的标语
Status  #当前服务器状态
Start   #启动子进程
Stop   #停止子进程
vcl.load <configname> <filename>   # 装载vcl文件
vcl.inline <configname> <quoted_VCLstring>  #编译加载给出的vcl数据
vcl.use <configname> #使用vcl
vcl.discard <configname>  #加载正确的配置数据
Vcl.list  #当前正在使用vcl文件
vcl.show <configname>   #显示当前的vcl
param.show [-l] [<param>] #显示参数
param.set <param> <value>  #设置参数
Panic.show #显示恐慌信息
Panic.clear #清除恐慌信息
Storage.list  #显示存储方式
Backend.list  #显示后端服务器
backend.set_health matcher state #设置后端服务器健康状况
ban.url <regexp>
ban <field> <operator> <arg> [&& <field> <oper> <arg>]...
Ban.list #列出活动的ban



使用varnish作为后端服务器的代理缓存

Ip172.16.5.15 varnish服务器ip

Ip172.16.5.16 后端服务器ip


# vim /etc/varnish/backend.vcl
backend default {
  .host = "172.16.5.16";
  .port = "8080";
}

在后端服务器上启动web服务,确保web服务监听的端口为8080


# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
200 
-----------------------------
Varnish Cache CLI 1.0
-----------------------------
Linux,2.6.32-358.el6.x86_64,x86_64,-sfile,-smalloc,-hcritbit
varnish-3.0.4 revision 9f83e8f
Type 'help' for command list.
Type 'quit' to close CLI session.
varnish> vcl.load backend1 /etc/varnish/backend.vcl
200 
VCL compiled.
varnish> vcl.use backend1
200 
varnish> vcl.list
200 
available       2 boot
active          0 backend1

varnish运行机制及管理优化_varnish_02



请求资源时的时间消耗


DNS查询时间


建立连接


服务器处理


响应报文传输时间


断开连接



一个完整的vcl配置文件,后端服务器自己配置,经本人验证,该配置的各种功能都能够实现。

# vim /etc/sysconfig/varnish
NFILES=131072
MEMLOCK=82000
NPROCS="unlimited"
RELOAD_VCL=1
VARNISH_VCL_CONF=/etc/varnish/backend.vcl
VARNISH_LISTEN_PORT=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_MAX_THREADS=1000
VARNISH_THREAD_TIMEOUT=120
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin
VARNISH_STORAGE_SIZE=1G
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"
VARNISH_TTL=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}"


# vim /etc/varnish/backend.vcl
############定义健康状态检测###############
probe healthcheck {
    .url = "/";                    #定义健康检查的页面为主页面
    .interval = 6s;                #探测请求的发送周期,默认为5秒;
   .timeout = 0.3 s;              #每次探测请求的过期时长
    .window = 8;                #设定在判定后端主机健康状态时基于最近多少次的探测进行
    .threshold = 3;               #在.window中指定的次数中,至少有多少次是成功的才判定后端主机正健康运行
   # .initial = 3;               #用法同.threshold
}
############定义两组服务器##############
backend web1 {
  .host = "172.16.5.15";           #服务器主机
  .port = "8080";                   #服务器端口
  .probe = healthcheck;           #健康状态检测
}
backend web2 {
  .host = "172.16.5.17";           #服务器主机
  .port = "8080";                   #服务器端口
  .probe = healthcheck;           #健康状态检测
}
backend web3 {
  .host = "172.16.5.16";           #服务器主机
  .port = "8080";                   #服务器端口
  .probe = healthcheck;           #健康状态检测
}
backend web4 {
  .host = "172.16.5.18";           #服务器主机
  .port = "8080";                   #服务器端口
  .probe = healthcheck;           #健康状态检测
}
###############显示命中与否的信息##############
sub vcl_deliver {
       if (obj.hits > 0) {
            set resp.http.X-Cache = "HIT from" + " " + server.ip;
       } else {
            set resp.http.X-Cache = "MISS";
       }
}
############定义集群,调用服务器##############
director webserver random {     #定义一个名为webserver的directory,由web1,和web2分但请求,使用random算法,处理静态请求
    {.backend = web1;
     .weight  = 2;               #设置权重为2
    }
    {.backend = web2;
     .weight = 5;
    }
}
director phpserver  random {     #定义一个名为appserver的directory,由app1,和app2分但请求,使用random算法,处理动态请求
        {.backend = web3;
         .weight = 2;}
        {.backend = web4;
         .weight = 5;}
}
###########################定义清理缓存的ip################
acl purgers {
    "127.0.0.1";
    "172.16.5.100";
}
###############将请求直接送给后端服务器###########
sub vcl_recv {
      if (req.url ~ "test.html") {
           return (pass);
      }
      if (req.url ~ "\.php$") {
           set req.backend = phpserver;
      } else {
           set req.backend = webserver;
      }
#############定义不能清除缓存的IP,调用上面的Acl############
    if (req.request == "PURGE"){                  #使用PURGE命令清除缓存
            if(!client.ip ~ purgers){              #非ACl定义的IP,则不能清除缓存  
         error 405 "Method not allowed";
            }
           return (lookup);         #将查找结果交由vcl_hit函数或者vcl_miss函数
}
#############不正常的访问不缓存############
  if (req.request != "GET" &&
       req.request != "HEAD" &&
              req.request != "PUT" &&
              req.request != "POST" &&
              req.request != "TRACE" &&
              req.request != "OPTIONS" &&
              req.request != "DELETE") {
                /* Non-RFC2616 or CONNECT which is weird. */
                return (pipe);
            }
            if (req.request != "GET" && req.request != "HEAD") {
                /* We only deal with GET and HEAD by default */
                return (pass);
            }
#############不缓存认证信息和Cookie############
            if (req.http.Authorization || req.http.Cookie) {
                /* Not cacheable by default */
                return (pass);
            }
############支持压缩功能###################
if (req.http.Accept-Encoding) {          
if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
# No point in compressing these
remove req.http.Accept-Encoding;
} else if (req.http.Accept-Encoding ~ "gzip") {
             set req.http.Accept-Encoding = "gzip";
} else if (req.http.Accept-Encoding ~ "deflate") {
             set req.http.Accept-Encoding = "deflate";
} else {
             remove req.http.Accept-Encoding;
}
        }
}
############vcl_hit函数段##############
sub vcl_hit {
    if (req.request == "PURGE"){                  #请求方法是PURGE,这清理缓存
        purge;
        error 200 "Purged";
}
}
############vcl_miss函数段##############
sub vcl_miss {
    if (req.request == "PURGE"){
          purge;
        error 404 "Not in cache";
}
}
############vcl_pass函数段##############
sub vcl_pass {
    if (req.request == "PURGE"){
        error 502 "PURGE on a passed object";
}
}
############vcl_fetch函数段##############
sub vcl_fetch
{
#############定义缓存时长############
    if (req.request == "GET" && req.url ~ "\.html$"){
    set beresp.ttl = 300s;                         #超时时长为300秒
if (req.request == "GET" && req.url ~ "\.(png|xsl|xml|pdf|ppt|doc|docx|chm|rar|zip|bmp|jpeg|swf|ico|mp3|mp4|rmvb|ogg|mov|avi|wmv|swf|txt|png|gif|jpg|css|js|html|htm)$") {
       set beresp.ttl = 600s;
    }
    return (deliver);
}
}

启动varnish监控工具

# varnishstat
Hitrate ratio:        3        3        3
Hitrate avg:     0.9989   0.9989   0.9989
       20496         0.00         1.82 client_conn - Client connections accepted
       20506         0.00         1.82 client_req - Client requests received
       20477         0.00         1.82 cache_hit - Cache hits
          29         0.00         0.00 cache_miss - Cache misses
          13         0.00         0.00 backend_conn - Backend conn. success
           3         0.00         0.00 backend_fail - Backend conn. failures
          15         0.00         0.00 backend_reuse - Backend conn. reuses
          12         0.00         0.00 backend_toolate - Backend conn. was closed
          28         0.00         0.00 backend_recycle - Backend conn. recycles
          15         0.00         0.00 fetch_length - Fetch with Length
          13         0.00         0.00 fetch_chunked - Fetch chunked
         387          .            .   n_sess_mem - N struct sess_mem
           0          .            .   n_sess - N struct sess
           1          .            .   n_object - N struct object
         101          .            .   n_objectcore - N struct objectcore
         101          .            .   n_objecthead - N struct objecthead
         100          .            .   n_waitinglist - N struct waitinglist
           1          .            .   n_vbc - N struct vbc
         100          .            .   n_wrk - N worker threads
         152         0.00         0.01 n_wrk_create - N worker threads created
        1652         0.00         0.15 n_wrk_queued - N queued work requests
           6          .            .   n_backend - N backends
          27          .            .   n_expired - N expired objects
          32          .            .   n_lru_moved - N LRU moved objects

Client connections accepted:表示客户端向反向代理服务器成功发送HTTP请求的总数量


Client requests received: 表示到现在为止,浏览器向反向代理服务器发送HTTP请求的累积次数,由于可能会使用长连接,所以这个值一般会大 于“Client connections accepted”


Cache hits:表示反向代理服务器在缓存区中查找并且命中缓存的次数。


Cache misses:表示直接访问后端主机的请求数量,也就是非命中数。


N struct object:表示当前被缓存的数量。


N expired objects:表示过期的缓存内容数量。


N LRU moved objects:表示被淘汰的缓存内容个数



监控后端服务的健康状态,varnishlogs


# varnishlog
    0 Backend_health - web2 Still sick ------- 0 3 8 0.000000 0.000000
    0 Backend_health - web3 Still sick ------- 0 3 8 0.000000 0.000000
    0 Backend_health - web1 Still healthy 4--X-RH 8 3 8 0.007568 0.011728 HTTP/1.1 200 OK
    0 Backend_health - web4 Still sick ------- 0 3 8 0.000000 0.000000
    0 Backend_health - web2 Still sick ------- 0 3 8 0.000000 0.000000
    0 Backend_health - web2 Still sick ------- 0 3 8 0.000000 0.000000
    0 Backend_health - web3 Still sick ------- 0 3 8 0.000000 0.000000
0 Backend_health - web1 Still healthy 4--X-RH 8 3 8 0.005915 0.010275 HTTP/1.1 200 OK




手动清理缓存


# curl -X PURGE http://172.16.5.15/
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
  <head>
    <title>405 Method not allowed</title>   # 配置文件中定义的清理缓存的ip非本机,修改配置文件再试一次
  </head>
  <body>
    <h1>Error 405 Method not allowed</h1>
    <p>Method not allowed</p>
    <h3>Guru Meditation:</h3>
    <p>XID: 614714334</p>
    <hr>
    <p>Varnish cache server</p>
  </body>
</html>
# curl -X PURGE 172.16.5.15/index.html
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
  <head>
    <title>200 Purged</title>  #手动清理成功
  </head>
  <body>
    <h1>Error 200 Purged</h1>
    <p>Purged</p>
    <h3>Guru Meditation:</h3>
    <p>XID: 614714341</p>
    <hr>
    <p>Varnish cache server</p>
  </body>
</html>



Varnish性能优化主要参数


# varnish -S /etc/varnish/sercet -T 127.0.0.1:6082
varnish> param.show
thread_pools               4 [pools]
thread_pool_min            50 [threads]
thread_pool_max            5120 [threads]
thread_pool_timeout        10 [seconds]
lru_interval               20 [seconds]
listen_depth               1024 [connections]

lru_interval:这是个时间参数,大概意思是说如果有一个对象在内存中超过了此参数设定的时间还没有被重用时,就把这个对象从LRU(Least Recently Used)队列中移除。这其实是缓存系统的一个常用算法,合理的设置这个时间,可以提高系统的运行效率。

listen_depth:这个参数是设置TCP连接队列的长度,设置大一点可以提高并发处理能力。对于这些优化参数,最好的方式是加到varnish的启动脚本中,通过varnishd的“-p”参数调用即可。

thread_pools:用来设置线程池的数量,一般认为这个值和系统CPU的数目相同最好,设置过多的pools,varnish的并发处理能力会更强,但是也会消耗更多的CPU和内存。

thread_pool_min:用来设置每个pools的最小threads数,当pools接收到可用的请求后,就会将请求分配给空闲的threads来处理。

thread_pool_max:表示所有pools对应的threads数总和的最大值,此值不能太大,可以设置为系统峰值的90%左右即可,设置过大,会导致进程hung住。

thread_pool_timeout:表示threads的超时过期时间,当threads数大于thread_pool_min设定值时,threads空闲超过thread_pool_timeout设定的时间时,thread就会被释放掉。