WEB缓存系统之varnish状态引擎
前文我们聊了下varnish的vcl配置以及语法特点,怎样去编译加载varnish的vcl配置,以及命令行管理工具varnishadm怎么去连接varnish管理接口进行管理varnish,回顾请参考;今天我们来说一下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去访问试试看;
上一篇: 在ASP.NET MVC中使用Unity进行依赖注入的三种方式
下一篇: 谁会给你看病啊