Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTP之缓存 #8

Open
yijinc opened this issue Aug 21, 2019 · 0 comments
Open

HTTP之缓存 #8

yijinc opened this issue Aug 21, 2019 · 0 comments
Labels
blog blogs and articles

Comments

@yijinc
Copy link
Owner

yijinc commented Aug 21, 2019

缓存已获取的资源能够有效的提升网站与应用的性能,Web 缓存能够减少延迟与网络阻塞,进而减少显示某个资源所用的时间。借助 HTTP 缓存,Web 站点变得更具有响应性。

缓存必须是由服务端设置响应头才会生效,一般客户端的请求头中只有设置了cache-control为:no-store | no-cache | max-age=0才会生效(也就是客户端不想走强缓存的时候生效)


我们先来看下与缓存相关的HTTP首部字段

请求首部字段

字段名称 说明
Cache-Control 控制缓存的行为
Pragma HTTP/1.0遗留物,Pragma: no-cache等同于Cache-Control: no-cache
If-Match 比较ETag是否一致(如果匹配中)
If-None-Match 比较ETag是否不一致(如果未匹配中)
If-Modified-Since 比较Last-Modified时间是否一致
If-Unmodified-Since 比较Last-Modified时间是否一致

响应首部字段

字段名称 说明
Cache-Control 控制缓存的行为
Pragma HTTP/1.0遗留物,Pragma: no-cache等同于Cache-Control: no-cache
Expires 过期时间
Last-Modified 资源最后一次修改时间
ETag 资源的特定版本的标识符
Vary 决定下一个请求头,应该用一个缓存的回复还是向源服务器请求一个新的回复
Pragma

Pragma是一个在 HTTP/1.0 中规定的通用首部,这个首部的效果依赖于不同的实现,所以在“请求-响应”链中可能会有不同的效果。当该字段值为no-cache的时候,与 Cache-Control: no-cache 效果一致,表示禁用缓存。

Expires

Expires的值对应一个GMT(格林尼治时间)来告诉浏览器资源缓存过期时间,如果还没到该时间点则不发请求。

Expires所定义的缓存时间是相对服务器上的时间而言的,要求客户端和服务器端的时钟严格同步。客户端的时间是可以修改的,如果服务器和客户端的时间不统一,这就导致有可能出现缓存提前失效的情况,存在不稳定性。 面对这种情况,HTTP1.1引入了 Cache-Control 头来克服 Expires 头的限制。

如果在Cache-Control响应头设置了 "max-age" 或者 "s-max-age",那么 Expires 头会被忽略。


Pragma 和 Expires 现在主要用来用来向后兼容只支持 HTTP/1.0 协议的缓存服务器。

Cache-Control

Cache-Control 是一个通用首部字段,被用于在http请求和响应中。缓存指令是单向的,这意味着在请求中设置的指令,不一定被包含在响应中

客户端可以在HTTP请求中使用的标准 Cache-Control 指令

Cache-Control 属性值 说明
no-cache 告知服务器不直接使用缓存,需要验证
no-store 禁止缓存,资源不会保存到缓存/临时文件
no-transform 表明客户端希望获取实体数据没有被转换
max-age=[seconds] 表明客户端愿意接收一个带缓存时间的资源
max-stale[=seconds] 表明客户端愿意接收一个已经过期的资源。可以设置一个可选的秒数,表示响应不能已经过时超过该给定的时间
min-fresh=[seconds] 表明客户端希望接收一个能在seconds秒内被更新过的资源
only-if-cached 表明客户端只接受已缓存的响应,并且不向原服务器检查是否有更新

服务器可以在响应中使用的标准 Cache-Control 指令

Cache-Control 属性值 说明
no-cache 缓存但重新验证(要求协商缓存验证)
no-store 不使用任何缓存,资源不应该存储到客户端缓存/临时文件
no-transform 不能对资源进行转换或转变。HTTP头/实体 不能由代理修改
public 表明响应可以被任何对象(包括:发送请求的客户端,代理服务器等)缓存,即使是通常不可缓存的内容(例如,该响应没有max-age指令或Expires消息头)
private 表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)
must-revalidate 一旦资源过期(比如已经超过max-age),必须向原服务器验证请求
proxy-revalidate 与must-revalidate类似,但仅适用于共享缓存(如代理)
max-age=[seconds] 设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)
s-maxage=[seconds] 覆盖max-age,但仅限于共享缓存(如代理)

Cache-Control 允许自由组合可选值,例如:

Cache-Control: public, max-age=3600, must-revalidate

强缓存

客户端不会向服务器发送任何请求,直接从本地缓存中读取文件并返回 Status Code: 200 OK。 服务器返回 ExpiresCache-Control: max-age=31536000 时会生效

优先顺序 from memory cachefrom disk cache,最后是请求网络资源

缓存验证(协商缓存)

当缓存的文档过期后,需要进行缓存验证或者重新获取资源。只有在服务器返回 Last-ModifiedETag 时才会进行验证。
如果缓存的响应头信息里 Cache-control 含有 must-revalidateno-cache 值,也会发起缓存验证

1. Last-Modified

Last-Modified 响应头可以作为一种弱校验器,其值为资源最后更改的时间。
Last-Modified 称为弱校验器 因为它不够精准:

  • 只能精确到一秒,在1秒内资源改变,因为时间比较相同,而不会去更新最新的资源
  • 一个资源被修改了,但其实际内容根本没发生改变,会因为 Last-Modified 时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)
Last-Modified: Tue Aug 20 2019 15:47:05 GMT

如果响应头里含有Last-Modified,客户端可以在后续的请求首部带上 If-Modified-Since 或者 If-Unmodified-Since 验证Last-Modified

If-Modified-Since:若客户端传递的时间值与服务器上该资源最后修改时间是一致(If-Modified-Since==Last-Modified),则说明该资源没有被修改过,直接返回304状态码,内容为空,这样就节省了传输数据量 。如果两个时间不一致,则服务器会发回该完整资源并返回200状态码,和第一次请求时类似。这样保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。一个304响应比一个静态资源通常小得多,这样就节省了网络带宽。

当前各浏览器均是使用 If-Modified-Since 请求首部来向服务器传递保存的 Last-Modified 值

If-Unmodified-Since:该值告诉服务器,若Last-Modified没有匹配上(资源在服务端的最后更新时间改变了),则应当返回412(Precondition Failed) 状态码给客户端

2. ETag

ETag可以解决上述 Last-Modified 可能存在的不准确的问题,因而称它为强校验器
ETag值为一个唯一标识符,服务器利用文件修改时间、文件大小和inode号等通过某种算法计算得出

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

如果响应头里含有ETag,客户端可以在后续的请求首部带上 If-Match 或者 If-None-Match 验证 ETag

If-None-Match:如果服务器发现 ETag 不匹配(If-None-Match != ETag),那么直接以常规GET 200回包形式将新的资源(当然也包括了新的ETag)发给客户端。若客户端传递的 etag 跟服务器的etag一致,则直接返回304知会客户端直接使用本地缓存即可;
当前各浏览器均是使用If-Match请求首部来向服务器传递保存的 ETag 值

If-Match:告诉服务器如果没有匹配到ETag,或者收到了“*”值而当前并没有该资源实体,则应当返回412(Precondition Failed) 状态码给客户端。否则服务器直接忽略该字段

需要注意的是,如果资源是走分布式服务器(比如CDN)存储的情况,需要这些服务器上计算ETag唯一值的算法保持一致,才不会导致明明同一个文件,在服务器A和服务器B上生成的ETag却不一样

@yijinc yijinc added the blog blogs and articles label Aug 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blog blogs and articles
Projects
None yet
Development

No branches or pull requests

1 participant