varnish: 

    vcl: varnish配置语言,配置varnish缓存策略;

        是“域”类型:

    state engine:(9个)

                    vcl_recv

                     vcl_pipe

                     vcl_pass

                     vcl_hash

                     vcl_hit

                     vcl_miss

                     vcl_fetch

                     vcl_deliver

                     vcl_error

(1)vcl_recv模块

用于接收和处理请求。当请求成功被调用后,Varnish通过判断请求的数据来决定如何处理请求。此模块一般以如下几个关键字结束。

pass:表示进入pass模式,把请求交给vcl_pass模块处理。

pipe:表示进入pipe模式,把请求交给vcl_pipe模块处理。

error code [reason]:表示把错误标识返回给客户端,并放弃处理该请求。错误标识包括200、405等。"reason"是对错误的提示信息。

(2)vcl_pipe模块

此模块在请求进入pipe模式时被调用,用于将请求直接传递至后端主机,在请求和返回的内容没有改变的情况下,也就是在当前连接未关闭时,服务器将不变的内容返回给客户端,直到该连接被关闭。

(3)vcl_pass模块

此模块表示当请求被pass后,用于将请求直接传递至后端应用服务器。后端应用服务器在接收请求后将数据发送给客户端,但不进行任何数据的缓存,在当前连接下每次都返回最新的内容。

(4)lookup

一个请求在vcl_recv中被lookup后,Varnish将在缓存中提取数据。如果缓存中有相应的数据,就把控制权交给vcl_hit模块;如果缓存中没有相应的数据,请求将被设置为pass并将其交给vcl_miss模块。

(5)vcl_hit模块

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

在此模块中,deliver表示将找到的数据发送给客户端,并把控制权交给vcl_deliver模块。

(6)vcl_miss模块

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

在此模块中,fetch表示从后端获取请求的数据,并把控制权交给vcl_fetch模块。

(7)vcl_fetch模块

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

(8)vcl_deliver模块

当一个没有被缓存的数据交付给客户端的时候被调用。

(9)vcl_timeout 模块

在缓存数据到期前调用此模块。

在此模块中,discard表示从缓存中清除到期数据。

(10)vcl_discard模块

在缓存数据到期后或缓存空间不够时,自动调用该模块。

在此模块中keep表示将数据继续保留在缓存中。


vcl配置文件:/etc/varnish/default.vcl

重启varnish将缓存数据清空。使用varnishadm加载


varnishadm

-S /etc/varnish/secret -T IP:PORT


---vcl.load

vcl.use

vcl.show

vcl.discard(删除vcl)


backend storage:

malloc

file

persistent(不用)


varnish:----示例

在vcl中使用条件判断:


单分支:

if (CONDITION) {

...;

}


双分支:

if (CONDITION) {

...;

} else {

...;

}


多分支:

if (CONDITION1) {

...

} elseif (CONDITION2) {

...

} else {

...

}


if(obj.hits>0) {

set resp.http.X-Cache = "HIT";

} else {

set resp.http.X-Cache= "MISS";

}


常用变量:

1、在任何引擎中均可使用:

now, .host(backend), .port(backend)

now:The current time, in seconds since the epoch. When used in string context it returns a formatted string.

.host:Host name or IP address of a backend.

.port:Service name or port number of a backend.


2、用于处理请求阶段(recv,hash,pipe,cache):----从客户端发往varnish服务器

client.ip(客户端), server.hostname(varnish主机), server.ip(varnish主机), server.port(varnish主机)

req.request:请求方法

req.url: 请求的URL

req.proto: HTTP协议版本

req.backend: 用于服务此次请求的后端主机;

req.backend.healthy: 后端主机健康状态;

req.http.HEADER(替换header或者host): 引用请求报文中指定的首部; 如:req.http.host

req.can_gzip:客户端是否能够接受gzip压缩格式的响应内容;

req.restarts: 此请求被重启的次数;


3、varnish向backend主机发起请求前可用的变量-----从服务器发往后端服务器

bereq.request: 请求方法

bereq.url:

bereq.proto:

bereq.http.HEADER(替换header或者host)

bereq.connect_timeout: 等待与be建立连接的超时时长


4、backend主机的响应报文到达本主机(varnish)后,将其放置于cache中之前可用的变量

beresp.do_stream: 流式响应(接一个发一个,不是全部汇总之后在发);

beresp.do_gzip:是否压缩之后再存入缓存;

beresp.do_gunzip:在存入之前解压缩到缓存

beresp.http.HEADER(替换header或者host):

beresp.proto: 

beresp.status:响应状态码

beresp.response:响应时的原因短语

beresp.ttl:响应对象的生存周期,单位为second;

beresp.backend.name: 此响应报文来源backend主机名称;

beresp.backend.ip

beresp..backend.port

beresp.storage----指定的存储后端


5、缓存对象存入cache之后可用的变量(变量只读)

obj.proto--The HTTP protocol version used when the object was retrieved.

obj.status---The HTTP status code returned by the server.

obj.response--The HTTP status message returned by the server.

obj.ttl--The object's remaining time to live, in seconds. obj.ttl is writable.

obj.hits--The approximate number of times the object has been delivered. A value of 0 indicates a cache miss. This variable is also available in vcl_deliver

obj.http.HEADER---The corresponding HTTP header.


6、在决定对请求键做hash计算时可用的变量

req.hash:The hash key used to refer to an object in the cache. Used when both reading from and writing to the cache.


7、在为客户端准备响应报文时可用的变量

resp.proto

resp.status

resp.response

resp.http.HEADER


状态引擎:

vcl_init:在装载vcl,用其处理任何请求之前;

vcl_recv:请求被接入,但在其被分析、处理完成之前;

是否服务此请求、如何服务、使用哪个后端主机为其提供服务;


示例:

# Drop any cookies sent to Wordpress.

sub vcl_recv {

if (!(req.url ~ "wp-(login|admin)")) {

unset req.http.cookie;(unset是撤销变量)

}

}


sub vcl_recv {

 if (req.http.host ~ "(?i)^(www.)?magedu.com$") {     

   set req.http.host = "www.magedu.com";

   set req.backend = www;

 } elsif (req.http.host ~ "(?i)^p_w_picpaths.magedu.com$") {

   set req.backend = p_w_picpaths;

 } else {

   error 404 "Unknown virtual host";

 }

}

注释:(?i)是不区分大小写


# Drop any cookies Wordpress tries to send back to the client.

sub vcl_fetch {

if (!(req.url ~ "wp-(login|admin)")) {

unset beresp.http.set-cookie;

}

}


vcl_fetch状态引擎:

从backend主机收到响应报文之前被调用;可return的值:

deliver

error code [reason]

restart


移除单个缓存对象


purge用于清理缓存中的某特定对象及其变种(variants),因此,在有着明确要修剪的缓存对象时可以使用此种方式。HTTP协议的PURGE方法可以实现purge功能,不过,其仅能用于vcl_hit和vcl_miss中,它会释放内存工作并移除指定缓存对象的所有Vary:-变种,并等待下一个针对此内容的客户端请求到达时刷新此内容。另外,其一般要与return(restart)一起使用。下面是个在VCL中配置的示例。


acl purgers {

"127.0.0.1";

"192.168.0.0"/24;

}


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;

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

}

}


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

# curl -I -X PURGE http://varniship/path/to/someurl


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

sub vcl_recv {

    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;

       }

    }

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

       if (!client.ip ~ purgers) {

           error 405 "Method not allowed.";

       }

    }

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

      req.request != "HEAD" &&

      req.request != "PUT" &&

      req.request != "POST" &&

      req.request != "TRACE" &&

      req.request != "OPTIONS" &&

      req.request != "DELETE" &&

      req.request != "PURGE" ) {

        /* Non-RFC2616 or CONNECT which is weird. */

        return (pipe);

    }

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

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

        return (pass);

    }

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

        /* Not cacheable by default */

        return (pass);

    }

    return (lookup);

}



定义要使用后端主机:

backend NAME {

.host = 

.port =

}


vcl_recv {

...

if (CONDITION) {

set req.backend = BE_NAME;

}

}


Varnish检测后端主机的健康状态


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


每个后端服务器当前探测的健康状态探测方法通过.probe进行设定,其结果可由req.backend.healthy变量获取,也可通过varnishlog中的Backend_health查看或varnishadm的debug.health查看。


backend web1 {

.host = "www.magedu.com";

.probe = {

.url = "/.healthtest.html";

.interval = 1s;

.window = 5;

.threshold = 2;

}

}


.probe中的探测指令常用的有:

(1) .url:探测后端主机健康状态时请求的URL,默认为“/”;

(2) .request: 探测后端主机健康状态时所请求内容的详细格式,定义后,它会替换.url指定的探测方式;比如:

.request =

"GET /.healthtest.html HTTP/1.1"

"Host: www.magedu.com"

"Connection: close";

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

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

(5) .initial:Varnish启动时对后端主机至少需要多少次的成功探测,默认同.threshold;

(6) .expected_response:期望后端主机响应的状态码,默认为200;

(7) .interval:探测请求的发送周期,默认为5秒;

(8) .timeout:每次探测请求的过期时长,默认为2秒;


probe healthcheck {

  .url = "/status.cgi";

  .interval = 60s;

  .timeout = 0.3 s;

  .window = 8;

  .threshold = 3;

  .initial = 3;

  .expected_response = 200;

}


backend www {

 .host = "www.example.com";

 .port = "http";

 .probe = healthcheck;

}


Varnish使用多台后端主机


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


backend web1 {

.host = "backweb1.magedu.com";

.port = "80";

}


director webservers random {

 .retries = 5;

 {

   .backend = web1;

   .weight  = 2;

 }

 {

   .backend  = {

     .host = "backweb2.magedu.com";

 .port = "80";

   }

  .weight         = 3;

 }

}


如上示例中,web1为显式定义的后端主机,而webservers这个directors还包含了一个“匿名”后端主机(backweb2.magedu.com)。webservers从这两个后端主机中挑选一个主机的方法为random,即以随机方式挑选。


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


Varnish 2.1.0后,random挑选方法又多了两种变化形式client和hash。client类型的director使用client.identity作为挑选因子,这意味着client.identity相同的请求都将被发送至同一个后端主机。client.identity默认为client.ip,但也可以在VCL中将其修改为所需要的标识符。类似地,hash类型的director使用hash数据作为挑选因子,这意味着对同一个URL的请求将被发往同一个后端主机,其常用于多级缓存的场景中。然而,无论是client还hash,当其倾向于使用后端主机不可用时将会重新挑选新的后端其机。


另外还有一种称作fallback的director,用于定义备用服务器,如下所示:


director b3 fallback {

 { .backend = www1; }

 { .backend = www2; } // will only be used if www1 is unhealthy.

 { .backend = www3; } // will only be used if both www1 and www2

                      // are unhealthy.

}


在vcl_recv上,一般要用

set req.backend = DIRECTOR


例子:


backend www {

 .host = "www.magedu.com";

 .port = "80";

}


backend p_w_picpaths {

 .host = "p_w_picpaths.magedu.com";

 .port = "80";

}


sub vcl_recv {

 if (req.http.host ~ "(?i)^(www.)?magedu.com$") {

   set req.http.host = "www.magedu.com";

   set req.backend = www;

 } elsif (req.http.host ~ "(?i)^p_w_picpaths.magedu.com$") {

   set req.backend = p_w_picpaths;

 } else {

   error 404 "Unknown virtual host";

 }

}


sub vcl_deliver {


 if (obj.hits > 0) {

   set resp.http.X-Cache = "HIT";

 } else {

   set resp.http.X-Cache = "MISS";

 }

}


sub vcl_deliver {

       if (obj.hits > 0) {

               set resp.http.X-Cache = "HIT via" + " " + server.hostname;

       } else {

               set resp.http.X-Cache = "MISS via" + " " + server.hostname;

       }

}


sub vcl_recv {

       if (req.url ~ "^/test.html$") {

               return(pass);

       }

}


sub vcl_fetch {


       if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") {

               set beresp.ttl = 3600s;

       }

}


sub vcl_fetch {

if (beresp.http.cache-control !~ "s-maxage") {

if (req.url ~ "\.jpg(\?|$)") {

set beresp.ttl = 30s;

unset beresp.http.Set-Cookie;

}

if (req.url ~ "\.html(\?|$)") {

set beresp.ttl = 10s;

unset beresp.http.Set-Cookie;

}

} else {

if (beresp.ttl > 0s) {

unset beresp.http.Set-Cookie;

}

}

}


sub vcl_error {

synthetic "<html><body><!-- Something was wrong! --></body></html>";

set obj.status = 200;

return (deliver);

}


生产环境案例一则:


acl purge {

 "localhost";

 "127.0.0.1";

 "10.1.0.0"/16;

 "192.168.0.0"/16;

}


sub vcl_hash {

 hash_data(req.url);

 return (hash);

}


sub vcl_recv {

 set req.backend = shopweb;

#  set req.grace = 4h; 

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

   if (!client.ip ~ purge) {

     error 405 "Not allowed.";

   }

   return(lookup);

 }

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

   if (!client.ip ~ purge) {

     error 405 "Not allowed.";

   }

   ban("req.http.host == " + req.http.host + " && req.url ~ " + req.url);

   error 200 "Ban OK";

 }

 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;

   }

 }

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

 }

 if (req.http.Authorization) {

   /* Not cacheable by default */

   return (pass);

 }

 

 if ( req.url == "/Heartbeat.html" ) {

   return (pipe);

 }

 if ( req.url == "/" ) {

   return (pipe);

 }

 if ( req.url == "/index.jsp" ) {

   return (pipe);

 }


 if (req.http.Cookie ~ "dper=") {

   return (pass);

 }

 if (req.http.Cookie ~ "sqltrace=") {

   return (pass);

 }

 if (req.http.Cookie ~ "errortrace=") {

   return (pass);

 }

#   if ( req.request == "GET" && req.url ~ "req.url ~ "^/shop/[0-9]+$" ) {

 if ( req.url ~ "^/shop/[0-9]+$" || req.url ~ "^/shop/[0-9]?.*" ) {

   return (lookup);

 }


if ( req.url ~ "^/shop/(\d{1,})/editmember" || req.url ~ "^/shop/(\d{1,})/map" || req.url ~ "^/shop/(\d+)/dish-([^/]+)" ) {

   return (lookup);

 } 


 return (pass);

#   return (lookup);

}


sub vcl_pipe {

 return (pipe);

}


sub vcl_pass {

 return (pass);

}


sub vcl_hit {

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

   purge;

   error 200 "Purged.";

 }

 return (deliver);

}


sub vcl_miss {

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

   error 404 "Not in cache.";

 }

#   if (object needs ESI processing) {

#     unset bereq.http.accept-encoding;

#   }

 return (fetch);

}


sub vcl_fetch {

 set beresp.ttl = 3600s;

 set beresp.http.expires = beresp.ttl;

 #set beresp.grace = 4h;

#   if (object needs ESI processing) {

#     set beresp.do_esi = true;

#     set beresp.do_gzip = true;

#   }


 if ( req.url ~ "^/shop/[0-9]+$" || req.url ~ "^/shop/[0-9]?.*" ) {   

   set beresp.ttl = 4h;

 }


if ( req.url ~ "^/shop/(\d{1,})/editmember" || req.url ~ "^/shop/(\d{1,})/map" || req.url ~ "^/shop/(\d+)/dish-([^/]+)" ) {

    set beresp.ttl = 24h;

 } 


 if (beresp.status != 200){

   return (hit_for_pass);

 }

 return (deliver);

}


sub vcl_deliver {

 if (obj.hits > 0){

   set resp.http.X-Cache = "HIT";

 } 

 else {

   set resp.http.X-Cache = "MISS";

 }

 set resp.http.X-Powered-By = "Cache on " + server.ip;

 set resp.http.X-Age = resp.http.Age;

 return (deliver);

}


sub vcl_error {

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

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

 synthetic {""} + obj.status + " " + obj.response + {""};

 return (deliver);

}


sub vcl_init {

 return (ok);

}


sub vcl_fini {

 return (ok);

}



varnish的线程模型:

cache-worker线程

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

ban luker:

acceptor:

epoll:线程池管理器

expire:清理过期缓存


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

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

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

thread_pool_min

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

thread_pool_purge_delay:空闲多少时间后清理空闲线程。最短保留最少线程。


varnish的param查看及改变:

param.show [-l] [param]

param.set [param] [value]


varnish的命令行工具:

varnishadm, 


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

RxHeader User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36


其中:

RxHeader:称为tag, 基于tag过滤,可使用-i或-x选项(白名单和黑名单);

User-Agent起始的内容:称为日志信息,可使用-I或-X选项进行过滤;


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

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

-C: 忽略字符大小写;

-d: 显示已有日志;


varnishstat:

-f field, field, ...

-l: 列出所有可用字段

-x: xml输出格式 

-j: json输出格式


varnishlog, varnishncsa