性能优化-http缓存

以前对http的缓存知识只知其一;不知其二,只能说出个大概。和同事交流这块内容时稍一深刻探讨就捉襟见肘,自惭形秽。故痛定思痛,花了一两天时间去研究了下这块内容,写下这篇笔记方便之后的查询与修正。php

首先介绍下和缓存相关的http头字段参数。

http的头字段包括通用头字段(general-header)、请求头字段(request-header)、响应头字段(response-header)以及实体头字段(entity-header)。html

我我的对实体头字段这个说法有点陌生,因此查阅了相关资料:根据RFC-2616(7.1 Entity Header Fields)中的解释是:实体头字段(entity header)定义了实体主体(entity-body)的元信息,若是实体主体(entity-body)不存在,则表示http请求所标识的资源的元信息。web

Entity-header fields define metainformation about the entity-body or,
if no body is present, about the resource identified by the request.算法

general header


Cache-Control: 整个请求响应链的缓存机制必须遵循的特别指令。这个指令是单向的,由于请求中该指令的效果和响应中的效果并不同。其值包括max-age,no-cache等。segmentfault

The Cache-Control general-header field is used to specify directives that MUST be obeyed by all caching mechanisms along the request/response chain. 浏览器

Cache directives are unidirectional in that the presence of a directive in a request does not imply that the same directive is to be given in the response.缓存

Date: 请求或响应的时间和日期。(注:这里的日期格式使用的是GMT格式,即格林威治时间,要换算成北京时间的话要加上8小时)性能优化

Pragma:可被应用到整个请求响应链中的自定义设置,例如:Pragma: no-cache。服务器

The Pragma general-header field is used to include implementation-specific directives that might apply to any recipient along the request/response chain.网络

request header


If-Modified-Since: 若是从该参数指定的时间开始,请求并无被修改的话,服务器会返回304(not modified)状态码,而不是一个实体。

if the requested variant has not been modified since the time specified in this field, an entity will not be returned from the server; instead, a 304 (not modified) response will be returned without any message-body.

If-Unmodified-Since: 若是从该参数指定的时间开始,请求并无被修改的话,服务器应该执行请求的操做。若是被修改过的话,服务器不执行操做病返回412(Precondition Failed)状态码

If the requested resource has not been modified since the time specified in this field, the server SHOULD perform the requested operation as if the If-Unmodified-Since header were not present.

If the requested variant has been modified since the specified time, the server MUST NOT perform the requested operation, and MUST return a 412 (Precondition Failed).

If-None-Match: 容许在对应的内容未被修改的状况下返回304未修改( 304 Not Modified ),判断依据为客户端该参数存储的Etag值与服务器上存储的Etag是否相匹配。(原文太长,暂时拿wiki百科来填坑)

If-Match: 仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操做。主要用做像 PUT 这样的方法中,仅当从用户上次更新某个资源以来,该资源未被修改的状况下,才更新该资源。若是不匹配,则返回412(Precondition Failed)状态码,该参数也会被忽略。(原文太长,暂时拿wiki百科来填坑)

response header


ETag: 指定请求变量的当前实体Tag。该字段用来比较来自同一资源的变化的实体。(只是直译,后面会有详细解释和用法)

The ETag response-header field provides the current value of the entity tag for the requested variant.

Entity Header


Expires: 指定过时时间。

The Expires entity-header field gives the date/time after which the response is considered stale.

Last-Modified: 指定了服务器认为资源最近一次修改的时间。

The Last-Modified entity-header field indicates the date and time at which the origin server believes the variant was last modified.

字段参数可能会有翻译或者理解错误,请各位大大指出。

了解了参数的含义后,接下来就是看具体的缓存机制了。

HTTP 1.0时代 (基于Pragma&Expires的缓存实现)

Pragma在RFC1945文档中只提供了“no-cache”值,当"no-cache"出如今请求消息中时,应用程序应当向原始服务器推送此请求,即便它已经在上次请求时已经缓存了一份拷贝。这样将保证客户端能接收到最权威的回应。它也用来在客户端发现其缓存中拷贝不可用或过时时,对拷贝进行强制刷新。
(HTTP1.1会把请求中的pragma="no-cache"视为发送了Cache-Control: no-cache)

有Pragma用来禁用缓存的话,那就有Expires来设置缓存。
就如上文介绍所说,Expires是用来指定过时时间的,时间格式为GMT,若是客户端时间没有超过该时间点的话,则不发送请求。可是值得注意的是,Expires所定义的缓存时间是相对服务器上的时间而言的,若是客户端的时间和服务器的时间不一致的话(例如用户修改了系统时间),那么这个资源失效时间也没有意义了。

HTTP 1.1时代

http1.1新增了 Cache-Control 来定义缓存过时时间,若报文中同时出现了Expires 和 Cache-Control,会以 Cache-Control 为准。
如上文介绍所说,Cache-Control是通用头部参数,格式为

"Cache-Control" ":" cache-directive

做为请求头部时,cache-directive的可选值为:(图片来自网络)

做为响应头部时,cache-directive的可选值为:(图片来自网络)

cache-control设置缓存的使用方法为:Cache-Control:max-age=31536000,缓存单位为秒。

若是客户端请求的资源未超过缓存时间,则直接取本地缓存,状态码为200(from memory cache),
可是若是超过缓存时间或者直接不走缓存的话,那咱们就须要和其余的字段配合来校验缓存了。

1. Last-Modified

服务端传递资源给客户端的时候会在响应报文中带上这个信息,而客户端接受到资源后会将该信息的值设置为请求头中If-Modified-Since参数的值(If-Modified-Since: Last-Modified value)。在下次发送请求的时候(max-age过时以后),会将请求头中的If-Modified-Since参数的值与服务端Last-Modified的值去比较,若是还是一致的,那么说明该资源未被修改,直接返回304状态码便可,若是不一致,则返回状态码200,以及新的Last-Modified。

可是Last-Modified会有不许确的时候,好比服务端的资源作了无实际变化的修改(加一个空格再删掉),这样也会使服务端Last-Modified的时间更新,致使客户端请求中If-Modified-Since的值与其没法匹配,就会返回整个实体。(即便返回的实体内容与客户端的缓存内容没有区别)

2. Etag

Etag是一种比Last-Modified更为精确的校验方式。服务器会经过某种算法,给资源计算得出一个惟一标志符。其判断依据和Last-Modified相似,只是服务端在传递资源给客户端的相应报文中带上Etag后,客户端会将其设置为请求头中If-None-Match参数的值(If-None-Match: Etag-value),而后在传递资源时进行比较。

若是同时使用Last-Modified和Etag进行验证的话,那么二者中有一个经过校验(即传递的值能够匹配),则能够认为资源并无被修改。

基本的缓存机制就是由这些参数造成的。

最后咱们来看下,不一样的页面打开方式对缓存机制的影响

主要有如下两点要注意:

1.手动刷新页面(F5刷新),浏览器会直接认为缓存已通过期,即便缓存并无过时,在请求中加上字段:Cache-Control:max-age=0,发包向服务器查询是否有文件是否有更新。

2.强制刷新页面(ctrl+F5刷新),浏览器会直接忽略本地缓存内容,即便本地有缓存可用,在请求中加上字段:Cache-Control:no-cache(或 Pragma:no-cache),发包向服务器从新请求文件。
其余的直接上图(图片来自网络):

此篇文章的不少想法是借鉴网上高手的文章,我会在底部标明参考出处。若是此文章中有什么问题的话,烦请必定要指出,谢谢!

参考资料
RFC2616
H5 缓存机制浅析 移动端 Web 加载性能优化
浅谈浏览器http的缓存机制

相关文章
相关标签/搜索