HTTP 中的缓存机制
前言
最近在思考如何优化 web 应用的响应速度,常见的方式就是使用缓存,而最常接触的技术其实是服务端做共享缓存,例如 redis 缓存热点数据、CDN 缓存静态资源。在客户端做缓存这一块,知识面还比较窄,经过资料查询和思考,总结一些心得
缓存的目的
缓存是一种保存资源副本并在下次请求时直接使用该副本的技术
在不考虑成本的情况下,不使用缓存其实是体验最好方式,可以通过无限堆服务器来达到快速响应的目的,并且不会有一致性等问题。
所以缓存的目的很简单,就是缓解服务器压力,来达到在有限的成本情况下相对最优质的用户体验。
私有缓存和共享缓存
缓存的种类大致可分为:
- 共享缓存:可被多个用户重复使用,例如 redis 缓存热点数据,CDN 缓存静态资源
- 私有缓存:只能被单个用户使用,例如浏览器缓存
缓存控制
HTTP header: Cache-control
在 HTTP/1.1
中引入 Cache-control
头用来定义浏览器应该如何进行缓存决策
Cache-control
的值有:
no-store
:客户端不得存储任何关于客户端请求和服务端响应的内容。每次由客户端发起的请求都会下载完整的响应内容no-cache
:客户端会缓存响应内容,但每次请求都会携带 验证字段 到服务器,验证缓存是否过期,未过期才使用本地已缓存内容public
:响应内容可以被中间人如 CDN、网关等缓存private
:响应内容只能被客户端缓存max-age
:缓存过期的相对时间(秒),超过该时间后会再次发起请求must-revalidate
:本地缓存未过期时使用本地缓存,本地缓存过期时必须携带 验证字段 到服务器,验证缓存是否过期
HTTP header: Expries
Expries
是 HTTP/1.0
中的内容,作用是指定缓存过期的绝对时间,例如 Expires: Wed, 21 Oct 2015 07:28:00 GMT
和 Cache-control: max-age
的区别:
Expries
是绝对时间,Cache-control
是相对时间- 在
HTTP/1.0
中Expries
优先级大于Cache-control
,在HTTP/1.1
及以后Cache-control
优先级大于Expries
缓存决策
强制缓存
由 Cache-control: max-age
或 Expires
指定的缓存,在时间过期之前,浏览器不会再次请求服务器
协商缓存
当缓存失效时,会发出 条件式请求,这时候和服务器的通信就称为协商缓存
协商缓存的优点:
- 省去了服务器生成 html 的时间
- 省去了响应体内容,传输更快
Last-Modified
请求响应流程:
- 当浏览器第一次请求服务器时,服务器会返回
Last-Modified
头部表明当前资源的最后修改日期 - 浏览器会将这个日期值缓存在本地
- 当需要发出
条件请求
时,将日期值放在If-Modified-Since
或If-Unmodified-Since
头部中向服务器发出 - 服务器判断日期,决定响应内容
- 如果该资源新鲜,则返回
304
,客户端使用该缓存渲染内容 - 如果该资源被更改过,则返回
200
,及全新内容
Etag
和 Last-Modified
的请求响应流程类似,不同的是服务器响应的是 ETag
头,浏览器发出请求时的头部为 If-Match
和 If-None-Match
- 强验证:HTTP 协议默认使用强验证,强验证要求文档的每一个字节都必须相同
ETag: "618bbc92e2d35ea1945008b42799b0e7"
- 弱验证:
ETag
头以W/
开头时为弱验证,此时只要求文档的内容含义是相同的。ETag: W/"618bbc92e2d35ea1945008b42799b0e7"
因为存在当资源时间更改,但是内容未改变等情况,Last-Modified
对资源的判断没有 ETag
准确,所以服务端处理的时候通常优先判断 ETag
决策图
刷新页面对 http 缓存的影响
三种刷新操作:
- 正常操作:地址栏输入 url,跳转链接,前进后退等
- 手动刷新:F5(mac 为 Cmd + R),点击刷新按钮,点击菜单刷新
- 强制刷新:Ctrl + F5(mac 为 Cmd + Shift + R)
不同刷新操作,不同的缓存策略:
- 正常操作:强制缓存有效,协商缓存有效
- 手动刷新:强制缓存失效,协商缓存有效
- 强制刷新:强制缓存失效,协商缓存失效