HTTP 缓存

·  阅读 483

前言

HTTP 缓存是指一个 HTTP 资源(如html页面,图片,js,数据等)存在于 HTTP 服务器和客户端(浏览器)之间的副本。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,如果是相同的 URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。

使用 HTTP 缓存的作用其实是非常显而易见的:

  1. 减少网络带宽消耗,当 HTTP 缓存副本被使用时,只会产生极小的网络流量,可以有效的降低运营成本;
  2. 降低服务器压力,给网络资源设定有效期之后,用户可以重复使用本地的缓存,减少对源服务器的请求,间接降低服务器的压力;
  3. 减少网络延迟,加快页面打开速度,对于最终用户,缓存的使用能够明显加快页面打开速度,达到更好的体验。

浏览器缓存分类

浏览器缓存分为强缓存和协商缓存,浏览器加载一个页面的简单流程如下:

  1. http 缓存都是从第二次请求开始的。第一次请求资源时,服务器返回资源,并在 respone header 头中回传资源的缓存参数;
  2. 第二次请求时,浏览器判断这些请求参数,命中强缓存就直接200。服务器并不会返回资源信息,浏览器继续从缓存加载资源;
  3. 否则就把请求参数加到 request header 头中传给服务器,看是否命中协商缓存,命中则返回304,否则服务器会返回新的资源。

强缓存

命中强缓存时,浏览器并不会将请求发送给服务器。在Chrome的开发者工具中看到http的返回码是200,但是在Size列会显示为(from cache)。

强缓存是利用http的返回头中的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间。

Expires

缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说,Expires=max-age + 请求时间,需要和Last-modified结合使用。但在上面我们提到过,cache-control的优先级更高。 Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。

该字段会返回一个时间,比如 Expires: Mon, 22 Feb 2021 15:02:11 GMT,这个时间代表着这个资源的失效时间,也就是说在2021年2月22日15点02分11秒之前都是有效的,即命中缓存。这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当客户端本地时间被修改以后,服务器与客户端时间偏差变大以后,就会导致缓存混乱。于是发展出了Cache-Control。

Cache-Control

Cache-Control 是一个相对时间,例如Cache-Control:3600,代表着资源的有效期是3600秒。由于是相对时间,并且都是与客户端时间比较,所以服务器与客户端时间偏差也不会导致问题。 Cache-Control与Expires可以在服务端配置同时启用或者启用任意一个,同时启用的时候Cache-Control优先级高。

Cache-Control 可以由多个字段组合而成,主要有以下几个取值:

  1. max-age:指定一个时间长度,在这个时间段内缓存是有效的,单位是s。例如设置 Cache-Control:max-age=600,也就是说缓存有效期为600s。在没有禁用缓存并且没有超过有效时间的情况下,再次访问这个资源就命中了缓存,不会向服务器请求资源而是直接从浏览器缓存中取;
  2. no-cache:可以在本地缓存,可以在代理服务器缓存,但是这个缓存要服务器验证才可以使用;
  3. no-store:彻底得禁用缓冲,本地和代理服务器都不缓冲,每次都从服务器获取。

协商缓存

若未命中强缓存,则浏览器会将请求发送至服务器。服务器根据http头信息中的Last-Modify/If-Modify-Since或Etag/If-None-Match来判断是否命中协商缓存。如果命中,则http返回码为304,浏览器从缓存中加载资源。

Last-Modify/If-Modify-Since

浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间,例如 Mon, 22 Feb 2021 13:33:55 GMT。

当浏览器再次请求该资源时,发送的请求头中会包含If-Modify-Since,该值为缓存之前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。

如果命中缓存,则返回http304,并且不会返回资源内容,并且不会返回Last-Modify。由于对比的服务端时间,所以客户端与服务端时间差距不会导致问题。但是有时候通过最后修改时间来判断资源是否修改还是不太准确(资源变化了最后修改时间也可以一致),因为根据时间判断只能精确到秒级,不能精确到毫秒级。于是出现了ETag/If-None-Match。

ETag/If-None-Match

与Last-Modify/If-Modify-Since不同的是,Etag/If-None-Match返回的是一个校验码(ETag: entity tag)。ETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化*。ETag值的变更则说明资源状态已经被修改。服务器根据浏览器上发送的If-None-Match值来判断是否命中缓存。

  1. Last-Modified 标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间;
  2. 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET。

Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。

怎么设置强缓存与协商缓存

nodejs设置:

res.setHeader(‘max-age’: ‘3600 public’)
res.setHeader(etag: ‘5c20abbd-e2e8’)
res.setHeader(‘last-modified’: Mon, 24 Dec 2018 09:49:49 GMT)

nginx设置:

虽然可以自己设置协商缓存,但是大部分web服务器都默认开启协商缓存,而且是同时启用Last-Modified,If-Modified-Since 和 ETag、If-None-Match。但是我们可以自己设置强缓存,确保2文件缓存时间。

总结

第一次请求:

第二次请求:

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改