这篇文章改编自 Owen Garrett 主持的网络研讨会,由 Andrew Alexeev 主持。
目录
介绍
0:00 | 介绍 |
1:22 | 关于本次网络研讨会 |
2:17 | 内容缓存的基本原理 |
2:35 | 基本原则 |
3:59 | HTTP 缓存机制 |
7:46 | NGINX 缓存什么? |
内容缓存和 NGINX
9:55 | NGINX 运行中 |
10:06 | NGINX 配置 |
11:14 | 缓存过程 |
15:32 | 缓存不仅仅适用于 HTTP |
17:10 | 如何理解正在发生的事情 |
17:38 | 缓存检测 |
19:08 | 缓存检测(续) |
20:09 | 缓存状态 |
21:57 | NGINX 中的内容缓存功能 |
22:40 | 工作原理 |
23:53 | 缓存内容如何存储? |
26:36 | 从磁盘加载缓存 |
28:07 | 管理磁盘缓存 |
29:22 | 从磁盘清除内容 |
控制缓存
31:27 | 控制缓存 |
32:30 | 延迟缓存 |
34:45 | 控制缓存时间 |
36:18 | 缓存 / 不缓存 |
37:25 | 多个缓存 |
39:07 | 快速回顾——为什么要缓存? |
39:44 | 为什么页面速度很重要? |
40:46 | 谷歌改变了规则 |
41:28 | 绩效不佳的代价 |
43:00 | NGINX 缓存让您 |
45:21 | 结束语 |
问答
47:20 | 字节范围请求 |
49:13 | 代理缓存重新验证 |
50:07 | 将缓存分布到相等的磁盘上 |
50:50 | 变化 标头 |
51:25 | 缓存原语 |
51:52 | 上行标头和数据 |
52:13 | *‑编码 标头 |
52:56 | 快闪记忆体 |
53:15 | 第 2 轮, Vary Heading |
53:45 | 页面速度 |
54:00 | 其他缓存 |
安德鲁·阿列克谢耶夫: 欢迎参加我们的最新网络研讨会;我叫安德鲁。 NGINX由 Igor Sysoev 编写,旨在帮助世界各地的网站运行得更快、响应更快、并易于扩展。 如今,NGINX 为互联网上超过 30% 的顶级网站和超过 20% 的所有网站提供支持。[编辑 – 这些统计数据适用于 2014 年 5 月网络研讨会期间的数据;请在此处查看当前值。 ] 我希望您会发现本次网络研讨会的内容对您现有或计划中的 NGINX 环境有用且适用。
现在请允许我向你们介绍欧文·加勒特。 Owen 负责 NGINX 的产品开发。今天,Owen 将讨论如何在 NGINX 中应用强大的缓存机制,让您的应用摆脱一遍又一遍生成重复内容的负担。
欧文·加勒特: 谢谢安德鲁和各位朋友,非常感谢你们在接下来的 45 或 50 分钟内加入我们。 我将讨论 NGINX 在内容缓存方面的功能,我们将研究一些可以提高性能的方法,我们将深入研究内容缓存的实际工作原理,以便您能够调试和诊断 NGINX 内部发生的情况,最后我将提供一些巧妙的提示和技巧,让您可以对 NGINX 对可以缓存的内容执行的操作进行真正细粒度的控制。
正如安德鲁所描述的,所有这些都是为了同一个核心原因,即减轻上游服务器生成重复内容的负担,以便它们可以自由地运行您的业务真正需要的应用。 增加这些服务器的数量,为您的最终用户提供更好的服务水平,并提高服务的可靠性,以应对来自互联网的流量激增以及可能的上游服务器故障。
在我们讨论 NGINX 的实现配置之前,我想快速回顾一下内容缓存的功能,以便我们都从同一个页面、从相同的核心信息基线开始。
内容缓存的基本原理是卸载上游服务器的重复工作。 当第一个用户请求网站上的某个内容(以蓝色图标和蓝线表示)时,他或她的 HTTP 请求会被转发到 NGINX,然后从那里转发到右侧灰色的上游服务器。
响应被转发回远程用户,但如果它是可缓存的(我们将很快讨论这意味着什么),那么 NGINX 会存储该响应的副本。 当另一个用户(橙色小伙子)请求相同内容时,NGINX 可以直接从其本地缓存中提供该内容,而不必伪造来自上游服务器的请求。
您的 Web 浏览器、CDN、您访问的网站、利用 CDN 以及其他设备上的 NGINX 都使用存储可缓存、不变内容的基本原则。 它作为反向代理缓存运行,通常部署在数据中心或云端,靠近托管您的 Web 内容和 Web应用的原始服务器。
原始服务器使用一个或多个易于理解和熟知的 HTTP 响应标头来声明内容的可缓存性。 当然,缓存服务器可以选择忽略或覆盖或改变这种行为。 但是,为了理解配置的内容缓存,您首先需要很好地理解原始服务器指示内容可缓存、不变以及[缓存的]副本具有一定生存期的方式。
内容缓存始于一个名为“Expires”
的简单 HTTP 响应标头。 原始服务器将提供一些内容并声明该内容在Expires
标头中的日期之前有效。 该方法很快被一种更有效、更灵活的方法——Cache -Control
标头所取代。
Expires
使用起来有点笨拙;效率低下。 日期需要正确的格式和解析,而Cache-Control
更加精简并与内容缓存的需求和速度保持一致。 Cache-Control
将内容声明为公共或私有,如果是公共的,则声明一个最大期限
- 即缓存对象需要重新请求该内容之前可以缓存的秒数。
直接控制缓存的第三个标头是X-Accel-Expires
。 这个标头是 NGINX 所特有的;只有 NGINX 能够理解它,并且如果你想覆盖上述标头的行为并直接告诉 NGINX 某项内容应该缓存多长时间,就可以使用它。
在某些情况下,你可能希望 Web 浏览器长时间缓存内容,但你也乐意让代理缓存(位于原始服务器前面的 NGINX)短时间缓存内容,这样就可以更快地反映更改并将其推送到新客户端,而旧客户端则不会在不需要时不断重新请求内容。
但是,该方法也可以使用最后两个标题来实现。 原始服务器可以声明某项内容的最后修改时间,并可以声明一个称为ETag
(实体标签)的东西——一个不透明的字符串,通常是哈希值,用于标识该内容。
然后客户端可以使用条件GET
发出请求。 他们可以在请求中包含If-Modified-Since
或If-None-Match
标头。 通过这样做,客户端声明他们拥有最后修改于特定日期或具有特定ETag
的内容的缓存版本。 如果服务器保存的最新版本与客户端拥有的版本匹配,则服务器只会响应304
未修改
。
这是一种快速响应,可节省网络带宽,并允许客户端随时检查缓存的内容副本是否仍然有效。
这五个标头从原始服务器的角度定义了内容如何可缓存 - 它的有效性,它的新鲜度,以及就ETag
而言,内容本身的详细信息。
NGINX 等代理缓存可以相对自由地解释它们遵守这些标头的严格程度。 显然,他们不应该缓存不可缓存的内容,但如果原始服务器说可以缓存,他们当然没有义务缓存某些内容。
NGINX 的基本行为是缓存所有由原始服务器指示为可缓存的GET
和HEAD
请求,前提是响应中没有Set-Cookie
标头。 这是因为Set-Cookie
标头通常包含特定于每个请求的唯一数据,并且默认情况下不适合缓存该值。
NGINX 通过特定的键(缓存键)缓存每个资源。 所有生成相同缓存键的请求都将得到相同资源的满足。 默认情况下,缓存映射到原始 URL,或者在配置中映射到此幻灯片中所示的字符串 [ $scheme$proxy_host$uri$is_args$args
]。 当 NGINX 缓存内容时,[有效期] 由X-Accel-Expires
标头(如果存在)或Cache-Control
标头或旧版Expires
标头定义(按优先顺序)。
您可以调整 NGINX 来屏蔽某些标头或提供固定的缓存时间,完全不管原始服务器说什么。 作为参考, RFC 2616定义了相对于 HTTP 的代理缓存所需的行为。
因此,这让您简要了解了缓存的普遍性和 NGINX 在缓存通常可以安全缓存的内容方面的基本默认行为,以加速您的网站并减轻原始服务器的负载。
现在让我们看一下 NGINX 的运行情况。 配置 NGINX 以启用内容缓存真的非常容易。
它是 NGINX 配置文件中的几行 - 一行用于在磁盘上创建缓存,并使用一组特定的参数来声明文件的布局方式、[该缓存中的对象的]到期时间以及该缓存的大小。 然后,第二个, proxy_cache
指令,与 NGINX 代理关联,并告诉它内容(结果、响应)应该缓存在命名缓存中。
因此在这里,我创建了一个名为one的缓存;它在内存中用于存储元数据,大小为 10 MB,但在磁盘上用于存储缓存内容,大小不受限制。 内容将被缓存,如果 60 分钟内没有活动,则会被回收。 那个名为one的缓存被我们的默认服务器使用。 这两个[指令], proxy_cache_path
和proxy_cache
,足以为 NGINX 中的代理服务器启用可靠、一致的缓存。
NGINX 接收请求并查询缓存时经历的过程定义如下。 我们首先读取一个请求(此幻灯片的左上角框),将缓存键组装到原始 URI 和我们将用于识别与该请求对应的资源的其他参数。 然后,我们通过访问内存中的元数据来检查磁盘上的缓存,以查看是否有该请求的响应的有效、最新的副本。
如果我们这样做了,那就算是成功了。 然后 NGINX 可以直接从缓存中响应。 它通过从磁盘提供内容来从缓存中进行响应,就像 NGINX 提供静态内容一样。 因此,您可以获得 NGINX 设计的性能、可靠性和可扩展性级别。 使用静态内容时,当您从 NGINX 的内容缓存提供内容时,您可以获得完全相同程度的[性能]。
另一方面,当我们检查缓存时,可能会发生缓存未命中。 这意味着我们没有缓存中的内容,或者缓存中的内容已过时,需要刷新。 那么,在最简单的情况下,该未命中意味着我们将从原始服务器请求该内容,接收响应,并检查它是否可缓存。
如果是,我们会将其流式传输到磁盘,就像 NGINX 对代理模式下处理的任何大型响应所做的那样。 然后,一旦它 [响应] 流到磁盘,我们就会将其复制到缓存中并直接从缓存中响应。 这种方法的挑战之一是,如果 NGINX 同时收到对同一内容的多个请求,那么它们都会导致丢失。
NGINX 通常会将所有这些请求转发到原始服务器,从而可能导致其过载 - 特别是对于那些需要很长时间才能生成响应的请求。 因此,您可以使用缓存锁。 proxy_cache_lock
指令确保如果正在刷新某个内容,则每次只会向上游服务器发送一个请求。
因此,在我描述的场景中,第一个请求将发送到上游服务器,但对相同内容的其余请求将被阻止,直到响应已被提供并已插入缓存(此时所有请求都可以得到满足),或者达到超时[由proxy_cache_lock_timeout
指令设置] - NGINX等待内容从服务器返回足够长的时间,然后在此时,您发布的请求将被转发到原始服务器。
因此, proxy_cache_lock
和超时为您提供了一些强大的控制,以确保当您的站点很繁忙并且对同一内容有许多请求时,如果该内容在缓存中过期,您不会突然用多个请求使原始服务器超载。
NGINX 中的缓存过程还有一个元素不太适合该流程图,因为它涵盖了图表的几乎每个阶段。 这是使用proxy_cache_use_stale
指令配置的功能。 在任何时候,如果其中一个阶段由于某种原因失败,例如当我们更新内容时遇到超时或者我们从上游服务器收到错误响应或其他类型的错误,我们可以选择直接从缓存中响应,即使缓存的内容已经过时。
如果您的上游服务器因流量过大或由于维护或灾难性错误而出现故障,这是一个非常强大的工具。 它确保 NGINX 可以使用缓存中的陈旧内容继续传递您的内容,而不是向客户端返回错误消息。
NGINX 中的缓存不仅仅适用于 HTTP。 NGINX 不仅仅是一个将请求转发到上游 Web 服务器的 HTTP 代理。 通常,这些上游 Web 服务器用于与 FastCGI 或 SCGI 等 API 交互。NGINX 可以直接以与 HTTP 代理非常相似的代理方式执行此操作。
NGINX 可以将其缓存技术用于HTTP 代理、 FastCGI 代理、 uWSGI 代理和SCGI 代理。 所有的运行方式都与 HTTP 代理大致相同,缓存存储在磁盘上并直接响应,避免了代理这些上游服务的需要。
NGINX 还可以与 memcached 服务器交互;这是一种略有不同的缓存方法。 在这种情况下,NGINX 不会直接存储内容,它使用 memcached 作为代理,并依赖外部代理将所需的内容填充到 memcached 中。 这可以是另一个有用的工具,并且如果需要,有很多方法可以从外部站点填充 memcached。 因此,如果这是您的业务需求,您可以将其视为为 NGINX 播种缓存的一种方式。
如果您拥有一个包含多个层的大型基础架构,有些进行缓存,有些不进行缓存,有些生成内容,有些不生成内容,那么缓存可能会非常复杂;那么开始追踪正在发生的事情(内容来自哪里)并诊断和调试您遇到的任何问题可能是一个相当大的挑战。 在 NGINX 中,我们尽可能为您提供便利。
利用一些复杂的检测手段,您可以动态地控制检测手段来跟踪内容的来源、存储在缓存中的位置以及缓存中的状态。
第一步是$upstream_cache_status
变量,该变量针对 NGINX 响应的每个请求进行计算,无论它是否来自缓存。 并且您可以使用add_header
指令不断将该变量的值添加到您的响应中。 按照惯例,我们将该值放在响应中的X-Cache-Status
标头中。 它可以采用七种不同值之一,声明如何提供该内容。 它是否绕过了缓存,是否来自重新验证,是否是命中。
这是您尝试了解您的回应来自何处的第一步。 它们是来自本地 NGINX 缓存,还是来自上游服务器? 您可以通过多种方式检查响应标头 - 从命令行使用curl
等工具,或者在 Web 浏览器中使用交互式调试器(这很常见)。
当然,您可能不想向所有最终用户声明该值。 您可能希望选择何时将该值插入到标头响应中。
NGINX 的配置语言让您可以灵活地进行选择。 此示例仅显示了实现此目的的众多方法之一。 我们获取远程地址,如果它恰好是本地主机,那么我们会将$upstream_cache_status
放入临时变量( $cache_status
)中。 最后,当我们返回响应时,我们将临时变量放入响应中。
这样,只有来自特定 IP 地址的请求才能看到$upstream_cache_status
变量的值。 您还可以做很多其他的事情;很快,我们将看看内容是如何存储在磁盘上的。 您可以将用于计算磁盘上位置的密钥放入响应中。 您可以在响应中放置各种参数,以帮助您在缓存运行时对其进行诊断。
NGINX Plus是我们的 NGINX 商业版本,它提供了许多附加功能,可帮助处理缓存等用例。 NGINX Plus 是 NGINX 的商业支持版本,由我们在莫斯科的工程团队构建,并经过大量回归测试以确保正确运行。
NGINX Plus 还包含许多针对希望使用 NGINX 作为反向代理和负载均衡器的企业的功能。 功能包括负载均衡、健康检查、高级视频流。 在此背景下,功能围绕扩展状态、更好的诊断以及 NGINX 中发生的情况的可视化。
[编辑——上面的幻灯片和下面的段落已经更新,以引用NGINX Plus API ,它取代并弃用了这里最初讨论的单独状态模块。]
作为一个演示,您可以跳转到demo.nginx.com/dashboard.html ,您将看到一个网页,其中显示了使用 NGINX Plus API 从 NGINX Plus 发布的状态数据。如果您运行指示的curl
命令,您将看到直接从 NGINX Plus 二进制文件获取的原始 JSON 数据(这里它被传送到jq
实用程序,将每个元素放在自己的行上并按层次缩进)。
在该 JSON 数据中,您将找到有关 NGINX Plus 部署中每个缓存状态的实时数据。 它与$upstream_cache_status
变量以及其他可以检测缓存的方法一起,为您提供了有关 NGINX 如何缓存内容的非常好的概述,并允许您深入了解各个请求,以确定该请求是否来自缓存以及缓存中的当前状态。
现在,我们已经了解了如何从外部检查联系人缓存,接下来让我们从内部看一下。 它在 NGINX 中如何发挥作用? 正如我之前提到的,NGINX 中的内容缓存功能与磁盘上的文件处理方式非常相似。 当您从我们的内容缓存提供内容时,您会获得与提供静态内容时相同的性能、相同的可靠性、相同的操作系统优化 - 这是 NGINX 闻名的性能。
内容缓存以持久缓存的形式存储在磁盘上。 我们与操作系统协同将磁盘缓存交换到内存中,并向操作系统页面缓存提供有关应将哪些内容存储在内存中的提示。 这意味着当我们需要从缓存中提供内容时,我们可以非常快速地完成。
有关缓存的元数据(有关缓存内容及其过期时间的信息)分别存储在所有 NGINX 进程的共享内存部分中,并且始终存在于内存中。 因此 NGINX 可以非常快速地查询缓存、搜索缓存;只有在需要提取响应并将其返回给最终用户时才需要转到页面缓存。
我们将研究内容是如何存储在缓存中的,我们将研究持久缓存是如何在启动时加载到空的 NGINX 工作进程中的,我们将研究 NGINX 对缓存自动执行的一些维护,我们将总结如何在特定情况下手动从缓存中删除内容。
您还记得,内容缓存是使用名为proxy_cache_path
的指令声明的。 该指令指定了参数的数量:缓存在文件系统上的存储位置、缓存的名称、元数据在内存中的缓存大小以及磁盘上的缓存大小。 在这种情况下,磁盘上有 40 MB 的缓存。
了解内容存储位置的关键是了解缓存键——NGINX 为每个可缓存资源分配的唯一标识符。 默认情况下,该标识符由请求的基本参数构成:方案、主机
标头、URI 和任何字符串参数。
但是如果您愿意,您可以使用诸如 cookie 值或身份验证标头,甚至是您在运行时计算的值来扩展它。 也许您想为英国用户和美国用户存储不同的版本。 通过配置proxy_cache_key
指令,这一切都成为可能。
当 NGINX 处理请求时,它将计算proxy_cache_key
,然后根据该值计算 MD5 和。 您可以使用我在幻灯片下方展示的命令行示例自行复制。 我们获取缓存键httplocalhost:8002/time.php并通过md5sum
进行传输。 要小心,当你从 shell 执行此操作时,不要同时泵入新的线路。
这将计算与可缓存内容相对应的 MD5 哈希值。 NGINX 使用该哈希值来计算应存储内容的磁盘位置。 您会在proxy_cache_path
中看到,我们指定了一个带有一个字符的两级缓存,然后是一个带有两个字符的目录。 我们从字符串末尾提取这些字符来创建一个名为4和名为9b的子目录,然后我们将缓存的内容(加上标题和少量元数据)放入磁盘上的文件中。
您可以测试内容缓存。 您可以将缓存键打印出来作为响应头之一,然后可以通过md5sum
对其进行计算以计算该值的哈希对应关系。 然后,您可以检查磁盘上的值以查看它是否真的存在以及 NGINX 缓存的标头,以了解所有这些是如何组合在一起的。
现在内容已存储在磁盘上并且是持久的,当 NGINX 启动时,它需要将该内容加载到内存中 - 或者更确切地说,它需要通过磁盘缓存,提取元数据,然后将元数据加载到每个工作进程使用的共享内存段的内存中。 这是通过称为缓存加载器的过程完成的。
缓存加载器在启动时启动并运行一次,将元数据以小块的形式加载到磁盘上: 每次处理 100 个文件,沙盒化时间为 200 毫秒,中间暂停 50 毫秒,然后重复此操作,直到遍历整个缓存并填充共享内存段。
然后缓存加载器退出并且不需要再次运行,除非重新启动或重新配置 NGINX 并且需要重新初始化共享内存段。 您可以调整缓存加载器的操作,如果您的磁盘速度非常快且负载较轻,这可能是合适的。 您可以让它运行得更快,或者如果您存储的缓存包含大量文件和慢速磁盘,并且您不希望缓存加载器在 NGINX 启动时使用过多的 CPU,那么您可能希望将它稍微放慢一点。
一旦缓存进入内存并且文件存储在磁盘上,就存在从未访问过的缓存文件可能永远存在的风险。 NGINX 会在第一次看到它们时存储它们,但如果没有对文件的更多请求,那么 [该文件] 就会留在磁盘上,直到有东西出现并将其清除。
这个东西就是缓存管理器;它定期运行,清除磁盘上在一定时间内未访问的文件,如果缓存太大并溢出了其声明的大小,它会删除文件。 它会以最近最少使用的方式删除它们。 您可以使用proxy_cache_path
[指令] 的参数来配置此操作,就像配置缓存加载器一样:
不活动
时间默认为10分钟。max-size
参数没有默认限制。 如果您对缓存施加最大大小
限制,有时它可能会超出该限制,但当缓存管理器运行时,它将修剪最近最少使用的文件以使其回到该限制以下。最后,有时您可能希望从磁盘中清除内容。 您想要找到一个文件并删除它;如果您知道我们之前讨论过的技术,那么这相对容易做到 - 通过md5sum
运行缓存键 - 或者只是在文件系统中运行递归grep
来尝试识别您需要删除的文件。
或者,如果您使用 NGINX Plus,则可以使用该产品内置的缓存清除功能。 缓存清除功能允许您从请求中获取特定参数;通常我们使用一种名为PURGE
的方法来识别它是缓存清除请求。
清除由特殊的 NGINX Plus 处理程序处理,该处理程序检查 URI 并从缓存中删除与该 URI 匹配的所有文件。 URI 可以加上星号作为后缀,从而成为词干。 在这种情况下,我们将使用清除功能删除从 localhost 主机端口 8001 提供的每个文件,但当然您也可以放置子目录。
无论您使用哪种方法,在任何时候您都可以完全安全地从磁盘上的缓存中删除文件,甚至rm
-rf
整个缓存目录。 NGINX 不会错过任何一个节拍;它会继续检查磁盘上是否存在文件。 如果它们丢失了,就会导致缓存未命中。 然后,NGINX 将继续从原始服务器检索缓存并将其存储回磁盘上的缓存中。 因此,如果您需要从缓存中清除单个文件,它始终是安全、可靠和稳定的。
因此,我们研究了缓存的工作原理,研究了 NGINX 中的实现,并深入研究了它如何将文件存储在磁盘上以获得与静态内容相同的性能。 现在让我们更深入地了解缓存。
对于简单的网站,您可以打开缓存,通常它会精确地执行其需要的操作以继续为您提供所需的性能级别和缓存行为。 但总是需要进行优化,并且经常会出现默认行为与您想要的行为不匹配的情况。
也许您的原始服务器没有设置正确的响应标头,或者您想覆盖 NGINX 内部指定的内容。 有多种方法可以配置 NGINX 来微调缓存的操作方式。
您可以延迟缓存。 如果您拥有大量内容,而其中大部分内容在一小时或一天内仅被访问一两次,那么这种情况非常常见。 在这种情况下,如果您的公司宣传册很少有人阅读,那么尝试缓存该内容通常会浪费时间。 延迟缓存允许您放置水印。 仅当请求次数达到一定值时,它才会存储该内容的缓存版本。 直到您达到proxy_cache_min_uses
水印,它才会在缓存中存储版本。
这使您可以更好地辨别哪些内容进入缓存。 缓存本身是一种有限的资源,通常受服务器内存量的限制,因为您需要确保缓存尽可能地分页到内存中。 因此,您常常希望限制某些类型的内容,并且只将热门的请求放入缓存中。
缓存重新验证是最近添加到 NGINX Open Source 和 NGINX Plus 的功能。 它修改了If-Modified-Since
功能,以便当 NGINX 需要刷新已缓存的值时,它不会进行简单的GET
来获取该内容的新版本;它会进行条件GET
,说“我有一个在这个特定时间和日期修改的缓存版本”。
源服务器可以选择响应304
未修改
,
实际上表示您拥有的版本仍然是最新版本。 这节省了上行带宽;原始服务器不必重新发送未改变的内容,并且还潜在地节省了磁盘写入量。 NGINX 不必将该联系人传输到磁盘,然后将其交换到位并覆盖旧版本。
您可以细粒度地控制内容缓存的时间。 通常,原始服务器将使用适合浏览器的缓存标头提供内容——长期缓存并相对频繁地请求刷新内容。 但是,您可能希望 NGINX 代理直接位于原始服务器前面,以便更频繁地刷新文件,从而更快地获取更改。
如果将浏览器的缓存超时时间从 60 秒减少到 10 秒,负载会大幅增加,但如果将 NGINX 中的缓存超时时间从 60 秒增加到 10 秒,负载只会略有增加。 对于每个请求,这将每分钟向您的原始服务器添加五个请求,而对于远程客户端,这完全取决于您的站点上活跃的类似事物的客户端数量。
因此,您可以覆盖原始服务器所说的逻辑和意图。 您可以屏蔽或告诉 NGINX 忽略某些标头: X-Accel-Expires
、 Cache-Control
或Expires
。 您可以使用 NGINX 配置中的proxy_cache_valid
指令提供默认缓存时间。
有时您可能不会缓存源服务器认为可缓存的内容,或者您可能希望确保绕过 NGINX 中存储的内容版本。proxy_cache_bypass 和 proxy_no_cache
指令
为您提供了这种程度的控制。
你可以使用它们作为一种快捷方式,表示如果设置了某组请求标头中的任何一个(例如 HTTP 授权)或者存在请求参数,那么你想要绕过缓存 - 要么自动更新 NGINX 中的缓存,要么完全跳过它并始终从原始服务器检索它。
通常,这些都是为了相当复杂的缓存决策而完成的,您需要对 cookie 和授权标头的值做出细粒度的决策,以控制应该缓存什么、应该始终从原始服务器接收什么以及什么永远不应该存储在 NGINX 缓存中。
最后,对于非常大规模的部署,您可能希望在单个 NGINX 实例中探索多个缓存,原因如下。 您可能针对 NGINX 代理上的不同租户采用不同的缓存策略,具体取决于您网站的性质以及该网站性能的重要性 - 甚至取决于每个租户在共享托管情况下注册的特定计划。
或者您可能在 NGINX 主机中拥有多个磁盘,并且在每个磁盘上部署单独的缓存是最有效的。 黄金法则是尽量减少从一个磁盘到另一个磁盘的复制次数,您可以通过将缓存固定到每个磁盘并将使用该缓存的每个代理的临时文件固定到正确的磁盘来实现这一点。
标准操作是,当 NGINX 从上游代理接收内容时,它会将该内容流式传输到磁盘,除非内容足够小且适合内存。 然后,一旦内容流到磁盘,它就会将其移动到缓存中。 如果临时文件在缓存中的位置(存储临时文件的磁盘)与存储缓存的磁盘相同,则该操作的效率会提高无限倍。
因此,我们讨论了缓存,讨论了 NGINX 使用的方法、NGINX 内部的实现以及可以调整它的方法。 当我们接近结束时,让我们快速回顾一下,以提醒自己为什么首先要缓存内容。 NGINX 已部署在全球 1.14 亿个网站上,其中许多用户部署 NGINX 是因为其具有 Web 加速和内容缓存功能。[编辑 - 此统计数据适用于 2014 年 5 月举行的网络研讨会。]
这些功能可以提高网站的速度——为最终用户提供更好的体验——网页速度确实非常重要。 多年来,分析师一直在监控用户行为,并提出了俗称的“N 秒规则”。 这是普通用户愿意等待页面加载和呈现的时间,之后他或她会感到无聊和不耐烦并转到其他网站或竞争对手的网站。
随着标准的提高和用户期望的越来越高,用户愿意等待的时间越来越短。 你可以通过一些略显可疑的数学知识推断出用户的耐心程度到 2016 年左右将达到负数。
但事实上,技术已经超越了我们。 几年前,谷歌在推出谷歌即时搜索时就以图形方式说明了这一点。 现在有了谷歌,当你在搜索框中输入搜索词时,甚至在你输入完成之前,谷歌就会显示候选搜索结果。 这体现了人们对现代互联网的期望发生了巨大的转变。 正如谷歌自己所说,“用户现在希望网页的反应就像翻书一样”——快速、无缝和流畅。
如果您未能达到该绩效水平,那么您的网站或网络服务所附加的 KPI 可能会受到重大影响。 无论是广告点击率: 谷歌自己发现,当他们的搜索页面加载时间增加半秒时,他们的广告点击率下降了 20%。 无论是收入:为了故意调查网页速度慢的影响,亚马逊故意将页面加载时间增加 100 毫秒的倍数,结果发现每增加 100 毫秒,受影响客户的收入通常会下降 1%。
许多其他分析师、网站和调查人员报告了对网站指标的类似影响,无论是页面停留时间、跳出率等。 最近,谷歌在计算搜索结果中的页面排名时开始考虑页面速度。 看起来要计算的是第一个字节的时间。 获取页面加载第一个字节的时间越长,页面排名受到的惩罚就越严重。 一个网站可能会遭遇这样的情况,即它甚至没有人访问它,因为它出现在谷歌搜索结果的第三、第四或第五页。
NGINX 的缓存功能允许您通过减少第一个字节的时间并使 Web 内容感觉更快、响应更快来改善最终用户体验。
NGINX 允许您整合和简化您的 Web 基础设施。 NGINX 不仅仅是一个独立的 Web 缓存。 NGINX 包含一个 Web 源服务器,它包含一个到 FastCGI 等 API 的直接网关,而在 NGINX Plus 中,它包含构建复杂的、企业级负载均衡器和应用交付控制器的功能。 这使您可以将 Web 基础架构中的多个不同网络组件合并为一个组件 - NGINX 或 NGINX Plus - 为您提供更可靠、更易于调试、更快速的解决方案。
NGINX 允许您通过从上游服务器中移除重复任务来增加服务器容量。 事实上,即使对于看似不可缓存的内容(例如,博客网站的首页),在 NGINX 代理上缓存一秒钟也是有价值的。
当一百个用户在同一秒内请求相同的内容时,NGINX 会将其减少为对原始服务器的单个请求,并且它会从其缓存中向这些用户提供内容,并保证该内容的过期时间不会超过一秒。 对于博客网站或类似的网站来说,这通常绰绰有余,但在性能上却有很大差异——无论是在上游服务器的负载还是在管理和部署足够容量的费用方面。
最后,不要忘记 NGINX 的战略用途之一:通过代理缓存的“使用陈旧”功能使您免受上游服务器故障的影响。 如果它们运行缓慢,如果它们返回错误,如果出现某种故障,那么 NGINX 可以回退到本地缓存的内容版本并继续使用该版本,直到上游服务器恢复。
全球最繁忙的网站中有 38% 主要使用 NGINX 来实现其网页加速和内容缓存功能。[编辑 – 此统计数据适用于 2014 年 5 月举行的网络研讨会;请在此处查看当前值。 ] 有关更多解决方案和详细信息,请查看nginx.com上的博客和功能简介,其中介绍了 NGINX 和 NGINX Plus 的功能。 请查看我们的网络研讨会列表,不仅包括未来的网络研讨会,还包括即将添加的本系列过去活动的网络研讨会。
如果您想进一步了解这些功能,当然您可以在nginx.org和nginx.com上找到文档和解决方案,但没有什么比下载并尝试更好的了。 您可以在nginx.org上找到开源产品,也可以在nginx.com上找到具有附加负载均衡、应用交付、管理和易用性功能的商业受支持产品。
所以各位,非常感谢你们的时间和关注。 我希望本次关于 NGINX 内容缓存的演示和讲解对大家有用且具有启发性。
让我们开始解答这些问题,看看我们做得如何。
我们对字节范围请求有疑问。 当客户端请求一段内容但只需要该内容的子集时,使用字节范围请求。 假设这是一个视频文件,而客户只需要视频的一部分。 或者很常见的是 PDF 文件:用户已经阅读了 PDF 文件中带有索引的标题,并且客户端只想下载一组特定的页面。 NGINX 的内容缓存如何发挥作用?
流程如下。 当 NGINX 收到资源的字节范围请求并且整个资源已在缓存中时,NGINX 将从缓存中响应客户端请求的字节范围。 如果该资源不在缓存中,NGINX 将向上游服务器发出整个资源的请求,并将该资源存储在缓存中。 目前,此时 NGINX 将不会遵循字节范围请求,它会将整个资源返回给客户端。 大多数情况下,这是可以接受的行为。
例如,如果客户端正在下载 PDF 文档,则它的第一个请求无论如何都会针对整个文档,并且只有当该文档被流式传输时,客户端才会中止连接并开始发出字节范围请求。 因此,对于缓存内容,NGINX 尊重字节范围请求。 对于未缓存的内容,NGINX 会从上游服务器检索全部内容,并在该实例中将全部内容返回给客户端。
这是关于代理缓存重新验证功能的问题。 此功能允许 NGINX 向上游服务器发出条件GET
以检查内容是否已发生更改。 问题是:
代理缓存重新验证因素是否考虑了ETag
或仅仅是内容的If-Modified-Since
日期?
答案是它只检查内容的If-Modified-Since
日期,并且作为实践,通常最好在响应中始终包含If-Modified-Since
并将ETag
视为可选,因为它们不像您在响应中处理的“上次修改”日期那样一致或广泛地处理。
NGINX 是否可以在几个相等的磁盘之间对单个站点的缓存进行负载平衡,以获得最佳性能?
是的,但需要做一点工作。 典型的情况是部署一组没有 RAID 的磁盘,然后部署单独的缓存,每个磁盘固定一个缓存。 它需要一些额外的配置和流量分区。 如果您需要一些配置方面的帮助,请联系我们的社区,我们会在那里处理您的具体请求,或者如果您使用 NGINX Plus,请联系我们的支持团队,我们会很乐意为您提供帮助。
变化
头球NGINX 是否将Vary
标头考虑在缓存命中和未命中中?
不,NGINX 不会自动处理Vary
标头。 如果这是一个问题,那么将Vary
标头添加到代理缓存键很简单,这样用于存储响应的唯一键就包含Vary
标头的值。 然后您可以存储多个不同的版本。
是否所有缓存原语和指令都受到尊重?
总体来说,是的。 有几个边缘情况,例如Vary
标头,不受尊重。 在许多情况下,各种缓存对 RFC 要求的解释存在一定程度的自由度。在可能的情况下,我们选择了可靠、一致且易于配置的实现。
上游标头和数据是否都被缓存了?
是的,他们是。 如果您从缓存收到响应,那么标头和响应主体都会被缓存。
*‑编码
标头标头值和响应主体都被缓存,所以...如果 NGINX 无法在使用各种不同的Transfer-Encoding
组合时正常运行,我会感到非常惊讶。 Accept-Encoding
通常通过Vary
标头实现,因此之前关于需要将Vary
标头放入缓存键的评论适用于此(对于不支持此功能的客户端的情况)。
缓存对 SPDY 有用吗?
绝对地。 你可以将它视为 NGINX 的前端代理,尽管实际上它与 NGINX 内核有着非常非常紧密的交织。 是的,SPDY 用于缓存。
瓦里
头球,第二轮这是有关Vary
标头的另一个问题。 为了确认,如果您使用Vary
‑header 响应和 gzip,请查看Trac或我们的社区网站上的一些讨论以寻找解决方案。 最常见的方法是将Vary
标头嵌入缓存键中。
问: PageSpeed 是否使用 NGINX 缓存还是其自己的缓存机制?
您需要与PageSpeed 开发人员分享这个问题。
问: 其他内容缓存与 NGINX 相比如何?
CDN 是非常有效的内容缓存解决方案。 CDN 作为一种服务进行部署;您对内容如何缓存以及内容如何在其中过期的控制非常有限,但它们是将内容更贴近最终用户的非常有效的工具。 NGINX 是一种非常有效的加速 Web应用的工具,并且通常两者一起部署。 对于 Varnish 等独立缓存而言:它们又是十分强大的技术,在许多方面其工作方式与 NGINX 类似。 NGINX 的优点之一是它将原始服务应用网关、缓存和负载均衡整合到一个解决方案中。 这样就为您提供了一个更简单、更整合的基础设施,如果您遇到任何问题,该基础设施将更易于推出、更易于管理、更易于调试和诊断。
要观看此帖子所基于的网络研讨会,请访问此处。
“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”