6.2 VCL

Varnish配置


Varnish 的配置系统与其他软件不同,一般的配置是使用配置指令,用于打开或关闭某个配置项。而 Varnish 使用 VCL 语言进行配置。

查看 varnish 服务的主配置文件

[root@CentOS74 ~]# cat /etc/varnish/varnish.params | grep ^[^#]
RELOAD_VCL=1   #此值为1时可以在不重启varnish的情况下加载vcl文件
VARNISH_VCL_CONF=/etc/varnish/default.vcl   #vcl配置文件路径
VARNISH_LISTEN_PORT=6081    #主进程监听端口,默认监听在0.0.0.0
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1   #命令行管理接口监听IP
VARNISH_ADMIN_LISTEN_PORT=6082           #命令行管理接口监听端口
VARNISH_SECRET_FILE=/etc/varnish/secret  #连接管理接口所需的秘钥文件路径
VARNISH_STORAGE="malloc,256M"            #使用的缓存方式及所能使用的空间大小
VARNISH_USER=varnish                     #以谁的身份运行
VARNISH_GROUP=varnish                    #以哪个组的身份运行
DAEMON_OPTS="-p thread_pool_min=5 -p thread_pool_max=500 -p thread_pool_timeout=300"  #自定义运行时的选项,使用-p设定,可使用多次
  • vcl语法

语法格式:

    从varnish 4.0版本开始,每一个VCL文件都必须在文件开始处说明版本号VCL4.0
    //, # 单行注释 /* ... */ 多行注释
    sub 后跟子例程,例如sub vcl_recv { ...}
    不支持循环,受限于引擎的内建变量
    return() 函数使用关键字指定下一条引擎,用于实现状态引擎转换
    引擎需要单独定义

请求与请求之间是无关联的
请求之间在被处理时是被隔离的
VCL中的子程序既不带参数,也不返回值
VCL中的子程序只能通过HTTP头交换数据

vcl 有许多默认配置,其内容无法修改。vcl 中的配置可以分为 Client Side 与 Backend Side

Client Side 中使用的引擎有:
    vcl_recv, vcl_pass, vcl_hit, vcl_miss, vcl_pipe, vcl_purge, vcl_synth, vcl_deliver

Backend Side 中使用的引擎有:
    vcl_backend_fetch, vcl_backend_response, vcl_backend_error

查看 vcl 默认配置

vcl 4.0;   #开头声明使用的是vcl 4.0

#######################################################################
# Client side   #面向客户端侧vcl的配置段

sub vcl_recv {
    if (req.method == "PRI") {  #如果请求方法时PRI
	/* We do not support SPDY or HTTP/2.0 */
	return (synth(405));        #narnish4.0不支持HTTP2.0协议,下一跳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);           #下一跳到pipe引擎
    }

    if (req.method != "GET" && req.method != "HEAD") {   #如果不是GET或HEAD方法
        /* We only deal with GET and HEAD by default */
        return (pass);                                   #下一跳到pass引擎
    }
    if (req.http.Authorization || req.http.Cookie) {     #如果请求中包含敏感信息
        /* Not cacheable by default */
        return (pass);                                   #下一跳到pass引擎
    }
    return (hash);                                       #符合上面所有if语句,则使用hash引擎
}

通过查看上面的默认 vcl 配置,可以总结出 vcl 的基本语法:

sub subroutine {
    ...
}

if CONDITION {
    ...
} else {
    ...
}

return(), hash_data()
  • 内建函数与关键字

函数

    regsub(str, regex, sub)  正则匹配替换
    regsuball(str, regex, sub)  正则匹配全局替换
    ban(boolean expression)  
    hash_data(input)  数据hash计算
    synthetic(str)  字符串处理

关键字

    call subroutine  调用子例程
    return(action)  返回值
    new  新键对象
    set  设置变量
    unset  撤销变量

操作符

比较操作符
    == , != , ~ , > , >= , < , <=

逻辑操作符
    && , || , !

  • 变量

内建变量

    req.*  request  表示由客户端发来的请求报文相关
        req.http.*
            req.http.User-Agent, req.http.Referer, ...
    bereq.*  由varnish发往BE主机的httpd请求相关
        bereq.http.*
    beresp.*  由BE主机响应给varnish的响应报文相关
        beresp.http.*
    resp.*  由varnish响应给client相关;
    obj.*  存储在缓存空间中的缓存对象的属性;只读

常用变量

    bereq.*, req.*  
        bereq.http.HEADERS
        bereq.request, req.request  请求方法
        bereq.url, req.url  请求的url
        bereq.proto  请求的协议版本
        bereq.backend  指明要调用的后端主机
        req.http.Cookie  客户端的请求报文中Cookie首部的值;
        req.http.User-Agent ~ "chrome"

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

    beresp.*, resp.*  
        beresp.http.HEADERS
        beresp.status, resp.status  响应的状态码
        reresp.proto, resp.proto  协议版本
        beresp.backend.name  BE主机的主机名
        beresp.ttl  后端主机响应的内容的余下的可缓存时长

sub vcl_backend_response {
    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;
        }
    }
}

    obj.*
        obj.hits  此对象从缓存中命中的次数
        obj.ttl  对象的ttl值

    server.*
        server.ip  varnish主机的IP
        server.hostname  varnish主机的Hostname
    client.*
        client.ip  发请求至varnish主机的客户端IP

sub 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 对请求的 url 进行清除

acl Admin {
    "192.168.30.174"/32;   #定义管理主机组
    "192.168.30.74";       
}

sub vcl_recv {
    if (req.method == "PURGE") {  #如果使用PURGE方法请求
        if (!client.ip ~ Admin) {
            return(synth(405,"Permission denied for " + client.ip));  #且对端IP属于管理机组
        }
	    return(purge);   #则下一跳至purge引擎
    }
}

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

acl 定义访问控制列表

acl 名称 {
    "IP地址"[/子网掩码];
    ...
}

在测试的过程中,发现 varnish 在缓存时,会对整个 URL 进行 hash 计算,也就是说

curl http://192.168.30.75
curl http://192.168.30.75/index.html

访问的内容虽说一样,但是在缓存时是两条不同的记录,但是

curl http://192.168.30.75
curl 192.168.30.75

却是同一条记录

Banning

ban 规则推荐在命令行管理接口中使用,在配置文件中定义使用反而更加死板

在命令行管理接口中使用

    语法:ban <field> <operator> <arg>

ban req.http.host ~ (?i)^192.168.30.74  #清除指定目标主机的缓存
200        

ban req.url ~ (?i)^/index.html          #清除指定URL的缓存
200        

在配置文件中使用

sub vcl_recv {
    if (req.method == "BAN") {
	ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
	return(synth(200, "Ban added"));
    }
}
  • Varnish调度

varnish 作为一个缓存服务器,同时也是一台代理服务器。所以同样业具有向后端主机调度的功能。当后端主机有多台时,就需要在varnish 中定义后端主机组,并根据使用场景规定调度算法。

Round_Robin

vcl 4.0;
import directors;   #在使用调度功能之前,必须事前声明使用directors模块

backend server1 {   #定义单个后端主机
    .host = "192.168.30.174";
    .port = "80";
}

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

sub vcl_init {   #在init引擎中新键组,并将后端主机加载进指定组中
    new webservers = directors.round_robin();
    webservers.add_backend(server1);
    webservers.add_backend(server2);
}

sub vcl_recv {
    set req.backend_hint = webservers.backend();   #在recv引擎中调用指定的组
}
[root@CentOS75 ~]# for i in {1..10} ;do curl http://192.168.30.75/test$i.html ;done
This is 174 test1
This is 69 test2
This is 174 test3
This is 69 test4
This is 174 test5
This is 69 test6
This is 174 test7
This is 69 test8
This is 174 test9
This is 69 test10

Random

sub vcl_init {   #在init引擎中新键组,并将后端主机加载进指定组中
    new webservers = directors.random();  #使用random调度算法
    webservers.add_backend(server1,2);
    webservers.add_backend(server2,1);
}
[root@CentOS75 ~]# for i in {1..10} ;do curl http://192.168.30.75/test$i.html ;done
This is 174 test1
This is 174 test2
This is 174 test3
This is 174 test4
This is 174 test5
This is 174 test6
This is 174 test7
This is 69 test8
This is 174 test9
This is 69 test10

基于cookie的session sticky

sub vcl_init {
    new webservers = directors.hash();  #声明使用hash调度算法
    webservers.add_backend(server1,1);
    webservers.add_backend(server2,1);  #基于hash的会话绑定也支持定义权重
}

sub vcl_recv {
    set req.backend_hint = webservers.backend(req.http.cookie);  #指定进行hash计算的目标
}
  • 健康状态检测

probe HTTP_check {
    .url = "/index.html";  #请求的方法
    .interval = 2s;   #检测频率
    .timeout = 1s;   #超时时间
    .expected_response = 200;  #期望的到的返回码
    .window = 5;   #基于最近的多少次检查来判断其健康状态
    .threshold = 4;  #最少成功次数
}

backend server1 {
    .host = "192.168.30.174";
    .port = "80";
    .probe = HTTP_check;   #调用probe方法
}

backend server2 {
    .host = "192.168.30.69";
    .port = "80";
    .probe = HTTP_check;
}

检测的方法:

    url:检测时要请求的URL,默认为”/"; 

    request:发出的具体请求;

request = 
    "GET /.healthtest.html HTTP/1.1"  #使用的方法与协议
    "Host: www.magedu.com"  #请求的host
    "Connection: close"   #短连接

使用 backend.list 查看后端主机状态

backend.list
200        
Backend name                   Refs   Admin      Probe
default(192.168.30.174,,80)    4      probe      Healthy (no probe)
server1(192.168.30.174,,80)    4      probe      Healthy 5/5
server2(192.168.30.69,,80)     4      probe      Sick 0/5

    语法:backend.set_health <backend_expression> <state>

    state:
sick:管理down
healthy:管理up
auto:probe auto

varnish> backend.set_health server1 sick
200        

varnish> backend.list
200        
Backend name                   Refs   Admin      Probe
default(192.168.30.174,,80)    4      probe      Healthy (no probe)
server1(192.168.30.174,,80)    4      sick       Healthy 5/5   #设定其状态为sick
server2(192.168.30.69,,80)     4      probe      Sick 0/5      #检测状态为sick

后端主机属性

backend default {
    .host = "192.168.30.174";
    .port = "80";
    .connect_timeout = 0.5s;      #连接超时时间
    .first_byte_timeout = 20s;    #响应超时时间
    .between_bytes_timeout = 5s;  #传输间隔超时时间
    .max_connections = 50;        #最大并发连接书

}

猜你喜欢

转载自blog.csdn.net/M30_Miriam/article/details/81273189