Web缓存基础:术语、HTTP报头和缓存策略 ===================================================================== ### 简介 对于您的站点的访问者来说,智能化的内容缓存是提高用户体验最有效的方式之一。缓存,或者对之前请求的临时存储,是HTTP协议实现中最核心的内容分发策略之一。分发路径中的组件均可以缓存内容来加速后续的请求,这受制于对内容所声明的缓存策略。 在这份指南中,我们将讨论一些Web内容缓存的基本概念。这主要包括如何选择缓存策略以保证互联网范围内的缓存能够正确的处理您的内容。我们将谈一谈缓存带来的好处、副作用、以及不同的策略能带来的性能和灵活性的最大结合。 什么是缓存? ------------ 缓存是存储可重用资源以便加快后续请求的术语。有许多不同类型的缓存,每种都有其自身的特点,应用程序缓存和内存缓存由于其对特定回复的加速,都很常用。 Web缓存,这份指南的中心,是一种不同类型的缓存。Web缓存是HTTP协议的一个核心特性,它能最小化网络流量,并且提升用户感知到的整个系统的响应速度。内容从服务器到浏览器的传输过程中,每个层面都可以找到缓存的身影。 Web缓存根据特定的规则缓存相应HTTP请求的回复。对于缓存内容的后续请求便可以直接由缓存满足而不是重新发送请求到Web服务器。 好处 ---- 有效的缓存技术不仅帮助用户,还帮助内容的提供者。一些缓存对内容分发带来的好处有: - **减少网络开销**:内容可以在从内容提供者到内容消费者网络路径之间的许多不同的地方被缓存。当内容在距离内容消费者更近的地方被缓存时,由于缓存的存在,请求将不会消耗额外的网络资源。 - **加快响应速度**:由于整个网络往返变得并不是必需的,缓存可以使内容的获得变得更快。缓存在距用户更近的地方被维护,例如浏览器缓存,使得内容的获取几乎是瞬时的。 - **在同样的硬件上提高速度**:对于保存原始内容的服务器来说,更多的性能可以通过允许激进的缓存策略从硬件上压榨出来。内容拥有者们可以利用交付路径上强大的服务器来应对某个内容负载的冲击。 - **网络中断时内容依旧可用**:使用某种策略,缓存可以保证内容对用户的可用,尽管当原始服务器可能在某段时间内变得不可用。 术语 ---- 在面对缓存时,您可能对一些经常遇到的术语可能不太熟悉。一些常见的术语如下: - **原始服务器**:原始服务器是内容的原始存放地点。如果您是Web服务器管理员,它就是您所管理的机器。它负责为任何不能从缓存中得到的内容进行回复,并且负责设置所有内容的缓存策略。 - **缓存命中率**:一个缓存有效性依照缓存的命中率进行度量。他是可以从缓存中得到的数据的请求数与所有请求数的比率。缓存命中率高意味着有很高比例的数据可以从缓存中获得。这通常是大多数管理员想要的结果。 - **新鲜度**:新鲜度用来描述一个缓存中的项目是否依旧适合返回给客户端。缓存中的内容只有在由缓存策略指定的新鲜度时间内才会被返回。 - **过期内容**:缓存中根据缓存策略的新鲜度设置已过期的内容。过期的内容被标记为"陈旧"。通常,过期内容不能用于回复客户端的请求。必须重新从原始服务器请求新的内容或者至少验证缓存的内容是否仍然准确。 - **校验**:缓存中的过期内容可以被验证有效以便刷新过期时间。验证过程包括联系原始服务器以检查缓存的数据是否依旧代表了最近的版本。 - **失效**:失效是依据过期日期从缓存中移除内容的过程。当内容在原始服务器上已被改变并且缓存中过期的内容会导致客户端签名问题时这个步骤是必须的。 还有许多其他的缓存术语,不过上面的这些应该能帮助你开始。 什么能被缓存? ------------- 某些特定的内容比其他内容更容易被缓存。对大多数站点来说,一些缓存友好的内容如下: - Logo和商标图像 - 通用的非旋转的图像(例如,导航图标) - 样式表 - 通用Javascript文件 - 可下载的内容 - 媒体文件 这些文件更倾向于不经常改变,所以长时间的对他们进行缓存能获得好处。 一些项目在缓存中必须加以注意: - HTML页面 - 旋转的图像 - 经常修改的Javascript和CSS文件 - 经过登陆的cookies访问的内容 一些内容从来不应该被缓存: - 与敏感信息相关的资源(银行数据,等) - 用户相关且经常更改的数据 除上面的通用规则外,通常您需要指定一些规则以便于更好地缓存不同种类的内容。例如,如果登陆的用户都看到的是同样的网站视图,就应该在任何地方缓存这个页面。如果登陆的用户会在一段时间内看到站点中用户敏感的视图,您应该让用户的浏览器缓存该数据但不让中间的任何中介缓存该视图。 Web内容缓存的位置 ----------------- Web内容会在整个分发路径中的许多不同的地点被缓存: - **浏览器缓存**:Web浏览器自身会维护一个小型缓存。典型地,浏览器使用一种策略指示缓存最重要的内容。这可能是用户相关的内容或可能会再次请求且下载代价较高。 - **中间缓存代理**:任何在客户端和您的基础架构之间的服务器都可以按期望缓存一些内容。这些缓存可能由ISP(网络服务提供者)或者其他独立组织提供。 - **反向缓存**:您的服务器基础架构可以为后端的服务实现自己的缓存。如果实现了缓存,那么便可以在请求接触点返回相应的内容而不用每次请求都使用后端服务。 上面的这些位置通常都可以根据它们自身的缓存策略和内容源点的缓存策略缓存一些相应的内容。 缓存头部 -------- 缓存策略依赖于两个不同的因素。缓存实体本身需要决定是否应该缓存可接受的内容。它可以只缓存部分可以缓存的内容,但不能缓存超过限制的内容。 缓存行为主要由缓存策略决定,而缓存策略由内容拥有者设置。这些策略主要由特定的HTTP头部来清晰的表达。 经过不同HTTP协议的迭代,一些不同的聚焦在缓存的头部出现了,它们的复杂度各不相同。下面列出了那些你也许仍应该注意的: - **`Expires`**:尽管使用范围相当有限,但`Expires`头部是非常简洁明了的。通常它设置一个未来的时间,内容会在此时间过期。这时,任何对同样内容的请求都应该回到原始服务器处。这个头部或许仅仅最适合回退模式(fall back)。 - **`Cache-Control`**:这是`Expires`的一个更加现代化的替换物。它已被很好的支持,且拥有更加灵活的实现。在大多数案例中,它比`Expires`更好,但同时设置两者的值也无妨。稍后我们将讨论您可以设置的`Cache-Control`的详细选项。 - **`ETag`**:`ETag`用于缓存验证。源服务器可以在首次服务一个内容时为该内容提供一个独特的`ETag`。当一个缓存需要验证这个内容是否即将过期,他会将相应的`ETag`发送回服务器。源服务器或者告诉缓存内容是一致的,或者发送更新后的内容(带着新的`ETag`)。 - **`Last-Modified`**:这个头部指明了相应的内容最后一次被修改的时间。它可能会作为保证内容新鲜度的验证策略的一部分被使用。 - **`Content-Length`**:尽管并没有明确的在缓存中被涉及,`Content-Length`头部在设置缓存策略时很重要。某些软件如果不提前获知内容的大小以留出足够空间,则会拒绝缓存该内容。 - **`Vary`**:缓存系统通常使用请求的主机和路径作为存储该资源的键。`Vary`头部可以被用来在决定一个请求是否是请求同样的内容时提醒缓存系统注意另一个附加的头部。它通常被用来告诉缓存系统同样的使用`Accept-Encoding`头部作为键,因此缓存系统能够区分压缩和未压缩的内容。 ### Vary头部的隐语 `Vary`头部提供给您存储同一个内容不同版本的能力。代价是稀释了缓存的入口。 在`Accept-Encoding`的案例中,设置`Vary`头部允许压缩和未压缩的内容拥有决定性的区别。这在服务某些不能处理压缩数据的浏览器时很重要,它可以保证基本的使用。`Accept-Encoding`是`Vary`的不错的候选值是因为它只有两到三个可用的值这一特性。 `User-Agent`这样的头部可能一开始看上去是区分移动浏览器和桌面浏览器以便您的站点提供差异化的服务的好主意。但`User-Agent`字符串是非标准的。结果将会造成在中间缓存中对同一内容的许多不同版本的缓存,这会导致缓存命中率的降低。`Vary`头部应该被保守地使用,尤其是您不具备在您控制的中间混存(例如,您控制着内容分发网络)中使请求标准化的能力。 缓存控制标志怎样影响缓存 ------------------------ 上面我们提到了`Cache-Control`头部如何被用与现代缓存策略标准。许多不同的缓存指令能够通过这个头部被设定,多个不同的指令通过逗号进行分分隔。 一些您可以使用的指示内容缓存策略的`Cache-Control`的选项如下: - **`no-cache`**:这个指令指示所有缓存的内容在新的请求到达时必须先重新验证,再发送给客户端。这条指令实际将内容立刻标记为过期的,但允许它们被验证技术重新验证以避免重新下载整个内容。 - **`no-store`**:这条指令指示缓存的内容不能以任何方式被缓存。它适合在回复敏感信息时设置。 - **`public`**:它将内容标记为公有的,这意味着它能被浏览器和其他任何中间节点缓存。通常,对于使用HTTP验证的请求,其回复被默认标记为`private`。`public`标记将会覆盖这个设置。 - **`private`**:它将内容标记为私有的。私有数据可以被用户的浏览器缓存,但*不能*被任何中间节点缓存。它通常用于用户相关的数据。 - **`max-age`**:这个设置指示了缓存内容的最大生存期,它在最大生存期后必须在源服务器处被验证或被重新下载。在现代浏览器中这个选项大体上取代了`Expires`头部,浏览器也将其作为决定内容的新鲜度的基础。这个选项的值以秒为单位表示,最大可以表示一年的新鲜度(31536000秒)。 - **`s-maxage`**:这个选项非常类似于`max-age`,它指明了内容能够被缓存的时间。区别是这个选项只在中间节点的缓存中有效。结合这两个选项可以构建更加灵活的缓存策略。 - **`must-revalidate`**:它指明了由`max-age`、`s-maxage`或`Expires`头部指明的新鲜度信息必须被严格的遵守。它避免了缓存的数据在网络中断等类似的场景中被使用。 - **`proxy-revalidate`**:它和上面的选项有着一样的作用,但只应用于中间的代理节点。在这种情况下,用户的浏览器可以在网络中断时使用过期内容,但中间的缓存内容不能用于此目的。 - **`no-transform`**:这个选项告诉缓存在任何情况下都不能因为性能的原因修改接收到的内容。这意味着,缓存不允许压缩接收到的内容(没有从原始服务器处接收过压缩版本的该内容)并发送。 这些选项能够以不同的方式结合以获得不同的缓存行为。一些互斥的值如下: - `no-cache`,`no-store`以及由其他前面为提到的选项指明的常用的缓存行为 - `public`和`private` 如果`no-store`和`no-cache`都被设置,那么`no-store`会取代`no-cache`。对于非授权的请求的回复,`public`是隐含的设置。对于授权的请求的回复,`private`选项是隐含的。他们可以通过在`Cache-Control`头部中指明相应的相反的选项以覆盖。 开发一种缓存策略 ----------------