现代 CSS

基于 Varnish ESI 的缓存控制方案

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

对于优化移动端的加载速度,我非常坚信适用于所有用户的优化方案,才是最佳方案。做出这一判断的出发点是因为,我们无法预知用户准确的联网方式,可能他使用的并不是手机,但却在使用手机热点的信号,或者使用的是不稳定的无线信号...

在上一篇文章《使用 Varnish 优化移动端站点》中,我向各位解释了使用 Varnish 这类反向代理服务器的具体方法,最终实现了动态更新、优化加载速度的目的。如果你是第一次接触 Varinish,我相信那篇文章将会是很好的入门教程,而今天,我将会继续深入介绍一些配置方面的细节。

本文中,我将会为你解析,如何使用 ESI(Edge Side Includes,页面片段缓存)优化页面中无法长期缓存的部分。

窘境

网页的大块头往往是内容部分,而这些内容往往不会频繁更新。一般而言,当你发表了一些内容之后,它通常会以一种静态资源的形式存在。即使更新之后,也只是花费少量时间重新解析一次,但之后依然是长期的静态形式。当发表的内容已经失效时,更新相关内容就会变得非常重要,此时可以使用 cache invalidation 清空页面缓存。此外,页面中还有一些部分你是完全不想缓存的,比如页面中的个人信息。还有一些内容是你希望短期缓存的,比如一个频繁更新的新闻插件。当页面包含了这些类型的内容时,面对过于复杂的缓存策略,你可能会考虑放弃缓存它们。但是,腾空出世的 ESI 解决了这一难题,并进一步优化了网站的总体性能。

ESI

ESI 是一个语言规范,具体来说是一种收集页面片段,复用到其他页面的技术。这一规范已于 2001 年提交 W3C,但是目前仍然处于 “W3C Note” 状态,等待更深入的讨论,并没有真正加入 W3C 规范中,也还没有工作组来更新。

ESI 收集页面片段的方式,类似于 SSI(Server Side Includes,服务端缓存)和 PHP 收集片段的方式,但是 ESI 是为类似 Varnish 的反向代理服务器设计的,这类服务器会置于 Apache 等网页服务器之前,用来缓存数据。

工作方式

Varnish 实现了 ESI 七条语句中的三条。相关规范还在持续推进中,更多内容请参考 Varnish 官方文档 中关于 ESI 的支持列表。这意味着我们可以在页面中使用 ESI,并可以告知 Varnish 短期缓存哪些部分而不是整个文档,甚至可以完全不缓存。如果你已经成功安装和运行了 Varnish,那么很简单就可以启用 ESI。快来拥抱应对高点击率的新方案吧:将大量页面缓存起来。

实战

如果你不想搭建 Varnish 而又想继续学习下去,那么可以下载我在 GitHub 的 Vagrant 软件包,使用该软件包可以安装一个基础的 LAMP 环境和 Varnish 服务器。使用前,请仔细阅读 README 文件,了解环境配置的方式。

我这里有一个使用 Bootstrap 模板创建的标准博客布局。在侧栏,我想设置一些频繁更新的内容,并要求不能缓存太长的时间。博客文章和其他内容,我要求可以缓存几分钟,因为这部分不会频繁更新。

The layout I am working with as an example.

缓存侧栏

首先,需要使用 PHP 创建一个侧栏。我将这个侧栏保存为 inc/sidebar.php,然后将其引用到主页面。虽然页面看起来还是那样,但实际上我已经将侧栏和页面分离了。

在主页面和导入的片段部分,将会输出当前日期和具体时间。这有助于我查看缓存是否发生了变化。如果没有使用缓存,那么每次刷新页面,相应的时间都会改变。如果使用了 Varnish,那么由于缓存的原因就会发现刷新前后的时间是相同的,只有再次缓存时才会更新时间。

The times change on page reload and should be the same.

使用 ESI 导入文件

下面我会使用 Varnish 所支持的两个标签:

esi:include
esi:remove

现在,使用 ESI 的 esi:include 标签导入文件,所以需要替换 PHP 的导入方式为:

<esi:include src=“inc/sidebar.php”/>

创建回调字段应对 ESI 不可用状态

esi:include 标签之后追加一个 esi:remove 标签,该标签内是原生的 PHP 导入文件的代码。为了测试,我还在后面添加了一行提示 “Not ESI!”,便于分辨文件是由 PHP 还是 ESI 导入的。

<esi:include src=“inc/sidebar.php”/>
<esi:remove>
  <?php include(‘inc/sidebar.php’); ?>
  <p>Not ESI!</p> 
</esi:remove>

此时,对于没有配置 ESI 的 Varnish,重载页面之后会出现一行 “Not ESI!” 文本。这是因为 ESI 没有被配置,所以转由 PHP 执行的结构。

esi:remove 标签便于你创建回调字段,应对 ESI 不起作用的情况。这种情况可能是由于 Varnish 未对某些页面配置 ESI 引起的,也有可能是开发环境下,站点直接使用了 Apache 解析,并未经过 Varnish。

如果可以使用 ESI,那么服务端解析时整个的 <esi:remove></esi:remove> 标签就会被移除。使用 esi:remove 标签并不会阻塞 PHP 代码的执行。当 ESI 不可用时,又会执行相关的该标签内的语句,并根据缓存规则进行缓存。

另一个可替换的方案是使用 esi:remove 标签,这也是 Varnish 支持的第三种语句:<!—esi … —>。如果你希望 ESI 不可用时不发生任何回调或提示,那么就使用这个标签吧。

<!—esi <esi:include src=“inc/sidebar.php”/>  —>

我们再来回顾一下前面的案例,如果使用 <!—esi … —> 标签且 ESI 不可用的情况下,那么侧栏就不会加载,浏览器会将其视为一条 HTML 注释,最终用户不会看到任何内容。如果使用 ESI 解析,那么标签外部的注释形式就会被移除,内部导入文件的语句就会被执行。

Varnish 下配置 ESI

对于我们的 VCL(Visual Component Library,可视化组件库),我们使用 vcl_backend_response 子程序通知 Varnish 使用 ESI:

sub vcl_backend_response { 
  set beresp.do_esi = true;  
}

提醒:在 Varnish 3 中需要将这条语句放入 sub vcl_fetch 中。更多 Varnish 3 和 4 版本的差异,请参考官方升级日记

此时,如果重启 Varnish 并重载页面,就会发现 “Not ESI!” 这句提示不见了;如果查看源代码时没有发现注释形式的导入语句,那么说明侧栏已经成功导入了。这时候页面两部分的时间还是相同的,因为我们使用了相同的方式缓存两个部分的内容。

微调 TTL

我期望能够这样做:侧栏只会被缓存 30 秒,然后就刷新一次相关内容(在服务端的缓存区刷新,而不是客户端),而主内容区则可以缓存 120 秒。实现这个设计,需要继续在 vcl_backend_response 子程序中添加相关语句:

if (bereq.url ~ “sidebar.php”) { 
  set beresp.ttl = 30s; } else { 
  set beresp.ttl = 120s; 
}

这段代码的意思是,如果 URL 匹配字符串 sidebar.php,那么就使用 30 秒的缓存周期,否则使用 120 秒的缓存周期。

现在,保存 VCL 重启 Varnish 并重载页面,页面内的两个栏目时间还是一致的。等待 30 秒后再次重载页面:就会发现侧栏的时间更新了,但是主内容区的时间仍然没有改变。这就意味着页面组件,已经通过不同的 ESI 策略实现了缓存。

The layout after adding ESI, page components being cached differently.

这里传递给 bereq.url 的是一个正则表达式。有时候,你可能期望将具有相同缓存规则的文件放入同一个文件夹中,然后对该文件夹应对相关策略。

if (bereq.url ~ “^/inc”) { 
  set beresp.ttl = 30s; } else { 
  set beresp.ttl = 120s; 
}

更多的表达式示例请参考 Fastly 网站的正则表达式速查表

为部分内容设置永不缓存

如果页面的某些部分是你不期望缓存的(比如个人信息的部分),那么可以选择这部分的一些标志,将其设为不可缓存,或者绕过 Varnish 直接从服务器获取相关信息。

这里有一个 personalized 文件夹,我希望这个文件夹里的所有数据可以直接从服务器获取,而不使用缓存。实现这个目标,可以使用类似上面设置 TTL(Time to Live,生存周期)的 if 语句。还是在 vcl_backend_response 子程序:

if (bereq.url ~ “^/personalized”) {
  set beresp.uncacheable = true; 
  return(deliver); 
}

相比较其他页面的缓存方式,return(deliver); 表示获取内容时绕过缓存并实时更新数据。可以通过查看导入文件的时间测试这一设置:

<esi:include src=“personalized/panel.php”/>  
<esi:remove>  
  <?php include(‘personalized/panel.php’); ?>
</esi:remove>

这部分内容在每次刷新后都会更新时间。

完整的 VCL 请参考上述的 GitHub 地址。你可以通过测试不同的设置实现最适合自己的配置。

扩展延伸

对于优化 Varnish 的性能,ESI 是一种强有力的方式,特别是针对于你的个人站点。希望这篇文章恰当指出了它的亮点。最后,下面是一些我写作本文时的参考资料,有一些专门是对各种 CMS 的优化方案。需要提醒的时,阅读文章是注意文章内使用的 Varnish 版本,这里面有大量的 Varnish 3,甚至是 Varnish 2 的语法信息。虽然这些语法已经改变了很多,将相应的语法转换到新版本还是比较简单的。

本文根据@Rachel Andrew的《Controlling The Cache: Using Edge Side Includes In Varnish》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://www.smashingmagazine.com/2015/02/16/using-edge-side-includes-in-varnish/

南北

在校学生,本科计算机专业。狂热地想当一名作家,为色彩和图形营造的视觉张力折服,喜欢调教各类JS库,钟爱CSS,希望未来加入一个社交性质的公司,内心极度肯定“情感”在社交中的决定性地位,立志于此改变社交关系的快速迭代。

如需转载,烦请注明出处:http://www.w3cplus.com/performance/using-edge-side-includes-in-varnish.htmlNike EPIC React Flyknit

返回顶部