前言:
在 2020 年 Hasura 的企业 GraphQL 会议上,Airbnb爱彼迎 展示了 Viaduct,一个面向数据的服务网格(Service Mesh) ,其为爱彼迎基于微服务的 SOA(Service-Oriented Architecture)的模块化带来了阶梯性的提升。我们将在这篇文章中介绍 Viaduct 背后的思想及大致运作原理。
SOA 大型依赖图
一段时间以来,面向服务的架构(SOA)一直向越来越细粒度的方向发展。现代应用程序中可能存在成千上万个小型的微服务,它们之间无限制地相互关联,常会导致以下这种依赖图。
这张图来自爱彼迎,但不只在爱彼迎,在 Amazon、Netflix、Uber 等公司都有类似的依赖图过于复杂的情况。这就像微服务层级上的面条式代码,随着时间的推进愈发难以修改。爱彼迎的工程师们经过不断调研和尝试,采用了面向数据的服务网格解决这一问题,帮助管理微服务架构中的大量服务,我们相信这会将 SOA 的模块化带到一个新的阶段。
面向过程 VS 面向数据
将庞大程序拆分为模块化的单元并不是软件工程领域的新话题。在1970年代以前,主流的编程思想一直是将代码拆分为过程,再拆分为模块。在面向过程的设计中,模块给模块外的代码提供公共接口,将所有的实现细节隐藏在接口背后。Pascal 和 C 等语言就是基于这种编程范式的。
从 80 年代起,软件开发的标准范式从面向过程设计变为面向数据设计。在面向数据的设计中,模块通过类(Class)对对象(Object)的内部表示进行封装,提供公用接口供外部访问。Simula 和 Clu 等语言开创了这种编程范式。
SOA 是一种偏向于面向过程设计的架构。微服务是对过程式的接口的集合,是经典的 70 年代风格的模块。我们认为将服务网格从面向过程的架构转变为面向数据的架构,可以实现 SOA 向面向数据设计方向的进化。
面向数据的服务网格
服务网格是现代可扩展 SOA 应用程序的核心,它负责将服务调用路由到正确的微服务实例进行处理。目前,服务网格的行业标准是专门围绕远程过程调用(Remote procedure invocation)设计的,忽略了构成应用程序的数据。我们的愿景是建立面向数据的服务网格,取代当前面向过程的服务网格。
爱彼迎使用 GraphQL 建立了面向数据的服务网格 Viaduct,其 GraphQL Schema包括:
Type,Interface:描述服务网格管理的数据
Query,Subscription:通过抽象服务入口,提供访问数据的方法
Mutation:通过抽象服务入口,提供更新数据的方法
Schema 中的 Type 和 Interface 能够组成一个管理服务网格内所有数据的图,例如,在电子商务公司,Schema 可能定义一个 productById(id: ID) 字段,返回 Product 类型的结果。以此为起点,数据消费者可以通过单次查询获取相关数据,例如通过 productById { manufacturer }; 查询产品制造商的信息,通过 productById { reviews }; 查询产品评价,甚至通过 productById { reviews { author } }; 获取这些评论的作者。
这种查询请求的数据可能来自多个不同的微服务,在面向过程的服务网格中,数据消费者需要将涉及到的服务都作为显式依赖项,而在我们的面向数据的服务网格 Viaduct 中,是由 Viaduct 来了解各服务提供哪些数据,不需要数据消费者来处理服务的依赖关系。
以 Schema 为中心
与其他分布式 GraphQL 系统(例如 GraphQL Modules,Apollo Federation)不同,Viaduct 将 Schema 作为单独的构件,并通过原语保证在多团队同时在 Schema 上进行协作时,Schema 始终保持统一。随着 Viaduct 逐渐取代了爱彼迎底层的面向过程的服务网格,它的 Schema 管理了越来越完整的应用程序数据,我们将其称为 Central Schema,并将一些 API 的定义写在其中。特别地是,我们开始将 GraphQL 用在部分微服务的 API 上,这些微服务的 GraphQL Schema 被定义为 Central Schema 的子集。未来,我们希望进一步发展这种想法,使用 Central Schema 来定义数据库中存储的数据的 Schema。
除此之外,使用 Central Schema 定义 API 和数据库 Schema 能够解决大规模 SOA 应用程序的另一个挑战:数据敏捷性。在目前的 SOA 应用程序中,数据库 Schema 的变化通常需要手动更新,反映在两层、三层甚至更多的微服务 API 改动后,才能在客户端代码中生效。这样的更新可能需要多个团队之间长达几个星期的合作。而通过从 Central Schema 派生 API 和数据库的 Schema,数据库 Schema 的变化仅需一次更新即可传递到客户端代码。
向 Serverless 转变
大型 SOA 应用程序中通常有许多无状态的“派生数据(derived-data)”服务和“服务于前端(Backend-for-frontend)”服务,这些服务从更底层的服务获取原始数据,并将其转换为适合客户端展示的格式。这样的无状态逻辑非常适合 Serverless 计算模型,通过在“云函数”结构中托管,完全消除微服务的运营开销。
Viaduct 使用 Serverless 的云函数来计算我们的“派生字段”,这些函数在不了解底层服务的情况下根据依赖图运行。通过云函数,我们将转换逻辑从服务网格移动到无状态容器,使依赖图保持简单,并减少了所需服务的数量和复杂性。
总结
Viaduct 建立在 graphql-java 之上,通过 GraphQL 支持细粒度的字段选取,使用了现代数据加载技术、短路和软依赖等可靠性技术,并实现了请求内缓存。Viaduct 提供了数据可观测性,能够在字段层次上观测各服务数据消费的情况。同时, 作为 GraphQL 接口,Viaduct 能够兼容开源社区提供的丰富的生态系统,提供实时 IDE 、模拟服务器、Schema 可视化等功能。
Viaduct 从 2019 年开始应用到爱彼迎的生产环境,从最初由少数实体(Entity) 组成的简单 Schema,一年后发展到包括 80 个核心 Entity、服务 75% 的 API 流量的规模。
作者:Raymie Stata, Arun Vijayvergiya, Adam Miskiewicz,译者:Yidi Wei,校对:Keyao Yang。
特别感谢所有项目的参与者,以及相关开源库的贡献者。
如果你想了解关于爱彼迎技术的更多进展,欢迎关注我们的 Github 账号(https://github.com/airbnb/) 以及微信公众号(爱彼迎技术团队)