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

[歸納] 304 Status Code 下的头部信息 #29

Open
aleen42 opened this issue Mar 9, 2017 · 0 comments
Open

[歸納] 304 Status Code 下的头部信息 #29

aleen42 opened this issue Mar 9, 2017 · 0 comments

Comments

@aleen42
Copy link
Owner

aleen42 commented Mar 9, 2017

image

最近的两次前端开发面试中都感觉到公司开发对头部信息的一些要求,尤其是要求面试者对于 304 下的缓存机制需要有一定的了解。因此,在此不才先开一个 Issue 来初略归纳总结一下 304 Status Code 下到底涉及到哪些头部信息,以及固中的原理。然而,若想真正了解背后的道理,我认为应该通篇去阅读 MDN 下的定义文档。

在谈及这些头部信息之前,我们需要了解 304 Status Code 到底是意味着什么?其实,根据不才与劣者的一些学习,可以得知 3xx (以数字3开头的 Status Code 一般意指该请求需要重定向,如301、302等)。那么 304 所具体表达的重定向指的是什么呢?在我看来,所重定向的是获取资源的目标地点由服务器转换到浏览器,换而言之,就是从本来在服务器获取资源转到在本地获取缓存。

那么,HTTP 缓存机制所涉及的头部信息总共有四个组成部分:

首先,若服务器希望资源能被服务器缓存时,可以在响应头部添加相应的 Last-ModifiedEtag 头部。当浏览器收到该两个头部信息时,在下次请求同样的资源时则会在请求头添加有相应的 If-Modified-SinceIf-None-Match 头部。而后,服务器则会根据所请求的两个头部信息判断资源近期是否产生更新,若是,则返回新的资源;若否,则返回 304 Status Code 告诉浏览器从本地获取所缓存的资源。

1. If-Modified-Since (浏览器) / Last-Modified (服务器)

如上所述,服务器会通过 Last-Modified 头部告诉浏览器该资源的最后修改时间。当浏览器对同样的资源作请求时,则会以该时间作为 If-Modified-Since 的值传递给服务器,而后让服务器来判断是否返回 304。然而,这种方式中所采用的时间值只能精确到秒,因此服务器是无法通过这样的值来区分出秒级别内的资源改动。所以,If-None-Match / Etag 的这组头会采用一特征值作为判断标准。

2. If-None-Match(浏览器)/ Etag(服务器)

该特征值在我看来,服务器与浏览器可以约定以资源的 hash 值作为特征值来判断资源是否最新。

3. 带有 304 Status Code 的响应头

服务器在返回 304 响应头时,还可以通过另外的两个头部信息来告知浏览器该缓存资源的有效期:

  • Expires:Expires 会跟 If-Modified-Since / Last-Modified 一样置有时间值,用于表示在该时间段前的资源都有效(即有效期)。然而,需要注意的是服务器与浏览器两者间的时间设置可能不同,从而导致差错(没有对上“表”)。
  • Cache-Control:Cache-Control 一般值会是 max-age=3600no-cache 等,用于表示有效期的一些判断标准,如 max-age=3600 则表示3600秒内缓存依然有效。

Expires 由 HTTP/1.0 所支持,而 Cache-Control 则是在 HTTP/1.1 下支持,因此,为了防止不对表的现象,建议最好是同时设置这两种头部信息(浏览器默认情况下会优先采用 Cache-Control 作为判断标准)。

4. 所需要注意的细节

  1. Last-Modified 和 Expires 之间的一些区别

至此,我们知道若响应头设置了 Expires 头部信息,那么浏览器在下次请求同样的资源时理应就不会有一次额外的 HTTP 请求。若是这样,为何还保留有 Last-Modified 呢?理论上,的确是会少一次 HTTP 请求,然而在低版本的 IE 或 Firefox 浏览器中,若 Refresh 页面,对于设有 Expires 头部的 URI,浏览器同样还是会对服务器作一次 HTTP 请求。形象点来说的话,Expires 似乎给浏览器提供了一次自主检查缓存的机会。

  1. If-None-Match / Etag 较为有意思的一点:索引存储

这组头其实有一点意思,因为采用有特征值策略来进行资源的修动判断,所以会有一种资源部署策略的意思。浏览器在响应头检查到有 Etag 头部信息时,理应可以建立一个索引表(Hash)来存放该 Etag 值。倘若资源产生变动时,浏览器则在另一个索引上存放有新的 Etag,而非直接取代。这样的好处在于,若服务器资源产生版本回退时,仍然能根据此次索引表来快速判断是否返回 304 Status Code 的响应。(当然,代价当然会是需要占用一定的存储空间)

  1. If-None-Match / Etag 所带来的鸡肋

虽然从上面一点可以看出,这组头所采用的索引存储方式的确能在性能上优化判断,但是它却存在有一个令人诟病的鸡肋。在谈及该问题之前,我们首先需要意识到一点的是 Etag 的计算在 Apache、Nginx 或 IIS 上的计算会截然不同,因而很难保证多台服务器能计算出一样的 Etag。换句话所,同样的资源在不同服务器上可能会计算出不同的 Etag 值,这也就意味着若有多服务器做负载均衡时,浏览器请求资源的方式就不尽人意了。也许你好想,If-Modified-Since / Last-Modified 可以解决此类问题。然而,根据 RFC2616 的规定,若设有 If-None-Match / Etag 的情况下,优先考虑该头部信息而忽略 If-Modified-Since / Last-Modified。

对于此类弊端,若是能有统一 Etag 计算算法且好,但若觉得费劲可以考虑像 Yahoo 一样直接取消 Etag 的计算。(这细节好像跟前端不大有关系,逃)

@aleen42 aleen42 self-assigned this Mar 9, 2017
@aleen42 aleen42 changed the title [归纳] 304 Status Code 下的头部信息 [歸納] 304 Status Code 下的头部信息 Mar 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant