缓存
什么是缓存
浏览器访问某个网站,第一次访问的时候电脑会把相关素材下载到电脑,
再次访问的时候,就会去从电脑直接加载出来。
好处
- 缓存服务器压力
- 提升性能
- 减少宽带消耗
Web缓存种类
- 数据库缓存
- CDN缓存
- 代理服务器缓存
- 浏览器缓存
强缓存
强缓存的设置
- 第一次请求
判断响应头,是否含有cache-control
,program
,expires
字段,代表强缓存,浏览器会将文件缓存到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生效。
第一次请求
第二次请求
缓存方案
目前的项目大多使用这种缓存方案的:
- HTML: 协商缓存;
- css、js、图片:强缓存,文件名带上hash。
区别
- 强缓存不发请求到服务器,所以有时候资源更新了浏览器还不知道,
但是协商缓存会发请求到服务器,所以资源是否更新,服务器肯定知道。
强缓存expire是通过本地来判断的,所以会有误差,
通过cache-control的max-age使用相对时间来控制,但都不经过服务器,浏览器直接返回缓存内容,
协商缓存是通过Etag
和if-no-match
以及Last-Modified
以及 If-Modified-Since
来判断的,
所以会经过服务器,资源更新了服务器是知道的。
- 大部分web服务器都默认开启协商缓存。
刷新对于强缓存和协商缓存的影响
- 当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存。
2. 当f5刷新网页时,跳过强缓存,但是会检查协商缓存。
3. 浏览器地址栏中写入URL,回车 浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。(最快)禁用浏览器缓存
禁用浏览器缓存:Cache-Control、pragma、expires
response.setHeader("Cache-Control", "no-cache");
response.setHeader("pragma", "no-cache");
response.setDateHeader("expires", -1);
response.getWriter().print("hello hello!");
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
实战
Vue中使用CDN,
webpack-cdn-plugin
配合html-webpack-plugin
的使用
总结
使用webpack
打包的项目可以使用强缓存的方式,因为可以通过hash文件名去保证每次文件的唯一性,
如果是jQuery
,axios
,vue
这些第三方的核心库可以使用协商缓存的方式去加载,
以保证可以更新到最新的代码。
范围请求
https://juejin.cn/post/6844903642034765837
今天再来介绍一下 HTTP 的范围请求。范围请求主要是针对较大的文件的请求或者上传,可以仅操作它的某一段。
是否支持范围请求
HTTP 本身是一种无状态的“松散”协议,而在经历了很多版本的迭代之后,只在 HTTP/1.1(RFC2616) 之上,才支持范围请求。所以如果客户端或者服务端两端的某一端低于 HTTP/1.1,我们就不应该使用范围请求的功能。
而在 HTTP/1.1 中,很明确的声明了一个响应头部 Access-Ranges 来标记是否支持范围请求,它只有一个可选参数 bytes。
使用范围请求
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 两个参数任意一个,原样填入即可。
使用场景
- 断线续传
- 多线程下载
范围请求小结
HTTP
范围请求,需要HTTP/1.1
及之上支持,如果双端某一段低于此版本,则认为不支持。- 通过响应头中的
Accept-Ranges
来确定是否支持范围请求。 - 通过在请求头中添加
Range
这个请求头,来指定请求的内容实体的字节范围。 - 在响应头中,通过
Content-Range
来标识当前返回的内容实体范围,并使用Content-Length
来标识当前返回的内容实体范围长度。 - 在请求过程中,可以通过
If-Range
来区分资源文件是否变动,它的值来自ETag
或者Last-Modifled
。如果资源文件有改动,会重新走下载流程。