53.HTTP缓存详解

转载请注明原始链接:http://blog.csdn.net/a464057216/article/details/52780055。本篇博客用Mac OS X Sierra上的Firefox 47.0.1版本做的实验,对于其他浏览器或版本可能会有不同。

后续此博客不再更新,欢迎大家搜索关注微信公众号“测开之美”,测试开发工程师技术修炼小站,持续学习持续进步。
在这里插入图片描述

简介

缓存主要分为两类:服务端缓存、客户端缓存。
服务端缓存包括转发代理服务器缓存、反向代理服务器缓存、CDN缓存等。
客户端缓存一般指浏览器缓存,比如Chrome浏览器查看缓存可以访问chrome://cache

浏览器缓存机制

主要分为两种:通过HTML <meta>标签和通过HTTP报文头。

HTML meta标签

<meta http-equiv="pragma" content="no-cache" />用来告诉浏览器每次想要使用缓存前都需要到服务器验证缓存是否有更新。并不是所有浏览器都支持这个功能,缓存服务器也不会解析HTML文件的内容,所以一般不要使用这种方式。

HTTP 头部信息

expires字段

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前面灰色的圆圈表示加载的是浏览器缓存):
这里写图片描述

通过cache-control字段控制缓存策略

HTTP 1.1标准中设计了cache-control字段用于替代expires字段(如果两者同时存在,以cache-control的设置为准),cache-control可以实现比expires更精细的缓存控制比如:

  • max-age:缓存的过期时间(以秒为单位)。
  • public:响应可以被任何缓存区域缓存,比如客户端浏览器、代理服务器等。
  • private:响应的部分或全部内容属于一个单独的用户,只允许客户端浏览器缓存,不允许代理服务器缓存。
  • no-cache:无论如何,客户端浏览器必须先与服务器重验证缓存中的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。

如果之前的响应包含ETag字段,可能会使用If-Match字段或If-None-Match字段验证;如果之前的响应包含Last-Modified字段,可能会使用If-Modified-Since字段或If-Unmodified-Since字段验证。比如服务端设置了no-cache、30秒的max-ageETag字段:

# 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:如果资源过期,客户端浏览器必须先与服务器确认缓存中的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求(各个浏览器本身的实现也应该如此,只是must-revalidate将这一过程具体化了)。验证方法同上。比如服务端设置了Last-Modifiedmax-agemust-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:禁止任何缓存区域缓存响应。

比如服务端设置了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/If-None-Match

ETag可以理解为资源的摘要签名信息,如果资源未发生改变,该签名信息是不会改变的。客户端第一次请求服务端的某静态文件时,服务端可以在ETag字段回应资源的签名信息。接下来的请求中,如果客户端判断缓存已经过期(超过了max-age),会在If-None-Match字段填充缓存的ETag信息询问服务端资源是否发生过变动,如果发生过变化,服务端回应200的正常响应,如果未发生过变化,返回304 Not Modified响应,浏览器加载缓存,304响应不包含Response的body部分,提高了传输效率。

Last-Modified/If-Modified-Since

Last-Modified是客户端第一次请求服务端的某静态文件时,服务端填充的资源最后修改时间。接下来的请求中,如果客户端判断缓存已经过期(超过了max-age),会在If-Modified-Since字段填充缓存的Last-Modified信息询问服务端资源是否发生过变动,如果发生过变化,服务端回应200的正常响应,如果未发生过变化,返回304 Not Modified响应。

ETag与Last-Modified

使用Last-Modified字段可能有如下缺点:资源内容未发生变化,但是最后修改时间变了,造成带宽浪费;只能精确到秒级,如果资源在1秒内被多次修改,不能准确体现资源变化,造成客户端获取的数据不准确。ETag可以精确表示资源是否发生变化,当两个字段同时存在时,以ETag为准。

缓存与浏览器刷新的关系

比如Mac上FF浏览器有Command+r普通刷新和Shift+Command+r强制刷新。普通刷新相当于浏览器在发往Server端的请求头中添加字段Cache-Control:max-age=0,表示从客户端到服务端的沿途服务器都需要重新校验缓存:
这里写图片描述
强制刷新相当于浏览器在发往服务器的请求头中添加Pragama: no-cacheCache-Control: no-cache头,表示客户端到服务端的节点无需重新验证缓存是否过期而是强制要求服务端返回一个非缓存的版本(即使资源未发生变化,也不返回304 Not Modified,而是返回200的正常响应):
这里写图片描述

如果觉得我的文章对您有帮助,欢迎关注我(CSDN:Mars Loo的博客)或者为这篇文章点赞,谢谢!

如下几篇文章是我在学习HTTP协议时总结的博文,欢迎参考:

  1. HTTP代理及重定向
  2. HTTP认证(基本认证与摘要认证)
  3. HTTP、HTTPS基本原理
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值