1. 缓存简介

缓存是位于服务器和客户端的中间单元,主要根据用户代理发送过来的请求,向服务器请求相关内容后提供给用户,并保存内容副本,例如 HTML 页面、图片、文本文件或者流媒体文件。然后,当下一个针对相同 URL 的请求到来时,缓存直接使用副本来响应 HTTP 请求,而不需要向源服务器再次发送请求。

1.1 缓存的好处

  1. 减少响应延迟。缓存服务器距离用户更近,如果可以直接提供服务,响应时延将大大减少,使用户感觉 Web 服务器反应更块。
  2. 减少网络带宽消耗。当缓存直接使用副本为用户服务时,缓存与源服务器之间的通信链路带宽消耗将大大降低,提供服务的缓存越靠近用户,节约的网络资源越多,特别是对于拥塞程度较高的运营商骨干网来说更说如此。
  3. 降低源服务器负载。用户原本需要访问源服务器的大量请求都在缓存内直接得到服务,源服务器的响应次数大量降低。

2. 缓存的基本工作原则

  • 如果响应消息的头信息告诉缓存不要保留副本,缓存就不会缓存相应内容。
  • 如果请求信息需要源服务器认证或者涉及安全协议,相应的请求内容也不会被缓存。
  • 如果缓存的内容含有以下信息,内容将会被认为是足够新的,因此不需要从源服务器重新获取内容。
    1. 含有过期时间和寿命信息,并且此时内容仍然没有过期。
    2. 缓存内容近期被用来提供过服务,并且内容的最后更新时间相对于最近使用的时间较久。
  • 如果缓存的内容已经过期,缓存服务器将向源服务器发出验证请求(通过 ETag 头信息或者 Last-Modified 头信息),用于确定是否可以继续使用当前内容直接提供服务。
  • 在某些情况下(比如源服务器从网络断开了),缓存的内容在过期的情况下也可以直接提供服务。
  • 如果在响应消息中不存在用于判断内容是否变化的验证值(ETag 头信息或者 Last-Modified 头信息),并且也没有其他任何明显的新鲜度信息,内容通常不会被缓存。

3. 控制 HTTP 缓存的方法

1. HTML META 标签和 HTTP 头信息

HTML 文件的编写者会在文档的<HEAD>区域中加入描述文档的各种属性,这些 META 标签常常被用于标记文档不可以被缓存或者标记多长时间后过期。META 标签使用很简单,但是效率不高,因为能够读懂这个标记的浏览器只有少许几种,同时由于中间缓存几乎完全不解析文档中的 HTML 内容,所以也没有什么中间缓存(代理缓存和网关缓存)能读懂这个规则。如果要通过 META 标签来控制页面不缓存,一般情况下会在 Web 页面的<HEAD>区域中增加 "Pragma: no-cache" 的 META 标记。

2. 使用 Expires(过期时间)头信息来控制保鲜期

通常情况下,主要通过 HTTP 头信息来指示缓存和控制内容是否缓存。这些控制信息在 HTML 代码中是看不见的,一般由 Web 服务器自动生成,并在 HTTP 消息中进行标识。一个典型的 HTTP 1.1 协议响应消息的头信息看上去如下:

HTTP/1.1 200 OK
Data: Mon, 18 Jul 2011 22:14:49 GMT
Server: Apache
Cache-Control: max-age=3600, must-revalidate
Expires: Mon, 18 Jul 2011 23:14:49 GMT
Last-Modified: Sun, 5 Jun 2011 16:38:21 GMT
ETag: "5e36-767-576df70c"
Content-Length: 1040
(空行)
HTML 代码

该代码中包含了 Expires 头,指示Mon, 18 Jul 2011 23:14:49 GMT为过期时间。Expires 方式是 HTTP 控制缓存的基本手段,这个属性告诉缓存相关内容在多长时间内是新鲜的。过了这个时间,如果客户端向缓存请求这个内容,缓存就会向源服务器发送请求,检查文档是否已经发生了变化。大部分 Web 服务器设置 Expires 方式有多种,最常用的是设置成一个绝对的时间值,比如将内容最后被修改的时间点加上一个特定的时间段(比如一个小时)所得到的时间值。

3. 验证

在 HTTP 1.1 中对缓存提出了验证的概念,验证的目的就是检验缓存内容是否可用。当中间缓存存在一个过期的缓存内容,并且对应的访问请求到达时,缓存应该首先向源服务器或者其他保存有未过期的缓存服务器请求验证来确定本地的缓存内容是否可用。这个过程就是一个缓存消息的验证过程。

HTTP 1.1 把这种验证后再决定是否返回消息内容的方式叫 "有条件" 的请求返回方法,这样可以避免从源服务器或其他缓存服务器获取整个内容的消息,从而减少网络流量。当源服务器生成了一个完整的响应消息时,它会附带一个验证消息,中间缓存在缓存内容时可以保存这个验证信息,当缓存内容过期以后,中间缓存可以使用它生成一个 "有条件" 的请求来向源服务器请求验证。而源服务器或者在源服务器通信的路径上的其他缓存服务器(如果保存有未过期的内容)在收到这样的请求以后就可以将请求中包含的验证信息与自己本地的验证信息进行比较。如果两个验证信息相等,那么返回一个带有特定状态码(如 304 Not Modified,表示内容未修改过)且消息主体内容为空的响应消息,在这种情况下就减少了网络流量;如果两个验证信息不相等就需要传输一个包含新内容的完整响应消息。

4. Cache-Control(缓存控制)HTTP 头信息

指定过期时间和验证是 HTTP 1.1 的基本缓存机制,也是缓存的隐含指令。但是在某些情况下,服务器或客户端可能需要给 HTTP 缓存提出显示的指令。因此,HTTP 1.1 使用 Cache-Control 响应头信息来让网站的发布者可以更安全的控制他们的内容,并对过期时间进行限制。

有用的 Cache-Control 响应头信息包括如下几项:

  • max-age:缓存内容保持新鲜状态的最长时间。这个属性类似于过期时间,是基于请求时间的相对时间间隔,而不是绝对过期时间,单位是秒,即从请求时间开始到过期时间之间的秒数。
  • s-maxage:类似于 max-age 属性,它应用于共享缓存。
  • public:此属性标记认证内容也可以被缓存,一般来说,经过 HTTP 认证才能访问的内容是默认不能缓存的。
  • no-cache:强制将每次访问请求直接发送给源服务器,而不经过中间缓存进行前面提到的验证。这对那些需要在源服务器进行用户认证的应用非常有用,也适用于那些严格要求使用最新数据的应用。
  • no-store:强制缓存在任何情况下都不要缓存任何内容。
  • must-revalidate:告诉缓存必须遵循源服务器赋予的内容新鲜度。由于 HTTP 允许缓存在某些特定情况下返回过期数据,所以通过指定这个属性,源服务器可以告诉缓存,如果缓存内容处于过期状态,则在对访问请求进行响应前必须到源服务器进行重新验证。
  • proxy-revalidate:proxy-revalidate 和 must-revalidate 基本相同,只是它不能应用于非共享的代理缓存。它允许在客户端缓存中保存那些经过权限认证的响应消息(包含有 "public" 来保证它们可以被缓存),在缓存内容没有过期之前,遇到相同的访问请求则可以返回缓存的数据而无须经过源服务器的验证,如果内容过期则仍需经过服务器重新验证。对于服务多个用户的代理缓存来说,为了保证每个用户都是被授权的,仍需每次都去服务器进行验证(这样的授权响应同样需要使用 public 指令来允许它们能够被缓存)。

5. Pragma HTTP 头信息

HTTP 还有一种缓存控制机制,为使用 Pragma HTTP 头信息的方式。Pragma 属于通用头,用来包含特定的执行指令,这些指令可以适用于客户端、代理、网关、源服务器中的任何接收者,但是 HTTP 协议中认为 Pragma 指令规定的行为是可选的。

当 "Pragma: no-cache" 出现在请求消息中时,即使缓存设备中缓存了此请求响应所需的内容,也会直接将此请求转发到源服务器上。虽然在 HTTP 1.1 中提到了通过 Pragma 控制缓存的方法,但这主要是为了向 HTTP 1.0 兼容,因为支持 HTTP 1.0 的缓存主要还是通过这种方法来控制内容缓存的。HTTP 1.1 中主要还是通过 Cache-Control 头信息来控制缓存,所以协议要求当一个 HTTP 1.1 的请求从客户端发出时,既应该包含 Pragma 指令,也应该包含 Cache-Control 的控制指令,这样,请求从客户端发给源服务器的过程中,分别支持 HTTP 1.1 和 HTTP 1.0 的缓存设备都可以读懂指令的信息。如果发送指令的客户端本身只支持 HTTP 1.0,那么支持 HTTP 1.1 的中间缓存在收到请求消息后必须以 Pragma 中的指令来控制缓存。

注:在 HTTP 规范中 Pragme 属性没有任何关于响应信息头 Pragma 属性的说明,它仅出现在请求头信息中,即头信息由浏览器发送给服务器,但实际上只有少数几种缓存服务器会遵循请求消息中的这个头信息。因此,在很多情况下,使用 Pragma 属性不一定管用。

最新文章

  1. AnjularJS系列2 —— 表单控件功能相关指令
  2. TProfiler
  3. 使用Webview实现app启动引导页
  4. Tomcat Connector三种运行模式(BIO, NIO, APR)的比较和优化
  5. STC12C5A60S2笔记4(复位)
  6. 实记JLink刷固件方法
  7. 常用DOM笔记
  8. js运动
  9. 火狐的打开3D效果
  10. tangible T4 Editor 2.2.3 for VS2010 / VS2012 / VS2013 Preview
  11. solr总结 第六部分:solr查询语法
  12. PHP学习之-1.2 认识PHP脚本标识
  13. hdu 5073 Galaxy(2014acm鞍山亚洲分部 C)
  14. GCD实现倒计时
  15. Python内置函数(5)——pow
  16. (三) Keras Mnist分类程序以及改用交叉熵对比
  17. DOM4j 修改和删除
  18. C# 6.0:nameof操作符
  19. Nginx 磁盘IO的优化
  20. 【好用的Mac分屏软件】Magnet for Mac 2.3

热门文章

  1. [转载]边框回归(Bounding Box Regression)
  2. Windows 查看端口使用、根据pid查找引用程序
  3. 重拾MVC——第一天:数据库连接与SqlDbHelper
  4. libusb移植
  5. lua table vs closure
  6. 第五章、Django之多表查询进阶与事务
  7. SpringAOP的实现方式
  8. php 5.6 与 php 7 的区别
  9. R的数据结构--数组
  10. linux (wsl) npm 无法安装包