语雀的技术架构演进之路( 三 )


但是随着函数计算的引入,我们将这个消耗 CPU 的转换逻辑放到函数计算上,语雀的主服务稳定性不会再被影响 。

语雀的技术架构演进之路

文章插图
| 除了帮助 Web 系统分担一些 CPU 密集型操作以外,函数计算还能做什么呢?
在语雀上我们支持各种代码形式来绘图,包括 Plantuml、公式、Mermaid,还有一些将文档导出成 PDF、图片等功能 。这些场景有两个特点:
  • 他们依赖于一些复杂的应用软件,例如 Puppeteer、Graphviz 等;
  • 可能需要执行用户输入的内容;
支持这类场景看似简单,通过 process.exec子进程调用一下就搞定了 。但是当我们想把它做成一个稳定的对外服务时,问题就出现了 。这些复杂的应用软件可能从设计上并没有考虑要长期运行,长期运行时的内存占用、稳定性可能会有一些问题,同时在被大并发调用时,对 CPU 的压力非常大 。
再加上有些场景需要运行用户输入的代码,攻击者通过构建恶意输入,可以在服务器上运行攻击代码,非常危险 。
在没有引入函数计算之前,语雀为了支持这些功能,尽管单独分配了一个任务集群,在上面运行这些三方服务,接受主服务的请求来避免影响主服务的稳定性 。但是为了解决上面提到的一系列问题还需要付出很大的成本:
  • 需要维持一个不小的任务集群,尽管可能大部分时间都用不上那么多资源 。
  • 需要定时对三方应用软件进行重启,避免长时间运行带来的内存泄露,即便如此有些特殊请求也会造成第三方软件的不稳定 。
  • 对用户的输入进行检测和过滤,防止黑客恶意攻击,而黑客的攻击代码很难完全防住,安全风险依旧很大 。

语雀的技术架构演进之路

文章插图
最后语雀将所有的第三方服务都分别打包在函数中,将这个任务集群上的功能都拆分成了一系列的函数放到了函数计算上 。通过函数计算的特点一下解决了上面的所有问题:
  • 函数计算的计费模式是按照代码实际运行的 CPU 时间计费,不需要长期维护一个任务集群了 。
  • 函数计算上的函数运行时尽管会有一些常驻函数的优化,但是基本不用考虑长期运行带来的一系列问题,且每次调用之间都相互独立,不会互相影响 。
  • 用户的输入代码是运行在一个沙箱容器中,即便不对用户输入做任何过滤,恶意攻击者也拿不到任何敏感信息,同时也无法进入内部网络执行代码,更加安全 。

语雀的技术架构演进之路

文章插图
| 除了上面提到的这些功能之外,语雀最近还使用 OSS + 函数计算替换了之前使用的阿里云视频点播服务来进行视频和音频的转码 。
由于浏览器可以直接支持播放的音视频格式并不多,大量用户上传的视频想要能够直接在语雀上进行播放需要对它们进行转码,业界一般都是通过 FFmpeg 来对音视频进行转码的 。
转码服务也是一个典型的 CPU 密集型场景,如果要自己搭建视频转码集群会面临大量的资源浪费,而使用阿里云视频点播服务,成本也比较高,而且能够控制的东西也不够多 。
函数计算直接集成了 FFmpeg 提供音视频处理能力,并集成到应用中心,配合 SLS 完善了监控和数据分析 。语雀将音视频处理从视频点播服务迁移到函数计算之后,通过优化压缩率、减少不必要的转码等优化,将费用降低至之前的 1/5 。
语雀的技术架构演进之路

文章插图
从语雀的实践来看,语雀并没有像 SFF 一样将 Web 服务迁移到函数计算之上(SFF 模式并不是现在的函数计算架构所擅长的),但是函数计算在语雀整体的架构中对稳定性、安全性和成本控制起到了非常重要的作用 。总结下来函数计算非常适合下面几种场景:
  • 对于时效性要求不算非常高的 CPU 密集型操作,分担主服务 CPU 压力 。
  • 【语雀的技术架构演进之路】当做沙箱环境执行用户提交的代码 。
  • 运行不稳定的三方应用软件服务 。
  • 需要很强动态伸缩能力的服务 。
在引入函数计算之后,语雀现阶段的架构变成了以一个 Monolith Application 为核心,并将一些独立的功能模块根据使用场景和对能力的要求分别拆分成了 Microservices 和 Serverless 架构 。


推荐阅读