17611538698
info@21cto.com

可汗学院如何重写他们的后端

作者 场长 分类 架构 11月05日
导读:国外一家著名在线教育平台如何做后端迁移的,值得我们借鉴。

今天,我们来说说可汗学院如何重新编写和迁移他们的后端的。

图片

可汗学院(Khan Academy)如何将他们的 Python 2 单体重建为一个用 Go 编写的面向服务的后端。包括如下核心内容:

    • 可汗学院为何选择 Go 以及该语言的简要概述

    • 工程师如何使用并行测试来确保新后端正常运行

    • 重写的结果和经验教训


可汗学院如何重写他们的后端


可汗学院最近进行了大规模重写,他们用 Go 编写的面向服务的后端替换了旧的 Python 2单体架构。


Kevin Dangoor 和 Marta Kosarchyn 是可汗学院的高级研发工程师,他们写了一系列关于重写的技术选择、执行和结果的博客文章。我们在下面总结这个文章系列。


概述


2019 年底,可汗学院正在探索升级自己的后端。该站点原来是建立在 Python 2 单体架构上,并且运行良好 10 多年。


但是众所周知的原因,Python 2 即将在2020 年 1 月 1 日正式结束生命周期,因此可汗学院的工程师决定他们必须进行技术更新和升级。


KA(可汗学院)自己做了几个选择项:

  • 从 Python 2 迁移到 Python 3 

    这将使 KA 的后端服务器代码性能和 Python 3 的语言特性提高 10-15%。

  • 从 Python 2 迁移到 Kotlin

    KA 开始将 Kotlin用于计算密集型后端任务,因为它比 Python 2 性能更高。从 Python 切换到 Kotlin 可能意味着可汗学院的响应速度更快,服务器成本下降。

  • 从 Python 2 迁移到 Go

    Go 是一种简单而简洁的语言,编译时间非常快,对Google App Engine的一流支持以及比 Kotlin 更少的内存使用量(基于 KA 的测试)。


在这些选项中,可汗学院决定采用第三种选择,使用 Go 语言重写他们的 Python 2 单体。

他们并没有说干就干,而是先进行了全面的性能测试,发现 Go 和 Kotlin(在 JVM 上)的性能差不多,但 Kotlin 领先了几个百分点。

但是,他们发现 Go 占用的内存要少得多。

最后,他们总结 Go 和 Python 之间的巨大性能差异,这使得转换所涉及的成本和努力是值得的。

Go 语言简述


Go 是一种静态类型的编译编程语言,在语法上类似于 C语言。Go 是由Ken Thompson、Rob Pike和Robert Griesemer在 Google 设计的。


Ken Thompson 和 Rob Pike 是前贝尔实验室的骨干员工,他们在构建最初的 Unix 操作系统(以及许多其他东西,他们还开发了 UTF-8 编码)方面发挥了重要作用。


Go 包括诸如垃圾收集、结构类型和极快的编译时间之类的优秀特性。


从单体到服务


除了改用 Go 语言之外,可汗学院还决定改用面向服务的架构。


以前,可汗学院的所有服务器都运行相同的代码,并且可以响应对网站任何部分的请求。单独的服务用于存储数据和管理缓存,但无论哪个服务器响应,任何请求的逻辑都是相同的。


尽管服务型架构带来了额外的复杂性,但可汗学院团队还是决定采用它,因为它有几个很大的好处。

  • 更快的部署- 服务可以独立部署,因此部署和测试运行可以更快地移动。工程师将能够在部署活动上花费更少的时间,并在需要时更快地进行修改。

  • 对问题的影响有限- KA 工程师现在可以更加确定部署问题,另外故障对站点其他部分的影响有限。

  • 硬件和配置——通过拥有单独的服务,工程师现在可以选择每个服务所需的正确类型的实例和托管配置,这也有助于优化性能和成本。


尽管架构发生了变化,但可汗学院仍计划继续使用 Google App Engine 进行托管、使用 Google Cloud Datastore 进行数据库以及其他 Google Cloud 产品。

Kevin Dangoor 谈到可汗学院如何在不停机的情况下重写他们的后端。大的代码改写风险极大,因此 KA 选择了增量改写策略。


可汗学院新后端的中心基于GraphQL Federation。他们从使用 REST 切换到 GraphQL,GraphQL Federation 允许开发人员将多个后端服务组合到一个统一的图形界面中。


这样就可以为各种后端系统提供的通过 GraphQL 网关访问的所有数据提供一个单一的、类型化的模式。


每个后端服务都提供整个 GraphQL 架构的一部分,网关将所有这些单独的架构合并为统一一个。


可汗学院至此逐渐从旧后端切换到新后端。为了确保新服务正常工作,他们会同时查询新的分布式后端和旧的单体后端,并比较结果,然后返回其中一个。


KA 有一个 4 步流程来处理转换:

  1. 单体控制——起初,服务后端没有响应特定请求的功能,因此 GraphQL 网关将该请求路由到 Python 单体。此请求将被记录下来,KA 工程师开始编写 Go 代码来处理新后端中的请求。 

  2. Side-by-side - 一旦新的后端可以处理请求,KA 工程师将切换到新的API。在这种状态下,GraphQL 网关将同时调用Python 代码和新的 Go 代码,它比较结果,记录存在差异的实例,并将 Python 的计算结果返回给用户。

  3. 已迁移- 经过大量的测试后,请求的状态将升级为已迁移。此时,网关只会向 Go 服务发送流量。但是一旦出现问题,Python 代码仍然存在并积极准备响应。

  4. 删除了 Python 代码- 最后,经过大量的反复测试,删除了 Python 代码。


可汗学院用这个过程,来重写具有高可用性的后端。尽管网站流量大幅增加,而新后端仍然能够处理这项任务。

大部分改写是在 2020 年完成的,当时学校因 COVID-19 而转向远程,学生、家长和教师显着更多地使用可汗学院。

在 2 周内,KA 的使用量增加了 2.5 倍。截至 2021 年 8 月 30 日,新服务后端处理了 95% 的网站流量。

他们能够达到在 20 个月前做出的初步完成估计(尽管 COVID 带来了巨大的冲击),并实现了新后端的性能目标。

可汗学院依靠几个原则使升迁过程得到顺利进行:

  • 避免 Scope Creep - 在每一个转折点,工程师都试图尽可能直接地从 Python 移植到 Go,最终仍能得到 Go 代码的执行。可汗学院的旧代码库有一些结构不同的区域,但如果工程师试图在 Go 重写的同时要解决这些问题,他们将永远无法完成。

  • 并行测试- 并行测试方法对于代码重写的成功非常重要。这是确保被替换的功能是等效的有效方法。

  • 无边界工程——工程师致力于各种产品代码领域和新服务,超越他们通常的产品领域职责范围。这需要小心翼翼地完成,确保工程师花费足够的时间来学习新的服务领域,能够深思熟虑地做出贡献。


这表示工程师会切换团队,或者说服务所有权会在团队之间转移。

评论