Varnish页面缓存服务-2

VCL的简单介绍

VCL是用来定义Varnish的缓存策略的一种简单语言,它允许使用正则表达式进行字符串匹配、允许用户使用set自定义变量、支持if判断语句,也有内置的函数和变量等。使用VCL编写的缓存策略通常保存至.vcl文件中,其需要编译成二进制的格式后才能由varnish调用。

VCL在加载新配置时,由Manager进程创建的VCC进程将VCL代码转换为C.此C代码通常由gcc共享对象编译。然后将共享对象加载到cacher进程中。

Varnish中的vcl有单独的配置文件,在/etc/varnish/中默认名为default.vcl,与varnish.params文件组成varnish的两个主要配置文件

《Varnish页面缓存服务-2》

VCL有多个状态引擎,状态之间存在相关性,但状态引擎彼此间互相隔离;每个状态引擎可使用return(x)指明关联至哪个下一级引擎;每个状态引擎对应于vcl文件中的一个配置段,即为subroutine

两个特殊的引擎

  • vcl_init:在处理任何请求之前要执行的vcl代码:主要用于初始化VMODs;
  • vcl_fini:所有的请求都已经结束,在vcl配置被丢弃时调用;主要用于清理VMODs;

varnish默认配置

默认VCL配置也叫做隐式规则,在配置文件中无法看到,即使我们修改了配置文件,默认配置规则也是在最后做处理。

sub vcl_recv {
    if (req.method == "PRI") {
    /* We do not support SPDY or HTTP/2.0 */
    return (synth(405));
    }
    if (req.method != "GET" &&
      req.method != "HEAD" &&
      req.method != "PUT" &&
      req.method != "POST" &&
      req.method != "TRACE" &&
      req.method != "OPTIONS" &&
      req.method != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return (pipe);
    }

    if (req.method != "GET" && req.method != "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 (hash);
}
#下面每个状态引擎的配置省略
sub vcl_pipe
sub vcl_pass
sub vcl_hash
sub vcl_purge
sub vcl_hit
sub vcl_miss
sub vcl_deliver
sub vcl_synth
sub vcl_backend_fetch
sub vcl_backend_response
sub vcl_backend_error
sub vcl_init
sub vcl_fini

VCL操作符

  • 操作符: ==, !=, ~, >, >=, <, <=
  • 逻辑操作符:&&, ||, !
  • 变量赋值:=

VCL内置公用变量

VCL内置的公用变量可以用在不同的VCL函数中

内建变量类型

  • req.*:request,表示由客户端发来的请求报文相关;
  • resp.*:由varnish响应给client相关;
  • bereq.*:由varnish发往BE主机的httpd请求相关;
  • beresp.*:由BE主机响应给varnish的响应报文相关;

  • obj.*:存储在缓存空间中的缓存对象的属性;只读;

《Varnish页面缓存服务-2》

常用变量

**bereq.*, req.***
– bereq.http.HEADERS

  • bereq.request:请求方法;

  • bereq.url:请求的url;

  • bereq.proto:请求的协议版本;

  • bereq.backend:指明要调用的后端主机;

  • req.http.Cookie:客户端的请求报文中Cookie首部的值;

  • req.http.User-Agent ~ “chrome”

beresp.*, resp.*:

  • beresp.http.HEADERS

  • beresp.status:响应的状态码;

  • reresp.proto:协议版本;

  • beresp.backend.name:BE主机的主机名;

  • beresp.ttl:BE主机响应的内容的余下的可缓存时长;

**obj.*: **

  • obj.hits:此对象从缓存中命中的次数;

  • obj.ttl:对象的ttl值

**server.***

  • server.ip:varnish主机的IP;

  • server.hostname:varnish主机的Hostname;

  • client.*

  • client.ip:发请求至varnish主机的客户端IP;

用户自定义变量

  • set : 设置变量

  • unset : 撤销变量

举例:obj.hits是内建变量,用于保存某缓存项的从缓存中命中的次数;

if (obj.hits>0) {
                    set resp.http.X-Cache = "HIT via" + " " + server.ip;
                } else {
                    set resp.http.X-Cache = "MISS from " + server.ip;
                }

VCL配置

VCL的修改在/etc/varnish/default.vcl文件中修改,默认情况下,各状态引擎都没有定义,可以直接在相应位置定义vcl,vcl配置文件定义后直接生效。

1.修改对客户端提供服务的对外端口,默认为6081

[root@varnish ~]# vim /etc/varnish/varnish.params
#修改端口

VARNISH_LISTEN_PORT=80   #改为80
#重启varnish
[root@varnish varnish]# systemctl restart varnish

varnish.params配置文件修改后要重启才能生效,varnish重启后缓存就丢失,所以在使用前要配置好这里的设置

vcl的配置修改完后不需要再重启服务,有专门的指令执行vcl的重载,执行:varnish_reload_vcl生效

2.使用内置变量obj.hits给请求报文中打上缓存状态的标签,统计缓存命中次数

[root@varnish ~]# vim /etc/varnish/default.vcl

vcl 4.0;       #指定varnish版本标识

#定义后端web服务器
backend default {
    .host = "192.168.214.135";
    .port = "80";
}

#打标签,统计缓存命中次数
sub vcl_deliver {

        if (obj.hits>0) {
                        set resp.http.X-Cache = "HIT via" + " " + server.ip;
                } else {
                        set resp.http.X-Cache = "MISS from " + server.ip;
        }

测试缓存

[root@varnish ~]# for i in {1..5};do curl -I -s 192.168.214.134/test3.html | grep X-Cache;done
X-Cache: MISS from 192.168.214.134
X-Cache: HIT via 192.168.214.134
X-Cache: HIT via 192.168.214.134
X-Cache: HIT via 192.168.214.134
X-Cache: HIT via 192.168.214.134

3.强制对某类资源的请求不检查缓存

sub vcl_recv {
        if (req.url ~ "(?i)^/(login|admin)") {
                return(pass);
        }
}

#测试

[root@varnish ~]# for i in {1..5};do curl -I -s 192.168.214.134/admin/ | grep X-Cache;done
X-Cache: MISS from 192.168.214.134
X-Cache: MISS from 192.168.214.134
X-Cache: MISS from 192.168.214.134
X-Cache: MISS from 192.168.214.134
X-Cache: MISS from 192.168.214.134

3. 对于特定类型的资源,例如公开的图片等,取消其私有标识,并强行设定其可以由varnish缓存的时长

if (beresp.http.cache-control !~ "s-maxage") {
                if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") {
                    unset beresp.http.Set-Cookie;
                    set beresp.ttl = 3600s;
                }
            }

4.向后端服务器发送真实客户端ip地址

#定义在vcl_recv中;

  if (req.restarts == 0) {
                if (req.http.X-Fowarded-For) {
                        set req.http.X-Forwarded-For = req.http.X-Forwarded-For + "," + client.ip;
                } else {
                        set req.http.X-Forwarded-For = client.ip;
                        }
                }

缓存对象的修剪

purge

在请求时缓存才被清除,
匹配请求方法,是否为purge,如果匹配则进入purge模块,purge函数要定义在默认的请求匹配规则前面,因为purge不是标准的http请求方法,如果如果请求不是定义的请求方法会被送往pipe

定义方式:何时执行purge操作

                sub vcl_recv {
                    if (req.method == "PURGE") {
                        return(purge);
                    }
                    ...
                }

执行purge操作

            sub vcl_purge {
                    return (synth(200,"Purged"));
                }

请求通过vcl_purge后会自动被删除,并响应一个合成的200状态码

5. 添加purge清除缓存

sub vcl_recv {
     ...
if (req.method == "PURGE") {
                return(purge);
        }

        ...
 }

 #

 sub vcl_purge {
        return(synth(200,"Purged."));
}

使规则生效:

#登录管理终端
[root@Varnish ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082

Linux,3.10.0-693.el7.x86_64,x86_64,-smalloc,-smalloc,-hcritbit
varnish-4.0.5 revision 07eff4c29

varnish> vcl.load conf3 default.vcl
200        
VCL compiled.
vcl.use conf3
200        
VCL 'conf3' now active

测试缓存清除
使用purge清除缓存,在请求报文中加purge方法即可

#访问缓存内容
[root@client ~]# curl -I -s 192.168.214.134/test1.html | grep X-Cache
X-Cache: HIT via 192.168.214.134

#清除缓存
[root@client ~]# curl -X PURGE  192.168.214.134/test1.html 
<!DOCTYPE html>
<html>
  <head>
    <title>200 Purged.</title>   #清除指定的缓存并响应200的响应码
  </head>
  <body>
    <h1>Error 200 Purged.</h1>
    <p>Purged.</p>
    <h3>Guru Meditation:</h3>
    <p>XID: 25</p>
    <hr>
    <p>Varnish cache server</p>
  </body>
</html>

#再测试

[root@client ~]# curl -I -s 192.168.214.134/test1.html | grep X-Cache
X-Cache: MISS from 192.168.214.134  #此时由后端服务响应为MISS

#再访问

[root@client ~]# curl -I -s 192.168.214.134/test1.html | grep X-Cache
X-Cache: HIT via 192.168.214.134 #缓存又被命中

注意:这种方式存在危险,这样任何人都可以清除掉缓存,一般来讲这种修剪操作只允许特定客户端操作,比如管理员等,可以设置只允许特定的地址段操作PURGE,在VCL配置文件中使用acl访问控制列表,定义允许使用purge的操作的地址段用户,随后在vcl_recv中匹配acl定义的规则,如果请求的客户端ip与vcl_purge中定义的地址不匹配则拒绝操作,并返回提示信息

[root@Varnish ~]# vim /etc/varnish/default.vcl
#

acl purgers {   #acl在整个配置文件中作为独立的配置段
                "192.168.214.0"/24;
        }

sub vcl_recv {
        ...
       if (req.method == "PURGE") {
                if (client.ip ~ purgers) {
                        return(purge);
                } else {
                        return(synth(403,"purging not allowed for" + client.ip));
                        }
        }
}

ban

ban可以给缓存资源设置围栏,被匹配到的资源就被清除,比如,清除URL以/imgs目录开头的缓存,可以在管理终端使用ban的指令设置:ban req.url ~ ^/imgs ,指令的方式比较灵活,同时ban规则也可以写在配置文件中。
管理终端定义:

varnish> ban req.url ~ ^/imgs

在配置文件中定义,使用ban()函数;

#示例:
if (req.method == "BAN") {
            ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
            return(synth(200, "Ban added"));
}   


等于匹配:http://www.ilinux.io/test1.html ,用ban的方式将链接分开匹配:
ban req.http.host==www.ilinux.io && req.url==/test1.html

后端多主机定义

varnish定义多个后端主机示例

backend default {
                .host = "192.168.214.135";
                .port = "80";
            }

            backend appsrv {
                .host = "192.168.214.187";
                .port = "80";
            }

            sub vcl_recv {              
                if (req.url ~ "(?i)\.php$") {
                    set req.backend_hint = appsrv;
                } else {
                    set req.backend_hint = default;
                }   

                ...
            }

上面的配置实现了动静分离的配置。

后端主机组调度:

varnish可以将多个后端主机定义成一个director主机组,然后使用调度规则定义这个组中的后端主机,达到轮询调度的功能,使用前,需要定义导入的声明,import directors,并在 vcl_recv中定义请求通过的主机组响应
示例如下:

import directors;

backend default {
    .host = "192.168.214.135";
    .port = "80";
}

backend server2 {
    .host = "192.168.214.187";
    .port = "80";
}

sub vcl_init {
        new web = directors.round_robin();
        web.add_backend(default);
        web.add_backend(server2);

}

sub vcl_recv {      #在vcl_recv中定义请求调用该组后端主机
# send all traffic to the bar director:
                    set req.backend_hint = web.backend();
                }

基于cookie的session sticky


sub vcl_init { new h = directors.hash(); h.add_backend(one, 1); // backend 'one' with weight '1' h.add_backend(two, 1); // backend 'two' with weight '1' } sub vcl_recv { // pick a backend based on the cookie header of the client set req.backend_hint = h.backend(req.http.cookie); }

后端主机健康检查

  • .probe:定义健康状态检测方法;
  • .url:检测时要请求的URL,默认为”/”;
  • .request:发出的具体请求;
  • .request =
    • “GET /.healthtest.html HTTP/1.1”
    • “Host: www.gudaoyufu.com”
    • “Connection: close”
  • .window:基于最近的多少次检查来判断其健康状态;
  • .threshold:最近.window中定义的这么次检查中至有.threshhold定义的次数是成功的;
  • .interval:检测频度;
  • .timeout:超时时长;
  • .expected_response:期望的响应码,默认为200;

健康状态检查配置

import directors;

probe web_chk {
        .url = "/index.html";
        .interval = 2s;   #检查时间间隔
        .timeout = 2s;   #超时时间
        .window = 10; #最近10次检查
        .threshold = 7;  #最近检查10次,有7次成功就认为主机为健康状态

}


backend server1 {
    .host = "192.168.214.135";
    .port = "80";
    .probe = web_chk;   #在后端主机调用检查
}


backend server2 {
    .host = "192.168.214.187";
    .port = "80";
    .probe = web_chk;
}

sub vcl_init {
        new web = directors.round_robin();
        web.add_backend(server1);
        web.add_backend(server2);

}

sub vcl_recv {

        set req.backend_hint = web.backend();
        ......
        }

查看后端主机列表

backend.list    #varnishadm管理终端执行指令
200        
Backend name                   Refs   Admin      Probe
server1(192.168.214.135,,80)   1      probe      Healthy 10/10
server2(192.168.214.187,,80)   1      probe      Healthy 10/10

如果后端主机下线,这里会显示后端主机状态改变

Backend name                   Refs   Admin      Probe
server1(192.168.214.135,,80)   1      probe      Healthy 10/10
server2(192.168.214.187,,80)   1      probe      Healthy 7/10
backend.list
200        
Backend name                   Refs   Admin      Probe
server1(192.168.214.135,,80)   1      probe      Healthy 10/10
server2(192.168.214.187,,80)   1      probe      Sick 6/10

手动设置后端主机状态

varnish> backend.set_health server1 Sick  #手动设置后主机状态
200        

varnish> backend.list   #查看后端主机
200        
Backend name                   Refs   Admin      Probe
server1(192.168.214.135,,80)   1      sick       Healthy 10/10
server2(192.168.214.187,,80)   1      probe      Sick 0/10

手动设定backend主机的状态有:

  • sick:管理down;

  • healthy:管理up;

  • auto:probe auto;

后端主机其他属性

backend BE_NAME {

  • .connect_timeout = 0.5s; #连接超时时间
  • .first_byte_timeout = 20s; #第一个字节20s不响应则为超时
  • .between_bytes_timeout = 5s; #第一个字节和第二个字节间隔超时时间
  • .max_connections = 50; #最大连接数

}

varnish运行时参数

线程相关的参数:使用线程池机制管理线程;在线程池内部,其每一个请求由一个线程来处理; 其worker线程的最大数决定了varnish的并发响应能力。

  • thread_pools:最好小于或等于CPU核心数量;
  • thread_pool_max:每线程池的最大线程数
  • thread_pool_min:最大空闲线程数
  • 最大并发连接数 = thread_pools * thread_pool_max
  • thread_pool_timeout: 线程空闲阈值。 超过thread_pool_min的线程将被销毁,这些线程已经空闲了至少这么长时间
  • thread_pool_add_delay:生成线程之前等待的时间
  • thread_pool_destroy_delay:清除超出最大空闲线程数的线程之前等待的时间

设置

param.set 指令:临时生效

设置上面的参数永久有效的方法:varnish.params配置文件
– DEAMON_OPTS=”-p PARAM1=VALUE -p PARAM2=VALUE”

varnish日志管理

varnishstat

常用参数

  • -1
    • -1 -f FILED_NAME
  • -l:可用于-f选项指定的字段名称列表
  • -f FILED_NAME :指定查看某个段,可以连接使用该参数查看多个区段
    • 重要的参数
      MAIN.cache_hit :命中次数
      MAIN.cache_miss :未命中次数

varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss:显示指定参数的当前统计数据;

varnishstat -l -f MAIN -f MEMPOOL:列出指定配置段的每个参数的意义;

varnishtop

  • -1 :打印统计信息一次并退出
  • -i taglist,可以同时使用多个-i选项,也可以一个选项跟上多个标签;
  • -I <[taglist:]regex>:对指定的标签的值基于regex进行过滤;
  • -x taglist:排除列表
  • -X <[taglist:]regex>:对指定的标签的值基于regex进行过滤,符合条件的予以排除;
点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注