web缓存
缓存是什么?
用户在浏览网站的时候,浏览器能够在本地保存网站中的图片或者其他文件的副本,这样用户再次访问该网站的时候,浏览器就不用再下载全部的文件,减少了下载量意味着提高了页面加载的速度。
如果中间加上一层CDN,那么用户浏览器与服务器的交互如下:
客户端浏览器先检查是否有本地缓存是否过期,如果过期,则向CDN边缘节点发起请求,CDN边缘节点会检测用户请求数据的缓存是否过期,如果没有过期,则直接响应用户请求,此时一个完成http请求结束;如果数据已经过期,那么CDN还需要向源站发出回源请求(back to the source request),来拉取最新的数据。CDN的典型拓扑图如下:
可以看到,在存在CDN的场景下,数据经历了客户端(浏览器)缓存和CDN边缘节点缓存两个阶段,下面分别对这两个阶段的缓存进行详细剖析。
insert缓存分类图:
浏览器缓存
缓存行为主要由缓存策略决定,而缓存策略由内容拥有者设置。这些策略主要通过特定的HTTP头部来清晰地表达。
当一个用户发起一个静态资源请求的时候,浏览器会通过以下几步来获取资源(结合上图)
:
-
本地缓存阶段: 先在本地查找该资源,如果有发现该资源,而且该资源还没有过期,就使用这一个资源,完全不会发送http请求到服务器;
-
协商缓存阶段: 如果在本地缓存找到对应的资源,但是不知道该资源是否过期或者已经过期,则发一个http请求到服务器,然后服务器判断这个请求,如果请求的资源在服务器上没有改动过,则返回304,让浏览器使用本地找到的那个资源;
-
缓存失败阶段: 当服务器发现请求的资源已经修改过,或者这是一个新的请求(在本来没有找到资源),服务器则返回该资源的数据,并且返回200, 当然这个是指找到资源的情况下,如果服务器上没有这个资源,则返回404。
用户操作行为与缓存
浏览器中的操作对缓存的影响:
- 强制刷新 – 当按下ctrl+F5来刷新页面的时候, 浏览器将绕过各种缓存(本地缓存和协商缓存), 直接让服务器返回最新的资源;
-
普通刷新 – 当按下F5来刷新页面的时候,浏览器将绕过本地缓蹲来发送请求到服务器, 此时,
协商缓存
是有效的 - 回车或转向 – 当在地址栏上输入回车或者按下跳转按钮的时候, 所有缓存都生效
以下分别对各个阶段进行分析:
本地缓存阶段
- Expires
指定缓存到期GMT的绝对时间,如果设了Cache-Control(下文)的max-age
,max-age就会覆盖expires。如果expires到期需要重新请求。
-
Cache-Control
Cache-Control:这个是http 1.1中为了弥补 Expires 缺陷新加入的。
浏览器总是优先使用cache-control,如果没有cache-control才考虑Expires.
-
max-age:(单位为s)指定设置缓存最大的有效时间,定义的是时间长短。当浏览器向服务器发送请求后,在max-age这段时间里浏览器就不会再向服务器发送请求了。
Expires的方法很好,但是我们每次都得算一个精确的时间。max-age 标签可以让我们更加容易的处理过期时间。我们只需要说,这份资料你只能用一个星期就可以了。
-
s-maxage(单位为s)同max-age,只用于共享缓存(比如CDN缓存)
-
public: 表示缓存的版本可以被代理服务器或者其他中间服务器识别。
-
private: 意味着这个文件对不同的用户是不同的。只有用户自己的浏览器能够进行缓存,公共的代理服务器不允许缓存。
-
no-cache: 应理解为“不建议使用本地缓存”,其仍然会缓存数据到本地
-
no-store: 绝对禁止缓存,告诉浏览器在任何情况下都不要进行cache,不在本地保留拷贝。
-
must-revalidate、proxy-revalidate…不常用,这里不做过多解释
-
协商缓存阶段
-
Last-Modified & if-modified-since
Last-Modified与If-Modified-Since是一对报文头,属于http 1.0。
last-modified是web服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间。
再次访问时,浏览器会再次请求服务端,并携带上之前response的Last-Modified 指定的时间去服务器对比。对比成功返回304在本地加载数据,对比失败从服务器获取数据。
- ETag & If-None-Match
ETag与If-None-Match是一对报文,属于http 1.1。
ETag是实体标签(Entity Tag)的缩写, 根据实体内容生成的一段hash字符串(类似于MD5或者SHA1之后的结果),可以标识资源的状态。
当资源发送改变时,ETag也随之发生变化。
为什么使用ETag呢? 主要是为了解决Last-Modified 无法解决的一些问题。
- 某些服务器不能精确得到文件的最后修改时间, 这样就无法通过最后修改时间来判断文件是否更新了。
- 某些文件的修改非常频繁,在秒以下的时间内进行修改. Last-Modified只能精确到秒。
- 一些文件的最后修改时间改变了,但是内容并未改变。 我们不希望客户端认为这个文件修改了。
浏览器缓存刷新 (同用户操作行为与缓存
)
1. 在地址栏中输入网址后按回车或点击转到按钮
浏览器以最少的请求来获取网页的数据,浏览器会对所有没有过期的内容直接使用本地缓存,从而减少了对浏览器的请求。所以,Expires,max-age标记只对这种方式有效。
2. 按F5或浏览器刷新按钮
浏览器会在请求中附加必要的缓存协商,但不允许浏览器直接使用本地缓存,它能够让 Last-Modified、ETag发挥效果,但是对Expires无效。
3. 按Ctrl+F5或按Ctrl并点击刷新按钮
这种方式就是强制刷新,总会发起一个全新的请求,不使用任何缓存。
CDN缓存
浏览器本地缓存失效后,浏览器会向CDN边缘节点发起请求。类似浏览器缓存,CDN边缘节点也存在着一套缓存机制。
CDN缓存的缺点
CDN的分流作用不仅减少了用户的访问延时,也减少的源站的负载。但其缺点也很明显:当网站更新时,如果CDN节点上数据没有及时更新,即便用户再浏览器使用Ctrl +F5的方式使浏览器端的缓存失效,也会因为CDN边缘节点没有同步最新数据而导致用户访问异常。
CDN缓存策略
CDN边缘节点缓存策略因服务商不同而不同,但一般都会遵循http标准协议,通过http响应头中的Cache-control: max-age的字段来设置CDN边缘节点数据缓存时间。
当客户端向CDN节点请求数据时,CDN节点会判断缓存数据是否过期,若缓存数据并没有过期,则直接将缓存数据返回给客户端;否则,CDN节点就会向源站发出回源请求,从源站拉取最新数据,更新本地缓存,并将最新数据返回给客户端。
CDN服务商一般会提供基于文件后缀、目录多个维度来指定CDN缓存时间,为用户提供更精细化的缓存管理。
CDN缓存时间会对“回源率”产生直接的影响。若CDN缓存时间较短,CDN边缘节点上的数据会经常失效,导致频繁回源,增加了源站的负载,同时也增大的访问延时;若CDN缓存时间太长,会带来数据更新时间慢的问题。开发者需要增对特定的业务,来做特定的数据缓存时间管理。
CDN缓存刷新
CDN边缘节点对开发者是透明的,相比于浏览器Ctrl+F5的强制刷新来使浏览器本地缓存失效,开发者可以通过CDN服务商提供的“刷新缓存”接口来达到清理CDN边缘节点缓存的目的。这样开发者在更新数据后,可以使用“刷新缓存”功能来强制CDN节点上的数据缓存过期,保证客户端在访问时,拉取到最新的数据。
H5缓存
还有一种我们都熟悉的缓存方式,LocalStorage和sessionStorage
LocalStorage是一种本地存储的公共资源,域名下很多应用共享这份资源会有风险;LocalStorage是以页面域名划分的,如果有多个等价域名之间的LocalStorage不互通,则会造成缓存多份浪费。
LocalStorage在PC上的兼容性不太好,而且当网络速度快、协商缓存响应快时使用localStorage的速度比不上304。并且不能缓存css文件。而移动端由于网速慢,使用localStorage要快于304。
在html中加载一个png图,首次加载的时候时间如下图:
然而将图片使用了LocalStorage存储后,再次刷新后加载时间为0。
而相对LocalStorage来说,SessionStorage的数据只存储到特定的会话中,不属于持久化的存储,所以关闭浏览器会清除数据。和localstorage具有相同的方法。
在前端开发中缓存是必不可少的,那么使用怎样的缓存方式更高效、让我们项目的性能更优,还是需要我们仔细斟酌。