不要再问关于缓存的问题了
背景:
前端 er 需要关注的点,缓存
它在移动端上尤其严重,因为手机随时随地会缓存你的资源,要想清缓存,不像 PC 使用强制刷新,还要手动找到浏览器的缓存,有时候还要重启等
所以 用实践理解缓存机制 写下此文记录
为了对比理解本文会涉及到
- DNS 缓存
- CDN 缓存
- 浏览器缓存 (HTTP 缓存)
先梳理以下 web 缓存的优缺点
缓存的优势
- 减少网络延迟 加快页面打开速度
- 降低服务器压力
缓存的缺点
- 缓存没有清理机制(时间一长 当你不需要浏览之前的这些网页,他们就变成了无用文件)
- 给开发带来困扰
DNS 缓存
什么是 DNS?
全称 Domain Name System
域名解析系统
万维网上作为域名和 IP 地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的 IP 数串。DNS 协议运行在 UDP 协议之上,使用端口号 53。
DNS 解析
简单的说,通过域名,最终得到该域名对应的 IP 地址的过程叫做域名解析(或主机名解析)
1 | www.dnscache.com (域名) - DNS解析 -> 11.222.33.444 (IP地址) |
DNS 缓存
有 DNS 的地方,就有缓存。浏览器、操作系统、Local DNS、根域名服务器,它们都会对 DNS 结果做一定程度的缓存。
DNS 查询过程
- 首先搜索浏览器自身的 DNS 缓存,如果存在,则域名解析到此完成。
- 如果浏览器自身的缓存里面没有找到对应的条目,那么会尝试读取操作系统的 hosts 文件看是否存在对应的映射关系,如果存在,则域名解析到此完成。
- 如果本地 hosts 文件不存在映射关系,则查找本地 DNS 服务器 (ISP 服务器,或者自己手动设置的 DNS 服务器), 如果存在,域名到此解析完成。
- 如果本地 DNS 服务器还没找到的话,它就会向根服务器发出请求,进行递归查询。
CDN 缓存
什么是 CDN?
全称 Content Delivery Network, 即内容分发网络。
类似于火车站代售点 这样儿 乘客不用再去售票大厅去排队买票 减轻了售票大厅的压力(起到分流作用,减轻服务器负载压力)
用户在浏览网站的时候,CDN
会选择一个离用户最近的 CDN 边缘节点来响应用户的请求,这样海南移动用户的请求就不会千里迢迢跑到北京电信机房的服务器(假设源站部署在北京电信机房)上了。
CDN 缓存
CDN缓存
, 在浏览器本地缓存失效后,浏览器会向 CDN 边缘节点发起请求。类似浏览器缓存,CDN 边缘节点也存在着一套缓存机制。CDN 边缘节点缓存策略因服务商不同而不同,但一般都会遵循 http
标准协议,通过 http 响应头中的Cache-control: max-age //后面会提到
的字段来设置 CDN 边缘节点数据缓存时间。
CDN 边缘节点数据缓存机制
- 当浏览器向 CDN 节点请求数据时,CDN 节点会判断缓存数据是否过期,
- 未过期:直接将缓存数据返回给客户端;
- 过期:CDN 节点向服务器发出回源请求,拉取最新数据同时更新本地缓存,并将最新数据返回给客户端。
CDN 服务商一般会提供基于文件后缀、目录多个维度来指定 CDN 缓存时间,为用户提供更精细化的缓存管理。
CDN 优势
- CDN 节点解决了跨运营商和跨地域访问的问题,访问延时大大降低。
- 大部分请求在 CDN 边缘节点完成,CDN 起到了分流作用,减轻了源服务器的负载。
浏览器缓存(HTTP 缓存)
对于一个数据请求来说,可以分为发起网络请求 后端处理 浏览器响应三个步骤
浏览器缓存可以 i 帮助我们在第一步和第三步中优化性能
比如我们可以直接使用缓存而不发起请求
盯着这张图
什么是浏览器缓存?
浏览器缓存其实就是浏览器保存通过 HTTP 获取的所有资源,是浏览器将网络资源存储在本地的一种行为。
我们从两个方面来看浏览器缓存
- 缓存位置
- 缓存策略
缓存的资源去哪里了(缓存位置)?
你可能会有疑问,浏览器存储了资源,那它把资源存储在哪里呢?
- memory cache (存在内存)
1 | MemoryCache顾名思义,就是将资源缓存到内存中,等待下次访问时不需要重新下载资源,而直接从内存中获取。Webkit早已支持memoryCache。 |
- disk cache(存在磁盘)
1 | DiskCache顾名思义,就是将资源缓存到磁盘中,等待下次访问时不需要重新下载资源,而直接从磁盘中获取,它的直接操作对象为CurlCacheManager。 |
访问缓存优先级
- 先在内存中查找,如果有,直接加载。
- 如果内存中不存在,则在硬盘中查找,如果有直接加载。
- 如果硬盘中也没有,那么就进行网络请求。
- 请求获取的资源缓存到硬盘和内存。
浏览器缓存的分类(缓存策略)
强缓存
协商缓存
需要说明的是 浏览器会先判断是否命中强缓存
浏览器缓存的优点
- 减少了冗余的数据传输 节省了网费
- 减少了服务器的负担,大大提升了网站的性能
- 加快了客户端加载网页的速度
浏览器在第一次请求发生后,再次请求时:
- 验证是否命中强缓存,如果命中,就直接使用缓存了。
- 如果没有命中强缓存,就发请求到服务器检查是否命中协商缓存。
- 如果命中协商缓存,服务器会返回 304 告诉浏览器使用本地缓存。
- 否则,返回最新的资源。
强缓存
强缓存是利用 http 的返回头中的 Expires
或者 Cache-Control
两个字段来控制的,用来表示资源的缓存时间。
Expires:
该字段是 http1.0 时的规范,它的值为一个绝对时间的 GMT 格式的时间字符串,比如 Expires:Mon,18 Oct 2066 23:59:59 GMT。这个时间代表着这个资源的失效时间,在此时间之前,即命中缓存。这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当服务器与客户端时间偏差较大时,就会导致缓存混乱。
Cache-Control:
Cache-Control 是 http1.1 时出现的 header 信息,主要是利用该字段的 max-age 值来进行判断,它是一个相对时间,例如 Cache-Control:max-age=3600,代表着资源的有效期是 3600 秒。cache-control 除了该字段外,还有下面几个比较常用的设置值:
- no-cache:不使用本地缓存。需要使用缓存协商,先与服务器确认返回的响应是否被更改,如果之前的响应中存在 ETag,那么请求的时候会与服务端验证,如果资源未被更改,则可以避免重新下载。
- no-store:直接禁止游览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源。
- public:可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器。
- private:只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存。
Cache-Control 与 Expires 可以在服务端配置同时启用,同时启用的时候 Cache-Control 优先级高。
协商缓存
当强缓存没有命中的时候,浏览器会发送一个请求到服务器,服务器根据 header
中的部分信息来判断是否命中缓存。如果命中,则返回 304
,告诉浏览器资源未更新,可使用本地的缓存。
header: Last-Modify/If-Modify-Since
和 ETag/If-None-Match
.
Last-Modify/If-Modify-Since
浏览器第一次请求一个资源的时候,服务器返回的 header 中会加上 Last-Modify,Last-modify 是一个时间标识该资源的最后修改时间。
当浏览器再次请求该资源时,request 的请求头中会包含 If-Modify-Since,该值为缓存之前返回的 Last-Modify。服务器收到 If-Modify-Since 后,根据资源的最后修改时间判断是否命中缓存。
如果命中缓存,则返回 304,并且不会返回资源内容,并且不会返回 Last-Modify。
缺点:
- 短时间内资源发生了改变 ,
Last-Modified
并不会发生变化。 - 周期性变化 , 如果这个资源在一个周期内修改回原来的样子了,我们认为是可以使用缓存的,但是
Last-Modified
可不这样认为,因此便有了ETag
ETag/If-None-Match
与 Last-Modify/If-Modify-Since
不同的是,Etag/If-None-Match
返回的是一个校验码。ETag
可以保证每一个资源是唯一的,资源变化都会导致 ETag
变化。服务器根据浏览器上送的 If-None-Match
值来判断是否命中缓存。
与 Last-Modified
不一样的是,当服务器返回 304 Not Modified
的响应时,由于 ETag
重新生成过,response header
中还会把这个 ETag
返回,即使这个 ETag
跟之前的没有变化。
Last-Modified 与 ETag 是可以一起使用的,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。
实践检验
我讨厌文字记忆,所以毫不犹豫选了理工科~~talk is cheap , show me your code
⚠️ 实践过程控制台不要禁用缓存
利用 koa
启动 server
服务 port:8000
引入静态资源 加载前端模版 去内蒙的航拍 哈哈哈 有我
如图可见 初次访问 正常加载模版页面 cache
与图片资源 1.jpeg
实现强缓存
- 服务端设置响应头 Cache-Control 资源有效期为 300 秒
1 | app.use(async (ctx, next) => { |
- 刷新页面
响应头的 Cache-Control
变成了 max-age=300
验证访问缓存的优先级:
第一次的网络请求,浏览器把图片资源缓存到了磁盘和内存里,根据约定 应该会先从内存中找资源
- 再次刷新页面
确实是从内存获取的
4. 关掉页面再重新打开 (内存是存在进程中的,所以关闭该页面,内存中的资源也被释放掉了,磁盘中的资源是永久性的,所以还存在)
from disk cache 从磁盘中获取资源
5. 接下来 有效期 300 秒 后.
缓存失效 重新向服务器载入资源
实现协商缓存
协商缓存本地测试直接拦截 url 给定 code
Cache-Control 取默认值 no-cache
1 | app.use( async(ctx, next) => { |
服务器返回 304
同时 size
变小了很多 因为只返回了必要信息
也可利用 现成的插件帮我们计算文件的 ETagnpm install koa-tag -D npm install koa-conditional-get -D
就不演示了
加载资源发生变化(比如换了张图片)ETag
改变 会导致协商缓存策略失效
然后 就行了. 😊
如果你想亲自体验
day day up