varnish配置语言(1)


知识来源为官方文档:
《Varnish用户指南》
《Varnish4.0电子书》

通过对varnish原理的学习,我们知道varnish缓存策略是基于VCL语言实现,处理逻辑是编写在.vcl配置文件中
涉及的总要知识点有以下几个

  1. vcl语法:运算符、条件语句、子程序、关键字
  2. 内置Subroutines
  3. Request and response VCL objects
  4. action:return(action)
  5. Functions:内建函数
  6. Variables:变量

1. vcl语法

varnish 4.0版本开始,vcl拥有自己的默认规则,它不可移除,总是追加在自定义的规则之后。

  • vcl配置文件以 vcl 4.0 开头;
  • C语言注释风格://or#or/* foo */;
  • 子函数使用sub关键字声明, 例如sub vcl_recv { ...};
  • 无循环, state-limited variables(受限于引擎的内建变量);
  • 使用return(action)中断引擎状态,指向下一步处理,action为关键字 ,例如: return(pass);
  • 可动态装载;

VCL4相比VCL3语法的改变点

  • 要在配置文件中指定版本:即在第一行写上 vcl 4.0;
  • vcl_fetch函数被vcl_backend_response代替,且req.*不再适用vcl_backend_response;
  • 后端源服务器组director成为varnish模块,需import directors后再在vcl_init子例程中定义;
  • vcl_error变更为vcl_backend_error,必须使用beresp.,而不是obj.
  • req.request变更为req.method,obj为只读对象了。
  • 自定义的子例程(即一个sub)不能以vcl_开头,调用使用call sub_name;
  • error()函数被synth()替代;
  • return(lookup)被return(hash)替代;
  • 使用beresp.uncacheable创建hit_for_pss对象;
  • 变量req.backend.healty被std.healthy(req.backend)替代;
  • 变量req.backend被req.backend_hint替代;
  • 关键字remove被unset替代;
  • 关键字"purge"命令,已被去除。在vcl_recv使用return(purge)。
  • vcl_synth采用resp.,而非原来的obj.

1.1 主体语法

sub subroutine {
...
}
if CONDITION {
...
} else {
...
}
return()  # Functions
hash_data() # Functions

1.2 操作符

= 赋值
== 等于
~ 匹配,可以与正则表达式或ACL一起使用。同时注意匹配的规则如果是字符串,则需要 " " 引起。
! 逻辑非
&& 逻辑与
|| 逻辑或

1.3 Subroutines

子例程
子例程用于对代码进行分组以提高可读性或可重用性,例:

sub pipe_if_local {
    if (client.ip ~ localnetwork) {
        return (pipe);
    }
}

VCL中的子例程不带参数,也不返回值。内置子例程的名称均以vcl_开头。
要调用子例程,请使用call关键字,后跟子例程的名称:

sub vcl_recv {
    call pipe_if_local;
}

Varnish具有许多内置的子例程,这些子例程在每次事务流经Varnish时都会被调用。这些内置的子例程都名为vcl_*。您自己的子例程不能以vcl_开头其名称。

Return
当执行return(action)语句时,正在进行的vcl_ *子例程执行结束。
该操作指定执行的方式。上下文定义了可用的动作。

1.4 关键字

call subroutine, return(action),new,set,unset

2. 内置的Subroutines

内置函数4.0

Varnish 处理 client 的请求和后端服务器的响应时,会调用多个内置的 subroutines 进行处理。通过 CLI 执行 vcl.load 和 vcl.discard 时,也会调用内置的 subroutines。

下面对前端(client-side)和后端(backend-side)的处理分别进行介绍:

2.1 client-side

vcl_recv

在请求开始时调用,在接收并解析完整的请求之后调用,在重新启动之后调用,或者作为ESI include的结果调用。
它的目的是决定是否为请求服务,可能会修改它,并决定如何进一步处理它。可以将后端提示设置为后端处理端默认设置。

vcl_recv 子例程可使用 return() 结合下面的其中一个关键字进行终止:

hash
    请求的对象被认为是一个可能被缓存的对象,将继续对其进行处理。将控制权转交给 vcl_hash 子例程。

pass
    转换至 pass 模式。控制权最终交给 vcl_pass 子例程。

pipe
    转换至 pipe 模式。控制权最终交给 vcl_pipe 子例程。

synth(status code, reason)
    转移到 vcl_synth 子例程,synth() 的参数值被预置为 resp.status 和 resp.reason

purge
    清除请求的对象,以及它的变量(variants)。控制权先交给 vcl_hash,最终交给 vcl_purge

vcl_pipe

进入 pipe 模式时,vcl_pipe 子例程将被调用。在这个模式中,请求将被传递给后端服务器,这时 Varnish 会降级成为一个 TCP 代理,只充当一个数据流的通道,不会对数据进行任何修改,当 client 或 server 端决定关闭连接时,该模式结束。在调用 vcl_pipe 之后,对于一个处于 pipe 模式的连接,其他任何的 VCL 子例程都不会被调用。

vcl_pipe 子例程可使用 return() 结合下面的其中一个关键字进行终止:

pipe
    继续以 pipe mode 运行

synth(status code, reason)
    转移到 vcl_synth 子例程,synth() 的参数值被预置为 resp.status 和 resp.reason

vcl_pass

在进入 pass 模式时,vcl_pass 将被调用,请求被转发给后端服务器,后端服务器的响应被转发给 client,但是响应不会被缓存。来自该 client 连接的后续请求,将被正常处理。

vcl_pass 子例程可使用 return() 结合下面的其中一个关键字进行终止:

fetch
    继续以 pass mode 运行 - 发起一个对后端服务器的请求

restart
    重启该 transaction。增加 restart 计数器的计数。如果计数超过了 max_restarts,Varnish 发出一个错误:guru meditation error.

synth(status code, reason)
    转移到 vcl_synth 子例程,synth() 的参数值被预置为 resp.status 和 resp.reason

vcl_hit

当缓存查找成功,vcl_hit 将被调用。缓存对象可能会过期,其 ttl 可能为 0 或者负数,with only grace or keep time left.

vcl_hit 子例程可使用 return() 结合下面的其中一个关键字进行终止:

deliver
    发送该对象。如果该对象过期,将触发一个 fetch 调用,更新该对象。

fetch
    尽管缓存命中,但是会同步地从后端服务器更新缓存对象。控制权最终转交给 vcl_miss。

pass
    转换至 pass 模式。控制权最终交给 vcl_pass 子例程。

restart
    重启该 transaction。增加 restart 计数器的计数。如果计数超过了 max_restarts,Varnish 发出一个错误:guru meditation error.

synth(status code, reason)
    转移到 vcl_synth 子例程,synth() 的参数值被预置为 resp.status 和 resp.reason    

vcl_miss

当缓存查找失败,或者当 vcl_hit 返回一个 fetch 时,调用 vcl_miss。
vcl_miss 用于决定是否尝试从后端服务器获取文件。

vcl_miss 子例程可使用 return() 结合下面的其中一个关键字进行终止:

fetch
    从后端服务器获取请求的对象。控制权最终转交给 vcl_backend_fetch。

pass
    转换至 pass 模式。控制权最终交给 vcl_pass 子例程。

restart
    重启该 transaction。增加 restart 计数器的计数。如果计数超过了 max_restarts,Varnish 发出一个错误:guru meditation error.

synth(status code, reason)
    转移到 vcl_synth 子例程,synth() 的参数值被预置为 resp.status 和 resp.reason

vcl_hash

当 vcl_recv 为请求创建了一个 hash 值时被调用。使用该值作为 key 进行缓存查找。

vcl_hash 子例程只能以 return(lookup) 终止:

lookup
    在缓存中查找请求的对象。如果从 vcl_recv 返回 return(purge),控制权转交给 vcl_purge。
    否则,如果缓存查找的结果是 hit,控制权转交给 vcl_hit;如果缓存查找的结果是 miss,控制权转交给 vcl_miss;
    如果缓存查找的结果是 hit on a hit-for-pass 对象 (object with obj.uncacheable == true),控制权转交给 vcl_pass。

vcl_purge

执行 purge 之后,vcl_purge 被调用,缓存对象被清除(失效),其所有变量(variants)将被回避。

vcl_purge 子例程可使用 return() 结合下面的其中一个关键字进行终止:

restart
    重启该 transaction。增加 restart 计数器的计数。如果计数超过了 max_restarts,Varnish 发出一个错误:guru meditation error.

synth(status code, reason)
    转移到 vcl_synth 子例程,synth() 的参数值被预置为 resp.status 和 resp.reason    

vcl_deliver

发送对象给客户端前调用,除了将一个 vcl_synth 结果发送给客户端时不会调用。

vcl_deliver 子例程可使用 return() 结合下面的其中一个关键字进行终止:

deliver
    发送对象给 client

restart
    重启该 transaction。增加 restart 计数器的计数。如果计数超过了 max_restarts,Varnish 发出一个错误:guru meditation error.

synth(status code, reason)
    转移到 vcl_synth 子例程,synth() 的参数值被预置为 resp.status 和 resp.reason

vcl_synth

调用 vcl_synth 可以发送一个 synthetic 对象给客户端。synthetic 对象由 VCL 生成,不是从后端获取的。可使用 synthetic() 函数构造 synthetic 对象。

vcl_synth 定义了一个对象,该对象不会被缓存,与其相反,vcl_backend_error 所定义的对象可能最终被缓存。

vcl_synth 子例程可使用 return() 结合下面的其中一个关键字进行终止:

deliver
    直接将 vcl_synth 定义的对象发送给客户端,不调用 vcl_deliver

restart
    重启该 transaction。增加 restart 计数器的计数。如果计数超过了 max_restarts,
    Varnish 发出一个错误:guru meditation error.

2.2 backend-side

vcl_backend_fetch(more)

对后端服务器发送请求时调用 vcl_backend_fetch。在这个子例程中,我们一般会修改请求,然后才发送给后端服务器。

vcl_backend_fetch 子例程可使用 return() 结合下面的其中一个关键字进行终止:

fetch
    从后端服务器获取对象

abandon
    放弃对后端发起请求。除非后端请求是一个 background fetch,否则控制权将被转交给 client-side 的 vcl_synth,
    其 resp.status 被设置为 503。

vcl_backend_response

当成功从后端服务器获取到 response headers 时,调用 vcl_backend_response。

vcl_backend_response 子例程可使用 return() 结合下面的其中一个关键字进行终止:

deliver
    对于一个 304 响应,创建一个更新的缓存对象。否则,从后端获取对象的 body,然后发起 delevery 返回给客户端。
    很可能是并行的(streaming)

abandon
    放弃对后端发起请求。除非后端请求是一个 background fetch,否则控制权将被转交给 client-side 的 vcl_synth,
    其 resp.status 被设置为 503。
        
retry
    重试发起 backend transaction。增加重试计数,如果重试次数超过 max_retries,控制权转交给 vcl_backend_error

vcl_backend_error

当尝试从后端获取对象失败,或则重试次数超过 max_retries 时,vcl_backend_error 将被调用。

VCL 生成一个 synthetic 对象,可使用 synthetic() 函数构造 synthetic 对象的 body。

vcl_backend_error 子例程可使用 return() 结合下面的其中一个关键字进行终止:

deliver
    发送 vcl_backend_error 定义的对象,可能的话,缓存该对象。就如同该对象是从后端获取的一般。这也被称为 "backend synth"。

retry
    重试发起 backend transaction。增加重试计数,如果重试次数超过 max_retries,调用 client-side 的 vcl_synth,
    其 resp.status 被设置为 503。

2.3 vcl.load / vcl.discard

vcl_init

当加载 VCL 之后,vcl_init 被调用。一般用于初始化 VMODs。

vcl_init 子例程可使用 return() 结合下面的其中一个关键字进行终止:

ok
    正常返回,VCL 继续加载

fail
    停止加载这个 VCL  

vcl_fini

当一个 VCL 被废弃,当该 VCL 处理完所有请求,调用 vcl_fini。一般用于清除 VMODs。

vcl_fini 子例程可使用 return() 结合下面的其中一个关键字进行终止:

ok
    正常返回,VCL 将被废弃。

3. Request and response VCL objects

VCL中有些需要你注意的重要对象。这些对象可以在VCL被使用和操作

---req
请求对象。当vanish接收到请求后,req对象被创建和生成。你可以在vcl_recv中使用req对象做很多事。

---bereq
The backend request object. Varnish contructs this before sending it to the backend. It is based on the req object.
后端请求对象。varnish在发送请求到后端之前构建这个对象。它基于req对象。

---beresp
后端响应对象。它包含在从后端响应对象的头里。如果你想修改后端server返回的响应信息,你可以在vcl_backend_response中修改beresp对象。

---resp
传递给客户端响应之前的response对象。通常在vcl_deliver中修改。

---obj
The object as it is stored in cache. Read only.
存储在缓存中的对象。 只读。

4. action:return(action)

actions 是在终止一个内置子例程时,配合 return() 使用的,如 return(pass),最常用的 actions 是这些:

---pass
当你在子程序中return(pass)请求和随后的响应将被传递到后端server和从后端server回传回来。响应将不会被缓存。pass可以从vcl_recv中返回。

---hash
在*vcl_recv*中return(hash),通知varnish从cache查找请求内容,除非这个请求不被标示,那么请求应当被pass。

---pipe .. XXX:What is pipe? benc
如果从 vcl_recv 返回 pipe,将会进入 pipe 模式,Varnish 将前端与客户端的连接,以及与后端服务器的连接合并成一个数据流的通道,Varnish 不对数据做任何修改,只是将数据在两端发送,所以你的日志是不完整的。
pipe也可以在*vcl_recv*中返回,return(pipe)。

---deliver
传递对象给客户端。通常在vcl_backend_response中return。

---restart
Restart processing of the request. You can restart the processing of the whole transaction. Changes to the req object are retained.
重新对请求进行处理。你可以在整个请求处理的阶段重启。更改过的req对象将被保留。

---retry
Retry the request against the backend. This can be returned from vcl_backend_response or vcl_backend_error if you don't like the response that the backend delivered .
重启指向后端的请求。如果你不想从后端获得响应,你可以在vcl_backend_response 或者vcl_backend_error 中return。

5. 内建函数

hash_data():指明哈希计算的数据;减少差异,以提升命中率;
regsub(str,regex,sub):把str中被regex第一次匹配到字符串替换为sub;主要用于URL Rewrite
regsuball(str,regex,sub):把str中被regex每一次匹配到字符串均替换为sub;
return():当某VCL域运行结束时将控制权返回给Varnish,并指示Varnish如何进行后续的动作;其可以返回的指令包括:lookup、hash、hit、miss、pass、pipe、hit_for_pass、purge等;但某特定域可能仅能返回某些特定的指令,而非前面列出的全部指令;
return(restart):重新运行整个VCL,即重新从vcl_recv开始进行处理;每一次重启都会增加req.restarts变量中的值,而max_restarts参数则用于限定最大重启次数。
ban(expression):清除能被表达式匹配的所有缓存对象
ban_url(regex):Bans所有的其URL可以被此处的regex匹配到的缓存对象;
synth(status,"STRING"):purge操作;

6. 变量

内建变量

req.*:request,表示由客户端发来的请求报文相关;
    req.http.* *可以是http请求报文的任意首部的名称,代表引用http的某个请求首部
        req.http.User-Agent, req.http.Referer, ...
        req.http.host
    req.method 表示客户端的请求方法
    req.url 表示客户端请求的url

bereq.*:varnish主机在向后端真实服务器发送http请求报文时的相关变量。
    如:可以将真实的客户端地址传递给后端真实web服务器,以便于后端真实服务器记录客户端的真实IP,而不是varnish的IP
    bereq.http.* 代表varnish发往后端的真实的web服务器的相关的请求报文中的首部

beresp.*:由后端真实服务器发来的http响应报文中的某些首部信息相关的变量,一般是在vcl_backend_response或vcl_backend_fenth引擎中调用
    beresp.http.*
resp.*:由varnish响应给客户端的响应报文相关的变量;
    一般用在vcl_deliver引擎中进行调用,因为deliver引擎是用于给客户端构建响应报文,发送响应报文  
    resp.http.: 可以是响应报文中的任意首部的名称,代表引用某个响应报文的值,可用于设定、添加、修改响应给客户端的响应报文中的响应首部的值

obj.*:对存储在缓存空间中的缓存对象属性的引用变量;只读;
    obj.hits: 某个缓存对象的缓存的命中次数

client.,server.,storage.*:可用在所有面向客户端一侧的引擎中,也就是vcl_recv、vcl_pipe、vcl_hash、vcl_pass、vcl_purge、vcl_miss、vcl_hit、vcl_deliver、vcl_synth中:
    client.ip 代表客户端的IP地址
    server.ip 代表当前varnish的IP地址
    client.port 代表客户端的端口
    server.port 代表当前varnish的端口

用户自定义变量
    可用set,来设定某个用户自定义变量或现有变量的值
    可用unset,来取消某个用户自定义变量,或删除现有变量

常用内建变量说明

  • bereq.*
    bereq.http.HEADERS: 表示varnish发往后端真实web服务器的请求报文中的某个首部
    bereq.request: 表示varnish发往后端真实web服务器的请求报文的请求方法(4.0版本的varnish改为了bereq.method)
    bereq.url:表示varnish发往后端真实web服务器的请求报文的请求的url
    bereq.proto:表示varnish发往后端真实web服务器的请求报文的http协议的协议版本
    bereq.backend:表示要varnish发送请求到后端真实web服务器时,后端服务器不止一台时,所调用的后端主机

  • beresp.*
    beresp.http.HEADERS:表示后端真实web服务器发给varnish的http响应报文的某个首部的信息
    beresp.proto:表示后端真实web服务器发给varnish的http响应报文的http协议版本
    beresp.status:表示后端真实web服务器发给varnish的http响应报文的响应状态码
    beresp.backend.name:表示后端真实web服务器发给varnish的http响应报文的后端主机的名称
    beresp.ttl:后端服务器响应中的内容的余下的生存时长

  • obj.*
    obj.hit 此对象在缓存中命中的次数
    obj.ttl 此对象的ttl值,也就是其缓存时长

  • server.*
    server.ip 当前varnish的IP
    server.hostname 当前varnish的主机名

  • req.*
    req.http.HEADERS: 表示客户端发送给varnish的请求报文中的某个首部
    req.request: 表示客户端发送给varnish的请求报文的请求方法(4.0版本的varnish改为了req.method)
    req.url:表示客户端发送给varnish的请求报文的请求的url
    req.proto:表示客户端发送给varnish的请求报文的http协议的协议版本

  • resp.*
    resp.http.HEADERS:表示varnish发送给客户端的响应报文的某个首部的信息
    resp.proto:表示varnish发送给客户端的http响应报文的http协议版本
    resp.status:表示varnish发送给客户端的http响应报文的响应状态码

  • 自定义变量:可用set 变量名= 值 来设定变量。例:
    set resp.http.X-Cache = "HIT"
    表示设定响应给客户端的响应报文中设定X-Cache首部的值为HIT
    set resp.http.IS-Cache = "YES"+" "server.ip
    表示设定响应给客户端的响应报文中的IS-Cache首部的值为"YES
    varnish服务器IP",多个元素之间要用+加号连接,如果要输出空格,需要用""引号引起来

  • 取消某变量:用unset 变量名。例:
    unset req.http.cookie
    表示取消客户端请求报文中http的cookie首部信息
    unset beresp.http.Set-cookie
    表示取消后端服务器发送到varnish上的响应报文http首部中的Set-cookie首部

同时注意变量是受状态限制的,下图为可用表:


 
[sleepy↓]

 

posted @ 2019-09-18 15:46  孙红雷哔哔  阅读(693)  评论(0编辑  收藏  举报