原文地址:https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn

通过网络获取内容既缓慢,成本又高:大的响应需要在客户端和服务器之间进行多次往返通信,这拖延了浏览器可以使用和处理内容的时间,同时也增加了访问者的数据成本。因此,缓存和重用以前获取的资源的能力成为优化性能很关键的一个方面。

好消息是每个浏览器都实现了 HTTP 缓存! 我们所要做的就是,确保每个服务器响应都提供正确的 HTTP 头指令,以指导浏览器何时可以缓存响应以及可以缓存多久。

  • 如果在应用中使用 Webview 来获取和显示网页内容,可能需要提供额外的配置标志,以确保启用了 HTTP 缓存,并根据用途设置了合理的缓存大小,同时,确保缓存持久化。查看平台文档并确认您的设置!

服务器在返回响应时,还会发出一组 HTTP 头,用来描述内容类型、长度、缓存指令、验证令牌等。例如,在上图的交互中,服务器返回了一个 1024 字节的响应,指导客户端缓存响应长达 120 秒,并提供验证令牌(x234dff),在响应过期之后,可以用来验证资源是否被修改。

使用 ETag 验证缓存的响应

TL;DR

  • 服务器通过 ETag HTTP 头传递验证令牌
  • 通过验证令牌可以进行高效的资源更新检查:如果资源未更改,则不会传输任何数据。

让我们假设在首次获取资源 120 秒之后,浏览器又对该资源发起了新请求。首先,浏览器会检查本地缓存并找到之前的响应,不幸的是,这个响应现在已经’过期’,无法在使用。此时,浏览器也可以直接发出新请求,获取新的完整响应,但是这样做效率较低,因为如果资源未被更改过,我们就没有理由再去下载与缓存中已有的完全相同的字节。

这就是 ETag 头中指定的验证令牌所要解决的问题:服务器会生成并返回一个随机令牌,通常是文件内容的哈希值或者某个其他指纹码。客户端不必了解指纹码是如何生成的,只需要在下一个请求中将其发送给服务器:如果指纹码仍然一致,说明资源未被修改,我们就可以跳过下载。

在上面的例子中,客户端自动在If-None-MatchHTTP 请求头中提供 ETag 令牌,服务器针对当前的资源检查令牌,如果未被修改过,则返回304 Not Modified响应,告诉浏览器缓存中的响应未被修改过,可以再延用 120 秒。注意,我们不必再次下载响应 - 这节约了时间和带宽。

作为网络开发人员,您如何利用高效的重新验证? 浏览器代替我们完成了所有的工作:自动检测是否已指定了验证令牌,并会将验证令牌附加到发出的请求上,根据从服务器收到的响应,在必要时更新缓存时间戳。实际上,我们唯一要做的就是确保服务器提供必要的 ETag 令牌:查看服务器文档中是否有必要的配置标志。

注意

  • 提示:HTML5 Boilerplate 项目包含了所有最流行的服务器的配置文件样例,并且为每个配置标志和设置都提供了详细的注释:在列表中找到您喜欢的服务器,查找适合的设置,然后复制/确认您的服务器配置了推荐的设置。

Cache-Control

TL;DR

  • 每个资源都可以通过 Cache-Control HTTP 头来定义自己的缓存策略
  • Cache-Control 指令控制谁在什么条件下可以缓存响应以及可以缓存多久

最好的请求是不必与服务器进行通信的请求:通过响应的本地副本,我们可以避免所有的网络延迟以及数据传输的数据成本。为此,HTTP 规范允许服务器返回 一系列不同的 Cache-Control 指令,控制浏览器或者其他中继缓存如何缓存某个响应以及缓存多长时间。

注意

  • Cache-Control 头在 HTTP/1.1 规范中定义,取代了之前用来定义响应缓存策略的头(例如 Expires)。当前的所有浏览器都支持 Cache-Control,因此,使用它就够了。

no-cache 和 no-store

no-cache表示必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载。

相比之下,no-store更加简单,直接禁止浏览器和所有中继缓存存储返回的任何版本的响应 - 例如:一个包含个人隐私数据或银行数据的响应。每次用户请求该资源时,都会向服务器发送一个请求,每次都会下载完整的响应。

public和 private

如果响应被标记为public,即使有关联的 HTTP 认证,甚至响应状态码无法正常缓存,响应也可以被缓存。大多数情况下,public不是必须的,因为明确的缓存信息(例如max-age)已表示 响应可以被缓存。

相比之下,浏览器可以缓存private响应,但是通常只为单个用户缓存,因此,不允许任何中继缓存对其进行缓存 - 例如,用户浏览器可以缓存包含用户私人信息的 HTML 网页,但是 CDN 不能缓存。

max-age

该指令指定从当前请求开始,允许获取的响应被重用的最长时间(单位为秒) - 例如:max-age=60表示响应可以再缓存和重用 60 秒。

定义最优 Cache-Control 策略

按照上面的决策树来确定您的应用使用的特定资源或一组资源的最优缓存策略。理想情况下,目标应该是在客户端上缓存尽可能多的响应、缓存尽可能长的时间,并且为每个响应提供验证令牌,以便进行高效的重新验证。

Cache-Control 指令 说明
max-age=86400 浏览器和任何中继缓存均可以将响应(如果是`public`的)缓存长达一天(60 秒 x 60 分 x 24 小时)
private, max-age=600 客户端浏览器只能将响应缓存最长 10 分钟(60 秒 x 10 分)
no-store 不允许缓存响应,每个请求必须获取完整的响应。

根据 HTTP Archive,在排名最高的 300,000 个网站中(Alexa 排名),所有下载的响应中,几乎有半数可以由浏览器进行缓存,对于重复性网页浏览和访问来说,这是一个巨大的节省! 当然,这并不意味着特定的应用会有 50% 的资源可以被缓存:有些网站可以缓存 90% 以上的资源, 而有些网站有许多私密的或者时间要求苛刻的数据,根本无法被缓存。

审查您的网页,确定哪些资源可以被缓存,并确保可以返回正确的 Cache-Control 和 ETag 头。

废弃和更新已缓存的响应

TL;DR

  • 在资源"过期"之前,将一直使用本地缓存的响应
  • 通过将文件内容指纹码嵌入网址,我们可以强制客户端更新到新版的响应
  • 为了获得最佳性能,每个应用需要定义自己的缓存层级

浏览器发出的所有 HTTP 请求会首先被路由到浏览器的缓存,以查看是否缓存了可以用于实现请求的有效响应。如果有匹配的响应,会直接从缓存中读取响应,这样就避免了网络延迟以及传输产生的数据成本。然而,如果我们希望更新或废弃已缓存的响应,该怎么办?

例如,假设我们已经告诉访问者某个 CSS 样式表缓存长达 24 小时 (max-age=86400),但是设计人员刚刚提交了一个更新,我们希望所有用户都能使用。我们该如何通知所有访问者缓存的 CSS 副本已过时,需要更新缓存? 这是一个欺骗性的问题 - 实际上,至少在不更改资源网址的情况下,我们做不到。

一旦浏览器缓存了响应,在过期以前,将一直使用缓存的版本,这是由 max-age 或者 expires 指定的,或者直到因为某些原因从缓存中删除,例如用户清除了浏览器缓存。因此,在构建网页时,不同的用户可能使用的是文件的不同版本;刚获取该资源的用户将使用新版本,而缓存过之前副本(但是依然有效)的用户将继续使用旧版本的响应。

所以,我们如何才能鱼和熊掌兼得:客户端缓存和快速更新? 很简单,在资源内容更改时,我们可以更改资源的网址,强制用户下载新响应。通常情况下,可以通过在文件名中嵌入文件的指纹码(或版本号)来实现 - 例如 style.x234dff.css。

因为能够定义每个资源的缓存策略,所以,我们可以定义’缓存层级’,这样,不但可以控制每个响应的缓存时间,还可以控制访问者看到新版本的速度。例如,我们一起分析一下上面的例子:

  • HTML 被标记成no-cache,这意味着浏览器在每次请求时都会重新验证文档,如果内容更改,会获取最新版本。同时,在 HTML 标记中,我们在 CSS 和 JavaScript 资源的网址中嵌入指纹码:如果这些文件的内容更改,网页的 HTML 也会随之更改,并将下载 HTML 响应的新副本。
  • 允许浏览器和中继缓存(例如 CDN)缓存 CSS,过期时间设置为 1 年。注意,我们可以放心地使用 1 年的’远期过期’,因为我们在文件名中嵌入了文件指纹码:如果 CSS 更新,网址也会随之更改。
  • JavaScript 过期时间也设置为 1 年,但是被标记为 private,也许是因为包含了 CDN 不应缓存的一些用户私人数据。
  • 缓存图片时不包含版本或唯一指纹码,过期时间设置为 1 天。

组合使用 ETag、Cache-Control 和唯一网址,我们可以提供最佳的方案:较长的过期时间,控制可以缓存响应的位置,以及按需更新。

缓存检查表

不存在最佳的缓存策略。根据您的通信模式、提供的数据类型以及应用特定的数据更新要求,必须定义和配置每个资源最适合的设置以及整体的’缓存层级’。

在定义缓存策略时,要记住下列技巧和方法:

  1. 使用一致的网址:如果您在不同的网址上提供相同的内容,将会多次获取和存储该内容。提示:注意,网址区分大小写
  2. 确保服务器提供验证令牌 (ETag):通过验证令牌,如果服务器上的资源未被更改,就不必传输相同的字节。
  3. 确定中继缓存可以缓存哪些资源:对所有用户的响应完全相同的资源很适合由 CDN 或其他中继缓存进行缓存。
  4. 确定每个资源的最优缓存周期:不同的资源可能有不同的更新要求。审查并确定每个资源适合的 max-age。
  5. 确定网站的最佳缓存层级:对 HTML 文档组合使用包含内容指纹码的资源网址以及短时间或 no-cache 的生命周期,可以控制客户端获取更新的速度。
  6. 搅动最小化:有些资源的更新比其他资源频繁。如果资源的特定部分(例如 JavaScript 函数或一组 CSS 样式)会经常更新,应考虑将其代码作为单独的文件提供。这样,每次获取更新时,剩余内容(例如不会频繁更新的库代码)可以从缓存中获取,确保下载的内容量最少。
作者

Ilya is a Developer Advocate and Web Perf Guru

除非另有声明,这篇文章的内容基于 创意共用 3.0 许可共享,样例代码基于Apache 2.0 许可共享。详细信息请查看我们的站点政策.

 
 

【转】http 缓存的更多相关文章

  1. 探究javascript对象和数组的异同,及函数变量缓存技巧

    javascript中最经典也最受非议的一句话就是:javascript中一切皆是对象.这篇重点要提到的,就是任何jser都不陌生的Object和Array. 有段时间曾经很诧异,到底两种数据类型用来 ...

  2. 哪种缓存效果高?开源一个简单的缓存组件j2cache

    背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...

  3. ASP.NET Core 中间件之压缩、缓存

    前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.Respons ...

  4. ASP.NET Core 折腾笔记二:自己写个完整的Cache缓存类来支持.NET Core

    背景: 1:.NET Core 已经没System.Web,也木有了HttpRuntime.Cache,因此,该空间下Cache也木有了. 2:.NET Core 有新的Memory Cache提供, ...

  5. [Java 缓存] Java Cache之 DCache的简单应用.

    前言 上次总结了下本地缓存Guava Cache的简单应用, 这次来继续说下项目中使用的DCache的简单使用. 这里分为几部分进行总结, 1)DCache介绍; 2)DCache配置及使用; 3)使 ...

  6. [原创]mybatis中整合ehcache缓存框架的使用

    mybatis整合ehcache缓存框架的使用 mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓 ...

  7. 探索ASP.NET MVC5系列之~~~5.缓存篇(页面缓存+二级缓存)

    其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...

  8. 深究标准IO的缓存

    前言 在最近看了APUE的标准IO部分之后感觉对标准IO的缓存太模糊,没有搞明白,APUE中关于缓存的部分一笔带过,没有深究缓存的实现原理,这样一本被吹上天的书为什么不讲透彻呢?今天早上爬起来赶紧找了 ...

  9. 缓存工厂之Redis缓存

    这几天没有按照计划分享技术博文,主要是去医院了,这里一想到在医院经历的种种,我真的有话要说:医院里的医务人员曾经被吹捧为美丽+和蔼+可亲的天使,在经受5天左右相互接触后不得不让感慨:遇见的有些人员在挂 ...

  10. .net 分布式架构之分布式缓存中间件

    开源git地址: http://git.oschina.net/chejiangyi/XXF.BaseService.DistributedCache 分布式缓存中间件  方便实现缓存的分布式,集群, ...

随机推荐

  1. 【MFC】MFC技巧学习 当做字典来查

    MFC技巧学习 摘自:http://www.cnblogs.com/leven20061001/archive/2012/10/17/2728023.html 1."属性页的添加:创建对话框 ...

  2. 数据库需要支持emoji表情

    由于需要实现emoji表情评论的功能,所以数据库需要支持emoji表情的存储,根据查询的资料最终实现了该功能,现将实现的过程以及过程遇到的一些问题记录下来,供大家参考和交流. mysql的utf8编码 ...

  3. c语言编译执行过程

    <h4>认识C编译执行过程</h4>认识C编译执行过程,是C学习的开端.简单说C语言从编码编译到执行要经历一下过程: C源代码编译---->形成目标代码,目标代码是在目标 ...

  4. C++之内核对象进行线程同步

    用户模式下的线程同步机制提供了非常好的性能,但他们也的确存在一些局限性,而且不适用于许多应用程序,例如,对Interlocked系列函数只能对一个值进行操作,它们从来不会把线程切换到等待状态.我们可以 ...

  5. asp.net core microservices 架构之eureka服务发现

    一 简介 微服务将需多的功能拆分为许多的轻量级的子应用,这些子应用相互调度.好处就是轻量级,完全符合了敏捷开发的精神.我们知道ut(单元测试),不仅仅提高我们的程序的健壮性,而且可以强制将类和方法的设 ...

  6. 关于 self = [super init];

    [plain] view plaincopyprint? - (id)init { self = [super init]; // Call a designated initializer here ...

  7. Oracle Sql Developer 连接oracle

    PL/Sql 初次使用需要配置文件内容,对于我这种Oracle新手来说各种配置有点凌乱,所以果断选择Sql Developer. 选择它是因为初次使用的时候它不用想PL/Sql那样配置文件,而只需要添 ...

  8. SqlServer 临时表

    SqlServer中临时表分为两种:一种是局部(本地)临时表,用#TableName表示.一种是全局(服务器)临时表,用##TableName表示. 创建临时表: 1. create table #T ...

  9. MongoDB:MapReduce基础及实例

    背景 MapReduce是个非常灵活和强大的数据聚合工具.它的好处是可以把一个聚合任务分解为多个小的任务,分配到多服务器上并行处理. MongoDB也提供了MapReduce,当然查询语肯定是Java ...

  10. JBPM的ORACLE脚本

    create table JBPM4_DEPLOYMENT ( DBID_ number(19,0) not null, NAME_ clob, TIMESTAMP_ number(19,0), ST ...