转载请注明原始链接:http://blog.csdn.net/a464057216/article/details/52780055。本篇博客用Mac OS X Sierra上的Firefox 47.0.1版本做的实验,对于其他浏览器或版本可能会有不同。
后续此博客不再更新,欢迎大家搜索关注微信公众号“测开之美”,测试开发工程师技术修炼小站,持续学习持续进步。
缓存主要分为两类:服务端缓存、客户端缓存。
服务端缓存包括转发代理服务器缓存、反向代理服务器缓存、CDN缓存等。
客户端缓存一般指浏览器缓存,比如Chrome浏览器查看缓存可以访问chrome://cache
。
主要分为两种:通过HTML 标签和通过HTTP报文头。
用来告诉浏览器每次想要使用缓存前都需要到服务器验证缓存是否有更新。并不是所有浏览器都支持这个功能,缓存服务器也不会解析HTML文件的内容,所以一般不要使用这种方式。
HTTP 1.0中,服务器通过回应给客户端的HTTP Response的头部的expires
字段说明资源的过期时间,使用该字段容易受到服务器与客户端时区及时间差异的影响。比如在服务端使用Flask将响应的缓存有效期设置为当前时间的18小时以后:
# Written by - CSDN: Mars Loo的博客
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
@app.route('/', methods=["GET",])
def index():
r = make_response(render_template('index.html'))
exp = datetime.datetime.utcnow() + datetime.timedelta(hours=18)
r.headers['Expires'] = exp.strftime(GMT_FORMAT)
return r.make_conditional(request)
在Mac OS X Sierra上使用Firefox 47.0.1访问该地址:
在服务端修改index.html
文件的内容,然后在浏览器地址栏重新输入地址后访问,发现并没有加载最新的服务器端内容(状态码200前面灰色的圆圈表示加载的是浏览器缓存):
HTTP 1.1标准中设计了cache-control
字段用于替代expires
字段(如果两者同时存在,以cache-control
的设置为准),cache-control
可以实现比expires
更精细的缓存控制比如:
如果之前的响应包含ETag
字段,可能会使用If-Match
字段或If-None-Match
字段验证;如果之前的响应包含Last-Modified
字段,可能会使用If-Modified-Since
字段或If-Unmodified-Since
字段验证。比如服务端设置了no-cache
、30秒的max-age
和ETag
字段:
# Written by - CSDN: Mars Loo的博客
@app.route('/', methods=["GET",])
def index():
r = make_response(render_template('index.html'))
r.add_etag()
r.cache_control.max_age = 30
r.cache_control.no_cache = True
return r.make_conditional(request)
使用FF访问该页面:
30秒之内在地址栏中输入地址敲回车后,浏览器向服务器发送请求重验证,如果资源没有发生过修改,返回304 Not Modified,浏览器从缓存加载页面:
must-revalidate
将这一过程具体化了)。验证方法同上。比如服务端设置了Last-Modified
、max-age
和must-revalidate
属性:# Written by - CSDN: Mars Loo的博客
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
@app.route('/', methods=["GET",])
def index():
r = make_response(render_template('index.html'))
r.last_modified = datetime.datetime.utcnow()
r.cache_control.max_age = 30
r.cache_control.must_revalidate = True
return r.make_conditional(request)
FF浏览器第一次访问:
30秒之内在地址栏中输入地址敲回车后,浏览器会读取缓存:
30秒之后在地址栏中输入地址敲回车后,浏览器会去服务端验证缓存是否过期。从上面服务端的代码看,意味着下次认证会返回资源已经发生了修改,响应如下:
将服务端资源改成Last-Modified
没有发生更改的情形:
# Written by - CSDN: Mars Loo的博客
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
lm = datetime.datetime.utcnow()
@app.route('/', methods=["GET",])
def index():
r = make_response(render_template('index.html'))
r.last_modified = lm
r.cache_control.max_age = 30
r.cache_control.must_revalidate = True
return r.make_conditional(request)
在30秒缓存过期后请求同样的地址,响应为304 Not Modified
,客户端浏览器加载缓存:
比如服务端设置了no-store
:
# Written by - CSDN: Mars Loo的博客
@app.route('/', methods=["GET",])
def index():
r = make_response(render_template('index.html'))
r.add_etag()
r.cache_control.no_store = True
return r.make_conditional(request)
使用FF浏览器访问该页面:
第二次在地址栏中输入地址敲回车后,浏览器还是会向服务器发送请求:
ETag
可以理解为资源的摘要签名信息,如果资源未发生改变,该签名信息是不会改变的。客户端第一次请求服务端的某静态文件时,服务端可以在ETag
字段回应资源的签名信息。接下来的请求中,如果客户端判断缓存已经过期(超过了max-age
),会在If-None-Match
字段填充缓存的ETag
信息询问服务端资源是否发生过变动,如果发生过变化,服务端回应200的正常响应,如果未发生过变化,返回304 Not Modified响应,浏览器加载缓存,304响应不包含Response的body部分,提高了传输效率。
Last-Modified
是客户端第一次请求服务端的某静态文件时,服务端填充的资源最后修改时间。接下来的请求中,如果客户端判断缓存已经过期(超过了max-age
),会在If-Modified-Since
字段填充缓存的Last-Modified
信息询问服务端资源是否发生过变动,如果发生过变化,服务端回应200的正常响应,如果未发生过变化,返回304 Not Modified响应。
使用Last-Modified
字段可能有如下缺点:资源内容未发生变化,但是最后修改时间变了,造成带宽浪费;只能精确到秒级,如果资源在1秒内被多次修改,不能准确体现资源变化,造成客户端获取的数据不准确。ETag
可以精确表示资源是否发生变化,当两个字段同时存在时,以ETag
为准。
比如Mac上FF浏览器有Command
+r
普通刷新和Shift
+Command
+r
强制刷新。普通刷新相当于浏览器在发往Server端的请求头中添加字段Cache-Control:max-age=0
,表示从客户端到服务端的沿途服务器都需要重新校验缓存:
强制刷新相当于浏览器在发往服务器的请求头中添加Pragama: no-cache
和Cache-Control: no-cache
头,表示客户端到服务端的节点无需重新验证缓存是否过期而是强制要求服务端返回一个非缓存的版本(即使资源未发生变化,也不返回304 Not Modified
,而是返回200的正常响应):
如果觉得我的文章对您有帮助,欢迎关注我(CSDN:Mars Loo的博客)或者为这篇文章点赞,谢谢!
如下几篇文章是我在学习HTTP协议时总结的博文,欢迎参考: