【AICon】探索八个行业创新案例,教你在教育、金融、医疗、法律等领域实践大模型技术! >>> 了解详情
写点什么

SSR 页面 CDN 缓存实践

  • 2021-06-16
  • 本文字数:3935 字

    阅读完需:约 13 分钟

SSR 页面 CDN 缓存实践

SSR 是一项资源密集型任务,要抵抗更大流量、提供更快的服务,缓存是其中的必修课。


而 CDN 缓存——作为静态资源的首要支撑,适合武装到 SSR 页面吗?


开始之前


大家对 CDN 应该已经耳熟能详,如果不甚了解也没关系,我们先通过一系列问答带诸位走近这个话题。


为什么接入 CDN?


抽象一个简单的请求链路,方便理解 CDN 的定位。


接入前:

用户 -> Nginx -> App Server

接入后:

用户 -> CDN -> Nginx -> App Server


看似增加了一层传输成本,其实不然。


CDN 利用自身广大的服务器资源,能动态优化访问路由、就近提供访问节点,以更低延迟、更高带宽从源站获取数据,优化了网络层面的用户体验。


为什么开启 CDN 缓存?


开启前:浏览器 -> CDN -> Nginx -> App Server1 -> App Server2 -> …

开启后:浏览器 <-> CDN


CDN 能够缓存用户请求到的资源,并且可以包含 HTTP 响应头。在下一次任意用户请求同样的资源时,用缓存的资源直接响应用户,节省了本该由源站处理的所有后续步骤。


更直观的表达,就是截短了请求链路。


如何开启 CDN 缓存?

在不考虑自研 CDN 的情况下,开启 CDN 缓存的步骤非常简单:

  1. 域名接入 CDN 服务,同时针对路径启用缓存

  2. 在源站设置 Cache-Control 响应头,为了更灵活地控制缓存规则,但并不是必须

哪些服务可以开启 CDN 缓存?

大部分网站都适合接入 CDN,但 SSR 页面只有满足一定条件才可以开启 CDN 缓存

  • 无用户状态

  • 对时效性要求不高,至少能接受分钟级的延迟

怎样判断是否命中缓存?

不同 CDN 平台检测的方法略有不同,本质上都是判断响应头的标识字段。以腾讯 CDN 为例,响应头 X-Cache-Lookup 分别表示

  • Hit From MemCache: 命中 CDN 节点的内存

  • Hit From Disktank: 命中 CDN 节点的磁盘

  • Hit From Upstream: 未命中缓存,回源

如果该字段不存在,说明该页面没有配置 CDN,或未开启缓存。


CDN 缓存优化


用来衡量缓存效果的重要指标是缓存命中率,在正式设置 CDN 缓存之前,我们再来了解几个提高缓存命中率的要点。这些要点也适合作为评估系统是否应该接入 CDN 缓存的标准。


延长缓存时间


提高 Cache-Control 的时间是最有效的措施,缓存持续时间越久,缓存失效的机会越少。

即使页面访问量不大的时候也能显著提高缓存命中率。

需要注意,Cache-Control 只能告知 CDN 该缓存的时间上限,并不影响它被 CDN 提早淘汰。流量过低的资源,很快会被清理掉,CDN 用逐级沉淀的缓存机制保护自己的资源不被浪费。

忽略 URL 参数


用户访问的完整 URL 可能包含了各种参数,CDN 默认会把它们当作不同的资源,每个资源又是独立的缓存。

而有些参数是明显不合预期的,例如,页面链接在微信等渠道分享后,末尾被挂上各种渠道自身设置的统计参数。平均到单个资源的访问量就会大大降低,进而降低了缓存效果。

CDN 支持后台开启 过滤参数 选项,来忽略 URL ? 后面的参数。此时同一个 URL 一律当作同一个资源文件。

在腾讯 CDN 中,忽略参数的功能无法针对某个 URL,仅支持整个域名生效,这让过滤参数成为了极具风险的操作。除非域名缓存专用,否则不建议开启这个选项,即便同域名内所有已接入 CDN 缓存的资源都不依赖 URL 参数,也不能保证将来不会因此踩坑。


主动缓存

化被动为主动,才有可能实现 100% 的缓存命中率。

常用的主动缓存是资源预热,更适合 URL 路径明确的静态文件,动态路由无法交给 CDN 智能预热,除非依次推送具体的地址。


代码演进


谈过 CDN 缓存优化的几个要点,便可得知 CDN 后台的配置是需要谨慎对待的。我在实际操作中,也经过了几个阶段的调整,可毕竟具体配置方式取决于 CDN 服务商,因此本文不再深入讨论。

现在,我们要把目光转到代码层的演进了。


一、掌控缓存

代码配置有一个前提,即 CDN 后台需要开启读取源站 Cache-Control 的支持。

而后,只要简单地添加响应头,就能从运维手中接管设置 CDN 缓存规则的主动权。

以 Node.js Koa 中间件为例,全局的初始化版本如下

app.use((ctx, next) => {  ctx.set('Cache-Control', `max-age=300`)})
复制代码

当然,上述代码的疏漏是非常多的。在 SSR 应用中,不太需要缓存所有的页面,这就要补充路径的判断条件。


二、控制路径

虽然 CDN 后台也可以配置路径,但配置方式乃至路径数量都有局限性,不如代码形式灵活。

假如我们只需要缓存 /foo 页面,就加入 if 判断

app.use((ctx, next) => {  if (ctx.path === '/foo') {    ctx.set('Cache-Control', `max-age=300`)  }})
复制代码

这就陷入了第一个陷阱,一定要注意路由对 path 的处理。一般地,'/foo' 和 '/foo/' 是两个独立的 path。可能因为 ctx.path === '/foo' 而漏掉了请求 path 为 /foo/ 的处理。


三、补充路径

伪代码如下

app.use((ctx, next) => {  if ([ '/foo', '/foo/' ].includes(ctx.path)) {    ctx.set('Cache-Control', `max-age=300`)  }})
复制代码

此外,CDN 后台的配置也需要规避这个问题。在腾讯 CDN 中,目录和文件适用于不同的页面路径。


四、忽略降级页面

在服务端渲染失败时,为了提高容错,我们会返回降级之后的页面,转为客户端渲染。如果因为偶然的网络波动,导致 CDN 缓存了降级页面,将在一段时间内持续影响用户体验。

所以我们又引入了 ctx._degrade 自定义变量,标识页面是否触发了降级

app.use(async (ctx, next) => {  if ([ '/foo', '/foo/' ].includes(ctx.path)) {    ctx.set('Cache-Control', `max-age=300`)  }
  await next()
  // 页面降级时,取消缓存  if (ctx._degrade) {    ctx.set('Cache-Control', 'no-cache')  }})
复制代码

没错,这并不是最后一个陷阱。


五、Cookie 和状态治理

上面已经提到了 CDN 可以选择性地缓存 HTTP 响应头,可是此选项是对整个域名生效,又普遍需要开启。

新的问题正是来自一个不希望被缓存的响应头。

应用 Cookie 的设置依赖于响应头 Set-Cookie 字段,Set-Cookie 的缓存直接会导致所有用户的 Cookie 被刷新为同一个。

有多个解决方案,一是该页面不要设置任何 Cookie,二是代理层过滤掉 Set-Cookie 字段。可惜腾讯 CDN 目前还不支持对响应头的过滤,这步容错必须自己操作。

app.use(async (ctx, next) => {  const enableCache = [ '/foo', '/foo/' ].includes(ctx.path)
  if (enableCache) {    ctx.set('Cache-Control', `max-age=300`)  }
  await next()
  // 页面降级时,取消缓存  if (ctx._degrade) {    ctx.set('Cache-Control', 'no-cache')  }  // 缓存页面不设 Set-Cookie  else if (enableCache) {    ctx.res.removeHeader('Set-Cookie')  }})
复制代码

上面增加的代码旨在页面响应前移除 Set-Cookie,但是中间件的加载顺序是难以控制的。特别是一些(中间件)插件,会隐式地创建 Cookie,这让 Cookie 的清理工作异常麻烦。如果后续维护人员不知情,很可能将 Set-Cookie 重新加入到响应头中。所以,这种擦屁股的工作,尽量在代理层处理,而不是放在代码逻辑中。

除了 Cookie,还可能面临其他状态信息管理问题。比如在 Vuex 的 renderState 中存放请求用户的登录状态,此时 HTML 页面嵌入了用户信息,如果被 CDN 缓存,在客户端将发生和未清除 Set-Cookie 相似的问题。类似的例子还有很多,它们的解决思路非常相像,接入 CDN 缓存前务必对状态信息做好全面的排查。


六、定制缓存路径

现在功能总算趋于正常,然而缓存规则复杂多变,如果想设置更多页面,还要单独定制缓存时间呢?这段代码仍需要不断地变动。

例如,我们只想缓存 /foo/:id,而不缓存 /foo/foo、/foo/bar 等路径。

注意 CDN 后台可能只支持配置一个 /foo/ 开头的缓存路径,这就要求我们需要将 ctx.set('Cache-Control', 'no-cache') 做为默认处理,加在中间件的第一行。

又比如,我们想缓存 /foo 页面 5 分钟,/bar 页面 1 天,又需要引入一个时间配置表。

这个中间件和相应的配置就会变得越来越难以维护。

因此,我们换一种思路,缓存规则不再交给中间件,而是转到 Vue SSR 的 entry-server,通过 metadata 可以做到页面级别的配置。由于 SSR 方案的差异性,不再赘述具体实现。


七、缓存失效

缓存失效是个中性词,如何处理 CDN 缓存失效,此中利弊不得不慎重权衡。

一方面,它会间歇增加服务压力,在 Serverless 应用中还会提高计算成本。而另一方面,许多场景我们不得不主动触发它,才能真正更新资源。

CDN 缓存的黑暗面无法让人忽视。对用户而言,缓存是透明的,对产品、技术却很可能成为阻碍。

如果处理不当,它将影响新功能能否及时发布、阻断后置所有服务的埋点、提高风险感知的成本,以及无法保障一致性,增加了线上问题的排查难度。

因此,十分有必要设立一个负责缓存刷新、预热的触发式服务,用以改进开发人员的体验。可是 CDN 缓存可控性很低,刷新也不能做到全然实时生效。

处于频繁变化的页面,最好考虑进入稳定期再开启 CDN 缓存。即使是稳定的、大流量的页面,也还需要考虑 CDN 缓存穿透的防范措施。

一旦 CDN 缓存在 SSR 架构中得到重用,就要做好长期调整决策的准备。


总结


CDN 缓存是一把利刃,在大流量的场景下,可以替源站拦截几乎所有的请求,能提供极强伸缩性的负载。


那么 SSR 应用适合接入 CDN 缓存吗?再一次细数上面提到的诸多问题…


  • 路径控制

  • 页面降级

  • 状态治理

  • 缓存失效


答案得你自己说了算。

实际上,极少数 SSR 页面场景才需要 CDN 缓存,如门户首页。

流量不高、路径分散的一般业务,只需要使用动态的 CDN 加速和静态文件缓存,就能基本满足 CDN 代理层的优化需要。



头图:Unsplash

作者:齐云雷

原文:https://mp.weixin.qq.com/s/2jW4Xc94IHeRsgHLl6hU_g

原文:SSR 页面 CDN 缓存实践

来源:微医大前端技术 - 微信公众号 [ID:wed_fed]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021-06-16 08:003409

评论

发布
暂无评论
发现更多内容

架构师训练营第 1 期 -- 第五周学习总结

发酵的死神

极客大学架构师训练营

hash一致性算法

橘子皮嚼着不脆

食堂就餐卡系统UML设计

架构师

week1作业

幸福小子

架构师训练营 1 期第 5 周:技术选型(一) - 作业

灵霄

极客大学架构师训练营

5.3分布式缓存架构:一致性hash算法

张荣召

第五周总结

第1周作业-学习总结

jingx

架构方法-学习总结笔记

Xuenqlve

架构师训练营第 5 周:技术选型(一)

子青

第五周 技术选型 作业一

应鹏

极客大学架构师训练营 课程作业

第五周作业

fmouse

极客大学架构师训练营

【架构师训练营第 1 期 05 周】 学习总结

Bear

极客大学架构师训练营

学习总结-week1

Mr_No爱学习

【架构师训练营第 1 期 05 周】 作业

Bear

极客大学架构师训练营

Week_05 作业

golangboy

极客大学架构师训练营

架构师训练营第5周作业

wanlinwang

极客大学架构师训练营

第五周 作业二

Yangjing

极客大学架构师训练营

作业

张荣召

架构师训练营二期 1周总结

月下独酌

极客大学架构师训练营

AirPods过河,苹果拆桥:被“钞能力”征服的Beats何以至此?

脑极体

5.2分布式缓存架构:常见的缓存实现形式

张荣召

Week 5 作业01

Croesus

架构师训练营第五周作业

xs-geek

第 1 周 架构方法 学习总结

心在那片海

架构师训练营第 1 期 - 第 5 周 - 学习总结

wgl

极客大学架构师训练营

第一周10/25

张冬冬

总结

架构师训练营第一周作业-周总结

张浩

5.1分布式缓存架构:架构原理与注意事项

张荣召

食堂就餐卡系统设计-week1

Mr_No爱学习

第五周 作业1

Yangjing

极客大学架构师训练营

SSR 页面 CDN 缓存实践_语言 & 开发_微医大前端技术_InfoQ精选文章