运维教程-缓存知识体系之-浏览器缓存

跨零代码为大家提供高品质的解决方案,请大家多多来访,跨零不胜感激,在此谢过。

【上篇】

前言

现在我们把注意力关注到浏览器本身,那么我们要知道的是浏览器本质上是一个HTTP代理,它帮助用户发送HTTP请求给Web服务器,然后Web服务器响应请求,返回数据给浏览器,浏览器进行本地渲染展示给用户。那么浏览器能不能把Web服务器返回的网页或者图片、css、js等这些资源保存下来,下次我们再访问相同的资源的时候直接读取缓存呢?

浏览器缓存协商

现在我们有两个伙伴,浏览器和Web服务器,Web服务器希望能缓存一些数据到浏览器上,但是浏览器并不知道哪些数据要缓存,哪些数据不缓存,那么他们两个就需要对话。由于我们浏览器和Web服务器本身的通信协议就是HTTP,那么是不是就可以通过HTTP来进行对话呢,我们把这个浏览器和Web服务器之间有关缓存的对话称之为“缓存协商”,虽然有点像小学课文的名词解释,但是我们现在知道了一个专业的名词-“缓存协商”。

Last-Modified

浏览器和Web服务器缓存协商的第一个方法是Last-Modified,也就是最后修改时间。我们知道我们的网页存放在Linux服务器上会有三个时间,我们可以使用stat来获取。

缓存知识体系之-浏览器缓存

那么Web服务器默认情况下可以通过stat()系统调用获取到静态文件index.html在硬盘上的最后修改时间,而且在响应HTTP请求的时候回会为静态文件在HTTP响应头部自动生成最后修改时间。比如下面我们使用Firefox的Firebug插件来查看alidns.com(阿里提供的公共DNS)。

缓存知识体系之-浏览器缓存

在上面的这个图中,我们打开http://alidns.com,浏览器发送的第一个请求GET alidns.com,我们可以看到Web服务器返回200的状态码,同时在响应头中,告诉了浏览器,它这个文件的最后修改时间。好的,我们先不要着急,先来研究下这些缓存存放在哪里。对于firefox浏览器来说,是使用二进制格式保存的,不过我们可以使用about:cache来查看。直接在浏览器的地址栏输入即可。

缓存知识体系之-浏览器缓存

我们可以看到firefox默认有两种缓存保存的地方,一个是内存中,一个是保存在磁盘中,还有一个是应用本身的缓存。(如果你的浏览器已经打开很久,可能需要清空缓存后,才能更快的找到刚才的alidns.com的缓存)我们现在再次刷新下页面,你可以按F5、回车键或者firefox上面的重载页面的图标。

缓存知识体系之-浏览器缓存

我们再次使用firefox的firebug看看发生了什么,首先之前的返回的状态码由200变成了304。我们先看下面的请求头的信息。再第二次访问alidns.com的时候浏览器在请求头部增加了If-Modified-Since      Fri, 11Jul 2014 03:25:04 GMT的内容,意思是询问浏览器:浏览器大哥,请问下这个文件在我这个时间后有没有更改过,如果没有更改过,我就使用我本地缓存给用户呈现页面了哦。再看最上面,Web服务器非常果断的回复304,也就是这个文件在你之前保存的最后修改时间后,没有更改过,你可以使用本地缓存。

好的,我们可以看到浏览器和Web服务器协商的很愉快,对于访问我们站点的用户来说,如果之前访问过我们的网站,那么第二次打开相同的静态页面,就不会产生实际的文件传输。为我们节约了服务器的带宽,同时由于浏览器直接使用本地缓存呈现给用户,所以用户打开我们站点的速度也是非常的快。

 Etag

我们现在要介绍另外一个缓存协商的方法,因为在某些场景下Last-Modified可能工作的并不愉快,比如有一种情况,我们的Web服务器上面的文件最后修改时间会频繁的变动,但是文件内容却没有修改。那么对于Last-Modified的缓存协商,每次都会重新获取文件,而不会使用缓存。

还有一种生产中比较常见的场景,比如我们在集群环境中,相同的网页可能被保存在不同的服务器上,在负载均衡的时候用户的请求会被分配到这些不同的服务器中,但是我们很难保证相同的文件在所有服务器上的最后修改时间都是一致的。那么这样用户请求被分发到时间不对的服务器上时,可能会导致浏览器会重新获取网页内容。

Etag这个时候站出来勇敢的承担一切,HTTP1.1协议中并没有规定Etag的具体格式和生产的方法,总之Web服务器可以给每一个静态文件都生产一个标签,或者叫指纹,总之是一个唯一标识符(比如给文件做一个md5),当文件内容改变的时候就修改这个标签,这样浏览器和Web服务器之间就通过询问网页的Etag是否改变来进行缓存协商。

这里我不准备再次截图,读者可以自己使用firebug观察下etag的工作方式,我要强调一下就是同时存在Etag和Last-Modified的时候,Etag的优先级要高。

Expires

我们先回过头在看之前的两种缓存协商办法,浏览器给Web服务器发送HTTP请求来询问是否可以使用缓存。如果Web服务器告诉浏览器使用缓存,那么浏览器就直接使用缓存呈现给用户,你是否发现,这个询问的HTTP请求是不是也占用了Web服务器的资源,即便不产生任何的数据传输,用户还是需要等待这个请求发送到响应完毕。那么能不能不发送HTTP请求呢?比如第一次浏览器请求完毕后,Web服务器告诉浏览器这个网页保存1小时,你1个小时之内不要再过来烦我哦。

和Last-Modified、Etab一样我们常用的Web服务器Apache、Nginx都支持过期时间(Expires)的设置,当浏览器第一次请求资源的时候,Web服务器设置一个过期时间,那么在这段时间内,浏览器会直接使用本地缓存,而不发送任何的缓存协商。请看下面这个Nginx的配置实例:

缓存知识体系之-浏览器缓存

这里将所有后缀为gif、jpg、jpeg、png、bmp、swf这些资源设置了30天的过期时间,而将js和css设置了1个小时的过期时间。你可以继续使用firebug来观察使用expires的情况。

【下篇】

开始之前

昨天的公众号讲解的浏览器缓存的上篇,那么开始之前你可以需要了解的是在Web架构中还都有哪些缓存。刚好我准备了一个相对完整的《缓存知识体系》:https://www.unixhot.com/page/cache

前言

三种浏览器协商的办法让浏览器和我们Web服务器愉快的玩耍,我们把用户的浏览器当做我们派发到千家万户的缓存管理员,他们帮我们Web站点存放和管理缓存,并按照协商的办法给用户呈现,突然有一天有一个看似巧合而又普遍的时间打破了这一切。

如果服务器和用户浏览器的时间不一致呢?我们Web服务器在工程师的管理下可以自动更新时间,保证时钟同步,但是我们怎么保证用户本地电脑的时间都是对的呢?我们并没有办法!那这样就会带来一个问题。比如上面我们将css和js的过期时间为一个小时,但是用户的电脑比服务器时间晚了2个小时,那么用户访问Web站点每次浏览器都会认为立即过期,重新获取数据而不会使用本地缓存,所以我们需要有机制来解决这个问题。


Cache-Control

HTTP1.1中的Cache-Control出现了,用于弥补Expires的不足,通过max-age告诉浏览器缓存过期的相对时间,注意这个相对时间是相对于本地浏览器时间而设置的。

缓存知识体系之-浏览器缓存

可以看到上图中。max-age的为3600秒,也就是我们在Nginx设置的一个小时。这样浏览器就会根据用户本地时间来计算一个小时后,再让缓过期。

还有一个小细节,不知道你之前是否注意到。浏览器和Web服务器之间协商的时间都是GMT时间,而我们中国实用的时间是GMT+8的时区,所以我们看到的时候比实际上少了8个小时,但是这丝毫不影响他们之间的协商。

Cache-control除了max-age还有以下一个不同的参数:

  • Cache-control: public 表示缓存的版本可以被代理服务器或者其他中间服务器识别。
  • Cache-control: private 意味着这个文件对不同的用户是不同的。只有用户自己的浏览器能够进行缓存,公共的代理服务器不允许缓存。
  • Cache-control: no-cache 意味着文件的内容不应当被缓存。这在搜索或者翻页结果中非常有用,因为同样的URL,对应的内容会发生变化。

浏览器缓存刷新

您真的会使用浏览器的刷新按钮吗?不同的刷新机制可以导致不同级别的缓存失效,让我们来一起测试下。

1. 在地址栏中输入网址后按回车或点击转到按钮。

浏览器会对所有没有过期的内容直接使用本地缓存,这个时候Last-Modified、Etag、和Expires均不会受刷新的影响。

2. 按F5或浏览器刷新按钮。

浏览器会在请求中附加必要的缓存协商,但不允许浏览器直接使用本地缓存。这个时候Last-Modified、ETag就要受影响,要发起缓存协商的动作,但是对Expires无效。

3. 按Ctrl+F5或按Ctrl并点击刷新按钮。

这种方式就是我们说的强制刷新,每次浏览器都发起一个全新的请求,不使用任何缓存。

不知道你是否发现问题,如果你修改了一个设置了Expires缓存1小时的文件,难道你要通知所有用户使用Ctrl+F5强制刷新页面吗?这显然不太可能,接下来我们要来谈谈浏览器缓存过期。

浏览器缓存过期

我们最终对于我们现在的所有静态资源使用了expires的过期时间,彻底消灭了HTTP缓存协商中的HTTP请求,而且由于Cache-Control中适应本地的过期时间,我们应该明显的看到网站流量的下降,但是如果有一天,我们上线了一个js,结果由于bug,我们必须马上替换这个js文件。你们团队敏捷的修复这个bug并发布上线。

这个时候问题来了:然而你会发现已经访问过的用户,依然使用的本地缓存的旧js,造成网站故障。这并不能怪浏览器,是你的Web服务器告诉他3小时之内不要过来烦我,直接使用浏览器本地缓存:比如你再nginx设置的是

缓存知识体系之-浏览器缓存

解决的思路就是最简单的就是修改文件名,这样浏览器对于新的js文件肯定是会重新获取的,如果你的Web框架有这样的支持,这是再好不过的了,那么还有一种方式就是给文件使用参数加上时间戳:

http://www.example.com/js/code.js?20160101

这样浏览器再解析这个url的时候,就会认为这个是一个新的文件,会重新获取并不会使用浏览器本地缓存。那么不同的语言、不同的Web框架都会有不同的实现方式,这里不在举例。


小技巧:如何查看已发布的小干货?

  1. 进入“运维社区”公众号;
  2. 点击右上角“小人”图标;
  3. 点击“查看历史消息”。

分享嘉宾

趁现在,关注我们


牛人并不可怕,可怕的是牛人比我们还努力!关注“运维社区(微信ID:cloud-oaas)”公众号,每天利用空闲时间阅读一篇技术原创干货,满满的小幸福。

缓存知识体系之-浏览器缓存

长按图片,选择“识别图中二维码”,关注我们。

原创声明:本文章由赵班长原创,请必须全文转载,包括本行。

从零到一,创造未来!跨零综合IT问题解决服务站,欢迎你的到来。运维教程 只为你绽放。

本文固定链接: http://kua0.com/2019/01/02/运维教程-缓存知识体系之-浏览器缓存/

为您推荐

发表评论

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