os:

[root@aliyun_test html]# cat /etc/system-release

CentOS release 6.5 (Final)

首先安装varnish:

配置好varnish源

[root@aliyun_test yum.repos.d]# cat >> varnish.repo << EOF

> [varnish]

> name=varnish for enterprise linux 6

> baseurl=https://repo.varnish-cache.org/redhat/varnish-4.0/el6/

> enabled=1

> gpgcheck=0

> cost=500

> EOF

安装varnish

yum -y install varnish

[root@aliyun_test html]# varnishd -V

varnishd (varnish-4.0.3 revision b8c4a34)

Copyright (c) 2006 Verdens Gang AS

Copyright (c) 2006-2014 Varnish Software AS


Built in subroutines

Varnish 处理 HTTP 请求的过程如下

Receive 状态(vcl_recv):也就是请求处理的入口状态,根据 VCL 规则判断该请求应该 pass(vcl_pass)或是 pipe(vcl_pipe),还是进入 lookup(本地查询);

Lookup 状态:进入该状态后,会在 hash 表中查找数据,若找到,则进入 hit(vcl_hit)状态,否则进入 miss(vcl_miss)状态;

Pass(vcl_pass)状态:在此状态下,会直接进入后端请求,即进入 fetch(vcl_fetch)状态;

Fetch(vcl_backend_fetch)状态:在 fetch 状态下,对请求进行后端获取,发送请求,获得数据,并根据设置进行本地存储;

Deliver(vcl_deliver)状态:将获取到的数据发给客户端,然后完成本次请求;

注:Varnish4中在vcl_fetch部分略有出入,已独立为vcl_backend_fetch和vcl_backend_response 2个函数;

内置函数(也叫子例程)

vcl_recv:用于接收和处理请求;当请求到达并成功接收后被调用,通过判断请求的数据来决定如何处理请求;

vcl_pipe:此函数在进入pipe模式时被调用,用于将请求直接传递至后端主机,并将后端响应原样返回客户端;

vcl_pass:此函数在进入pass模式时被调用,用于将请求直接传递至后端主机,但后端主机的响应并不缓存直接返回客户端;

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

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

vcl_hash:在vcl_recv调用后为请求创建一个hash值时,调用此函数;此hash值将作为varnish中搜索缓存对象的key;

vcl_purge:pruge操作执行后调用此函数,可用于构建一个响应;

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

vcl_backend_fetch:向后端主机发送请求前,调用此函数,可修改发往后端的请求;

vcl_backend_response:获得后端主机的响应后,可调用此函数;

vcl_backend_error:当从后端主机获取源文件失败时,调用此函数;

vcl_init:VCL加载时调用此函数,经常用于初始化varnish模块(VMODs)

vcl_fini:当所有请求都离开当前VCL,且当前VCL被弃用时,调用此函数,经常用于清理varnish模块;

vcl_pass:请求后端,但不进入到cache中


client---------->varnish-------------->backend severs

client------>varnish:req请求报文

varnish----->backend:bereq向后端请求

backend----->varnish:beresp后端响应给缓存服务器

varnish----->client:resp缓存响应给client

缓存处理的具体步骤:

    接受客户端请求

    解析请求(具有代理的功能)

    查询缓存(检测本地缓存中是否存在对方请求的内容的副本)

    副本的新鲜度检测(检查本地的缓存副本是否为最新版本)

    构建响应(代理的功能)

    发送响应

    记录日志


首部:

  通用首部

    Connection:close|keep-alive

    Date:日期时间

    host:请求的主机

    Pragma:no-cache

    Via:请求或响应在客户端和服务器之间传递时所经过的代理

    Transfer-Encoding:消息主体的传输编码方式,chunked表示采用块编码的方式

  请求首部

    if-modified-since

    if-none-match

    Referer:跳转

    User-Agent:用户的浏览器类型

    Host:请求的主机

    Accept-Encoding:接受的编码方式

    Accept-Language:接受的自然语言

    Authorization:服务器发送www-authenticate时,客户端通过此首部提供认证信息

    Accept-Charset:接受的字符集

  响应首部

    ETag:内容的扩展标签

    Location:重定向后的新位置

    Server:服务器软件信息

    www-authenticate:要求对客户端进行认证

  实体首部

    Content-Encoding:内容编码

    Content-Language:内容语言

    Content-Length:内容长度

    Content-Type:内容的MIME格式

    Expires:内容的过期时间

    Last-Modified:最后一次修改的时间


配置文件:

vi /etc/sysconfig/varnish

NFILES=131072:打开的最大文件数

NFILES=131072:默认日志内存大小

NPROCS="unlimited":最大线程数(ulimit -u)

RELOAD_VCL=1:直接加载,不用重启

VARNISH_LISTEN_PORT=6081:varnish服务监听的端口,一般改为与后端web服务一样的端口

VARNISH_ADMIN_LISTEN_PORT=6082:远程管理接口监听的端口

VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1:远程管理端监听的ip(本机varnish服务)

VARNISH_SECRET_FILE=/etc/varnish/secret:对管理接口秘钥文件

VARNISH_MIN_THREADS=50:varnish服务开启的最小线程

VARNISH_MAX_THREADS=1000:varnish服务开启的最大线程

VARNISH_STORAGE_SIZE=256M:varnish服务存储的内存大小

VARNISH_STORAGE="malloc,${VARNISH_STORAGE_SIZE}":varnish缓存存储的方式,malloc内存

VARNISH_TTL=120

vi /etc/varnish/default.vcl

backend default {:后端web服务器以及名称

    .host = "127.0.0.1";:后端web服务器主机

    .port = "8080";:后端web服务器监听的端口

}

varnish存储缓存内容的方法:      

    1、file:自管理的文件系统,黑盒,只对当前进程有效,不支持持久机制;

    2、malloc:使用内存分配函数malloc()库调用varnish启动时向内存申请指定大小的空间;

    3、persisent:与file功能相同;仍处于测试期,基于文件的持久存储。


开始配置varnish并启动:

后端服务器:120.26.68.152端口为8080,为nginx

varnish服务器:120.26.68.152端口为7480

echo "<h1> test </h1>" > /usr/share/nginx/html/index.html

配置:vim /etc/varnish/test.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;

        }             ##判断如果命中就在http响应首部设置X-Cache为HIT,否则

                就在http响应首部设置X-Cache为MISS。

    }

如下结果:

wKiom1e4Z-Gzki_lAAA2qTSNIHM744.jpg

Age:表示缓存的时长(这里设置一个小时),当超过这个时长之后,请求报文会再次miss,之后的1个小时才是hit

sub vcl_backend_response {

  set beresp.ttl = 1h;

}

vcl_backend_response:子程序为后端响应给varnish


一些动态解析的程序文件都不能够缓存:

backend default {     ----》先定义后端服务器

    .host = "192.168.1.112";

    .port = "80";

}

sub vcl_recv {

    set req.backend_hint = default;     ---》设置使用的backend为default

    unset req.http.Cookie;     -----》不设置客户端请求的cookie信息

    if (req.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)$") {   ----》匹配的是默认后端主机

      return(pass);     ----》请求的是默认后端主机,如果不先设置主机将会网页访问不存在

    }

}

设置默认后端效果图:

wKioL1e5prPx0HhwAAEmfiV4IGk716.jpg

如果不先设置默认主机,效果如下:

wKioL1e5pzqBM_CyAAB9fnm18c8340.jpg-wh_50


不正常的请求不缓存   -----》在vcl_rcv子程序中

   if (req.method != "GET" &&

       req.method != "HEAD" &&

       req.method != "PUT" &&

       req.method != "POST" &&

       req.method != "TRACE" &&

       req.method != "OPTIONS" &&

       req.method != "PATCH" &&

       req.method != "DELETE") {

       return (pipe);

    }


如果请求不是GET或者HEAD,不缓存

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

       return (pass);

    }


如果请求包含Authorization授权或Cookie认证,不缓存

   if (req.http.Authorization || req.http.Cookie) {

       return (pass);

    }


启用压缩,但排除一些流文件压缩

   if (req.http.Accept-Encoding) {

           unset req.http.Accept-Encoding;

       } elseif (req.http.Accept-Encoding ~ "gzip") {

           set req.http.Accept-Encoding = "gzip";

       } elseif (req.http.Accept-Encoding ~ "deflate") {

           set req.http.Accept-Encoding = "deflate";

       } else {

           unset req.http.Accept-Encoding;

       }

    }

       return (hash);

}


定义vcl_pipe函数段

sub vcl_pipe {

   return (pipe);

}

sub vcl_miss {

   return (fetch);

}


定义vcl_hash函数段

sub vcl_hash {

   hash_data(req.url);

   if (req.http.host) {      -----》如果请求host首部,利用has算法返回host首部信息

       hash_data(req.http.host);

    }else {

       hash_data(server.ip);

    }

   if (req.http.Accept-Encoding ~ "gzip") {

       hash_data ("gzip");

    }elseif (req.http.Accept-Encoding ~ "deflate") {

       hash_data ("deflate");

    }

}


sub vcl_recv {

    if (req.restarts ==0) {:刚接收到请求,或者再一次请求时,记录客户端首部及ip

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

  set req.http.X-Forward-For = set req.http.X-Forward-For + " " client.ip;

       } else {

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

       }

    }

}

这里我已经改用虚拟机测试了

varnish:192.168.1.155

nginx:192.168.1.11

隐藏后端Server系统及版本

if (resp.http.Server) {

  unset resp.http.Server;

}属于sub vcl_deliver子程序

wKiom1e4eEugI16KAAA2KeGdMzk812.jpg

可以看见响应报文没有Server段了


定义两个后端:

backend default {

    .host = "192.168.1.11";

    .port = "80";

}


backend java {

    .host = "192.168.1.12";

    .port = "80";

}

当匹配java时,选择java后端,其他默认选择默认端

sub vcl_recv {

    if (req.url ~ "^/java/") {

        set req.backend_hint = java;-----》hint暗示、示意

    } else {

        set req.backend_hint = default;

    }

}

根据请求报文的主机来判断:

sub vcl_recv {

    if (req.http.host ~ "foo.com") {

        set req.backend_hint = foo;

    } elsif (req.http.host ~ "bar.com") {

        set req.backend_hint = bar;

    }

}

sub vcl_recv {

    if (req.http.host == "foo.com" || req.http.host == "www.foo.com") {

        set req.backend_hint = foo;

    }

}


定义后端组,并且采用round robin算法和后端服务器监控检测机制

import directors;    # load the directors

backend web1 {

  .host = "192.168.1.11";

  .port = "80";

  .probe = {

    .url = "/";

    .interval = 5s;

    .window = 5;

    .timeout = 1s;

    .threshold = 3;

  }

}


backend web2 {

  .host = "192.168.1.12";

  .port = "80";

  .probe = {

    .url = "/";

    .interval = 5s;

    .timeout = 1s;

    .window = 5;

    .threshold = 3;

  }

}


sub vcl_init {

  new nginx = directors.round_robin();  -----》nginx就是后端组的组名称,进行轮训

  nginx.add_backend(web1);

  nginx.add_backend(web2);

}

sub vcl_recv {

    # send all traffic to the bar director:

    set req.backend_hint = nginx.backend();

}


ACL控制列表

# Who is allowed to purge....

acl local {

    "localhost";

    "192.168.1.0"/24; /* and everyone on the local network */

    ! "192.168.1.13"; /* except for the dialin router */

}


sub vcl_recv {

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

    if (client.ip ~ local) {

       return(purge);

    } else {

       return(synth(403, "Access denied."));

    }

  }

}

不设置请求报文的cookie信息

sub vcl_recv {

  unset req.http.Cookie;

}


不设置响应报文的cookie信息

sub vcl_backend_response {

   if (bereq.url ~ "\.(png|gif|jpg)$") {

     unset beresp.http.set-cookie;

     set beresp.ttl = 1h;

  }

}

更多信息参考:https://www.varnish-cache.org/docs/4.0

http://my.oschina.net/monkeyzhu/blog/482051