写点什么

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:003834

评论

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

从国内最早的开放银行浅聊技术创新

FinFish

技术创新 开放银行 小程序化 小程序技术

直播软件APP源码iOS提交到APP store系列之上架指南

山东布谷科技

软件 直播 源码搭建 iOS APP上架 app store

华为云低代码平台Astro Canvas 搭建汽车展示大屏——实验指导手册

华为云PaaS服务小智

软件开发 低代码 数据可视化 华为云

SpringCloud Gateway 在微服务架构下的最佳实践

阿里巴巴云原生

阿里云 云原生 Spring Cloud Gateway

软件测试 | 什么时候使用表锁

测吧(北京)科技有限公司

免费MES系统:助力企业数字化转型的利器

万界星空科技

开源 数字化转型

代码随想录Day34 - 贪心算法(三)

jjn0703

2023年开放原子校源行(清华大学站)成功举行

开放原子开源基金会

开源 开放原子开源基金会 清华大学

打通数据治理全链路,火山引擎DataLeap数据治理平台公有云版本正式发布

字节跳动数据平台

大数据 数据中台 数据研发 企业号 8 月 PK 榜

全链路灰度的挑战、实现思路与解决方案

阿里巴巴云原生

阿里云 云原生 全链路灰度

CCIA数安委等组织发起“个人信息保护影响评估专题工作”,合合信息首批入选试点

合合技术团队

人工智能 信息安全 个人信息保护

深度解读陕西省国资委最新发文,聚焦国资国企建设一流司库

用友BIP

全球司库 国资国企

百度APP iOS端包体积50M优化实践(五) HEIC图片和无用类优化实践

百度Geek说

ios 性能优化 企业号 8 月 PK 榜

构建高效物理计划:从逻辑查询到算子实现

KaiwuDB

KaiwuDB SQL编译 物理计划构建

【专家观点】贸易企业的数智化绩效管理

用友BIP

贸易

软件测试 | table_cache的设置

测吧(北京)科技有限公司

测试

企业税务管理为什么需要数智化转型

用友BIP

税务管理

企业诊断屋:服饰美妆电商如何用A/B测试赋能业务

字节跳动数据平台

大数据 A/B 测试 对比实验 企业号 8 月 PK 榜

大文件传输的有效可用方式

镭速

大文件传输 传输大文件

软件测试 | 影响MySQL性能的重要参数

测吧(北京)科技有限公司

测试

软件测试 | 升级MySQL

测吧(北京)科技有限公司

测试

什么是跨境数据传输。如何避免跨境数据传输出现的问题

镭速

跨境数据传输

软件测试 | 源码包安装的性能考虑

测吧(北京)科技有限公司

测试

基于YonGPT 的企业收入/利税经营分析,让企业经营决策更从容

用友BIP

git rebase介绍与可视化工具(sourceTree)提效

时常看看太阳

git git rebase sourcetree

内网穿透之 ngrok

陈皮

5分钟get:Uni-App网络请求完美封装指南!

陇锦

微信小程序 uni-app 前端开发 uniapp 前端开发框架

智慧消防大数据监控系统 城市火警智能监测

2D3D前端可视化开发

智慧城市 智慧消防 消防物联网云平台 消防云控平台

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