Day7--字节跳动-HTTP的场景实践|青训营

165 阅读16分钟

1. 什么是HTTP缓存策略

HTTP 缓存是一种提高网页性能和用户体验的重要技术,它可以减少网络流量,降低服务器负载,加快响应速度,节省用户流量等。HTTP 缓存的基本原理是,当客户端(如浏览器)第一次请求一个资源(如网页、图片、样式表等)时,服务器会返回该资源,并在响应头中添加一些缓存相关的信息,如 Cache-Control、Last-Modified、ETag 等。客户端会根据这些信息来决定是否将该资源缓存在本地(如浏览器缓存)。当客户端再次请求该资源时,如果本地缓存还有效(fresh),则直接从缓存中读取该资源,而不需要再向服务器发送请求;如果本地缓存已经过期(stale),则客户端会向服务器发送一个验证请求(如 If-Modified-Since、If-None-Match 等),询问该资源是否有更新。服务器会根据验证请求中的信息来判断该资源是否有更新,如果没有更新,则返回一个 304 Not Modified 的状态码,告诉客户端可以继续使用本地缓存;如果有更新,则返回一个 200 OK 的状态码,并返回最新的资源和缓存信息。这样就可以实现缓存的有效利用和及时更新。

2. 具体分析

为了更好地理解 HTTP 缓存的工作原理和效果,我们选择使用 edge 浏览器来访问一个具体的网站,并使用开发者工具来查看其涉及的请求中的缓存策略。我们选择 百度 作为例子,因为其页面中包含了多种类型的资源,如 HTML、CSS、JS、图片等,可以较为全面的提供案例。我们可以按照以下步骤来进行分析:

  1. 打开 edge 浏览器,并在地址栏输入 [www.baidu.com/] ,然后按回车键。
  2. 打开开发者工具(按 F12 键或者右键点击页面空白处选择“检查”),并切换到“网络”选项卡。
  3. 刷新页面(按 F5 键或者点击浏览器工具栏上的刷新按钮),并观察网络面板中显示的请求列表。 QQ截图20230805125346.png
  4. 在网络面板中,我们可以看到每个请求的名称、状态码、类型、大小、时间等信息。我们可以点击某个请求,在右侧面板中查看其详细信息,包括请求头、响应头、预览、响应等。 QQ截图20230805125501.png

QQ截图20230805125540.png

  1. 我们可以根据不同的状态码来区分不同的缓存策略:

    • 如果状态码是 200 OK,并且大小列显示了具体的字节数(如 12.3 KB),则表示该请求是从服务器获取了最新的资源,并没有使用本地缓存。
    • 如果状态码是 200 OK,并且大小列显示了 (from memory cache) 或者 (from disk cache),则表示该请求是从本地缓存中读取了资源,并没有向服务器发送请求。
    • 如果状态码是 304 Not Modified,并且大小列显示了 (from memory cache) 或者 (from disk cache),则表示该请求是向服务器发送了验证请求,服务器返回了 304 状态码,告诉客户端可以继续使用本地缓存。

QQ截图20230805125628.png

QQ截图20230805125927.png

  1. 我们还可以根据不同的类型来区分不同的资源:

    • 如果类型是 document,则表示该请求是获取网页的 HTML 文档。
    • 如果类型是 stylesheet,则表示该请求是获取网页的 CSS 样式表。
    • 如果类型是 script,则表示该请求是获取网页的 JS 脚本。
    • 如果类型是 image,则表示该请求是获取网页的图片。
    • 如果类型是 font,则表示该请求是获取网页的字体文件。
    • 如果类型是 xhr,则表示该请求是通过 AJAX 发送的异步请求。
  2. 此外,我们还可以根据响应头中的缓存相关的信息来分析不同的缓存策略:

    • 如果响应头中有 Cache-Control 字段,则表示该资源使用了基于 age 的缓存策略,即根据资源的最大有效期(max-age)来判断是否过期。例如,Cache-Control: max-age=31536000 表示该资源的最大有效期为一年,即一年内不会过期。
    • 如果响应头中有 Last-Modified 和 ETag 字段,则表示该资源使用了基于验证的缓存策略,即根据资源的最后修改时间(Last-Modified)或者实体标签(ETag)来判断是否更新。例如,Last-Modified: Tue, 22 Feb 2022 22:22:22 GMT 和 ETag: “5d8c72a5-2c” 表示该资源在 2022 年 2 月 22 日 22 点 22 分 22 秒被修改过,且其实体标签为 “5d8c72a5-2c”。当客户端再次请求该资源时,会在请求头中添加 If-Modified-Since: Tue, 22 Feb 2022 22:22:22 GMT 和 If-None-Match: “5d8c72a5-2c” 字段,询问服务器该资源是否有更新。如果没有更新,则服务器会返回 304 状态码,否则会返回 200 状态码和最新的资源。
    • 如果响应头中没有任何缓存相关的信息,则表示该资源没有使用任何缓存策略,即每次都需要从服务器获取最新的资源。

下面我们以几个具体的请求为例,来分析其缓存策略:

  • 请求 [www.baidu.com/] ,状态码为 200 OK,类型为 document,大小为 12.3 KB。从响应头中我们可以看到,该请求没有使用任何缓存策略,即每次都需要从服务器获取最新的 HTML 文档。这是合理的,因为 HTML 文档通常是动态生成的,可能会随时变化,所以不能缓存。 QQ截图20230805130309.png
  • 请求 [pss.bdstatic.com/static/supe…] ,状态码为 200 OK,类型为 image,大小为 (from disk cache)。从响应头中我们可以看到,该请求使用了基于 age 的缓存策略,其最大有效期为一年(Cache-Control: max-age=31536000)。这意味着该图片在一年内不会过期,所以可以直接从本地缓存中读取,而不需要再向服务器发送请求。这是合理的,因为图片通常不会经常变化,所以可以长时间缓存。

QQ截图20230805130429.png

  • 请求 [www.baidu.com/sugrec?&pro…] ,状态码为 200 OK,类型为 xhr,大小为 1.4 KB。从响应头中我们可以看到,该请求使用了基于验证的缓存策略,并且禁止了共享缓存(Cache-Control: no-cache, private)。这意味着该资源每次都需要向服务器发送验证请求,并且禁止了共享缓存(Cache-Control: no-cache, private)。这意味着该资源每次都需要向服务器发送验证请求,并且不能被中间代理(如 CDN)缓存。这是合理的,因为该资源是通过 AJAX 发送的异步请求,用于获取用户输入的搜索建议,这是一个动态变化和用户相关的数据,所以不能缓存或者共享。从请求头中我们可以看到,该请求使用了 ETag 字段来进行验证,例如 ETag: “5d8c72a5-2c”。当客户端再次请求该资源时,会在请求头中添加 If-None-Match: “5d8c72a5-2c” 字段,询问服务器该资源是否有更新。如果没有更新,则服务器会返回 304 状态码,否则会返回 200 状态码和最新的资源。

QQ截图20230805130558.png

  • 请求 [www.baidu.com/favicon.ico] ,状态码为 304 Not Modified,类型为 image,大小为 (from disk cache)。从响应头中我们可以看到,该请求使用了基于验证的缓存策略,并且设置了一个较长的最大有效期(Cache-Control: max-age=31536000, public)。这意味着该资源在一年内不会过期,但是每次都需要向服务器发送验证请求。这是合理的,因为该资源是网站的图标文件,通常不会经常变化,但是也需要及时更新。从请求头中我们可以看到,该请求使用了 If-Modified-Since 字段来进行验证,例如 If-Modified-Since: Tue, 22 Feb 2022 22:22:22 GMT。当客户端再次请求该资源时,服务器会根据该字段来判断该资源是否有更新。如果没有更新,则服务器会返回 304 状态码,告诉客户端可以继续使用本地缓存;如果有更新,则服务器会返回 200 状态码和最新的资源。

QQ截图20230805130619.png

http缓存策略以及强缓存和协商缓存浅析

本地缓存-强缓存

本地缓存,也就是我们常说的强缓存:是指当浏览器请求资源时,如果请求服务端的资源命中了浏览器本地的缓存资源,那么浏览器就不会发送真正请求给服务器。 此时的请求过程:

第一次请求

  • 当浏览器还是第一次发送请求到后端的时候,本地还没有缓存资源,这个时候的服务器返回给浏览器的资源,响应码是200
  • 当浏览器收到资源后,会将资源和对应的响应头一起缓存下来。

第二次请求

  • 第二次浏览器准备发送请求给服务器时候,浏览器会先检查上一次服务端返回的响应头信息中的Cache-Control(它的值是一个相对值,单位为秒,表示资源在客户端缓存的最大有效期)
  • 过期时间为第一次请求的时间加上Cache-Control的值,
  • 过期时间跟当前的请求时间比较,如果本地缓存资源没过期,那么命中缓存,不再请求服务器。
    • 如果没有命中,浏览器就会把请求发送给服务器,进入缓存协商阶段。

缓存协商

场景1

  • 当客户端浏览器第一次请求的时候,分为两种情况:
    • 第一种:服务器端返回的响应头中没有Cache-Control和Expires或者Cache-Control和Expires
    • 第二种:Cache-Control的属性设置为no-cache时

场景2

  • 浏览器第二次请求时就会与服务器进行协商,对比浏览器中的缓存资源,是否是最新的。
  • 当缓存和服务端资源的最新版本是一致的,那么就无需再次下载该资源,服务端直接返回重定向304 Not Modified 状态码,
  • 如果服务器发现浏览器中的缓存已经是旧版本了,那么服务器就会把最新资源的完整内容返回给浏览器,状态码就是200
  • 服务端是根据HTTP的另外两组头信息,分别是:Last-Modified/If-Modified-Since 与 ETag/If-None-Match,这两组数据一 一对应,互相结合使用

http缓存策略浅析


本地缓存-强缓存相关字段解析

Cache-control 缓存头部

Cache-Control、Expires,Cache-Control有多个可选值代表不同的意义,而Expires就是一个日期格式的绝对值。

  • Cache-Control是HTTP缓存策略中最重要的头信息,它是HTTP/1.1中出现的
  • Cache-Control 头用来区分对缓存机制的支持情况, 请求头和响应头都支持这个属性。通过它提供的不同的值来定义缓存策略

没有缓存

缓存中不得存储任何关于客户端请求和服务端响应的内容。每次由客户端发起的请求都会下载完整的响应内容。

Cache-Control: no-store


缓存但重新验证

如下头部定义,此方式下,每次有请求发出时,缓存会将此请求发到服务器(译者注:该请求应该会带有与本地缓存相关的验证字段),服务器端会验证请求中所描述的缓存是否过期,若未过期(注:实际就是返回304),则缓存才使用本地缓存副本。

Cache-Control: no-cache


私有和公共缓存

"public" 指令表示该响应可以被任何中间人(译者注:比如中间代理、CDN等)缓存。若指定了"public",则一些通常不被中间人缓存的页面(译者注:因为默认是private)(比如 带有HTTP验证信息(帐号密码)的页面 或 某些特定状态码的页面),将会被其缓存。

而 "private" 则表示该响应是专用于某单个用户的,中间人不能缓存此响应,该响应只能应用于浏览器私有缓存中。

Cache-Control: private Cache-Control: public


过期

过期机制中,最重要的指令是 "max-age=",表示资源能够被缓存(保持新鲜)的最大时间。相对Expires而言,max-age是距离请求发起的时间的秒数。针对应用中那些不会改变的文件,通常可以手动设置一定的时长以保证缓存有效,例如图片、css、js等静态资源。

详情看下文关于缓存有效性的内容。

Cache-Control: max-age=31536000

验证方式

当使用了 "must-revalidate" 指令,那就意味着缓存在考虑使用一个陈旧的资源时,必须先验证它的状态,已过期的缓存将不被使用。详情看下文关于缓存校验的内容。

Cache-Control: must-revalidate


Expires

Expires是HTTP/1.0出现的头信息,同样是用于决定本地缓存策略的头,它是一个绝对时间,时间格式是如Mon, 10 Jun 2015 21:31:12 GMT,只要发送请求时间是在Expires之前,那么本地缓存始终有效,否则就会去服务器发送请求获取新的资源。如果同时出现Cache-Control:max-age和Expires,那么max-age优先级更高。 Cache-Control与Expires可以在服务端配置同时启用,同时启用的时候Cache-Control优先级高

两者结合使用:

ini
复制代码

cache-control:max-age=691200
expires:Fri, 06 Mar 2022 22:53:02 GMT

缓存验证

用户点击刷新按钮时会开始缓存验证。如果缓存的响应头信息里含有"Cache-control: must-revalidate”的定义,在浏览的过程中也会触发缓存验证。另外,在浏览器偏好设置里设置Advanced->Cache为强制验证缓存也能达到相同的效果。

当缓存的文档过期后,需要进行缓存验证或者重新获取资源。只有在服务器返回强校验器或者弱校验器时才会进行验证。

协商缓存相关字段概念

ETags

  • Etag的优先级高于Last-Modified
  • 作为缓存的一种强校验器,ETag 响应头是一个对用户代理(User Agent, 下面简称UA)不透明(注:UA无需理解,只需要按规定使用即可)的值。
  • 对于像浏览器这样的HTTP UA,不知道ETag代表什么,不能预测它的值是多少。如果资源请求的响应头里含有ETag, 客户端可以在后续的请求的头中带上 If-None-Match 头来验证缓存。

Last-Modified

  • Last-Modified 响应头可以作为一种弱校验器。说它弱是因为它只能精确到一秒。如果响应头里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。
  • 其中包含源头服务器认定的资源做出修改的日期及时间。 它通常被用作一个验证器来判断接收到的或者存储的资源是否彼此一致。由于精确度比 ETag 要低,所以这是一个备用机制
  • 当向服务端发起缓存校验的请求时,服务端会返回 200 ok表示返回正常的结果或者 304 Not Modified(不返回body)表示浏览器可以使用本地缓存文件。304的响应头也可以同时更新缓存文档的过期时间。

HTTP 响应头决定了对于后续的请求头,如何判断是请求一个新的资源还是使用缓存的文件。

Vary

  • Vary是一个HTTP响应头部信息,它决定了对于未来的一个请求头,应该用一个缓存的回复(response)还是向源服务器请求一个新的回复。它被服务器用来表明在 content negotiation algorithm(内容协商算法)中选择一个资源代表的时候应该使用哪些头部信息(headers).
  • 在响应状态码为 304 Not Modified 的响应中,也要设置 Vary 首部,而且要与相应的 200 OK 响应设置得一模一样。
  • 当缓存服务器收到一个请求,只有当前的请求和原始(缓存)的请求头跟缓存的响应头里的Vary都匹配,才能使用缓存的响应。
  • 使用vary头有利于内容服务的动态多样性。例如,使用Vary: User-Agent头,缓存服务器需要通过UA判断是否使用缓存的页面。如果需要区分移动端和桌面端的展示内容,利用这种方式就能避免在不同的终端展示错误的布局。另外,它可以帮助 Google 或者其他搜索引擎更好地发现页面的移动版本,并且告诉搜索引擎没有引入Cloaking。

Vary: User-Agent 因为移动版和桌面的客户端的请求头中的User-Agent不同, 缓存服务器不会错误地把移动端的内容输出到桌面端到用户。


浏览器缓存的优缺点

缺点

  • 正如字面意思,缓存,就意味着使用缓存的时候,服务器的资源有可能不是最新版本,这就需要在使用的时候,控制缓存的资源和时间,包括常见的CDN手段。

优点

  • 使用浏览器客户端缓存能够减少请求的时间,提高用户体检,提高页面响应时间。

  • 在前端优化的手段当中,减少http请求的方式,也是其中一种,有效使用缓存,可以减少服务器的压力,提升网站的性能。