滴答是什么

去年转岗来到 AE 技术部以后,和团队同事们一起做一个基于 FaaS 的一体化研发平台。首先简单介绍一下开发使用视角的滴答是什么。

如何使用

作为开发者,你接到一个需求,做一个网站,比如我们的 F1 项目,重新做一套 AE 购物流程。对于前端来说,我们核心关心三个元素,HTML、接口数据、静态资源

在滴答平台上,一个应用,可以把上面三个事情都搞定了。

  • 页面渲染 — dida-render 渲染源站
  • 接口数据 — FaaS 运行时
  • 静态资源(css + js) — 构建、打包

滴答 FaaS 研发平台小结 - 图1

解决方案

滴答给的解决方案,大致架构模式如下图所示:

滴答 FaaS 研发平台小结 - 图2

整个平台分为三大块功能

  • 面向研发本地场景提供配套工具,这一块同时包括前端运行时 sdk
  • 滴答平台,页面配置和发布流程
  • FaaS 运行时,提供一个公共 sdk ,包装滴答通用逻辑

从开发者视角,我们希望能够做到模块级别交付、开发。技术实现上,我们把页面拆分成一个个一体化模块,开发者开发模块业务逻辑,模块组装由滴答来完成。通过这种组织模式,开发者可以分散在不同的团队,各自开发自己的模块,最终在滴答平台上整合成完整的页面功能。

下面主要讲讲我们是如何实现的,我只讲一下 FaaS 运行时体系的演化过程。

FaaS 运行时

FaaS 运行时链路是直接面向用户的,老的模式全是 Java 应用,我们需要重头开始建设 node 体系。

第一步

首先,我们选择把一个 Java 应用 ae-msite 一个接口迁移到新的 FaaS 模式上。我们先分析了现有 Java 接口的大致逻辑分层,大致分为两大块

  • 登陆 session 解析、通用限流、爬虫处理逻辑
  • 业务数据查询逻辑

下面是 ae-msite 请求处理流程图 滴答 FaaS 研发平台小结 - 图3 第一层实际上是阿里一些公共的中间件能力,同时在 AE 国际化业务场景下,我们会有一些业务定制的公共处理逻辑,比如爬虫处理,国际化站点跳转逻辑。这些逻辑如果要彻底迁移到 Node ,开发成本还是比较大的,所以一期包括未来很长一段时间,这一部分逻辑,我们都是直接由一个 Java 网关应用来承担。

第一期,我们直接在 ae-msite 这个业务系统上新增一个接口,实现一个临时的网关转发请求逻辑。第二层就是业务逻辑,直接用 node 重写即可,最终我们选择的运行时方案是这样的
滴答 FaaS 研发平台小结 - 图4

上线

上线以后,我们首先做了一个专门的切流大盘,主要监控新老版本流量、成功率、耗时等数据。
image.png
功能完成以后,最重要的是就是灰度切流控制逐步上线过程,切流过程要精细化,可以随时切回。一期灰度控制逻辑是这样实现的:
滴答 FaaS 研发平台小结 - 图6
分流逻辑在 ae-msite 实现,当 cdn 回流的时候,ae-msite 模板渲染代码,首先读取分流配置,如果命中了灰度,渲染新的模板 item2.vm 。新的模板代码会请求新的 /fetch/function 接口,新版本接口再读取分流配置,如果命中灰度,请求到 Node 应用,返回新的数据结构。

这里比较关键的是,前端需要兼容新老数据的渲染,新的模板如果返回老的数据,需要同样能够正常渲染

模板是回源到 cdn 的,所以就算 diamond 更新了,模板还是不能马上切回到老版本。另外,如果新版本接口挂了,网关可以直接返回老版本数据,这样可以座一层异常自动兜底。这样的兜底逻辑在技术升级过程中是非常重要,最初我们没有做这个功能,导致线上出现过一个严重的故障。

最后,终于开发完成上线了,线上我们就正常慢慢切流,慢慢放流即可,我们单独搞了一个文档记录整个过程。

渲染 dida-render

后来 F1 项目启动,我们需要重新搭建一套类似于 AE 的网站,正好乘此机会把我们新的架构方案用上。我们的顺便搭建了一个渲染源站 dida-render 。新的一期,我们终于完整的实现了我们的运行时框架
滴答 FaaS 研发平台小结 - 图7
这一期关键在于 fn-gateway 的建设,有了标准化的网关,我们的 FaaS 应用可以任意接入其他应用了。第一期实际上做的是把单个应用切过来,这个模式是不可复制的。同时我们完成了 dida-render ,实现了一个中心化的渲染源站。

整体架构是这样的,前置网关是 Java 应用,主要做转发,核心渲染逻辑由 FaaS 应用 dida-render 控制
滴答 FaaS 研发平台小结 - 图8
具体源站 dida-render 本身,渲染过程分为三个大的步骤:路由、模板加载、模板解析。当然非常重要的这个过程中的一些异常情况处理
滴答 FaaS 研发平台小结 - 图9

爬虫逻辑支持

F1 项目完成以后,我们开始马不停蹄的继续第一个 ae-msite 迁移项目。首先把应用新的网关迁移上,并且把模板同时迁移到新的渲染源站上。但这一些都只是一部分流量,在我们的国际业务中,爬虫 SEO 场景是同样重要的场景。

但是爬虫和用户请求有一些不一样,如下图所示,真实用户请求 HTML 和数据是分开的,我们可以通过浏览器 service work 做一些性能优化,做到页面 html 渲染和请求同时请求。

但爬虫无法做类似的优化,目前爬虫请求数据和模板是一并下发的。
滴答 FaaS 研发平台小结 - 图10

ae-msite 详情逻辑要切过来,爬虫肯定要支持,我们研究了几个不同的方案

  • 边缘计算,就是在 cdn 节点直接发起模板和数据请求,然后 cdn 做一些数据合并的逻辑
  • FaaS 应用做 SSR 渲染,直接返回页面结构 + 数据(或者完整的 HTML)

第一个方案依赖 cdn 节点的计算能力,同时有一些限流缓存的逻辑不是很方便处理,这个方案被我们放弃了。

然后我们开始往 SSR 方向做了,做 SSR 肯定只能用 react 渲染了,毕竟我们的前端都是 react,第一步先在渲染层支持 react 。

react 渲染

渲染接入 react ,整体改造成本并不大,主要是考虑到安全性问题。react 本身就是 js ,这会导致开发者可以提交任意代码到源站,源站本身的稳定性无法保障了,任何一个站点都可能把整个应用拖垮。通常一个模板代码是这样的

  1. import { DocumentContext, Main, DidaConfig } from '@ali/dida-render/jsx';
  2. export default function Page() {
  3. const { getAssetsURL } = useContext(DocumentContext);
  4. return (
  5. <html>
  6. <link href={getAssetsURL('index.css')} />
  7. <DidaConfig />
  8. <Main />
  9. <script src={getAssetsURL('index.js')} crossOrigin="anonymous" />
  10. </html>
  11. );
  12. }

耗时通过 vm 来控制,防止开发者写死循环代码,同时 vm 还可以控制运行上下文,只有一个 require 可用,并且 require 是受限制的 myRequire ,这样的逻辑首选在滴答平台上运行,可以保证用户问题在开发阶段暴露

  1. function getDoc(input: string) {
  2. const context = vm.createContext({ exports: {}, require: myRequire });
  3. const DocumentElement = vm.runInContext(ts.transpileModule(input), context, {
  4. // 最多运行 50ms
  5. timeout: 50,
  6. }) as React.FunctionComponent;
  7. return DocumentElement;
  8. }

当然还可能有运行时死循环,这种情况必须把代码真正执行一次才能暴露了,下面是简化代码

  1. function testRunner(input: string) {
  2. ReactServer.renderToStaticMarkup(
  3. <DocumentContext.Provider value={context}>
  4. {getDoc(input)}
  5. </DocumentContext.Provider>
  6. )
  7. }
  8. function runIt(input: string) {
  9. const code = `(${testRunner.toString()})(input)`;
  10. const context = vm.createContext({ React, DocumentElement, ctx, input });
  11. try {
  12. vm.runInContext(code, context, { timeout: 50 })
  13. } catch (e) {
  14. const err = new Error(`jsx 模板渲染异常,请检查模板语法: ${e.message}`);
  15. throw err;
  16. }
  17. }

去中心化渲染

react 渲染安全问题彻底解决,就是去中心化渲染方案,方案运行流程图如下,HTML 请求,直接从网关转发业务应用,应用执行渲染和数据查询逻辑,最终返回完整 HTML 。
滴答 FaaS 研发平台小结 - 图11
dida-render 作为一个 npm 包,集成在已有的 dida-sdk 中。页面 HTML 请求直接转发到业务应用,同时在业务应用中完成模板渲染和数据查询。

问题与未来

滴答虽然包装很多通用逻辑,但是对于开发者来说,业务逻辑才是最核心,最复杂的部分。

这些复杂的业务状态,目前我们通过页面方案配置来支持不同状态,配置不同的模块。但是这种拆分也只能是比较粗的粒度,一个大的业务状态下,还是存在非常多流程分支处理。而这些,没有更好的办法,只能开发者编码来处理。

滴答从无到有做到现在,将近一年时间,应该是刚刚开始起步。但做得越久,我们会发现最难做的还是平台本身。平台功能完成并不难,但要做得好用就非常难了。存在大量的细节需要打磨,包括发布流程控制、迭代切换等等。做产品过程中,选择做什么,不做什么是很难的,未来还是需要慢慢打磨平台本身。