缓存的优点

当Web请求抵达缓存时,如果本地有“已缓存的”副本,就可以从本地存储设备而不是原始服务器中提取这个文档。使用缓存有下列优点。
· 缓存 减少了冗余的数据传输,节省了你的网络费用。
· 缓存 缓解了网络瓶颈的问题。不需要更多的带宽就能够更快地加载页面。
· 缓存 降低了对原始服务器的要求。服务器可以更快地响应,避免过载的出现。
· 缓存 降低了距离时延,因为从较远的地方加载页面会更慢一些。

缓存命中和未命中

可以用已有的副本为某些到达缓存的请求提供服务。这被称为缓存命中(cachehit),参见图7-4a。其他一些到达缓存的请求可能会由于没有副本可用,而被转发给原始服务器。这被称为缓存未命中(cache miss),参见图7-4b。

再验证

原始服务器的内容可能会发生变化,缓存要不时对其进行检测,看看它们保存的副本是否仍是服务器上最新的副本。这些“新鲜度检测”被称为HTTP再验证。为了有效地进行再验证,HTTP定义了一些特殊的请求,不用从服务器上获取整个对象,就可以快速检测出内容是否是最新的。缓存可以在任意时刻,以任意的频率对副本进行再验证。但由于缓存中通常会包含数百万的文档,而且网络带宽是很珍贵的,所以大部分缓存只有在客户端发起请求,并且副本旧得足以需要检测的时候,才会对副本进行再验证。image.png
缓存对缓存的副本进行再验证时,会向原始服务器发送一个小的再验证请求。如果内容没有变化,服务器会以一个小的304 Not Modified进行响应。只要缓存知道副本仍然有效,就会再次将副本标识为暂时新鲜的,并将副本提供给客户端(参见图7-5a)这被称作再验证命中(revalidate hit)或缓慢命中(slow hit)。这种方式确实要与原始服务器进行核对,所以会比单纯的缓存命中要慢,但它没有从服务器中获取对象数据,所以要比缓存未命中快一些。
image.png
HTTP为我们提供了几个用来对已缓存对象进行再验证的工具,但最常用的是If-Modified-Since首部。将这个首部添加到GET请求中去,就可以告诉服务器,只有在缓存了对象的副本之后,又对其进行了修改的情况下,才发送此对象。这里列出了在3种情况下(服务器内容未被修改,服务器内容已被修改,或者服务器上的对象被删除了)服务器收到GET If-Modified-Since请求时会发生的情况:

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

    命中率

    由缓存提供服务的请求所占的比例被称为缓存命中率(cache hit rate,或称为缓存命中比例)。命中率在0到1之间,但通常是用百分数来描述的,0%表示每次请求都未命中(要通过网络来获取文档),100%表示每次请求都命中了(在缓存中有一份副本)。

    字节命中率

    由于文档并不全是同一尺寸的,所以文档命中率并不能说明一切。有些大型对象被访问的次数可能较少,但由于尺寸的原因,对整个数据流量的贡献却更大。因此,有些人更愿意使用字节命中率(byte hit rate)作为度量值(尤其那些按流量字节付费的人!)。
    字节命中率表示的是缓存提供的字节在传输的所有字节中所占的比例。通过这种度量方式,可以得知节省流量的程度。100%的字节命中率说明每个字节都来自缓存,没有流量流到因特网上去。

区分是否命中

不幸的是,HTTP没有为用户提供一种手段来区分响应是缓存命中的,还是访问原始服务器得到的。
客户端有一种方法可以判断响应是否来自缓存,就是使用Date首部。将响应中Date首部的值与当前时间进行比较,如果响应中的日期值比较早,客户端通常就可以认为这是一条缓存的响应。客户端也可以通过Age首部来检测缓存的响应,通过这个首部可以分辨出这条响应的使用期

缓存的拓扑结构

缓存可以是单个用户专用的,也可以是数千名用户共享的。专用缓存被称为私有缓存(private cache)。私有缓存是个人的缓存,包含了单个用户最常用的页面(参见图7-7a)。共享的缓存被称为公有缓存(public cache)。公有缓存中包含了某个用户团体的常用页面(参见图7-7b)。
image.png

私有缓存

Web浏览器中有内建的私有缓存——大多数浏览器都会将常用文档缓存在你个人电脑的磁盘和内存中,并且允许用户去配置缓存的大小和各种设置。通过特殊的URL about:cache可以查看网景的Navigator的缓存内容,这个URL会给出一个显示了缓存内容的“磁盘缓存统计”页面。

公有代理缓存

公有缓存是特殊的共享代理服务器,被称为缓存代理服务器(caching proxyserver)。公有缓存会接受来自多个用户的访问,所以通过它可以更好地减少冗余流量。
image.png

代理缓存的层次结构

层次化(hierarchy)的缓存是很有意义的,在这种结构中,在较小缓存中未命中的请求会被导向较大的父缓存(parent cache),由它来为剩下的那些“提炼过的”流量提供服务image.png

缓存的处理流程

image.png

与缓存有关的http首部

缓存验证

控制缓存的能力

服务器可以通过HTTP定义的几种方式来指定在文档过期之前可以将其缓存多长时间。按照优先级递减的顺序,服务器可以:
· 附加一个Cache-Control: no-store首部到响应中去;
· 附加一个Cache-Control: no-cache首部到响应中去;
· 附加一个Cache-Control: must-revalidate首部到响应中去;
· 附加一个Cache-Control: max-age首部到响应中去;
· 附加一个Expires日期首部到响应中去;
· 不附加过期信息,让缓存确定自己的过期日期。

no-Store与no-Cache响应首部

标识为no-store的响应会禁止缓存对响应进行复制。缓存通常会像非缓存代理服务器一样,向客户端转发一条no-store响应,然后删除对象。
标识为no-cache的响应实际上是可以存储在本地缓存区中的。只是在与原始服务器进行新鲜度再验证之前,缓存不能将其提供给客户端使用。

must-revalidate响应首部

响应首部告诉缓存,在事先没有跟原始服务器进行再验证的情况下,不能提供这个对象的陈旧副本。缓存仍然可以随意提供新鲜的副本

max-age响应首部

Cache-Control: max-age首部表示的是从服务器将文档传来之时起,可以认为此文档处于新鲜状态的秒数。

expires响应首部

不推荐使用Expires首部,它指定的是实际的过期日期而不是秒数。HTTP设计者后来认为,由于很多服务器的时钟都不同步,或者不正确,所以最好还是用剩余秒数,而不是绝对时间来表示过期时间。

试探性过期

如果响应中没有Cache-Control:max-age首部,也没有Expires首部,缓存可以计算出一个试探性最大使用期。常用的试探性过期算法有LM-Factor。

缓存和广告

很多内容提供商的收益都是通过广告实现的——具体来说,每向用户显示一次广告内容,内容提供商就会得到相应的收益。这就是缓存的问题——它们会向原始服务器隐藏实际的访问次数。如果缓存工作得很好,原始服务器可能根本收不到任何HTTP访问,因为这些访问都被因特网缓存吸收了。
一种解决方案就是配置缓存,每次访问时都与原始服务器进行再验证。这样,每次访问时都会将命中推向原始服务器,但通常不会传送任何主体数据。当然,这样会降低事务处理的速度。
理想的解决方案是不需要将命中传递给服务器的。毕竟,缓存就可以记录下所有的命中。缓存只要将命中日志发送给服务器就行了。

命中计数

RFC 2227, “HTTP的简单命中计数和使用限制”中定义了一种简单得多的方案。这个协议向HTTP中添加了一个称为Meter的首部,这个首部会周期性地将对特定URL的命中次数回送给服务器。通过这种方式,服务器可以从缓存周期性地获取对已缓存文档命中次数的更新。
而且,服务器还能控制在缓存必须向服务器汇报之前,其中的文档还可以使用多少次,或者为缓存文档设置一个时钟超时值。这种控制方式被称为使用限制;通过这种方式,服务器可以对缓存向原始服务器汇报之前,已缓存资源的使用次数进行控制。
命中率测量扩展建议使用新增加的首部Meter,缓存和服务器可以通过它在相互间传输与用法和报告有关的指令,这与用来进行缓存指令交换的Cache-Control首部很类似。
image.png
下面给出了一个执行中的命中率测量实例。事务的第一部分就是客户端和代理缓存之间一个普通的HTTP事务,但在代理请求中,要注意有插入的Meter首部和来自服务器的响应。这里,代理正在通知服务器它可以进行命中率测量,作为回应,服务器则请求代理报告它的命中次数。image.png
从客户端的角度来看,请求正常结束了,代理开始代表服务器跟踪该请求资源的命中次数。稍后,代理会尝试与服务器再次验证资源。代理会在发送给服务器的条件请求中嵌入它跟踪记录的计量信息。