最佳实践

腾讯在线教育

最近更新时间:2020-07-16 14:47:27

本文分享了腾讯在线教育使用云函数的真实案例。腾讯在线教育团队是:

  • IMWeb 团队隶属腾讯公司,是国内最专业的前端团队之一。
  • 专注前端领域多年,负责过 QQ 资料、QQ 注册、QQ 群等亿级业务。
  • 目前聚焦于在线教育领域,精心打磨腾讯课堂、腾讯企鹅辅导及 ABCmouse 三大产品。

技术方案的尝试

腾讯在线教育团队在传统的 Web 应用方向其实有众多技术方面的尝试,包括传统离线包、PWA 离线应用等,但每个技术栈都有其优点及缺点。目前团队技术方案的多维度对比如下图所示:

通过对比可发现 SSR 在首屏渲染以及 SEO 等方面都较为突出。基于此结果,团队较倾向于 SSR 技术。在选择 SSR 技术方案时,可从以下两个方面进行考虑:

SSR 应用的性能

我们重点关注以下性能优化问题:

  • 处理大量 CPU 密集型计算:类 React 的应用的 SSR 本质为:在服务端调用 React 的 renderToString 方法,将 React 组件渲染为 HTML 字符串。对于复杂的 SSR 应用来说,此过程可能存在大量的 CPU 密集型计算,且这不属于 Node 擅长的领域。
  • 提高首屏性能:由于离线包的环境依赖性(依赖 App),在传统的 Web 环境内是否可以有一套完整的解决方案来缓存相关的页面,从而提高首屏的性能。

SSR 的运维成本

服务的可用性:大部分前端工程师可能不擅长服务运维方面的工作,服务的可用性问题也成为了技术选型时的重要关注点之一。

根据以上两个方面,可将考虑因素总结为下图中的两点:

  • 如何设计一种方式同时拥有这三种方案的优点?
    • 不依赖客户端环境的离线功能。
    • 首屏时间短,SEO 体验好。
    • 维护成本低,问题定位更方便。
  • 方案是否通用且可复制?

腾讯在线教育团队 SSR 架构方案介绍

团队使用的 SSR 技术架构图如下所示:

接下来我们从代码组织、性能优化、运行上下文三个方面来详细讲解团队现有方案。

代码组织

在 PC/H5 项目中均采用了同构的模式来构建 SSR 应用。如下图所示:

在同构的模式下,业务开发者更关注业务的功能本身,而不用太过关心运行时的问题,但同时也要注意以下几点:

  • 传统浏览器中的常量使用,例如 windowdocument 等。
  • HTTP 数据请求库必须同时支持服务端和客户端。
  • 合理使用 React 应用的生命周期。
  • 通过注入环境变量来区分当前运行时环境。

性能优化

接口动静分离

页面的渲染通常依赖后端的相关数据,而数据可以拆分为动态数据与静态数据两个部分:

  • 静态数据:页面中不经常变更的数据。例如,企鹅辅导产品产品的课程标题、课程描述等。
  • 动态数据:页面中与用户登录态相关的数据。例如,课程是否已经购买、当前课程的折扣等。

对接口做动静分离的意义在于,我们可以利用静态数据的时延性敏感度低的特性做缓存,在服务端利用静态数据渲染页面,之后在服务端利用动态数据做二次渲染。主要逻辑如下图所示:

我们利用 Redis 对静态数据渲染出来的页面做缓存,不仅可以加快 SSR 的渲染时间,同时可以提高单机的 QPS(renderToString 在一定意义上为 CPU 密集型操作)。

浏览器中利用 PWA 做离线缓存

在客户端中,我们可以利用 PWA 来做离线缓存,缓存静态数据直出的 HTML 页面,从而进一步的提高了直出页面的首屏性能。主要逻辑如下图所示:

运行上下文

由于后端应用的运维复杂性、维护成本较高等问题,这里我们使用了 Serverless(腾讯云云函数 SCF) 来做直出应用的部署。得益于 Serverless 架构模式的天然优势,不用再关心服务的运维、服务的扩容等问题。

如上图所示,SSR 的应用本质为一个 Node 应用,SCF 的调用本质为一个 Event 事件。针对此问题我们采用了如下方法兼容这两种模式:
借助腾讯云 Serverless Framework 提供的很多标准化接口,给自研 Node 框架(imserver)增加了一层 Serverless 的封装。同时还在入口增加了 Event 到 Koa Request Context 的兼容。如下图所示:

SSR 的技术方案落地过程中的问题

问题1:云函数拆分

业务中有多个页面是通过 SSR 实现的,在采用了 SCF 实现 SSR 后,需确定是合并至一个云函数中(业务级),还是拆分为多个云函数(页面级)。推荐选择拆分为多个云函数(页面级),优点如下:

  • 云函数互相独立。假设页面 A 云函数调用失败,并不会影响到业务 B 的云函数。
  • 云函数包的大小会降低。由于云函数的冷启动过程,代码的包的大小对函数的冷启动时间也有一定的影响。

此处基于当前项目实现了云函数的自动化构建,通过 .scfssr.json 的配置文件自动生成相应的云函数,对现有的开发工作无任何影响,仅在构建时生成多个云函数,降低了应用的维护成本及开发成本。
同时基于云函数的构建过程,可使单个云函数代码更简练。通过分析 package.json 中的依赖,移除云函数容器中已经内置的工具包,并对云函数所依赖的第三方包做相应的引入分析,去除冗余。如下图所示:

问题2:云函数发布优化

下图为我们设计的基于 SCF 的多云函数直出方案逻辑,可查看当有版本更新时,发布流程及步骤是较为复杂的。

配置过程:初始化进行一次

  1. 在函数中创建 release、prohub 别名,可预先指向 $LATEST 版本。
  2. API 网关中创建服务 A,配置 API 网关指向函数 B release 别名,并发布到 API 服务的 release stage 中。
  3. 修改 API 网关,指向函数 B prehub 别名,并发布到 API 网关服务的 prehub stage 中。
  4. 修改 API 网关,指向函数 B 的默认流量,并发布到 API 网关服务的 dev stage 中。
    至此 API 网关的配置完成,后续无需在 API 网关上再次修改及发布配置。

开发测试发布过程:持续开发测试发布上线

  1. 在函数上持续开发,一次发布版本 1、2、3。
  2. 需开发并测试最近版本时,配置 $DEFAULT 别名指向 $LATEST 版本,可基于此版本持续开发修改,修改完成后发布版本。
  3. 版本3可进入预发布环境时,配置 prehub 别名指向版本3,在预发布环境可进行测试和体验。
  4. 版本2已经在预发布层完成体验,可上线,将 release 别名灰度从版本1切换至版本2。
  5. 通过监控及日志查看灰度过程,版本2的流量是否正常上涨,版本1的流量是否正常下降,及发布过程中的各版本错误情况以及总体错误情况。

为优化云函数的发布流程,我们基于腾讯云 Serverless 所提供的 Node SDK 做了一键发布 SCF 的工具。如下图所示:

一个完整的 SCF SSR 应用生命周期如下图所示:

腾讯云 Serverless SSR 方案的优点及后续规划

使用基于 SCF 的 SSR 方案,节省了众多的服务运维成本。基于腾讯云 Serverless 的日志系统,所有的单个 SSR 应用请求在日志平台都有完整的链路,定位问题与处理问题的速度都有了质的提升。

Serverless 的架构模式存在冷启动时间较长的问题,SCF 针对此问题已经进行了技术优化,例如预启动容器等。我们在实际业务方面,也可尝试进行优化。例如,在接入层做了服务的降级优化。如下图所示:

后续的优化方案还可以从灰度、多维降级等方面来做改进。

使用 SSR 技术的建议

  • 如果想追求更好的用户体验,建议针对核心业务做 SSR 优化,搭配 Serverless 来做服务的部署与运维。有了 Serverless 的支持,我们可以不用像以前一样关心机器的运维和扩容,可进一步提高团队生产力。
  • 使用 SSR 后,建议可进一步完善自己业务的 devops 流程,将整个研发链路打通,从开发到测试再到部署都可高效进行。
  • 推荐使用业务接入层来做服务降级,提高 SSR 应用的可用性。
目录