跳到主要内容

Http缓存

HTTP缓存是一种通过存储常用的网络资源(如文档、图像、样式表等)以便以后重用的机制,从而提高网页加载速度、减少网络流量和降低服务器负载。HTTP缓存可以在客户端(浏览器)和服务器之间进行,通过在HTTP请求和响应中添加特定的头部字段来控制缓存行为。这些头部字段告诉客户端和服务器如何处理和存储资源,以及何时从缓存加载资源或从服务器获取最新版本。

缓存的优缺点

缓存是一种在计算机系统中常见的优化技术,它可以显著提高性能和效率,但也可能引发一些问题。以下是缓存的优点和缺点:

优点:

  1. 提高性能: 缓存允许重复使用已经获取过的数据,减少了重复获取相同数据的时间和网络资源开销,从而加速数据的访问速度,提高了系统的响应性能。

  2. 减少网络流量: 通过在本地缓存中存储数据,可以减少对服务器的请求次数,从而降低了网络流量和延迟,特别是对于移动设备等带宽受限的环境。

  3. 降低服务器负载: 缓存可以减轻服务器的负担,因为部分请求可以被本地缓存满足,从而减少服务器的请求处理压力。

  4. 改善用户体验: 缓存可以加快页面加载速度,提供更快的响应时间,使用户能够更快地访问网站内容,提升用户体验。

  5. 节省能源: 减少服务器的请求次数和响应时间可以降低服务器的能源消耗,对环境友好。

缺点:

  1. 可能导致过期数据: 缓存的数据可能在服务器端发生了变化,但客户端仍然使用旧的缓存数据。这可能会导致显示不准确或过期的信息。

  2. 一致性问题: 在分布式系统中,不同节点的缓存可能会引发一致性问题。当一个节点更新了数据,其他节点的缓存可能仍然存有旧的数据,导致不一致状态。

  3. 内存开销: 缓存需要一定的内存来存储数据。如果缓存过多的数据,可能会导致内存开销增加,甚至影响系统的稳定性。

  4. 缓存更新问题: 当数据发生变化时,需要确保缓存及时更新。管理缓存的过程可能会变得复杂,特别是在需要同时更新多个缓存实例的情况下。

  5. 缓存穿透: 当请求查询一个不存在的数据,缓存无法命中,会导致请求直接传递给后端,增加服务器负担。

HTTP缓存分类

HTTP缓存可以根据不同的机制和特性进行分类。以下是HTTP缓存的主要分类:

1. 强缓存(Expires 和 Cache-Control):

  • Expires: 通过在HTTP响应头中设置Expires字段,服务器可以指定资源的过期时间,即资源在客户端本地缓存中的有效期限。浏览器在过期时间之前可以直接从缓存中加载资源,而不需要向服务器发起请求。然而,Expires是一个绝对时间,如果服务器与客户端时间不同步,可能会导致缓存失效问题。

  • Cache-Control: Cache-Control 是一个更灵活的头部字段,通过在HTTP响应头中设置不同的指令,可以更精确地控制缓存策略。例如,使用 max-age 指令来设置资源的最大缓存时间,no-cache 指令告诉客户端在使用缓存前要先验证资源是否过期,no-store 指令禁止缓存等。

    • Cache-Control 取值含义:

      1. public 表示资源可以被任何中间缓存(例如代理服务器)缓存,适用于公共资源,如公共样式表、图像等。

      2. private 表示资源只能被终端用户的浏览器缓存,而不能被共享缓存(如代理服务器)缓存。适用于个人用户相关的数据,如用户特定的页面。

      3. max-age=<seconds> 指定资源的最大缓存时间,以秒为单位。浏览器在该时间内可以从缓存中加载资源,而无需向服务器发起请求。

      4. no-store 禁止缓存,浏览器不应该将资源存储在缓存中,每次都要向服务器请求最新的资源。也就没有所谓的强缓存、协商缓存了。

      5. no-cache 要求浏览器在使用缓存前,先向服务器验证资源是否过期。服务器会发送验证请求,如果资源未过期,则返回状态码304,浏览器从缓存加载资源。跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,只有在强缓存失效了才走协商缓存的,设置了no-cache就不会走强缓存了,每次请求都回询问服务端。

      6. must-revalidate 如果资源过期,浏览器必须向服务器验证资源是否仍然有效,用于控制缓存的强制重新验证。

      7. proxy-revalidate 类似于must-revalidate,但只适用于共享缓存(如代理服务器)。

      8. s-maxage=<seconds> 类似于max-age,但仅适用于共享缓存,如代理服务器。

      9. immutable 表示资源在指定的有效期内不会发生变化,即使到期也不需要重新验证。适用于静态资源,可以提高缓存命中率。有一些 “聪明” 的用户会点击浏览器左上角的刷新按钮去刷新页面,这时候就算资源没有过期,浏览器也会直接去请求服务器,再加个immutable的话,就算用户刷新页面,浏览器也不会发起请求去服务,浏览器会直接从本地磁盘或者内存中读取缓存并返回200状态,(from memory cache)。这是2015年facebook团队向制定 HTTP 标准的 IETF 工作组提到的建议:他们希望 HTTP 协议能给 Cache-Control 响应头增加一个属性字段表明该资源永不过期,浏览器就没必要再为这些资源发送条件请求了。

      10. no-transform 禁止中间缓存对资源进行转换,如压缩、加密等。

      通过组合这些指令,可以实现灵活的缓存策略,控制缓存的行为。Cache-Control提供了更精细的控制,相对于Expires头部来说,它更现代、更可靠,更适用于实际应用中的缓存管理。

  • 使用场景: 协商缓存适用于那些可能会发生变化,但变化频率相对较低的资源。如果资源发生变化的频率较低,使用协商缓存可以减少不必要的数据传输。它也适用于需要准确判断资源变化的情况。

  • Cache-Control和Expires哪些优先级高

在HTTP响应头部中,Cache-Control和Expires都可以用来控制缓存,并且都是用来设置缓存最长有效时间。但是它们的方式和优先级有所不同。

Cache-Control是HTTP/1.1新增的缓存控制方式,它比Expires更加灵活,可以优先级更高地控制缓存,因此优先级更高。如果同时存在Cache-Control和Expires,Cache-Control会覆盖Expires的设置。

Expires是HTTP/1.0中定义的缓存控制方式,它只能设置缓存过期时间,精度只能到秒级别,且存在时区差异问题,缺少灵活性。如果同时存在Cache-Control和Expires,浏览器会优先使用Cache-Control来判断是否使用缓存。

因此,尽管Expires仍然被广泛支持,但缓存控制的首选方法是Cache-Control。在实践中,建议同时使用Cache-Control和Expires来确保更好的缓存控制,例如:

Cache-Control: public, max-age=86400
Expires: Wed, 22 Jul 2020 09:00:00 GMT

其中,Cache-Control指定了public可被公共缓存,max-age为缓存最长有效时间;Expires指定了缓存过期时间。在这个例子中,缓存将在2020年7月22日9:00:00 GMT之后失效,或者在一天之内没有任何更新情况下被浏览器重新加载。

2. 协商缓存(ETag 和 Last-Modified):

  • ETag: ETag 是服务器为资源生成的唯一标识符,它在资源内容发生变化时会改变。服务器在响应头中返回 ETag 值,浏览器在后续请求中发送 If-None-Match 头部,用以验证资源是否发生变化。如果资源没有变化,服务器会返回状态码304(未修改),浏览器从缓存加载资源。

  • Last-Modified: Last-Modified 是资源的最后修改时间,服务器在响应头中返回该字段。浏览器在后续请求中发送 If-Modified-Since 头部,用以验证资源是否发生变化。如果资源没有变化,服务器会返回状态码304,浏览器从缓存加载资源。然而,Last-Modified 只精确到秒级,如果资源的变化频率较高,可能无法准确判断。

  • 使用场景: 协商缓存适用于那些可能会发生变化,但变化频率相对较低的资源。如果资源发生变化的频率较低,使用协商缓存可以减少不必要的数据传输。它也适用于需要准确判断资源变化的情况。

  • 为什么要有etag?

    • 你可能会觉得使用last-modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要etag呢?HTTP1.1中etag的出现(也就是说,etag是新增的)主要是为了解决几个last-modified比较难解决的问题:
      1. 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新get;
      2. 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),if-modified-since能检查到的粒度是秒级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
      3. 某些服务器不能精确的得到文件的最后修改时间。
  • response header中的etag、last-modified在客户端重新向服务端发起请求时,会在request header中换个key名:

     // response header
    etag: '5c20abbd-e2e8'
    last-modified: Mon, 24 Dec 2018 09:49:49 GMT

    // request header 变为
    if-none-matched: '5c20abbd-e2e8'
    if-modified-since: Mon, 24 Dec 2018 09:49:49 GMT

3. Vary 头部:

  • Vary 头部用于告诉缓存服务器,响应的内容可能因请求的不同特征而变化,例如根据不同的User-Agent来提供不同的内容。这可以确保缓存服务器针对不同的请求特征,提供适当的缓存内容,避免交叉污染。
  • 使用场景: 当响应的内容可能因请求的不同特征而变化时,使用Vary头部可以避免交叉污染。这在根据不同的User-Agent提供不同内容、多语言网站的内容变化等情况下特别有用。

4. Pragma 头部:

  • 虽然 Pragma 头部在 HTTP/1.0 中用于控制缓存,但在 HTTP/1.1 中已经被 Cache-Control 取代。在现代网络环境中,很少使用 Pragma 头部来控制缓存。
  • 使用场景: 在现代网络环境中,很少使用Pragma头部来控制缓存,因为大部分情况下Cache-Control提供了更灵活的控制。然而,在旧版本的HTTP/1.0中,使用Pragma: no-cache可以实现禁用缓存的效果。

综合来说,HTTP缓存通过不同的机制和头部字段,允许服务器和客户端在资源请求和响应中协调缓存策略,从而提高性能和效率。不同的缓存策略适用于不同的应用场景,开发者应根据实际需求选择适当的缓存机制。

缓存策略和缓存行为分

缓存策略和缓存行为是关于如何处理和使用缓存的两个概念,它们在HTTP通信中起着重要的作用。

缓存策略:

缓存策略是在服务器端设定的指导浏览器和中间缓存如何缓存和重用特定资源的规则集合。它是一种用于控制缓存行为的高级方法。缓存策略可以根据资源的特性、重要性和变化频率来设定,以便提高性能、减少网络流量和改善用户体验。

缓存策略包括设置HTTP响应头部中的字段,如Cache-ControlExpiresETagLast-Modified等。通过这些字段,服务器可以告诉浏览器何时可以缓存资源、何时需要重新验证资源、何时需要重新获取最新资源等。

缓存行为:

缓存行为是浏览器或客户端根据服务器提供的缓存策略,对资源进行加载、缓存和重用的具体操作。缓存行为决定了浏览器在请求资源时是否可以从本地缓存中加载,或者是否需要重新获取资源。它直接影响到页面加载速度、网络流量和用户体验。

在执行缓存行为时,浏览器会根据服务器返回的响应头部中的指令(如Cache-ControlExpiresETag等),判断资源是否可以从缓存中加载,或者是否需要与服务器交互以获取更新的资源。浏览器还会根据缓存行为来验证资源是否过期,以及如何处理过期资源的情况。

综合来看,缓存策略是服务器为资源设定的指导规则,而缓存行为是浏览器或客户端根据这些规则执行的实际操作,从而实现缓存的有效管理和使用。

TODO