HTTP缓存小结
介绍
提到页面优化,浏览器缓存必定是一个绕不过的话题,判断一个网站的性能最直观的就是看网页打开的速度,而提高网页反应速度的一个方式就是使用缓存。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。因此理解浏览器的缓存机制,就显得尤为重要。
这里我们简单介绍下浏览器的缓存
浏览器中的HTTP
请求是一种应答模式,HTTP
发起请求,服务器响应该请求,那么浏览器如何确定一个资源是否缓存,当请求一个资源的时候,应该去浏览器的缓存取,还是发送请求来获取该资源呢
缓存的关键:
- 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
- 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中
我们根据是否向浏览器发起请求分为强制缓存和协商缓存
强制缓存
不会向服务器发送请求,直接从缓存中读取资源,在chrome
控制台的network
选项中可以看到该请求返回200的状态码,并且size
显示from disk cache
或from memory cache
状态码为灰色的请求则代表使用了强制缓存,请求对应的Size
值则代表该缓存存放的位置,分别为from memory cache
和 from disk cache
from memory cache
代表使用内存中的缓存,from disk cache
则代表使用的是硬盘中的缓存,浏览器读取缓存的顺序为memory – disk
。在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache)
;而css
文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)
。
HTTP1.0的缓存
在 HTTP1.0
时代,给客户端设定缓存方式可通过两个字段——Pragma
和Expires
来规范。虽然这两个字段早可抛弃,但为了做http协议的向下兼容,你还是可以看到很多网站依旧会带上这两个字段。
pragma
当该字段值为no-cache
的时候(事实上现在RFC中也仅标明该可选值),会知会客户端不要对该资源读缓存,即每次都得向服务器发一次请求才行。
expires
有了Pragma
来禁用缓存,自然也需要有个东西来启用缓存和定义缓存时间,对HTTP1.0
而言,Expires
就是做这件事的首部字段。 Expires
的值对应一个GMT(格林尼治时间)
,比如Mon, 22 Jul 2002 11:12:01 GMT
来告诉浏览器资源缓存过期时间,如果还没过该时间点则不发请求。
expires设置的时间是服务器的时间,设置的是资源"失效"的时刻,如果时间客户端的时间和服务器的时间不一致,那这个缓存就没有意义了
HTTP1.1新增的cache-control
针对上述的“ Expires
时间是相对服务器而言,无法保证和客户端时间统一”的问题,http1.1新增了 Cache-Control
来定义缓存过期时间。注意:若报文中同时出现了Expires
和 Cache-Control
,则以 Cache-Control
为准 ,使用格式:
"cache-control":cache-directive
作为响应首部时, cache-directive
的可选值有 :
public
:所有内容都将被缓存(客户端和代理服务器都可缓存)。具体来说响应可被任何中间节点缓存,如 Browser -- proxy1 -- proxy2 -- Server,中间的proxy可以缓存资源,比如下次再请求同一资源proxy1直接把自己缓存的东西给 Browser 而不再向proxy2要。private
:所有内容只有客户端可以缓存,Cache-Control的默认取值。具体来说,表示中间节点不允许缓存,对于Browser -- proxy1 -- proxy2 -- Server,proxy 会老老实实把Server 返回的数据发送给proxy1,自己不缓存任何数据。当下次Browser再次请求时proxy会做好请求转发而不是自作主张给自己缓存的数据。no-cache
:客户端缓存内容,是否使用缓存则需要经过协商缓存来验证决定。表示不使用 Cache-Control的缓存控制方式做前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存。需要注意的是,no-cache这个名字有一点误导。设置了no-cache之后,并不是说浏览器就不再缓存数据,只是浏览器在使用缓存数据时,需要先确认一下数据是否还跟服务器保持一致。no-store
:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存max-age
:max-age=xxx (xxx is numeric)表示缓存内容将在xxx秒后失效s-maxage(单位为s)
:同max-age,只用于共享缓存(比如CDN缓存)。比如当s-maxage=60时,在这60秒中,即使更新了CDN的内容,浏览器也不会进行请求。max-age用于普通缓存,而s-maxage用于代理缓存。s-maxage的优先级高于max-age。如果存在s-maxage,则会覆盖掉max-age和Expires header。
优先级从高到低分别是 Pragma -> Cache-Control -> Expires
Cache-Control使用的是时间间隔,Expires使用的是失效时刻
协商缓存
强缓存判断是否缓存的依据来自于是否超出某个时间或者某个时间段,而不关心服务器端文件是否已经更新,这可能会导致加载文件不是服务器端最新的内容,那我们如何获知服务器端内容是否已经发生了更新呢?此时我们需要用到协商缓存策略
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
缓存校验字段
Last-Modified
服务器将资源传递给客户端时,会将资源最后更改的时间以Last-Modified: GMT”
的形式加在实体首部上一起返回给客户端 ,值代表资源上次修改的时间
次再次请求时,会把该信息附带在请求报文中一并带给服务器去做检查,若传递的时间值与服务器上该资源最终修改时间是一致的,则说明该资源没有被修改过,直接返回304
状态码,内容为空,这样就节省了传输数据量 。如果两个时间不一致,则服务器会发回该资源并返回200
状态码,和第一次请求时类似。这样保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。一个304
响应比一个静态资源通常小得多,这样就节省了网络带宽
传递Last-Modified
的请求报文首部字段一共有两个:
- If-Modified-Since: Last-Modified-value
示例为 If-Modified-Since: Thu, 31 Mar 2016 07:07:52 GMT
该请求首部告诉服务器如果客户端传来的最后修改时间与服务器上的一致,则直接回送304 和响应报头即可。
当前各浏览器均是使用的该请求首部来向服务器传递保存的 Last-Modified 值。
- If-Unmodified-Since: Last-Modified-value
该值告诉服务器,若Last-Modified没有匹配上(资源在服务端的最后更新时间改变了),则应当返回412`(Precondition Failed) 状态码给客户端。 Last-Modified 存在一定问题,如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,会因为Last-Modified时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)
ETag
为了解决Last-Modified
的不准确(文件修改时间改了,但文件内容却没有变 )问题,HTTP1.1
还增加了ETag
实体首部字段, 服务器会通过某种算法,给资源计算得出一个唯一标志符(比如md5标志),在把资源响应给客户端的时候,会在实体首部加上“ETag: 唯一标识符”
一起返回给客户端。
客户端会保留该 ETag
字段,并在下一次请求时将其一并带过去给服务器。服务器只需要比较客户端传来的ETag
跟自己服务器上该资源的ETag
是否一致,就能很好地判断资源相对客户端而言是否被修改过了。 如果服务器发现ETag
匹配不上,那么直接以常规GET 200
回包形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag
是一致的,则直接返回304
知会客户端直接使用本地缓存即可
传递ETag
的请求报文也有两个:
- If-None-Match: ETag-value
示例为 If-None-Match: "5d8c72a5edda8d6a:3239" 告诉服务端如果 ETag 没匹配上需要重发资源数据,否则直接回送304 和响应报头即可。 当前各浏览器均是使用的该请求首部来向服务器传递保存的 ETag 值
- If-None-Match:ETag-value
告诉服务器如果没有匹配到ETag,或者收到了“*”值而当前并没有该资源实体,则应当返回412(Precondition Failed) 状态码给客户端。否则服务器直接忽略该字段
总结
强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回304,继续使用缓存。
用户刷新/访问行为
- 在URI输入栏中输入然后回车/通过书签访问,这讲触发缓存机制
- F5/点击工具栏中的刷新按钮/右键菜单重新加载,浏览器会设置max-age=0,跳过强缓存判断,会进行协商缓存判断
- Ctl+F5,跳过强缓存和协商缓存,直接从服务器拉取资源
HTTP缓存小结的更多相关文章
- ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十五节--缓存小结与ABP框架项目中 Redis Cache的实现
返回总目录:ABP+AdminLTE+Bootstrap Table权限管理系统一期 缓存 为什么要用缓存 为什么要用缓存呢,说缓存之前先说使用缓存的优点. 减少寄宿服务器的往返调用(round-tr ...
- http协议缓存小结
缓存可以使用expire方式,设置到期时间,缓存的时间等于expire设置的时间减去当前的时间 也可以使用no-cache的方式进行缓存,当设置了no-cache的方式时,以no-cache的为准,e ...
- MyBatis学习总结(四)——MyBatis缓存与代码生成
一.MyBatis缓存 缓存可以提高系统性能,可以加快访问速度,减轻服务器压力,带来更好的用户体验.缓存用空间换时间,好的缓存是缓存命中率高的且数据量小的.缓存是一种非常重要的技术. 1.0.再次封装 ...
- mybatis缓存机制
目录 mybatis缓存机制 Executor和缓存 一级缓存 小结 二级缓存 小结 mybatis缓存机制 mybatis支持一.二级缓存来提高查询效率,能够正确的使用缓存的前提是熟悉mybatis ...
- 3月web前端面试小结
说一下box-sizing的应用场景 box-sizing的属性值分为两个,border-box和content-box,其中, border-box:width=content+padding+bo ...
- .Net中的AOP系列之《方法执行前后——边界切面》
返回<.Net中的AOP>系列学习总目录 本篇目录 边界切面 PostSharp方法边界 方法边界 VS 方法拦截 ASP.NET HttpModule边界 真实案例--检查是否为移动端用 ...
- Map/Reduce 工作机制分析 --- 作业的执行流程
前言 从运行我们的 Map/Reduce 程序,到结果的提交,Hadoop 平台其实做了很多事情. 那么 Hadoop 平台到底做了什么事情,让 Map/Reduce 程序可以如此 "轻易& ...
- MyBatis 实践 -配置
MyBatis 实践 标签: Java与存储 Configuration mybatis-configuration.xml是MyBatis的全局配置文件(文件名任意),其配置内容和顺序如下: pro ...
- Hibernate3 第四天
Hibernate3 第四天 [第一天]三个准备七个步骤 [第二天]一级缓存.一级缓存快照.一对多和多对多配置 [第三天内容回顾] 1.各种查询 对象导航查询:配置信息不能出错, 根据OID查询:ge ...
随机推荐
- store在模块化后,获取state中的值时undefined
目录结构 用this.$store.getters.showNotif ,加上模块名this.$store.getters.apply.showNotif都取不到值, 控制台打印store,发现这样的 ...
- Maven+ajax+SSM实现查询
2.尚硅谷_SSM高级整合_使用ajax操作实现页面的查询功能 16.尚硅谷_SSM高级整合_查询_返回分页的json数据.avi 在上一章节的操作中我们是将PageInfo对象存储在request域 ...
- 万字总结Keras深度学习中文文本分类
摘要:文章将详细讲解Keras实现经典的深度学习文本分类算法,包括LSTM.BiLSTM.BiLSTM+Attention和CNN.TextCNN. 本文分享自华为云社区<Keras深度学习中文 ...
- Sentry 开发者贡献指南 - 测试技巧
作为 CI 流程的一部分,我们在 Sentry 运行了多种测试. 本节旨在记录一些 sentry 特定的帮助程序, 并提供有关在构建新功能时应考虑包括哪些类型的测试的指南. 获取设置 验收和 pyth ...
- 【刷题-LeetCode】238. Product of Array Except Self
Product of Array Except Self Given an array nums of n integers where n > 1, return an array outpu ...
- 【记录一个问题】android下opencl中的event.getProfilingInfo()测速时间并不准确
使用了类似的代码来做android下opencl的时间测试: cl::CommandQueue queue(context, devices[0], CL_QUEUE_PROFILING_ENABLE ...
- 【小记录】解决链接libcufft_static.a库出现的错误
程序中使用了 cv::cuda::dft() 函数,需要在链接的时候使用libcufft_static.a这个库.链接出现大量类似错误:error: undefined reference to __ ...
- vscode控制台中文乱码
原因 vscode中文控制台乱码原因是调用的cmd的显示. 所以问题实际上是cmd的显示中文乱码问题.当然还有其他方法仅仅修改vscode的显示,这里不在说明. cmd中国版本windows默认是93 ...
- python控制另一台电脑虚拟nao机器人
nao机器人ip地址 http://doc.aldebaran.com/1-14/software/choregraphe/howto_connect_to_simulated.html 结果 访问另 ...
- Golang 通过创建临时结构体实现 struct 内 interface struct 的 json 反序列化
原文链接 背景 type AData struct { A string `json:"a"` } type BData struct { B string `json:" ...