轻量级别的Cache和反向代理软件---Varnish

1、Varnish描述

1.1 Varnish的结构与特点

 Varnish是一个轻量级别的Cache和反向代理软件,先进的设计理念和成熟的设计框架是Varnish的主要特点:

  • 基于内存进行缓存,重启后数据将消失
  • 利用虚拟内存方式,I/O性能好.
  • 支持设置0~60秒的精确缓存时间
  • VCL配置管理比较灵活
  • 32位机器上缓存文件大小为最大2GB
  • 具有强大的管理功能,例如:top stat admin list等
  • 状态设计巧妙,结构清晰
  • 利用二叉堆管理缓存文件,达到可以积极删除目的。

1.2 Varnish与Squid的对比

  说到Varnish,就不能不提Squid,Squid是一个高性能的代理缓存服务器,它和Varnish的相比较有诸多的异同点,下面进行分析:

        相同点:

l  都是一个反向代理服务器

l  都是开源软件

        异同点,也是Varnish的优点:

l  Varnish的稳定性很高,两者在完成相同的负荷的工作时,Squid发生的故障几率要高于Varnish,因为Squid需经常重启.

l  Varnish访问速度更快,Varnish采用用了”Visual Page Cache”技术,所有缓存的数据都直接从内存读取,而且Squid是从硬盘读取缓存数据,因此Varnish访问速度更快

l  Varnish可以支持更多的并发连接,因为Varnish的TCP连接要比Squid释放快,所以在高并发连接情况可以支持更多地TCP连接。

l  Varnish可以通过管理端口,使用证则表达式清除部分缓存,而Squid做不到。

     Varnish缺点如下:

l  在高并发状态下CPU,I/O和内存等资源开销都要高于Squid。

l  Varnish进程一旦挂起,崩溃或者重启,缓存数据都会从内存当中完全释放,此时所有的请求会发送后端的WEB服务器,在高并发的情况下,这会给后端的服务器造成很大压力。

2、Varnish安装

2.1、安装前环境

Varnish-Server1 à10.0.0.201

Web—Server2à10.0.0.202

2.2、创建Varnish用户缓存目录和日志

# useradd varnish -s /sbin/nologin   创建varnish用户

# mkdir /varnish/cache –p         创建varnish缓存目录

# mkdir /varnish/log              创建varnish日志

# chown -R varnish:varnish /varnish/  修改赋权组

2.3、安装PCRE

为了兼容正则表达式,编译 2.0以上版本时否则会报错pcre找不到。

# yum install gcc

# yum install gcc-c++ libstdc++-devel

# yum install -y httpd-devel pcre perl pcre-devel zlib zlib-devel GeoIP GeoIP-devel

# wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.34.tar.bz2

# tar -xvf pcre-8.34.tar.bz2

# cd pcre-8.34/

# ./configure --prefix=/usr/local/prce/

# make && make install

注:如果你在编译的vasnish中的bin目录没有发现varnishstat, varnishtop, varnishhist这个三个程序的话,是因为编译前没有安装与操作系统位数对应的ncurses-devel 安装ncurses-devel

yum install ncurses-devel.x86_64

然后再次编译varnish 即可

2.4、安装Varnish

# wget http://repo.varnish-cache.org/source/varnish-3.0.0.tar.gz

# tar -xzvf varnish-3.0.0.tar.gz

# cd varnish-3.0.0

#./configure --prefix=/usr/local/varnish PKG_CONFIG_PATH=/usr/lib/pkgconfig

# make && make install

# cd /usr/local/varnish/sbin

./varnishd -V

3、Varnish配置

3.1、VCL使用说明

VCL,即Varnish Configaution Language 用来定义Varnish的存储策略

VCL内置函数

   (1)、vcl_recv函数

用于接收和处理请求,当请求到达并成功接收后被调用.

    函数一般以如下几个关键字结束

pass表示进入pass模式,把请求控制权交给vcl_pass函数

pige 表示进入pige模式,把请求控制权交给vcl_pipe函数

error code[reason] 表示返回”code”给客户,并放弃处理该请求,”code”是错误的标识

,例如:200和405等,”reason”是错误信息

(2)、vcl_pipe函数

   此函数在进入pipe模式时被调用,用于请求直接传至后端主机,在请求和返回内容没有改变的情况下,将不变的内容返回给客户端,直到这个连接被关闭。

   此函数一般以如下几个关键字结束:

     error code[reason]

     pipe

(3)、vcl_pass函数

   此函数在进入pass模式时被调用,用于请求直接传至后端主机,后端主机但应数据,将答应的数据传至客户端,但不进行任何缓存,当前连接下每次都返回最新的内容。

此函数一般以如下几个关键字结束:

error code[reason]

     pass

(4)、lookup函数

   表示在缓存中查找被请求的对象,并且根据查找的结果把控制权交给vcl_hit或者函数vcl_miss

(5)、vcl_hit函数

在执行lookup指令后,在缓存中找到请求的内容后将自动调用该函数

此函数一般以如下几个关键字结束:

fetch 表示从后端获取请求的内容,并且把控制权交给vcl_fetch函数

  deliver  表示将找到的内容发送给客户,并且把控制权交给函数vcl_deliver

error code[reason]

pass

(6)、vcl_miss函数

   在执行lookup指令后,在缓存中没有找到请求的内容时自动调用该方法,此函数可以于判断是否需要从后端服务器获取内容

  此函数一般以如下几个关键字结束:

fetch:表示从后端获取请求内容,并且把控制权交给vcl_fetch函数

error code[reason]

pass

(7)、vcl_miss函数

在后端主机更新缓存并且获取内容后调用该方法,接着,通过判断获取的内容来决定是将内容放入缓存,还是直接返回给客户端,

   此函数一般以如下几个关键字结束:

 error code[reason]

pass

deliver

(8)、vcl_deliver函数

  将在缓存中找到请求的内容发送给客户端调用此方法。

此函数一般以如下几个关键字结束:

error code[reason]

deliver

(9)、vcl_timeout函数

 在缓存内容到期前调用此函数.

此函数一般以如下几个关键字结束:

 discard:表示从缓存中清除内容

 fetch

(10)、vcl_discard函数

  在缓存内容到期后或者缓存空间不足时,自动调用该函数.

此函数一般以如下几个关键字结束:

  keep:表示将内容继续保留在缓存当中

discard

 

2.3.2、配置Varnish配置实战

由于本版不同,Varnish配置文件的写法也存在一定的差异,Varnish3.x版本不但在配置写法上和2.x版本不同,修复了很多不应用BUG,本文以3.x版本为基准

   Varnish安装完成后,默认的配置文件/usr/local/varnish/etc/varnish/default.vcl

 此配置文件默认是全部注释掉的.配置文件如下:

先编辑配置文件

# vi /usr/local/varnish/etc/varnish/default.vcl

# This is a basic VCL configuration file for varnish.  See the vcl(7)

# man page for details on VCL syntax and semantics.

#

# Default backend definition.  Set this to point to your content

# server.

#

 

# This is a basic VCL configuration file for varnish.  See the vcl(7)

# man page for details on VCL syntax and semantics.

#

# Default backend definition.  Set this to point to your content

# server.

#

#设置后端WEB服务

backend webtest1 {

    .host = "10.0.0.202";

    .port = "80";

    .connect_timeout = 1s;

    .first_byte_timeout = 5s;

    .between_bytes_timeout = 2s;

}

#可以定义多台WEB

#backend webtest2 {

#    .host = "192.168.100.6";

#    .port = "80";

#    .connect_timeout = 1s;

#    .first_byte_timeout = 5s;

#    .between_bytes_timeout = 2s;

#}

#多台WEB可以定义负载均衡

#director lb_test random {

#    {

#      .backend = webtest1;

#      .weight = 5;

    # }

    # {

    #   .backend = test2;

    #   .weight = 5;

    # }

 

#}

#定义那些IP或者IP段具有访问权

acl purge {

    "localhost";

    "127.0.0.1";

    "192.168.1.0"/24;

    "10.0.0.0"/16;

}

 

 

sub vcl_recv {

#开启压缩模式,图片格式取消压缩

if (req.http.Accept-Encoding) {

    if (req.url ~ "\.(jpg|png|gif|jpeg|flv)" ) {

        remove req.http.Accept-Encoding;

        remove req.http.Cookie;

    } 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;

    }

}

#根据host设置后端服务器

  if (req.http.Host ~ "(?i)(www.test1.com|www.test2.com|10.0.0.201)") {

     #set req.backend = lb_test; #如果开启负载用此项

     set req.backend = webtest1;

   }else

  if (req.http.Host ~ "(?i)image.wdj.com"){

     set req.backend = webtest1;

  }else

  {

    error 408 "Hostname not found";

  }

#如果为purge请求,客户端ip不在访问列表中,返回405拒绝

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

     if (!client.ip ~purge) {

       error 405 "Not Allowed";

   }

#本地缓存查找

   return(lookup);

  }

#如果为GET请求,url后缀为jpg,png,gif等 取出cookie

  if (req.request == "GET"&&req.url ~ "(?i)\.(jpg|png|gif|swf|jpeg|ico)$") {

        unset req.http.cookie;

  }

#如果GET请求,url为php,则穿过cache,不缓存

  if (req.request =="GET"&&req.url ~ "(?i)\.php($|\?)"){

        return (pass);

  }

#简单防盗链

if (req.http.referer ~ "http://.*") {

  if ( !(req.http.referer ~ "http://.*test1\.com"

     || req.http.referer ~ "http://.*test2\.com"

     || req.http.referer ~ "http://.*wdj\.com"

     || req.http.referer ~ "http://.*google\.com"

     || req.http.referer ~ "http://.*baidu\.com"

     || req.http.referer ~ "http://.*yahoo\.cn"

  )) {

      error 404 "Not Found!";

 }

}

#获取客户端ip

#     if (req.restarts == 0) {

        if (req.http.x-forwarded-for) {

            set req.http.X-Forwarded-For =

                req.http.X-Forwarded-For + ", " + client.ip;

        } else {

            set req.http.X-Forwarded-For = client.ip;

        }

#   }

#不是以下请求进入pipe模块

    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);

    }

#不是GET 和HEAD请求不缓存

    if (req.request != "GET" && req.request != "HEAD") {

        /* We only deal with GET and HEAD by default */

        return (pass);

    }

    if (req.http.Authorization) {

        /* Not cacheable by default */

        return (pass);

    }

    return (lookup);

}

#

 sub vcl_pipe {

     return (pipe);

 }

#

sub vcl_pass {

    return (pass);

}

#使用url+host hash算法查找数据

sub vcl_hash {

    hash_data(req.url);

    if (req.http.host) {

        hash_data(req.http.host);

    } else {

        hash_data(server.ip);

    }

    return (hash);

}

# 如果请求为purge 将清除缓存

sub vcl_hit {

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

       set obj.ttl = 0s;

       error 200 "Purged";

    }

    return (deliver);

}

 

sub vcl_miss {

    return (fetch);

}

#

sub vcl_fetch {

    if (beresp.ttl <= 0s ||

        beresp.http.Set-Cookie ||

        beresp.http.Vary == "*") {

                /*

                 * Mark as "Hit-For-Pass" for the next 2 minutes

                 */

                set beresp.ttl = 0 s;

                return (hit_for_pass);

    }

    if (beresp.http.Pragma ~"no-cache" ||

    beresp.http.Cache-Control ~"no-cache" ||

    beresp.http.Cache-Control ~"private") {

      return (deliver);

   }

#为特定格式文件设置缓存时间,有多种的可以直接在后米啊加

    if (req.request == "GET"&&req.url ~ "(?i)\.(js|css|mp3|jpg|png|gif|swf|jpeg|ico)$") {

    set beresp.ttl = 30d;

  }

   if (req.request == "GET"&&req.url ~ "(?i)\.(html|htm)$") {

    set beresp.ttl = 1d;

  }

    return (deliver);

}

# 设置返回状态

 sub vcl_deliver {

     set resp.http.x-hits = obj.hits;

     if (obj.hits > 0) {

      set resp.http.X-Cache = "Hit test.com";

   }else {

       set resp.http.X-Cache = "Miss test.com";

   }

     set resp.http.Server = "BWM";

     return (deliver);

 }

# 定义错误

sub vcl_error {

    set obj.http.Content-Type = "text/html; charset=utf-8";

    set obj.http.Retry-After = "5";

    synthetic {"

<?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>"} + obj.status + " " + obj.response + {"</title>

  </head>

  <body>

    <h1>Error "} + obj.status + " " + obj.response + {"</h1>

    <p>"} + obj.response + {"</p>

    <h3>Guru Meditation:</h3>

    <p>XID: "} + req.xid + {"</p>

    <hr>

    <p>Varnish cache server</p>

  </body>

</html>

"};

    return (deliver);

}

 

sub vcl_init {

        return (ok);

}

 

sub vcl_fini {

        return (ok);

}
default.vcl

 

4、Varnish运行

4.1、varnish启动

-u 以什么用运行
-g 以什么组运行
-f varnish配置文件
-a 绑定IP和端口
-s varnish缓存文件位置与大小
-w 最小,最大线程和超时时间
-t 缓存时间s
-T varnish管理端口,主要用来清除缓存
-p client_http11=on 支持http1.1协议
-P(大P) /usr/local/varnish/var/varnish.pid 指定其进程码文件的位置,实现管理
停止varnish

varnish两种缓存方式:

(1)基于malloc内存+swap交换模式

/usr/local/varnish/sbin/varnishd -f /usr/local/varnish/etc/varnish/default.vcl -s malloc,200M -a 10.0.0.201:80 -w 1024,512000,10 -t 10.0.0.201:3000

(2)基于file是mmap的文件内存映射的机制.

/usr/local/varnish/sbin/varnishd -u varnish -g varnish -f /usr/local/varnish/etc/varnish/default.vcl -a 10.0.0.201:80 -s file,/varnish/cache/varnish_cache.data,1G -w 1024,51200,10 -t 3600 -T 10.0.0.201:3000                  

企业里里面开启为100G

4.2、启动日志

# /usr/local/varnish/bin/varnishncsa -w  /varnish/log/varnish.log &

日志加入开机启动:

 echo "/usr/local/varnish/bin/varnishncsa -w /data/varnish/logs/varnish.log &" >> /etc/rc.local
参数: -w 指定varnish访问日志要写入的目录与文件

4.3、缓存验证:

可以通过浏览器访问对应的网页来查看Varnish缓存的效果。如果Varnish缓存成功,第二次打开网页的速度会明显比第一次快,但是这种方式并不能够充分说明问题。下面用命令行方式,通过查看网页头来查看命中情况。

  • [root@varnish-server ~]# curl -I http://www.ixdba.net/a/mz/2010/0421/11.html  
  • HTTP/1.1 200 OK  
  • Server: Apache/2.2.14 (Unix) PHP/5.3.1 mod_perl/2.0.4 Perl/v5.10.1  
  • Last-Modified: Sat, 10 Jul 2010 11:25:15 GMT  
  • ETag: "5e850b-616d-48b06c6031cc0"  
  • Content-Type: text/html  
  • Content-Length: 24941  
  • Date: Fri, 09 Jul 2010 08:29:16 GMT  
  • X-Varnish: 1364285597  
  • Age: 0  
  • Via: 1.1 varnish  
  • Connection: keep-alive  
  • X-Cache: MISS from www.ixdba.net  #这里的“MISS”表示此次访问没有从缓存读取

再次打开这个页面,查看网页的头信息。

·  [root@varnish-server ~]# curl -I http://www.ixdba.net/a/mz/2010/0421/11.html  
·  HTTP/1.1 200 OK  
·  Server: Apache/2.2.14 (Unix) PHP/5.3.1 mod_perl/2.0.4 Perl/v5.10.1  
·  Last-Modified: Sat, 10 Jul 2010 11:25:15 GMT  
·  ETag: "5e850b-616d-48b06c6031cc0"  
·  Content-Type: text/html  
·  Content-Length: 24941  
·  Date: Fri, 09 Jul 2010 08:30:35 GMT  
·  X-Varnish: 1364398612 1364285597  
·  Age: 79  
·  Via: 1.1 varnish  
·  Connection: keep-alive  
·  X-Cache: HIT from www.ixdba.net     #由“HIT”可知,第二次访问此页面时,从缓存中读取内容,也就是缓存命中

参考URL:http://book.51cto.com/art/201202/314875.htm

5、Varnish 日志切割

5.1、日志切割

# vim /data/shell/cut_varnish_log.sh

#!/bin/sh
logs_path=/varnish/logs
vlog=${logs_path}/varnish.log
date=$(date -d "yesterday" +"%Y-%m-%d")
pkill -9 varnishncsa
mkdir -p ${logs_path}/$(date -d "yesterday" +"%Y")/$(date -d "yesterday" +"%m")/
mv /varnish/logs/varnish.log ${logs_path}/$(date -d "yesterday" +"%Y")/$(date -d "yesterday" +"%m")/varnish-${date}.log
/usr/local/varnish/bin/varnishncsa -w /varnish/logs/varnish.log &

使用计划任务,每天晚上凌晨00点运行日志切割脚本

echo "0 0 * * * /data/shell/cut_varnish_log.sh" >> /etc/crontab

6、Varnish日常管理

6.1、日常管理

/usr/local/varnish/bin/varnishadm -T 10.0.0.201:3000 purge "req.http.host ~ www.server110.com$ && req.url ~ /static/image/tp.php"
详细介绍:
10.0.0.201:3000 ###为被清除缓存服务器地址
www.server201.com  ###为被清除的域名
/static/image/tp.php ###为被清除的url地址列表
清除所有缓存
/usr/local/varnish/bin/varnishadm -T 192.168.9.201:3000 ban.url  ^.*$
清除image目录下所有缓存
/usr/local/varnish/bin/varnishadm -T 192.168.9.201:3000 ban.url  /image/
查看Varnish服务器连接数与命中率
/usr/local/varnish/bin/varnishstat –n /varnish/cache/varnish_cache.data

7、Varnish内置公用变量

公用变量名称    含义
req.backend        指定对应的后端主机
server.ip              表示服务器端IP
client.ip               表示客户端IP
req.request          指定请求的类型,例如GET、HEAD、POST等
req.url                 指定请求的地址
req.proto            表示客户端发起请求的HTTP协议版本
req.http.header   表示对应请求中的http头部信息
req. restarts         表示请求重启的次数,默认最大值为4
Varnish               在向后端主机请求时,可以使用的公用变量如表3所示:
表3
公用变量名称 含义
beresp.request 指定请求的类型,例如GET、HEAD等
beresp.url 指定请求的地址
beresp .proto 表示客户端发起请求的HTTP协议版本
beresp .http.header 表示对应请求中的http头部信息
beresp .ttl 表示缓存的生存周期,也就是cache保留多长时间,单位是秒
从cache或者后端主机获取内容后,可以使用的公用变量如表4所示:
表4
公用变量名称 含义
obj.status 表示返回内容的请求状态代码,例如200、302、504等
obj.cacheable 表示返回的内容是否可以缓存,也就是说,如果HTTP返回是200、203、300、301、302、404、410等,并且有非0的生存期,则可以缓存
obj.valid 表示是否是有效的HTTP应答
obj.response 表示返回内容的请求状态信息
obj.proto 表示返回内容的HTTP协议版本
obj.ttl 表示返回内容的生存周期,也就是缓存时间,单位是秒
obj.lastuse 表示返回上一次请求到现在的间隔时间,单位是秒
对客户端应答时,可以使用的公用变量如表5所示:
表5
公用变量名称 含义
resp.status 表示返回给客户端的HTTP状态代码
resp.proto 表示返回给客户端的HTTP协议版本
resp.http.header 表示返回给客户端的HTTP头部信息
resp.response 表示返回给客户端的HTTP状态信息
在上面的讲述中,我们只是介绍了常用的VCL内置公用变量,如果需要了解和使用更多的公用变量信息,请登录varnish官方网站查阅。

posted on 2016-09-07 18:01  losbyday  阅读(2522)  评论(0编辑  收藏  举报

导航