varnish 简介


 

varnish是一款强大的http加速器,其设计初衷因为计算机越来越复杂,不像那个只有内存与硬盘的存储媒介的年代,如今的计算机系统除了内存外还有cpu的L1、L2、L3快取,因此当初的Squid cache自行处理物件替换的架构不可能得知这些情况而做到最佳,但操作系统可以得知该情况,此PoulHenning Kamp设计了varinish的架构


varnish术语解析


缓存的衡量参数:命中率
#  1 文档命中率
#  2 字节命中率
缓存类型:
#  私有缓存 (比如客户端的浏览器上的缓存)
#  公共缓存  (比如一个组织机构建立的缓存服务器)
缓存的层次结构
#客户端 <---...-->2级代理<--------->1级代理 <----------->原始服务器


wKioL1NqDpKwXNyyAABjYlZYI2Q559.jpg


内容路由
#ICP : Internet Cache Protocol 互联网缓存协议
#CARP: Cache Array Routing Protocol   缓存阵列路由协议---用的比较多



缓存处理客户端请求的具体步骤
#接受请求
#解析请求  (代理的功能)
#查询缓存  (检查本地缓存中是否存在对方请求的内容的副本)
#副本的新鲜度检测  (检查本地缓存的副本是否为最新版本)
#构建响应 (代理的功能---作为某个应用程序的代理服务器)
#发送
#日志



保证副本的新鲜度?
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
# 文档过期机制:-----------响应首部
      HTTP/1.0 : Expires (过期时间)
        绝对时间  -----比如2014年5月5日过期                   
      HTTP/1.1 :Cache-Control (max-age=)                                             
        相对时长 ------比如还有100小时可用
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
# 条件式请求: --------------请求首部
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
      1  mtime: If-Modified-Since
         基于时间的条件式请求
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
      2  ETag  :If-None-Match
         基于拓展标签条件式请求



原始服务器、缓存服务器、客户端 如何实现对缓存的控制
(1) 原始服务器或缓存服务器控制缓存的能力
# 由原始服务器定义:
  Cache-Control
  Cacmax-age  =(属于私有缓存中)
     s-maxage =(属于公共缓存中)
    no-store  :不能缓存
    no-cache :能缓存下来,但不能直接使用此缓存对象;  缓存对象在使用之间必须做 新鲜度验证
    must-revalidate:必须进行新鲜度验证
       private     : 客户端的私有数据,不缓存
         public    : 公共数据,缓存
#原始服务器不添加任何控制机制,而由缓存服务器自己决定缓存时长
(2)客户端对缓存使用情况的控制
# 客户端控制是否使用缓存:
   Cache-Control:
     max-stale:告知缓存机制可以使用过期的文件
     no-cache :告知缓存机制必须进行验证,否则不会接受任何缓存文档;
     no-store :告知缓存机制必须尽快删除缓存中的文档



Varnish 基本架构


wKiom1NqFFLjxjdNAAEHyOkOcsk016.jpg



如上图所示:
(1)  varnish主要运行两个进程: Management进程和Child进程(也叫Cache进程)。
#  Management进程主要实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等。Management进程会每隔几秒钟探测一下Child进程以判断其是否正常运行,如果在指定的时长内未得到Child进程的回应,Management将会重启此Child进程。
#  Child进程包含多种类型的线程
#   Accept线程:接收新的连接请求并响应;
#   Worker线程:child进程会为每个会话启动一个worker线程,因此,在高并发的场景中可能会出现数百个worker线程甚至更多;
#   Expiry线程:从缓存中清理过期内容;


(2)varnish日志
 #为了与系统的其它部分进行交互,Child进程使用了可以通过文件系统接口进行访问的共享内存日志(shared memory log)
 #共享内存日志大小一般为80M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。varnish提供了多个不同的工具如varnishlog、varnishncsa或varnishstat等来分析共享内存日志中的信息并能够以指定的方式进行显示。



(3)VCL
# Varnish Configuration Language (VCL)是varnish配置缓存策略的工具
# 使用VCL编写的缓存策略通常保存至.vcl文件中,其需要编译成二进制的格式后才能由varnish调用
# 整个缓存策略就是由几个特定的子例程如vcl_recv、vcl_fetch等组成
 VCL语法
# (1)//、#或/* comment */用于注释
# (2)sub $name 定义函数  ---即定义子例程
# (3)不支持循环,有内置变量
# (4)使用终止语句,没有返回值
# (5)域专用
# (6)操作符:=(赋值)、==(等值比较)、~(模式匹配)、!(取反)、&&(逻辑与)、||(逻辑或)



(4)varnish后端存储
# 1)file:使用特定的文件存储全部的缓存数据;
# 2)malloc:使用malloc()库调用在varnish启动时向操作系统申请指定大小的内存空间以存储缓存对象;
# 3)persistent(experimental):与file的功能相同,但可以持久存储数据(即重启varnish数据时不会被清除);仍处于测试期;



状态引擎(子例程)


  VCL中由多个子例程组成,各个子例程之间有必然的关系,如下图所示


wKioL1NqGrvBfwj3AADpIM1wJQk982.jpg


如上图 pipe  、lookup、pass 为vcl_recv 子例程的返回状态。依据此状态来判断下一步的去向



VCL中的变量


wKioL1NqHJrS0XPiAAGBQ89uOcY243.jpg



Varnish的安装配置


  环境搭建


wKioL1NqHpSwceg8AABpALxhjK4080.jpg


如上图
varnish服务器
172.16.13.2      外网网卡
192.168.20.11    内网网卡
后端服务器两个
192.168.20.12  服务器一内网地址 
192.168.20.13  服务器二内网地址


 准备工作



1 配置以上三台服务器的ip地址,此处不做详解                                
2 server1服务器 编辑web页面
#vim  /var/www/html/index.html
    <h1>node12.linux.com</h1>
#service httpd start
3  server2服务器 编辑web页面
#vim  /var/www/html/index.html
    <h1>node13.linux.com</h1>
#service  httpd start


安装varnish并配置


1)下载3.0版本的varnish
  varnish-3.0.4-1.el6.x86_64.rpm
  varnish-libs-3.0.4-1.el6.x86_64.rpm
  varnish-docs-3.0.4-1.el6.x86_64.rpm
2)安装
# rpm -ivh varnish*.rpm  安装以上三个包
# rpm -ql varnish
3)编辑varnish配置文件,定义varnish启动时的特性
#vim /etc/sysconfig/varnish
   NFILES=131072                         打开的最大文件数
   MEMLOCK=82000                         默认分配给日志log的内存数82M
   NPROCS="unlimited"                    最大线程数 ,无限制
   RELOAD_VCL=1                          重新启动服务时候,是否重新编译vcl 重新使用vcl
   VARNISH_VCL_CONF=/etc/varnish/default.vcl    vcl配置缓存策略的配置文件
   VARNISH_LISTEN_PORT=80                 varnish侦听的默认端口为6081,接受用户请求 ----------一般更改为80端口
   VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1   varnish管理侦听的地址
   VARNISH_ADMIN_LISTEN_PORT=6082           varnish管理接口,
   VARNISH_SECRET_FILE=/etc/varnish/secret  进入varnish管理接口的密钥文件
   VARNISH_MIN_THREADS=50    最小线程数                                              
   VARNISH_MAX_THREADS=1000  最大线程数---------varnish超过5000个线程后会不稳定                                        
   VARNISH_THREAD_TIMEOUT=120  空闲线程转向工作线程的延迟时间
                             VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin  基于文件存储时的文件路径 
  VARNISH_STORAGE_SIZE=1G   后端存储的的大小                                         
  VARNISH_STORAGE="malloc,100M"   自定义内存大小
  VARNISH_TTL=120           请求后端服务器的超时时间  
4)启动服务
# service varnish start
# ss -ntlp | grep 80



5 ) 进入varnish管理界面
#varnishadm  -S /etc/varnish/secret   -T  127.0.0.1:6082
 varnish> help  --------查看一下所有的命令参数
 200      
help [command]
ping [timestamp]
auth response
quit
banner
status
start
stop
vcl.load <configname> <filename>   加载编译某个vcl,加载的时候并指定一个配置名称(该名称随意指定)
vcl.inline <configname> <quoted_VCLstring>
vcl.use <configname>
vcl.discard <configname>
vcl.list                                  列出所有的vcl
vcl.show <configname>      查看已经列出的vcl
param.show [-l] [<param>]
param.set <param> <value>
panic.show
panic.clear
storage.list



6 )
创建vcl文件
#vim  /etc/varnish/test.vcl   内容如下
backend websrv1 {
  .host = "192.168.20.12";
  .port = "80";
}
进入管理界面编译使用
#varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
varnish> vcl.load test2 test.vcl    ------------------加载编译test.vcl
         200      
         VCL compiled.
varnish> vcl.use test2              -----------------使用已经编译好 test.vcl 200
varnish> vcl.show test2       ------------------查看test.vcl 的内容
        200      
       backend websrv1 {
         .host = "192.168.20.12";
         .port = "80";
       }


7)客户端测试


    wKioL1NqKBGwHz1ZAABnK2k4B6c681.jpg




  接下来进行vcl的功能拓展


    一、 判断上边的配置是否命中缓存



(1)编辑test.vcl
#vim /etc/varnish/test.vcl
backend websrv1 {
  .host = "192.168.20.12";
  .port = "80";
}
sub vcl_deliver {
  if (obj.hits > 0) {
  set resp.http.X-Cache = "HIT";
  } else {
    set resp.http.X-Cache = "MISS";
  }
}
(2)管理界面中重新编译 并使用
# varnish > vcl.load test3 test.vcl
# varnish > vcl.use  test3
 (3)在varnish服务器测试
# curl -I http://172.16.13.2

    wKioL1NqKbfAnVD8AAF9kLbwRU0457.jpg

     命中


   二、判断从哪个服务器命中缓存 ---使用变量server.ip



编辑test.vcl
#vim  /etc/varnish/test.vcl
  backend websrv1 {
  .host = "192.168.20.12";
  .port = "80";
 }
  sub vcl_deliver {
  if (obj.hits > 0) {
  set resp.http.X-Cache = "HIT" + server.ip;
  } else {
    set resp.http.X-Cache = "MISS";
   }
 } 
管理界面 重新编译并使用
#  varnish > vcl.load test4 test.vcl
#  varnish > vcl.use   test4
服务器测试
# curl -I http://172.16.13.2


    wKiom1NqKufRFFdfAAGfKQsrz_s086.jpg


   三、  拒绝使用缓存  ---使用变量req.url



server2服务器再创建一个测试页面
#vim /var/www/html/test.html
  <h1>test!!!</h1>
编辑vcl配置文件增加vcl_recv子例程      
#vim /etc/varnish/test.vcl
       sub vcl_recv {
        if (req.url ~ "test.html") {
            return(pass);
        }
            return(lookup);
        }
                                                                                                                                                                                                                                                          
varnish服务器测试
# curl -I http://172.16.13.2/test.html


    wKiom1NqK--CsQNFAAE8cJ-7bEM534.jpg

     全部miss,未命中



    四、定义缓存时长



如果客户端请求的为图片文件,就缓存7200s,
如果客户端请求的为静态网页,就缓存1200s
编辑test.vcl 增加子例程vcl_fetch
#vim  /etc/varnish/test.vcl
        sub vcl_fetch {
           if (req.url ~ "\.(jpg|jpeg|gif|png)$") {
              set beresp.ttl =  7200s;
           }
           if (req.url ~ "\.(html|css|js)$") {
               set beresp.ttl = 1200;
          }
       }
#serivice varnish reload
管理界面重新编辑 并使用
#varnish > vcl.load test6  test.vcl
#varnish > vcl.use   test6



   五 使用PURGE修剪缓存



修剪缓存非常危险,所以我们必须要做好准备
在具体执行某清理工作时,需要事先确定如下问题:
#(1)仅需要检验一个特定的缓存对象,还是多个?
#(2)目的是释放内存空间,还是仅替换缓存的内容?
#(3)是不是需要很长时间才能完成内容替换?
#(4)这类操作是个日常工作,还是仅此一次的特殊需求?
移除单个缓存的实现
 #vim  /etc/varnish/test.vcl   增加内容如下
acl purgers {
  "127.0.0.1";
  "172.16.0.0"/16;
}
sub vcl_recv {
  if (req.request == "PURGE") {
    if (!client.ip ~ purgers) {
     error 405 "Method not allowed";
    }
  }
  if (req.url ~ "test.html") {
     return(pass);
  }
  return(lookup);
}
          sub vcl_hit {
  if (req.request == "PURGE") {
     purge;
     error 200 "PURGE OK";
  }
}
sub vcl_miss {
  if (req.request == "PURGE") {
      purge;
      error 404 "Not in cache";
  }
}
sub vcl_pass {
  if (req.request == "PURGE") {
    error 502 "Purged on a passed object";
  }
}
管理界面重新编译vcl并使用
#varnish > vcl.load test8 vcl.test
#varnish > vcl.use   test8
测试
客户端在发起HTTP请求时,只需要为所请求的URL使用PURGE方法即可
使用 curl -I  -X  PURGE  http://varniship/path/to/someurl


    wKiom1NqLdnDJesAAAFE42JZhDQ701.jpg


    六、定义varnish服务器检测后端服务器健康状态

   

   

后端服务器为 192.168.12.12和 192.168.12.13
#vim  /etc/varnish/test.vcl
                                                                                                                                                                     
backend web1 {
  .host = "192.168.20.12";
  .port = "80";
  .probe = {
           .url = "/index.html";
           .interval = 1s;
           .window = 5;
           .threshold = 2;
  }
  }
backend web2 {
  .host = "192.168.20.13";
  .port = "80";
  .probe = {
           .url = "/index.html";
           .interval = 1s;
           .window = 5;
   }        .threshold = 2;
}
director webs random {
  {
    .backend = web1;
    .weight = 2;
  }
  {
     .backend = web2;
     .weight = 1;
  }
}
同时在子例程 vcl_recv 中添加调用req.backend
 set req.backend = webs;   
                                                                                                                                                                     
管理接口重新编译vcl 并好似用
# varnish >  vcl.load  test9 vcl.test
# varnish > vcl.use  test9


    客户端测试

    首先关闭192.168.20.12 服务器的httpd服务

         wKioL1NqL1WgpaFBAABa9IuW3nY734.jpg


    启动192.168.20.12 httpd服务

    wKioL1NqL32D60umAABiNtzZunM216.jpg



   七  将请求的静态网页发送至web1服务器

       将请求的图片发送至web2服务器


   

编辑test.vcl
# vim  /etc/varnish/test.vcl  添加如下内容
     sub vcl_recv {
  if (req.url ~ "\.(html|css|js)$") {
     set req.backend = web1;
  } else {
     set req.backend = web2;
  }
#同时注释掉director所有内容



   八  如何防盗链


   

搜的例子:
if (req.http.referer ~ "http://.*") { 
         if (  !(req.http.referer ~ "http://.*ixdba\.net" 
             || req.http.referer ~ "http://.*google\.com" 
             || req.http.referer ~ "http://.*yahoo\.cn"et 
             || req.http.referer ~ "http://.*google\.cn" 
             )) { 
                 set req.http.host = "www.ixdba.net"; 
                 set req.url = "/templets/default/p_w_picpaths/logo.gif"; 
         } 
                       return (lookup); 
         }


   

varnish常用的工具


   

1 varnishstat   查看状态的命令工具
  用法:
     -1
     -l:列出所有参数
     -f f1,f2 例如 varnishstat -f cache_hit,cache_miss  只查看这两项
#  需要关注的几个参数
          cache hit
          cache miss
          client_req
          client_conn
          n_wrk
          n_wrk_create
          n_backend
          n_expired
          n_lru_moved
          s_hdrbytes
          s_bodybytes
2 varnishtop  实时显示日志中的信息
    用法:
      -i  tag : 仅显示指定的tag,如RxHeader
      -I  regexp : 以模式匹配tag对应值;
      -C    :正则表达式匹配时不区分字符大小写;
3 varnishlog 
  #server  varnishlog start  开启varnishlog
  #tail  -f /var/log/varnish/varnish.log 
4 varnishreplay  日志重放工具,用于实现缓存预热



几个重要调整的参数


   

# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
# varnish> help
# varnish> param.show  查看所有参数
几个重要的参数
  thread_pool_add_delay    两个客户端创建线程池时的时间间隔
  thread_pool_fail_delay   创建线程池失败后,第二次创建时的时间间隔
  thread_pool_max          一个线程池中默认装载的最大的线程数
  thread_poo_min           一个线程池中默认装载的最小的线程数,限定避免其过于不均衡,防止在繁忙中有的线程池不能少于这个值
  thread_pool_purge_delay   多长时间清理一次线程,     
  thread_pool_timeout       一个线程空闲多长时间 就清除掉
  thread_pool_workspace     一个线程池
  thread_pools              工作线程池的个数
  thread_stats_rate         一批搜集多少个工作线程的状态信息
参数调整方法
#  varnish>  param.show  thread_pools
#  varnish>  param.set  thread_pools   3
#  varnish>  param.show  thread_pools

   


思考篇


   缓存命中率低的原因

缓存空间太小  --------------增大内存,增大磁盘
不存在明显的热点数据------------(二八法则:80%的请求落在20%的数据上 )---可以不用做缓存了,或者将原数据全部做到缓存
源文件更新过于频繁 -----------建议不要使用缓存了
缓存服务器的可用性  ---------------做分布式