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


而为了更好的服务企业用户与个人用户,语雀在企业服务、会员服务等方面也投入了很大精力 。在业务快速发展的同时,服务商业化对质量、安全和稳定性也提出了更高的要求 。
为了应对业务发展,语雀的架构也随之发生了演进:
我们将底层的依赖完全上云,全部迁移到了阿里云上,阿里云不仅仅提供了基础的存储、计算能力,同时也提供了更丰富的高级服务,同时在稳定性上也有保障 。

  • 丰富的云计算基础服务,保障语雀的服务端可以选用最适合语雀业务的的存储、队列、搜索引擎等基础服务;
  • 更多人工智能服务给语雀的产品带来了更多的可能性,包括 OCR 识图、智能翻译等服务,最终都直接转化成为了语雀的特色服务;
而在应用层,语雀的服务端依然还是以一个基于 Egg 框架的大型的 Node.js Web 应用为主 。但是随着功能越来越多,也开始将一些相对比较独立的服务从主服务中拆出去,可以把这些服务分成几类:
  • 微服务类:例如多人实时协同服务,由于它相对独立,且长连接服务不适合频繁发布,所以我们将其拆成了一个独立的微服务,保持其稳定性 。
  • 任务服务类:像语雀提供的大量本地文件预览服务,会产生一些任务比较消耗资源、依赖复杂 。我们将其从主服务中剥离,可以避免不可控的依赖和资源消耗对主服务造成影响 。
  • 函数计算类:类似 Plantuml 预览、Mermaid 预览等任务,对响应时间的敏感度不高,且依赖可以打包到阿里云函数计算中,我们会将其放到函数计算中运行,既省钱又安全 。
随着编辑器越来越复杂,在 slate 的基础上进行开发遇到的问题越来越多 。最终语雀还是走上了自研编辑器的道路,基于浏览器的 Contenteditable 实现了富文本编辑器,通过 Canvas 实现了表格编辑器,通过 SVG 实现了思维导图编辑器 。
语雀的技术架构演进之路

文章插图
语雀的这个阶段(也是现在所处的阶段)是商业化阶段,但是我们仍然保持了一个很小的团队,通过 JAVAScript 全栈进行研发 。底层的服务全面上云,借力云服务打造语雀的特色功能 。同时为企业级用户和个人知识工作者者提供知识创作和管理工具 。
语雀的技术架构演进之路

文章插图
 
和函数计算的不解之缘语雀是一个复杂的 Web 应用,也是一个典型的数据密集型应用(Data-Intensive Application),背后依赖了大量的数据库等云服务 。语雀服务端是 Node.js 技术栈 。
当提到 node 的时候,可能立刻就会有几个词浮现在我们脑海之中:单线程(single-threaded)、非阻塞(non-blocking)、异步(asynchronously programming),这些特性一方面非常的适合于构建可扩展的网络应用,用来实现 Web 服务这类 I/O 密集型的应用,另一方面它也是大家一直对 node 诟病的地方,对 CPU 密集型的场景不够友好,一旦有任何阻塞进程的方法被执行,整个进程就被阻塞 。
像语雀这样用 node 实现整个服务端逻辑的应用,很难保证不会出现一些场景可能会消耗大量 CPU 甚至是死循环阻塞进程的,例如以 markdown 转换举例,由于用户的输入无法穷举,总有各种可能让转换代码进入到一个低效甚至是死循环的场景之中 。
在 node 刚出世的年代,很难给这些问题找到完美的解决办法,而即便是 Java 等基于线程并发模型的语言,在遇到这样的场景也很头痛,毕竟 CPU 对于 web 应用来说都是非常重要的资源 。而随着基础设置越来越完善,当函数计算出现时,node 最大的短板看起来有了一个比较完美的解决方案 。
阿里云函数计算是事件驱动的全托管计算服务 。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传,只需要为代码实际运行所消耗的资源付费,代码未运行则不产生费用 。
把函数计算引入之后,我们可以将那些 CPU 密集型、存在不稳定因素的操作统统放到函数计算服务中去执行,而我们的主服务再次回归到了 I/O 密集型应用模型,又可以愉快的享受 node 给我们带来的高效研发福利了!
以语雀中遇到的一个实际场景来举例,用户传入了一些 html 或者 Markdown 格式的文档内容,我们需要将其转换成为语雀自己的文档格式 。
在绝大部分情况下,解析用户输入的内容都很快,然而依然存在某些无法预料到的场景会触发解析器的 bug 而导致死循环的出现,甚至我们不太敢升级 Markdown 解析库和相关插件以免引入更多的问题 。


推荐阅读