varnish3.0运维-vcl说明

描述

        VCL是专门为了varnish定制的特殊范围语言。

varnish配置文件被重新加载时,varnish的管理进程会依靠vcl重新编译配置文件,并且会将编译结果存放在一块内存中。

语法

        VCL语法其实比较简单,规则类似cperl;在正则表达式方面,VCL不会让你失望。在VCL中,已经定义好了几个函数:

        delivererrorfetchhashhit_for_passlookupokpasspiperestart。下面会说到。

咱们按照Varnish配置文件从上往下依次的介绍。

后端声明

后端server定义在以关键字backend开始的对象中:

backend www {
  .host = "www.example.com";
  .port = "http";
}

当需要调用backend时,你可以写些个自己的location规则:

if (req.http.host ~ "(?i)^(www.)?example.com$") {
  set req.backend = www;
}

当然了,和其他反向代理一样,varnish也支持后端健康监测:

backend www {
  .host = "192.168.122.100";
  .port = "80";
  .probe = {
    .url = "/monitor.html";    #健康监测的监控地址
    .timeout = 3s;     #first_byte的超时时间
.interval = 5s;    #检测间隔时间
.window = 5;       #每次检测创建5个确认点
.threshold =3;     #有3个确认点ok,就认为该server ok
    .expected_response = 200;  #健康的标准
  }
}

Directors

你也可以将多个backend放在一个director中:

director b2 random {
  .retries = 5;
  {
    // We can refer to named backends
    .backend = b1;
    .weight  = 7;
  }
  {
    // Or define them inline
    .backend  = {
      .host = "fs2";
    }
  .weight         = 3;
  }
}

Acls

Varnish在做acl时,和nginx类似:

acl local {
  "localhost";         // myself
  "192.0.2.0"/24;      // and everyone on the local network
  ! "192.0.2.23";      // except for the dialin router
}

定义好acl之后,可以这么调用:

if (client.ip ~ local) {
  return (pipe);
}

函数、子程序

vcl_init

vcl被重新加载时候调用,作用是通知varnish主进程varnish配置文件开始reload的了,需要刷新内存中的配置了。

sub vcl_init {
        return (ok);
}

vcl_recv

在请求开始时调用的。完成该子程序后,请求就被接收并解析了。用于确定是否需要服务请求,怎么服务,如果可用,使用哪个后端。

recv中,你可以随意的定制任何组合的匹配规则,在定制规则时,只能对请求对象req做判断,条件匹配之后,可以用的处理方式有:

pass不做任何缓存,直接转发
pipe当前client的所有请求varnish不再做处理,直接建立一条该cilent与后端server的“专线”。
lookup:直接从缓存中返回数据。

vcl_pipe

当前client的所有请求varnish不再做处理,直接建立一条该cilent与后端server的“专线”。只能自己调用自己:

sub vcl_pipe {
  return (pipe);
}

vcl_pass

不做任何缓存,直接转发,可以调用pass或者restart,一般是pass

sub vcl_pass {
  return (pass);
}

vcl_hash

缓存的hash规则。

sub vcl_hash {
  hash_data(req.url);
  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }
  return (hash);
}

vcl_hit

命中缓存之后的处理手段。可以调passdeliver

sub vcl_hit {
  if (obj.ttl <= 0s) {
    return (pass);
  }
  return (deliver);    #返回缓存给client。
}

vcl_miss

没命中。可以调passfetch

sub vcl_miss {
  return (fetch);      #重新去backend获取数据,之后按照vcl_fetch规则走
}

vcl_fetch

        vcl_fetch是在文档从后端被成功接收后调用的。通常用于调整响应头信息,触发ESI处理,万一请求失败就换个后端服务器。在vcl_fecth中,你还可以使用请求对象req。还有个后端响应对象berespBeresp包含了后端的HTTP头信息。可以调用deliverhit_for_passrestart

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 = 120 s;
        return (hit_for_pass); #类似与pass,但是只有vcl_fetch可以用。不像pass,hit_for_pass将在缓存中创建一个hitforpass对象。类似个pass的标记。
  }
  return (deliver);
}

vcl_deliver

cache返回client之前的处理过程。

sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT from cache.";       #打个标记
    set resp.http.X-Cache-Hits = obj.hits;           #计数器
  } else {
    set resp.http.X-Cache = "MISS from cache.";      #打个标记
  }
  return (deliver);
}

vcl_error

当触发了一个错误(400503等等)或是某些未知的错误。

sub vcl_error {
  set obj.http.Content-Type = "text/html; charset=utf-8";
  synthetic {"
  <html>
  <head>
    <title>Page Wrong.</title>
    <style>
      body { background: #efefef; text-align: center; color: white; font-family: Trebuchet MS, sans-serif; }
      #page { width: 500px;margin: 100px auto 0; padding: 30px; background: #888888; border-radius: 14px; -moz-border-radius: 14px; -webkit-border-radius: 14px;border: 0}
      a,a:link,a:visited{color: #cccccc;}
      .error {color: #222222}
      .commany {color: #868686}
    </style>
  </head>
  <body>
    <div id="page">
      <h1>Page Unavailable</h1>
      <p>The page you requested is temporarily unavailable.</p>
      <div>(Error "} + obj.status + " " + obj.response + {")</div>
    </div>
    <div>DangDang Cache Server</div>
  </body>
  </html>
  "};
  return (deliver);
}

vcl_fini

当所有的请求都已经退出完毕时,告诉varnish,可以清空内存数据了。

sub vcl_fini {
        return (ok);
}

对象

VCL中,有三种重要的数据结构。请求:来自客户端;响应:来自后端服务器;对象:存储在缓存中。在VCL中你应该知道以下结构:

req:请求对象。当varnish接受了请求,req就会创建并生产。许多在vcl_recv中要做的工作都需要用到req。
beresp:后端响应对象。包含了从后端返回的对象的头信息。vcl_fetch中,你会使用beresp对象。
obj:缓存了的对象。大多数是驻留在内存中的只读对象。obj.ttl是可以写的,剩下的都是只读的。

配置示例

# Default backend definition.  Set this to point to your content
# server.
backend dztree {
  .host = "10.64.5.141";
  .port = "80";
  .probe = {
    .url = "/";
    .timeout = 3s;
    .interval = 5s;
    .threshold =8;
  }
}
backend zabbix {
  .host = "172.16.224.23";
  .port = "8801";
  .probe = {
    .url = "/";
    .timeout = 3s;
    .interval = 5s;
    .threshold =8;
  }
}
backend zabbix_self {
  .host = "127.0.0.1";
  .port = "8080";
}
sub vcl_recv {
  #rewrite.
  if (req.url ~ "^/dztree") {
    set req.backend = dztree;
  } elseif (req.url ~ "^/zabbix") {
    set req.backend = zabbix;
  } else {
    set req.backend = zabbix_self;
  }
  #client 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;
        }
  }
  #Use anonymous, cached pages if all backends are down.
  if (!req.backend.healthy) {
    unset req.http.Cookie;
  }
  #When backend is down, then cache can be use in grace time.
  set req.grace = 10m;
  #do not cache these paths.
  if (req.url ~ "^/admin") {
    return (pass);
  }
  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 || req.http.Cookie) {
     /* Not cacheable by default */
     return (pass);
  }
  return (lookup);
}
sub vcl_pipe {
  return (pipe);
}
sub vcl_pass {
  return (pass);
}
sub vcl_hash {
  hash_data(req.url);
  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }
  return (hash);
}
sub vcl_hit {
  if (obj.ttl <= 0s) {
    return (pass);
  }
  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 = 120 s;
        return (hit_for_pass);
  }
  return (deliver);
}
sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT from cache.";
    set resp.http.X-Cache-Hits = obj.hits;
  } else {
    set resp.http.X-Cache = "MISS from cache.";
  }
  return (deliver);
}
sub vcl_error {
  set obj.http.Content-Type = "text/html; charset=utf-8";
  synthetic {"
  <html>
  <head>
    <title>Page Wrong.</title>
    <style>
      body { background: #efefef; text-align: center; color: white; font-family: Trebuchet MS, sans-serif; }
      #page { width: 500px;margin: 100px auto 0; padding: 30px; background: #888888; border-radius: 14px; -moz-border-radius: 14px; -webkit-border-radius: 14px;border: 0}
      a,a:link,a:visited{color: #cccccc;}
      .error {color: #222222}
      .commany {color: #868686}
    </style>
  </head>
  <body>
    <div id="page">
      <h1>Page Unavailable</h1>
      <p>The page you requested is temporarily unavailable.</p>
      <div>(Error "} + obj.status + " " + obj.response + {")</div>
    </div>
    <div>DangDang Cache Server</div>
  </body>
  </html>
  "};
  return (deliver);
}
sub vcl_init {
        return (ok);
}
sub vcl_fini {
        return (ok);
}