http缓存知多少
很久没有写博客了,趁现在也快过年,最近项目不是很忙,写一篇博客做为2018年的开始,重拾刚毕业的几年前写博客的冲动。http协议是每个程序猿应该需要知道的东西,不管是前端人员还是后端人员,以前在上家公司的时候,因为项目的需要,曾经专门去了解过cdn,其中就需要掌握http协议中的缓存相关知识。
http缓存流程图
直接上图,后文会具体的叙述
http协议
在介绍http缓存之前,作为知识铺垫,先简单介绍下http报文。http报文就是客户端(如浏览器)和web服务器通信时发送和响应的的数据。
http请求由三部分组成分别是请求行、消息报头、请求正文
http 响应也是由三个部分组成,分别是:状态行、消息报头、响应正文
与缓存相关的信息,都包含在消息报头(header)中。不了解http协议的,先补充下http协议的知识,google搜索一大堆,本文就直接略过了。
http响应头中相关缓存字段
随便请求一个网页,这里我们打开百度首页,打开浏览器的调试工具
Expires
Expires的值为web服务器返回的到期时间(GMT 格林威治时间),浏览器下次请求时间小于服务器返回的时间则浏览器直接从缓存中获取数据,而不用再次发送请求。
Cache-Control
Cache-Control常见的取值有private、public、no-cache、max-age,no-store
private:客户端可以缓存
public :客户端和代理服务器都可缓存
max-age=<seconds>:缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)
no-cache:这个很容易让人产生误解,使人误以为是响应不被缓存,实际上Cache-Control:no-cache是会被缓存的,
只不过每次在向浏览器提供响应数据时,浏览器每次都要向服务器发送请求,由服务器来决策来评估缓存的有效性
no-store: 所有内容都不缓存(真真的不缓存)
更多详细的Cache-Control的取值参考MDN文档https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control
强制缓存:Expires和Cache-Control区别
Expires和Cache-control称为强制缓存,都是在缓存未失效时,浏览器不向服务端发起请求,直接从缓存中取数据。也许你会有疑问,都是一样的作用,为什么要两个都存在呢?
Expires是上古时代Http1.0的东西了,现在默认浏览器均默认使用的是Http1.1了,所以它的作用是可以基本忽略。Expires有一个缺点,就是返回的时间的服务器的绝对时间,用本地时间和服务端时间比较是否过期,是不严谨的,用户是可以随便修改本地时间的,那这样缓存随时可以过期,所以被Cache-Control:max-age=<seconds>取代了,在http1.1中Cache-Control优先级高于Expires。软件工程的特点是向下兼容,Expires一直就没有抛弃,对于使用http1.0协议的浏览器仍然有作用。
Last-Modified
服务器响应浏览器请求,告诉浏览器资源的最后修改时间
Etag
服务器响应请求时,告诉客户端(浏览器)当前请求的资源在服务器标识(Etag的生成算法由服务器决定,不同的web服务器生成etag的算法可能还不一样,Http协议并没有要求etag的生成规则,如文件小的时候可以使用md5sum sha1sum,或者根据文件修改时间,文件大小,文件inode文件属性综合生成,这里不做详细叙述),我们可以将其理解为一个资源的唯一标识,只要文件发生变化Etag的值也变化。
对比缓存:Last-Modified/If-Modified-Since和Etag/If-None-Match
Last-Modified和Etag称为对比缓存,所谓对比缓存,顾明思议,就是需要服务器来比较判断,来告诉客户端(浏览器)是否可以使用本地缓存,对比缓存生效时,服务器返回给客户端(浏览器)的Http Code为304,服务器只是返回的http header信息,并无响应正文,客户端通过服务器返回的状态码304,知道本地缓存并无修改,可以直接使用本地缓存,这样大大的较少的客户端请求响应时间。
对比缓存大致流程是这样的,当浏览器请求服务器的某资源时, 服务器得到资源的最后修改时间(Last-Modified)或根据一定的算法生成资源的标识(Etag),并将Last-Modified或If-Modified-Since返回给浏览器,浏览器把Last-Modified或Etag 和 资源内容同时缓存在本地,当下次再次向服务器请求此资源时,会将If-Modified-Since: Mon, 07 Nov 2016 07:51:11 GMT 或If-None-Match: xxxxxxx"的请求头把发送给服务器,服务器再次计算资源的Last-Modified或Etag,如果和客户端传来的值比较不相同,则表明资源发生了变化,则给浏览器返回Http Code 200。并将资源内容返回给浏览器,如果相同表示没有变化就给浏览器返回Http Code 304 ,并不需要返回资源内容给浏览器。
对于浏览器来说的话,一般会在强制缓存过期的情况下(或者按F5刷新,不同的浏览器可能不一样,firefox是按F5)如果资源原先的响应header中带有Last-Modified和Etag的话,浏览器请求时会在请求header中带上If-Modified-Since和If-None-Match。
Last-Modified和Etag区别
在这里,也许你可能会问,通过Last-Modified就可以知道资源内容是否发生了变化,为什么还需要Etag呢,这不是多此一举吗?,主要原因Etag解决了Last-modified没能解决的问题,Etag比Last-Modified更加严谨。
1.一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了
2.某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断3.如需要对动态生成的内容做缓存,那就可以用etag来控制缓存了
需要注意的是,如果同时有Last-Modified和Etag存在,在发送请求时,浏览器会一次性的将这两个值都发给服务器,没有优先级,服务器是都比较,还是只比较一个,不同的web服务器可能比较逻辑不一样吧。具体的不深究了。
Http请求头中Cache-Control: no-cache的意义
一般按ctrl+f5强制刷新时,请求头里面都带有Cache-Control: no-cache,其实这是跳过本地的强制缓存和告诉服务器跳过对比缓存,也就是重新请求资源了。对于前端同学在通过GET请求后端API接口时,在ajax的请求头中统一带上Cache-Control: no-cache。
总结
1. 对于强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行对比缓存策略。
2. 对于对比缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。