1. 苏葳的备忘录首页
  2. 软件

Nginx的Proxy Cache的命中机制

nginx gzip ie chromeNginx是著名的HTTP服务器,也常用作反向代理服务器,所谓反向代理,意指代理的不是客户端,而是服务端。收到来自客户端的请求后,依据一定规则分发到后端服务器。对于客户而言,只知道访问了Nginx所在的服务,而不用关心Nginx将请求真实转发到了哪里,这就是反向代理。这种模式下,一个代理缓存就非常有用。nginx_proxy_cache模块就用于完成这种工作,配置得当的话,代理缓存能大大减轻Web服务器负担,提高网站响应速度。

缓存原理其实并不复杂,服务器接到浏览器请求后,将响应内容先在缓存代理里保存一份,再返回给客户端浏览器。下次再收到同样的请求,则不需要后端服务器处理,直接从缓存中取出页面内容并返回即可。这种方式对一些不会经常改动的动态页面尤其有用,由于不需要服务端重复运算,只需处理磁盘和通讯开销即可,因而对于一些服务器性能有限的网站来说,代理缓存有立竿见影的效果。

缓存代理的命中原理,大概就是将访问的URL哈希化,然后下次接受请求时将请求URL的哈希值与缓存中的URL进行对比,因为URL是URI的一种,所以完全可以作为唯一标识,唯一确定一个请求页面。按如上逻辑,假设服务器放上了一个新页面,用户A访问该页面URL,然后该页面被缓存至Proxy Cache中。那么之后任何地方的用户访问该网站的此页面,都将直接从缓存中得到响应,而非由服务器重复处理。

然而测试结果却并非如此。在本地一台Windows机上,用三大主流浏览器,IE、Chrome、FireFox访问同一个代理缓存后的URL,发现IE可以正确命中从FireFox请求并在服务端生成的的缓存,而Chrome却不予理睬,而是生成了另一份缓存。反之也一样,Chrome生成的缓存,IE与FireFox也无法命中。经过反复尝试,并查看调试日志才分析出原因。

原来作为网站性能优化的一部份,通常返回给浏览器的页面内容都是经过压缩的。Nginx服务器配置里的gzip on指令指明了支持压缩。而浏览器请求头中的Accept-Encoding指出了压缩算法。然而问题就出现在这里,某大神经过对自己网站的访问日志分析,发现现实世界中至少有44种以上的Accept-Encoding值存在。可想而知,若A用户使用的浏览器指示服务器用A算法压缩页面,而B用户使用的B浏览器只支持B算法,那么如果以同样的URL命中缓存页面,B用户下载到B浏览器的缓存页面将无法正确解压缩。

为了避免这个问题,在Nginx的配置文件gzip一节里,有gzip_vary on这条指令。这条指令指出缓存算法应对请求的Accept-Encoding值进行检查。仅仅URL匹配成功还不行,Accept-Encoding也必须匹配上才算是匹配成功。在Nginx 1.7.10源代码里能看到此段代码的存在。用VI查看一个缓存文件,会看到Accept-Encoding值已经被添加进了缓存文件头部。

通过浏览器的开发工具查看,可以看到FireFox和IE这次站到了一起,使用的Accept-Encoding值是最主流的:gzip, deflate。而坚持科学创造未来的谷歌在Chrome中使用了一种独家的sdch算法,Chrome浏览器发出的请求里的Accept-Encoding值是gzip,deflate,sdch。这就是Chrome用户无法命中FF和IE用户生成的服务器缓存的原因!

原因找到了,却不知道如何解决。关闭gzip_vary on最简单,可是也许会引发一些问题。另外对Accept-Encoding值的使用也有一些疑问,比如列出的几种算法总不至于同时使用吧?是按优先级别从前到后,从高到低的顺序尝试采用?如果这样的话,那么Nginx简单的把Accept-Encoding值作为一个字符串比较就不是合适的作法。正确的做法是在缓存文件中保存这个文件实际采用的压缩算法,同时在请求的校验中拆开Accept-Encoding串,逐个匹配算法。当然,没有搞清楚HTTP协议中Accept-Encoding报文头原理的前提下,以上想法纯属YY,留待高人解答吧。

原创文章,作者:苏葳,如需转载,请注明出处:https://www.swmemo.com/1794.html

发表评论

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