Skip to content

缓存

前端浏览器缓存知识梳理 - 掘金

谈谈http缓存 - 掘金

缓存

什么是缓存

浏览器访问某个网站,第一次访问的时候电脑会把相关素材下载到电脑,

再次访问的时候,就会去从电脑直接加载出来。

好处

  • 缓存服务器压力
  • 提升性能
  • 减少宽带消耗

Web缓存种类

  • 数据库缓存
  • CDN缓存
  • 代理服务器缓存
  • 浏览器缓存

强缓存

强缓存的设置

  • 第一次请求

判断响应头,是否含有cache-controlprogramexpires字段,代表强缓存,浏览器会将文件缓存到disk cache或者 memory cache

  • 第二次请求

符合强缓存条件,直接返回200,从硬盘读取文件,否则进入协商缓存。

符合协商缓存,返回304。
不符合协商缓存,返回全新资源。

Expires

缓存过期时间,格林尼治时间,使用本地时间判断,
如果没过去,使用本地缓存,如果过期了,则重新去请求

Cache-Control

HTTP1.1中控制缓存的字段,当Cache-Control都存在时,Cache-Control优先级更高,主要取值为:

public:资源客户端和服务器都可以缓存。

privite:资源只有客户端可以缓存。

no-cache:客户端缓存资源,但是是否缓存需要经过协商缓存来验证。

no-store:不使用缓存。

max-age:缓存保质期。

program

HTTP1.0中禁用网页缓存的字段,其取值为no-cache,和Cache-Control的no-cache效果一样。

对比

缓存位置

强缓存会将资源放在 memory cache,或者 disk cache

优先级

我们发现memory cache的Time为0ms,内存缓存的读取高效,约为 100纳秒,所以接近0ms。
service work > memory cache > disk cache > push work

1. Service Worker

是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。

2. Memory Cache

内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。

所以当关闭当前标签页,内存缓存也就消失了,原来的缓存会从 disk cache 中去读取。

3. Disk Cache

存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。
在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自 Disk Cache。
memory cache 要比 disk cache 快的多。举个例子:从远程 web 服务器直接提取访问文件可能需要500毫秒(半秒),那么磁盘访问可能需要10-20毫秒,而内存访问只需要100纳秒,更高级的还有 L1缓存访问(最快和最小的 CPU 缓存)只需要0.5纳秒。

很神奇的,我们又看到了一个prefetch cache,这个又是什么呢?
prefetch cache(预取缓存)
link标签上带了prefetch,再次加载会出现。
prefetch是预加载的一种方式,被标记为prefetch的资源,将会被浏览器在空闲时间加载。

4. Push Cache

Push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。

协商缓存

协商缓存就是强缓存失效后,浏览器携带缓存标识向服务器发送请求,由服务器根据缓存标识来决定是否使用缓存的过程。
主要有以下两种情况:

  • 协商缓存生效,返回304

  • 协商缓存失效,返回200和请求结果

协商缓存的设置

Last-Modified / If-Modified-Since

Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间。

若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件。

Etag/If-None-Match

通过标识符去判断是否修改。

服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200。

Etag / If-None-Match优先级高于Last-Modified / If-Modified-Since,同时存在则只有Etag / If-None-Match生效。

第一次请求

image.png

第二次请求

image.png

CDN

CDN会把源站的资源缓存到CDN服务器,当用户访问的时候就会从最近的CDN服务器拿取资源而不是从源站拿取,这样做的好处是分散了压力,同时也会提升返回访问速度和稳定性。

缓存方案

目前的项目大多使用这种缓存方案的:

  • HTML: 协商缓存;
  • css、js、图片:强缓存,文件名带上hash。

区别

  1. 强缓存不发请求到服务器,所以有时候资源更新了浏览器还不知道,
    但是协商缓存会发请求到服务器,所以资源是否更新,服务器肯定知道。

强缓存expire是通过本地来判断的,所以会有误差,
通过cache-control的max-age使用相对时间来控制,但都不经过服务器,浏览器直接返回缓存内容,

协商缓存是通过Etagif-no-match以及Last-Modified以及 If-Modified-Since来判断的,
所以会经过服务器,资源更新了服务器是知道的。

  1. 大部分web服务器都默认开启协商缓存。

刷新对于强缓存和协商缓存的影响

  1. 当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存。
    2. 当f5刷新网页时,跳过强缓存,但是会检查协商缓存。
    3. 浏览器地址栏中写入URL,回车 浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。(最快)

禁用浏览器缓存

禁用浏览器缓存:Cache-Control、pragma、expires

javascript
response.setHeader("Cache-Control", "no-cache");
response.setHeader("pragma", "no-cache");
response.setDateHeader("expires", -1);
response.getWriter().print("hello hello!");
javascript
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">

范围请求

https://juejin.cn/post/6844903642034765837

今天再来介绍一下 HTTP 的范围请求。范围请求主要是针对较大的文件的请求或者上传,可以仅操作它的某一段。
image.png

是否支持范围请求

HTTP 本身是一种无状态的“松散”协议,而在经历了很多版本的迭代之后,只在 HTTP/1.1(RFC2616) 之上,才支持范围请求。所以如果客户端或者服务端两端的某一端低于 HTTP/1.1,我们就不应该使用范围请求的功能。

而在 HTTP/1.1 中,很明确的声明了一个响应头部 Access-Ranges 来标记是否支持范围请求,它只有一个可选参数 bytes。

image.png

使用范围请求

HTTP/1.1 中定义了一个 Ranges 的请求头,来指定请求实体的范围。它的范围取值是在 0 - Content-Length 之间,使用 - 分割。
Content-Range 的格式也很清晰,首先标记它的单位是 bytes 然后标记当前传递的内容实体范围和总长度。

Content-Range: bytes 100-999/1000

在这个例子中,会传递 100 ~ 999 范围的内容实体,而该资源文件的总大小是 1000 bytes。并且此时的 HTTP 响应状态码为 206 Partial Content 。

资源变化

ETag
Last-Modified

在 HTTP 的范围请求中,也可以使用这两个字段来区分分段请求的资源,是否有修改过,只需要在请求头中,将它放在 If-Range 这个请求报文头中即可。If-Range 使用 ETag 或者 Last-Modified 两个参数任意一个,原样填入即可。

使用场景

  • 断线续传
  • 多线程下载

范围请求小结

  1. HTTP 范围请求,需要 HTTP/1.1 及之上支持,如果双端某一段低于此版本,则认为不支持。
  2. 通过响应头中的 Accept-Ranges 来确定是否支持范围请求。
  3. 通过在请求头中添加 Range 这个请求头,来指定请求的内容实体的字节范围。
  4. 在响应头中,通过 Content-Range 来标识当前返回的内容实体范围,并使用 Content-Length 来标识当前返回的内容实体范围长度。
  5. 在请求过程中,可以通过 If-Range 来区分资源文件是否变动,它的值来自 ETag 或者 Last-Modifled。如果资源文件有改动,会重新走下载流程。