HTTP:Web缓存

Web缓存是可以自动保存常见文档副本的HTTP设备。当Web请求抵达缓存时,如果本地有“已缓存的”副本,就可以从本地存储设备而不是原始服务器中提取这个文档。使用缓存有如下优点:

  • 减少了冗余的数据传输,节省网络费用
  • 缓解了对网络瓶颈的问题。不需要更多的带宽就能够更快的加载页面
  • 降低了对原始服务器的要求。服务器可以更快的响应,避免过载的出现
  • 降低了距离时延,因为从较远的地方加载页面会更慢一些

命中和未命中

可以用已有的副本为某些到达缓存的请求提供服务器,这被称为缓存命中(cache hit)。其他一些到达缓存的请求可能会由于没有副本可用,而被转发给原始服务器,这叫做缓存未命中

再验证

原始服务器的内容可能会发生变化,缓存要不时对其进行检测,看看它们保存的副本是否扔是服务器上最新的副本。这些“新鲜度检测”被称为HTTP再验证(revalidation)。为了有效的进行再验证,HTTP定义了一些特殊的请求,不用从服务器上获取整个对象,就可以快速检测出内容是否是最新的。
在这里插入图片描述
大部分缓存只有在客户端发起请求,并且副本旧得足以需要检测的时候,才会对副本进行再验证。

缓存对缓存的副本进行再验证时,会向原始服务器发送一个小的再验证请求。如果内容没有变化,服务器就会以一个304 Not Modified进程响应。只要缓存知道副本仍然有效,就会再次将副本标识为新鲜的,并将副本提供给客户端,这杯称为再验证命中(revalidate hit)或者缓慢命中(slow hit)

HTTP为我们提供了一个用来对已缓存对象进行再验证的工作,最常用的就是If-Modified-Since首部。将这个首部添加到GET请求中去,就可以告诉服务器,只有在缓存了对象的副本之后,又对其进行了修改的情况下,才发送此对象。

在这里插入图片描述

这里列出了三种情况下(服务器内容未被修改、服务器内容已经被修改、或者服务器上的对象被删除了)服务器收到GET If-Modified-Since请求时会发生的情况:

  • 再验证命中:如果服务器对象未被修改,服务器会向客户端发送一个小的HTTP 304 Not Modified响应。
  • 再验证未命中:如果服务器对象与已缓存副本不同,服务器向客户端发送一条普通的、带有完整内容的HTTP 200 OK响应
  • 对象被删除:如果服务器对象已经被删除了,服务器会返回一个404 Not Found响应,缓存也会将其副本删除。

文档命中率

由缓存提供服务的请求所占的比例叫做缓存命中率,也叫做文档命中率

字节命中率

由于文档并不全是同一尺寸的,所以文档命中率不能说明一切。有些大型对象被访问的次数可能更少,但是由于尺寸的原因,对整个数据流量的贡献却更大。因此,一般用字节命中率作为度量值

字节命中率表示的是缓存提供的字节在传输的所有字节中所占的比例。通过这种度量方式,可以得知节省流量的程度。

文档命中率和字节命中率对缓存性能的评估是很有用的。

  • 文档命中率说明阻止了多少通往外部网络的Web事务。事务有一个通常都很大的固定时间成分(比如,建立一条到服务器的TCP连接),提高文档命中率对降低时延很有用
  • 字节命中率说明阻止了多少字节传向互联网。提高字节命中率对节省带宽很有利

区别命中和未命中的情况

不幸的是,HTTP没有为用户提供一种手段来区分响应是缓存命中的还是访问原始服务器得到的。在这两种情况下,响应码都是200OK,说明响应有主体部分。有些商业代理缓存会在Via首部附加一些额外信息,以描述缓存中发生的情况。

客户端有一种方法可以判断响应是来自缓存,就是使用Date首部。将响应中Date首部。将响应中Date首部的值与当前时间进行比较,如果响应中的日期值比较早,客户端通常就认为这是一条缓存的响应。客户端也可以通过Age首部来检测缓存的响应,通过这个首部可以分辨出这条响应的使用期。

缓存的拓扑结构

缓存可以是单个用户单用的,也可以是数个用户共享的。专用缓存叫做私有缓存,共享的缓存叫做公有缓存
在这里插入图片描述

私有缓存

Web浏览器中有内建的私有缓存------大部分浏览器都会将常用文档缓存在你的个人电脑的磁盘和内存中,并且允许用户去配置缓存的大小和各种设置。

公有代理缓存

公有缓存是特殊的共享代理服务器,被称为缓存代理服务器(caching proxy server)或者代理缓存(proxy cache)。代理缓存会从本地缓存中提供文档,或者代表用户与服务器进行联系。公有缓存会接受来自多个用户的访问,所以通过它可以更好地减少冗余流量。
在这里插入图片描述

代理缓存的层次结构

在实际中,实现层次化是很有意义的,在这种结构中,在较小缓存中未命中的请求会被导向较大的父缓存,由它为剩下那些“提炼过的”流量提供服务。下图显示了一个两级的缓存层次结构,其基本思想是最靠近客户端的地方使用小型缓存,在更高层次中,则逐步采用更大、功能更强的缓存来装载多用户共享的文档。
在这里插入图片描述

缓存的处理步骤

对一条HTTP GET报文的基本缓存处理过程包括7个步骤:
在这里插入图片描述

第一步:接收

在第一步中,缓存检测到一条网络连接上的活动,读取输入数据。高性能的缓存会同时从多条输入连接上读取数据,在整条报文到达之前开始对事务进行处理。

第二步:解析

接下来,缓存将请求报文解析为片段,将首部的各个部分放入易于操作的数据结构中。这样,缓存软件就更容易处理首部字段并修改它们了。

第三步:查找

在第三步中,缓存获取了URL,查找本地副本。缓存会用快速算法来确定本地是否存放副本。如果没有,它可以根据情形和配置,到原始服务器或者父代理中去取,或者返回一条错误信息。

已缓存对象中包含了服务器响应主体和原始服务器响应首部,这样就会在缓存命中时返回正确的服务器首部。已缓存对象中还包含一些元数据,用来记录对象在缓存中停留了多长时间,以及它被用过多少次等。

第四步:新鲜度检测

HTTP通过缓存将服务器文档的副本保留一段时间。在这段时间里,都认为是“新鲜的”,缓存可以在不联系服务器的情况下,直接提供该文档。但一旦已缓存副本停留时间太长,超过了文档的“新鲜度限值”,就认为对象“过时”了,在提供该文档之前,缓存要再次与服务器进行确认,以查看文档是否发生了编号。客户端发送给缓存的所有请求自身都可以强制缓存进行再验证,或者完全避免认证。

第五步:创建响应

我们希望缓存的响应看起来像是原始服务器一样,缓存将已缓存的服务器响应首部作为响应首部的起点。然后缓存对这些基础首部进行了修改和扩充

缓存首部负责对这些首部进行改造,以便与客户端的要求相匹配。比如,服务器返回的可能是一条HTTP/1.0响应,而客户端期待的是一条HTTP/1.1响应,此时,缓存必须对首部进行相应的转换。缓存还会向其中插入新鲜度信息(Cache-Control,Age、Expires首部),而且通常会包含一个Via首部来说明请求是一个代理缓存提供的。

注意,缓存不应该调整Date首部,Date首部表示的是原始服务器最初产生这个对象的日期

第六步:发送

一旦响应首部准备好了,响应就将响应回送给客户端。和所有代理服务器一样,代理缓存要管理与客户端之间的连接。高性能的缓存会尽力高效的发送数据,通常可以避免在本地缓存和网络IP缓冲区之间进行文档内容的复制。

第七步:日志

大多数缓存都会保存日志文件以及与缓存的使用有关的一些统计数据。每个缓存事务结束之后,缓存都会更新缓冲命中和未命中数目的统计数据(以及其他相关的度量值),并将条目插入一个用来显示请求类型、URL和所发生事件的日志文件。
在这里插入图片描述

保持副本的新鲜

可能不是所有的已缓存的副本都与服务器上的文档一致。如果缓存提供的总是老的数据,就会变得毫无用处。已缓存数据要与服务器数据保持一致。

HTTP有一些简单的机制可以在不要求服务器记住有哪些缓存拥有其文档副本的情况下,保持已缓存数据与服务器数据之间充分一致。HTTP将这些简单的机制称为文档过期服务器再验证

文档过期

通过特殊的HTTP cache-Control首部和Expires首部,HTTP让原始服务器向每个文档附加了一个“过期时间”:
在这里插入图片描述
在缓存文档过期之前,缓存可以以任意频率使用这些副本,而无需与服务器联系----当然,除非客户端请求中包含有阻止提供已缓存或者未验证资源的首部。但一旦已缓存文档过期,缓存就必须与服务器进行核对,询问文档是否被修改过,如果被修改过,就要获取一份新鲜的副本

过期日期和使用期

服务器用HTTP/1.0+和Expires首部或HTTP/1.1的Cache-Control:max-age响应首部来指定过期日期,同时还会带有响应主体。Expires首部和Cache-Control:max-age首部所做的事情本质上是一样的,但由于Cache-Control首部使用的是相对时间而不是绝对日期(绝对日期依赖于计算机时钟的正确设置),所以我们期望使用的是Cache-Control首部
在这里插入图片描述

服务器再验证

仅仅是已缓存文档过期了并不意味着它和原始服务器上目前处于活跃状态的文档有实际的区别:这只是意味着到来要进行核对的时间了。这种情况被称为“服务器再验证”,说明缓存需要询问原始服务器文档是否发生了编号

  • 如果再验证显示内容发生了变化,缓存会获取一份新的文档副本,并将其存储在旧文档的位置上,然后将文档发送给客户端
  • 服务再验证显示内容没有发生变化,缓存只需要获取新的首部,包括一个新的过期时间,并对缓存中的首部进行更新就行了

HTTP协议要求行为正确的缓存返回下列内容之一:

  • “足够新鲜”的已缓存副本
  • 与服务器进行过在验证,确认其仍然新鲜的已缓存副本
  • 如果需要与之进行再验证的原始服务器出故障了,就返回一条错误报文
  • 附有警告信息说明内容可能不正确的已缓存副本

用条件方法进行再验证

HTTP的条件方法可以高效的实现再验证。HTTP允许缓存向原始服务器发送一个“条件GET”,请求服务器只有在文档与缓存中现有的副本不同时,才回送对象主体。通过这种方式,将新鲜度检测和对象获取结合成了单个条件GET。向GET请求报文中添加一些特殊的条件首部,将可以发起条件GET。只有条件为真时,Web服务器才会返回对象

HTTP定义了5个条件请求首部。对缓存再验证来说最有用的2个首部是If-Modified-SinceIf-Node-Match。所有的条件首部都以If-开头。
在这里插入图片描述

If-Modified-Since:Date再验证

If-Modified-Since再验证请求首部通常被称为IMS请求,只有自某个日期之后的资源发生了变化的时候,IMS请求才会指示服务器执行请求:

  • 如果自指定日期后,文档被修改了,If-Modified-Since条件就为真,通常GET就会成功执行。携带新首部的新文档就会被返回给缓存,新首部除了其他信息之外,还包含了一个新的过期日期
  • 如果自指定日期后,文档没被修改过,条件就为假,会向客户端返回一个小的304Not Modified响应报文,为了提高有效性,不会返回文档的主体。这些首部是放在响应中返回的,但只会返回那些需要在源端更新的首部。比如,Content-Type首部通常不会被修改,所以通常不需要发送,一般会发送一个新的过期日期。

If-Modified-Since首部可以与Last-Modified服务器响应首部配合工作。原始服务器会将最后的修改日期附加到所提供的文档上去。当缓存要对已缓存文档进行再验证时,就会包含一个If-Modified-Since首部,其中携带有最后修改已缓存副本的日期:

If-Modified-Since: <cached last-modified date>

如果在此期间内容被修改了,最后的修改日期就会有所不同,原始服务器就会会送新的文档。负责,服务器会注意到缓存的最后修改日期与服务器文档当前的最后修改日期相符,会返回一个304Not Modified响应。
在这里插入图片描述

If-None-Match: 实体标签再验证

有些情况下仅使用最后修改日期进行再验证是不够的。

  • 有些文档可能被周期性的重写,但实际包含的数据往往是一样的。尽管内容没有变化,但修改日期会发生变化
  • 有些文档可能被修改了,但所做修改并不重要,不需要让世界范围内的缓存都重装数据(比如修改拼音或者注释)
  • 有些服务器无法准确的判定其页面的最后修改日期
  • 有些服务器提供的文档会在亚秒间隙发送编号(比如实时监视器),对这些服务器来说,以一秒为粒度的修改日期可能就不够用了

为了解决这些问题,HTTP允许用户对被称为**实体标签(Etag)**的“版本标识符“进行比较。实体标签是附加到文档上的任意标签。它们可能包含了文档的序列号或者版本名,或者是文档内容的校验和以及其他指纹信息。

当发布者对文档进行修改时,可以修改文档的实体标签来说明这个新的版本,这样,如果实体标签被修改了,缓存就可以用If-None-Match条件首部来GET文档的新副本了。

如下图,缓存中有一个实体标签为v2.6的文档。它会与原始服务器进行再验证,如果标签v2.6不再匹配,就会请求一个新对象。下图中标签仍然匹配,因此返回一条304Not Modified响应。
在这里插入图片描述
如果服务器上的实体标签已经发生了编号(比如变成了v3.0),服务器会在一个200OK响应中返回新的内容以及新的Etag

可以在If-None-Match首部包含几个实体标签,告诉服务器,带有这些实体标签的对象副本在缓存上已经有了
在这里插入图片描述

强弱验证器

有时,服务器希望在对文档进行一些非实质性的或者不重要的修改时,不要使所有的已缓存副本都失效。HTTP/1.1支持“弱验证器”,如果只对内容进行了少量的修改,就运行服务器声明那是“足够好”的等价体。

只要内容发生了变化,强验证器就会变化。弱验证器允许对一些内容进行修改。但内容的主要含义发生变化时,通常它还是会变化的。有些操作不能用弱验证器来实现(比如有条件的获取部分内容),所以服务器会用前缀"w/"来标识弱验证器
在这里插入图片描述
不管相关的实体值以何种方式发生了变化,强实体标签都要发生变化。而相关实体在语义上发生了变化,弱实体标签也应该发生变化。

注意,原始服务器一定不能为两个不同的实体重用一个特定的强实体标签值,或者为两个语义不同的实体重用一个特定的弱引用标签值。不管挂起时间是多少,缓存条目都可能会留存任意长的时间,因此,假设缓存不会再次通过它在过去某个时刻获得的验证器,对一个条目进行验证是不合适的。

什么时候该用实体标签和最近修改日期

如果服务器回送了一个实体标签,HTTP/1.1客户端就必须使用实体标签验证器。如果服务器只回送了一个last-Modified值,客户端就可以使用If-Modified-Since验证。如果实体标签和最后修改日期都提供了,客户端就应该使用这两种再验证方案,这样HTTP/1.0和HTTP/1.1缓存就都可以正确响应了。

除非HTTP/1.1原始服务器无法生成实体标签验证器,否则就应该发送一个出去,如果使用弱实体标签有优势的话,发送的可能就是个弱实体标签,而不是强实体标签。而且,最好同时发送一个最近修改值。

如果HTTP.1.1缓存或者服务器收到的请求既有If-Modified-Since,又有实体标签条件首部,那么只有这两个条件都满足时,才能返回304Not Modeified响应

控制缓存的能力

HTTP/1.1定义的 Cache-Control 头用来区分对缓存机制的支持情况, 请求头和响应头都支持这个属性。通过它提供的不同的值来定义缓存策略。

"请求头和响应头都支持这个属性"是什么意思

这代表需要协商:说请求 - 应答的双方都可以用这个字段进行缓存控制,互相协商缓存的使用策略。

  • 当你点“刷新”按钮的时候,浏览器会在请求头里加一个“Cache-Control: maxage=0”。因为 max-age 是“生存时间”,max-age=0 的意思就是“我要一个最最新鲜的西瓜”,而本地缓存里的数据至少保存了几秒钟,所以浏览器就不会使用缓存,而是向服务器发请求。服务器看到 max-age=0,也就会用一个最新生成的报文回应浏览器。
  • Ctrl+F5 的“强制刷新”又是什么样的呢?它其实是发了一个“Cache-Control: no-cache”,含义和“max-age=0”基本一样,就看后台的服务器怎么理解,通常两者的效果是相同的。
  • 那么,浏览器的缓存究竟什么时候才能生效呢?着点一下浏览器的“前进”“后退”按钮,再看开发者工具,就会发现“from disk cache”的字样,意思是没有发送网络请求,而是读取的磁盘上的缓存。
    在这里插入图片描述

max-age响应首部

过期机制中,最重要的指令是 “max-age=”,表示资源能够被缓存(保持新鲜)的最大时间。

还有一个s-maxage首部,其行为与max-age类似,但只适用于共享缓存:
在这里插入图片描述

服务器可以请求缓存不要缓存文档,或者将最大使用期设置为0,从而在每次访问的时候都进行刷新:
在这里插入图片描述

整个流程如下:

  • 浏览器发现缓存无数据,于是发送请求,向服务器获取资源;
  • 服务器响应请求,返回资源,同时标记资源的有效期;
  • 浏览器缓存资源,等待下次重用。

在这里插入图片描述

注意:这里的 max-age 是“生存时间”(又叫“新鲜度”“缓存寿命”,类似 TTL,Time-To-Live),时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻),而不是客户端收到报文的时刻,也就是说包含了在链路传输过程中所有节点所停留的时间。

没有缓存:no-store

no_store:不允许缓存。每次由客户端发起的请求都会下载完整的响应内容。

  • 用于某些变化非常频繁的数据,例如秒杀页面;
  • HTTP/1.1中提供的Pragma:no-cache首部是为了兼容HTTP/1.0+。只用于HTTP/1.0应用程序,HTTP1.1应用程序都应该用Cache-Control:no-cache
Cache-Control: no-store

缓存但重新验证:no-cache

no_cache:它的字面含义容易与 no_store 搞混,实际的意思并不是不允许缓存,而是可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本,若未过期(注:实际就是返回304),则缓存才使用本地缓存副本。

Cache-Control: no-cache
  • 标识为no-cache的响应实际上是可以存储在本地缓冲区中的。只是在与原始服务器进行新鲜度验证之前,缓存不能将其提供给客户端使用。这个首部使用do-not-serve-from-cache-without-revalidation会更好一些
  • HTTP/1.1中提供的Pragma:no-cache首部是为了兼容HTTP/1.0+。只用于HTTP/1.0应用程序,HTTP1.1应用程序都应该用Cache-Control:no-cache

must-revalidate响应首部

must-revalidate:

  • Cache-Control:must-revalidate意思是如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证

  • 如果在缓存进行must-revalidate新鲜度检测时,原始服务器不可用,缓存就必须返回一条504 Gateway Timeout错误。

no-store、no-cache、must-revalidate的区别

  • no_store:买来的西瓜不允许放进冰箱,要么立刻吃,要么立刻扔掉;
  • no_cache:可以放进冰箱,但吃之前必须问超市有没有更新鲜的,有就吃超市里的;
  • must-revalidate:可以放进冰箱,保鲜期内可以吃,过期了就要问超市让不让吃。

私有缓存和公共缓存

public指令表示该响应可以被任何中间人(比如中间代理、CDN等)缓存。如果指定了public,则一些通常不被中间人缓存的页面(译者注:因为默认是private)(比如 带有HTTP验证信息(帐号密码)的页面 或 某些特定状态码的页面),将会被其缓存。

而 “private” 则表示该响应是专用于某单个用户的,中间人不能缓存此响应,该响应只能应用于浏览器私有缓存中。

Cache-Control: private
Cache-Control: public

在这里插入图片描述

Expires响应首部

不推荐使用Expires首部,它指定更得是实际的过期日期而不是秒数。但是很多服务器的时钟都不同步

试探性过期

如果响应中没有Cache-Control:max-age首部,也没有Expires首部,缓存可以计算出一个试探性最大使用期。可以使用任意算法,但如果得到的最大使用期大于24小时,就应该向响应首部添加一个Heuristic Expiration Warning(试探性过期警告)。很少用

客户端的新鲜度限制

Web浏览器都有Refresh(刷新)或Reload(重载)按钮,可以强制对浏览器或者代理缓存中可能过期的内容进行刷新。Refresh按钮会发布一个附加了Cache-Control请求首部的GET请求,这个请求会强制进行再验证,或者无条件的从服务器获取文档。Refresh的确切行为取决于特定的浏览器、文档以及拦截缓存的配置。

客户端可以使用Cache-Conrtol请求首部来强化或者放松对过期时间的限制:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值