前文我们聊了下 varnish 的 VCL 配置以及语法特点, 怎样去编译加载 varnish 的 vcl 配置, 以及命令行管理工具 varnishadm 怎么去连接 varnish 管理接口进行管理 varnish, 回顾请参考 https://www.cnblogs.com/qiuhom-1874/p/12626134.html; 今天我们来说一下 varnish 的状态引擎; 首先我们来回顾下 iptables 报文的走向, 在 iptables 里报文的走向有三种, 第一种是从别的主机发送过来的报文, 首先它会到达网卡, 然后进入 prerouting 链, 然后经过路由决策后, 如果是发往本机的, 则就走 input 链, 从而把报文送给本机上的应用程序; 第二种是从 prerouting 链通过路由决策后, 不是发往本机的报文而是发往其他主机, 通过本机转发的, 它会从 prerouting 链到 forward 链, 然后从 postrouting 链把报文发送给其他主机; 第三种是从本机发往其他主机的报文, 它的报文走向是从 output 链到 postrouting 链, 然后从网卡发送出去; 我们说 iptables 的原因是类比 varnish 的状态引擎; varnish 的状态引擎就类似 iptables 里的这 5 链; 我们写的 vcl 配置就相当于 iptables 里的规则; 他俩有个共同点就是在每个链上的规则只对当前链上的表或者被自定义链引用才会生效, 而 varnish 里的状态引擎也是同样的逻辑, 我们写的 vcl 配置只对当前状态引擎生效, 不同的状态引擎处有着不同的意义, 对其他状态引擎互不干扰; 这样描述相信大家对 varnish 的状态引擎有了初步的概念, 这也是我们在上一篇文中说到的, 发送给客户端的响应报文, 为什么要配置在 vcl_deliver 里, 而不是其他位置; 接下来我们看看 varnish 的状态引擎;
提示: 以上这张图上 varnish4.0 的状态引擎图, 每个状态引擎彼此的关系, 以及 varnish 内部缓存处理逻辑; 首先当 varnish 服务器收到来自客户端的请求报文, 最先到达的状态引擎是 vcl_recv, 我们可以在 vcl_recv 里面对客户端的请求报文做修改, 或者其他操作, 然后交给 vcl_hash 这个状态引擎, 这个状态引擎主要是看是否可查缓存, 如果可以查缓存, 会判断是否命中, 命中就交给 vcl_hit 处理, vcl_hit 处理后, 就直接交给 vcl_deliver 处理, 最后响应给客户端, 当然缓存命中后也可以将请求交给 vcl_pass 处理; 如果 vcl_hash 处理后不能查缓存, 就把报文发送给 vcl_miss 处理, 意思是不能查缓存, 或者缓存未能命中; 当然我们也可以直接把报文交给 vcl_pass 处理; 即便它可以被缓存命中, 我们也是可以强行让该请求不查缓存, 直接交给 vcl_pass 处理或者 vcl_miss 直接交给 vcl_backend_fatch 处理; vcl_backend_fetch 就是去后端真正的服务器上取对应资源, 然后它会对后端服务器的响应报文头部进行读的操作, 如果没有什么错误, 就把响应报文发送给 vcl_backend_response,vcl_backend_response 在处理响应报文时, 会判断是否可缓存, 如果可以缓存, 就在本地缓存一份, 然后通过 vcl_deliver 响应给客户端, 如果不可缓存, 在本地就不缓存, 直接将响应报文发送给 vcl_deliver 响应给客户端; 如果 vcl_backend_fetch 读后端服务器发来的响应报文是错误响应 (或者 vcl_backend_fatch 未取到对应资源, 或者后端主机宕机等等), 它就会把该处理逻辑交给 vcl_backend_error 处理; 如果用户的请求经过 vcl_hash 处理后, 发现缓存内容变了或者说缓存过期了需要修剪缓存, 它会把请求发往 vcl_purge,vcl_purge 主要处理缓存修剪相关的操作, 然后把请求报文发送给 vcl_synth 处理, 合成一响应发送给客户端; 如果通过 vcl_hash 处理后发现用户请求的方法我们压根就不认识, 这个时候会将请求报文交给 vcl_pipe 处理;
从上面的图来看, 我们大概可以总结为两点, varnish 的状态引擎分前端工作线程或者客户端状态引擎和后端工作线程或者服务端状态引擎; 客户端状态引擎, 主要处理客户端请求和响应相关的处理, 比如是否可查缓存, 是否命中, 是否修剪缓存, 是否识别用户请求的方法有或者直接交给 vcl_pass, 又或者说怎样响应客户端等等, 可以看到客户端状态引擎 vcl_pass, 是一个额外处理机制, 不管是否可查缓存, 是否命中, 都可以交给它处理; 对于服务端状态引擎主要是处理和后端服务器请求和响应相关操作, 比如怎样去后端服务器取资源, 对服务器的响应报文是否可缓存, 怎么缓存, 对后端服务器的响应报文错误怎么处理等;
varnish 的前端状态引擎有 vcl_recv,vcl_pass, vcl_hit, vcl_miss, vcl_pipe, vcl_purge, vcl_synth, vcl_deliver;vcl_recv 处理后可以通过 return 来指定下一跳处理的状态引擎是那个, 如果是 return(hash) 就表示交给 vcl_hash 处理; return(pass) 就表示交给 vcl_pass 处理; return(pipe) 就表示交给 vcl_pipe 处理; return(synth) 就表示交给 vcl_synth 处理; return(purge) 就表示交给 vcl_purge 处理; 对于 vcl_hash 来说, return(hit) 就表示缓存命中交给 vcl_hit 处理, return(miss) 表示缓存未能命中交给 vcl_miss 处理, return(pass) 或者 return(hit_for_pass) 就表示即便缓存命中也交给 vcl_pass 处理, return(purge) 就表示交给 vcl_purge 处理;
varnish 的后端状态引擎有 vcl_backend_fetch, vcl_backend_response, vcl_backend_error;vcl_backend_fetch 处理去后端取资源的操作, vcl_backend_response 处理后端服务器响应回来的报文, vcl_backend_error 处理后端服务器错误; 除此以外 varinsh4.0 还有两个特殊的状态引擎, 分别是 vcl_init 和 vcl_fini;vcl_init: 在处理任何请求之前要执行的 vcl 代码: 主要用于初始化 VMODs;vcl_fini: 所有的请求都已经结束, 在 vcl 配置被丢弃时调用; 主要用于清理 VMODs;
了解了上面的状态引擎, 我们在说一说 varnish 的变量, 在前文我们大概说了下 varnish 的变量大概可以分 5 类, 一类是客户端请求报文相关的, req.*; 一类是 varnish 服务器请求后端服务器报文, bereq.*; 一类是后端服务器响应 varnish 服务器的 beresp.*; 一类是 varnish 服务器响应客户端的 resp.*; 还有一类是 obj.*, 这类变量主要是储存缓存空间中的缓存对象的属性; 结合上面说的状态引擎, 不难里接在不同的状态引擎里, 对应变量是有限的, 比如 bereq.* 这类变量就不能用于 vcl_recv, 因为 vcl_recv 是接收用户请求相关的, 而 bereq.* 是 varnish 请求后端服务器的变量, 这两者很明显是不再一个级别的, 所以通常不同类的变量对应能够用于哪些状态引擎中是有限制的; 而对应变量的属性也是有要求的, 比如 obj.hit 这个变量是存储缓存项命中次数的, 通常可用在 vcl_hit 和 vcl_deliver 状态引擎中, 表示应用缓存命中次数, 相对于这个变量来说, 我们是不能修改的, 所以 obj.hits 这个变量在 vcl_hit 和 vcl_deliver 状态引擎中只可读, 不可修改; 而对于 obj.ttl 这个值就不一样了; obj.ttl 记录缓存项可缓存的时间; 很显然 obj.ttl 这个变量只能用于可缓存的状态引擎上, 比如 vcl_hit, 对于告诉客户端可缓存的时间, 很明显它不能是一个不可修改的值; 所以对于 obj.ttl 这个变量在 vcl_hit 状态引擎中就具有可读可写权限 (即我们可以修改该变量的值); 说这么多无外乎就是表达一个意思, 不同类型的变量受限状态引擎, 不同变量在不同的状态引擎上不是都可读可写, 有的变量只可读; 如下图
说明: 以上这张表就是对于不同类型的变量对应 varnish 的状态引擎是否可读写的, 没有读写就表示该类型变量不能用于对应状态引擎中; 比如 resp.* 只能在 error 和 deliver 状态引擎中使用; beresp.* 这类变量只能用于后端主机响应 varnish 服务器的过程中使用, 比如 fetch 这个状态引擎就是处理后端服务器响应 varnish 服务器请求的; 所以 beresp.* 这类变量只能用于 fetch; 当然这里的 fetch 是早期状态引擎的名称. 在 varnish4.0 它不叫 fetch, 而叫 vcl_backend_fatch;
了解了以上内容, 我们接下来看几个示例
示例: 强制对某类资源的请求不检查缓存
提示: 以上配置表示对客户端请求的 url 进行判断, 如果能够被. jpg,.jpeg,.PNG,.gif,.JS,.CSS,.HTML 匹配到, 那么就把用户请求交给 pass 状态引擎处理; pass 状态引擎处理就是不查缓存; 所以对于客户端请求. jpg 的资源, 其对应 obj.hits 的值会一直为 0; 因为我们明确指定了不查缓存;
测试: 用浏览器访问服务器上的. jpg 资源, 看看响应报文中我们自定义的 X-Cache 首部是否是 miss via 192.168.0.99;
提示: 可以看到我们访问 / day.jpg 这个资源时, 不管怎么刷新浏览器, 对应响应首部 X-Cache 的值都是 miss via 192.168.0.99, 说明我们请求. jpg 的资源的确没有查缓存;
示例: 把客户端 IP 传到后端服务器
提示: 以上 vcl 表示判断客户端请求首部 X-Forwarded-For 是否为空, 如果不为空就把它的值在原有的值的基础上和客户端 ip 地址做字符串连接, 并用逗号隔开; 如果该首部为空或者没有这个首部就把这个首部的值设置成客户端 ip 地址;
更改后端 web 服务的日志格式
提示: 以上配置表示在日志格式中应用 X-Forwarded-For 这个首部的值;
测试: 重新编译加载 vcl, 然后用浏览器访问, 看看是否能够把浏览器所在主机的 ip 地址传到后端 httpd 服务器日志中做记录?
提示: 从上面的日志结果看, 我们分别用不同的浏览器去访问, 在日志中可以看到不同浏览器所在主机的 IP 地址, 说明我们通过判断用户请求报文 X-Forwarded-For 首部是否为空, 从而实现对于非空和空值对应设置该首部值, 继而实现把对应请求首部值记录到后端服务器日志中的目的;
示例: 对于特定类型的资源, 例如公开的图片等, 取消其私有标识, 并强行设定其可以由 varnish 缓存的时长;
提示: 首先我们要清楚在那个位置去对报文操作, 取消私有标识, 是需要在后端服务器响应 varnish 这个过程中把对应响应首部的值给撤销了; 所以我们需要在 vcl_backend_response 这个状态引擎中来设置, 取消 set-cookie 首部, 这个首部主要是给对应客户端设置一个 cookie; 以上配置表示判断后端服务器响应 varnish 服务器的响应报文首部 cache-control 的值是否匹配 "s-maxage", 如果不匹配说明该资源不允许被共有缓存系统所缓存, 如果匹配, 则说明该资源允许被公有缓存系统所缓存; 如果不匹配再继续判断 varnish 向后端请求的首部 url 的值是否匹配. jpg|jpeg|PNG|gif|CSS|JS 结尾的资源, 如果匹配则取消后端服务器响应 varnish 服务器的响应首部 set-cookie 的值, 并设置后端服务器响应 varnish 的资源缓存时长为 1 小时; 简单讲就是判断后端服务器响应首部的 cache-control 的值是否匹配到 "s-maxage", 如果不能匹配到在判断向后端服务器请求的首部 url 是否是匹配指定结尾的资源, 如果是, 就取消后端服务器响应首部 set-cookie 这个首部, 同时把后端服务器响应资源的缓存时长设定为 1 小时;
测试: 为了验证以上 vcl 配置正确性, 我们把 beresp.ttl 的值通过 cache-control 这个首部传递到浏览器响应首部, 从而来判断 set-cookie 首部是被撤销了;
提示: 在上面的配置中加入了 set beresp.http.cache-control = beresp.ttl; 表示把后端响应给 varnish 的响应首部 beresp.ttl 的值 通过 beresp.http.cache-control 首部保存; 这样客户端访问. jpg 的资源就会在响应首部中把 cache-control 的值给显示出来, 如果该值是我们设置的 3600s, 就说明我们撤销 set-cookie 这个首部的 vcl 语句是生效的;
提示: 从上面的结果看, cache-control 的值为 3600 是我们设置 beresp.ttl 的值; 说明撤销 set-cookie 的 vcl 配置生效了; 同时这也告诉我们如果后端服务器响应 varnish 的报文中没有的首部, 在 varnish 响应客户端中就没有; 简单说就是 varnish 会把后端服务器响应给 varnish 中首部的值通过响应客户端首部传递出来; 比如我在后端响应报文中自定义一个 aaa 的报文首部, 其值为 bbb, 那么在客户端的响应报文首部中就会有对应 aaa 首部和对应的值; 如下
测试:
提示: 做以上测试需要考虑 varnish 上的缓存, 如果你始终访问同一个 url 可能会看到对应首部的值不会发生变化, 需要重启 varnish 或者换个其他符合 vcl 定义的 url 去访问试试看;
来源: https://www.cnblogs.com/qiuhom-1874/p/12643549.html