写点什么

Netflix 联邦 GraphQL 平台的实现过程及经验教训

  • 2021-01-20
  • 本文字数:5275 字

    阅读完需:约 17 分钟

Netflix联邦GraphQL平台的实现过程及经验教训

之前的文章QConPlus讨论中,我们讨论了 GraphQL Federation 作为一种解决方案是如何分发我们的 GraphQL 模式及其实现的。 在这篇文章中,我们会将注意力转移到成功运行联邦 GraphQL 平台所需的内容上——从它实现的过程到我们汲取的经验教训等方面。


我们迄今为止的旅程

在过去的一年中,我们已经实现了联邦 GraphQL 架构所需的核心基础架构,正如我们前一篇文章所描述的那样:

Studio Edge 架构

 

该平台上的第一个 Graph 域服务(Domain Graph Service,DGS)是我们在第一篇文章(Studio API)中讨论过的 GraphQL 单体应用。接下来,我们与其他几个应用程序团队合作一起制作了 DGS,使其 API 能与之前的单体应用一起公开。到 2019 年底,我们有了第一个使用联邦 Graph 的 Studio 应用程序,性能没有任何下降。我们知道这个架构是可行的之后,就专注于为它更广泛的使用做准备了。我们的目标是在 2020 年 4 月开放 Studio Edge 自助服务平台。

 

2020 年 4 月是一个动荡的时期,疫情大流行,人们一夜之间就过渡到了远程办公。尽管如此,各小组还是开始了成群结队地投入到 Graph 中。很快,我们每天都有数百名工程师直接为 API 贡献力量。而那个曾经是瓶颈的 Studio API 单体应用呢?我们将 Studio API 公开的字段迁移到个人拥有的 DGS 中,而不破坏用户的 API。原单体应用预计将在 2020 年底完全弃用。

 

这段旅程并非没有挑战。最大的挑战在于需在整个组织内协调这一战略。最初,有很多人持怀疑态度并有异议;这个概念相当新,需要整个组织高度一致才能成功。我们的团队花了很多时间来解决不同意见,并根据开发人员的反馈对架构进行了调整。通过我们的原型开发以及与一些关键批评声音的积极合作,我们能够逐渐增加信心并弥合关键差距。


一旦我们在这个想法上达成了广泛的一致,我们就需要确保其采用是无缝的。 这就需要构建鲁棒性强的核心基础设施,以确保良好的开发人员体验,并能解决关键的跨域问题。

核心基础设施

我们的 GraphQL 网关(GraphQL Gateway)基于 Apollo 的参考实现,并且用Kotlin编写。这使得我们能够访问 Netflix 的 Java 生态系统,同时也为我们提供了鲁棒性强的语言特性,比如用于高效并行抓取的协程(coroutines),以及具有 null 安全的表述性类型系统(expressive type system)。

 

模式注册(Schema Registry)是内部开发的,也是用的 Kotlin。为了存储模式变更,我们使用了一个内部库,该库是在Cassandra数据库之上实现的事件源模式。使用事件源允许我们实现新的开发者体验特性,比如 Schema History 视图。模式注册还集成了我们的 CI/CD 系统,如Spinnaker,可以为 DGS 自动设置云网络。

开发人员教育与经验

在之前的架构中,只有单体应用 Studio API 的团队需要学习 GraphQL。在 Studio Edge 中,每个 DGS 团队都需要在 GraphQL 上积累专业知识。GraphQL 有自己的学习曲线,对于像批处理(Batching)先行断言(Lookahead)这样的复杂情况,它会变得特别棘手。另外,正如前一篇文章所讨论的那样,理解 GraphQL Federation 和实现实体解析器(Entity Resolver)也不是一件容易的事。


我们与 Netflix 的开发者体验(Netflix’s Developer Experience,DevEx)团队合作,为开发人员提供文档、培训材料和教程。对于一般的 GraphQL 问题,我们依赖于开源社区,并建立了一个内部 GraphQL 社区来讨论诸如分页、错误处理、可空性和命名约定之类的热门话题。

DGS 框架和开发者工具

为了方便后端工程师构建 GraphQL DGS,DevEx 团队在 GraphQL Java 和 Spring Boot 的基础之上构建了一个“DGS 框架”(“DGS Framework”)。该框架解决了运行在生产环境中的 GraphQL 服务的所有跨域问题,同时也使开发人员能更容易地编写 GraphQL 解析器。此外,DevEx 还构建了强大的工具,可用于将模式推送到模式注册 Schema Registry 中,并构建了一个自助服务 UI(Self Service UI),可用于浏览各种 DGS 模式。点击查看他们的会议演讲,期待我们的同事将来能发表一篇相关的博文。DGS 框架计划于 2021 年初开源。

模式治理

Netflix 的 Studio 数据极其丰富和复杂。在早期,我们预期活动的模式管理对模式演进和整体的健康是至关重要的。我们组织中已经有一位 Studio 数据架构师(Studio Data Architect)了,他专注于跨 Studio 的数据建模和对齐。我们与他们合作,一起确定了最适用于 Studio Engineering 需求的 Graph 模式的最佳实践。

 

我们的目标是设计一个能够反映域本身而不是数据库模型的 GraphQL 模式。UI 开发人员不必构建面向前端的后端(Backends For Frontends,BFF)来根据他们的需要处理数据,相反,他们应该协助一起来塑造模式,以满足他们的需求。拥抱协作的模式设计方法对于实现这一目标至关重要。

Schema 设计工作流


协作设计过程涉及跨团队的反馈和审查。为了简化模式设计和审查,我们成立了一个模式工作组并开发了一个用于加入联邦架构的托管技术程序。虽然审查会增加产品开发过程的开销,但我们相信,优先考虑 Graph 模型的质量能减少未来的变更及其所需的反工量。审查的级别因受影响的实体而异;对于核心联邦类型,则需要更严格的审查(尽管工具有助于简化该流程)。

 

我们有一个用于改进模式的 deprecation 工作流。我们利用了 GraphQL 的deprecation特性,并且还跟踪了模式中每个字段的使用统计信息。一旦统计数据表明已弃用字段不再使用,我们就可以进行向后不兼容的变更,将该字段从模式中删除。


显示已弃用字段使用情况的客户端

 

我们采用了模式优先的方法,而不是从现有的模型中(例如 gRPC API 的 Protobuf 对象)生成模式。虽然Protobuf 和gRPC是构建服务 API 的优秀解决方案,但我们更喜欢将 GraphQL 模式与这些层解耦,以实现更清晰的 Graph 设计和独立的可扩展性。在某些场景中,我们实现了从 GraphQL 解析器到 gRPC 调用的通用映射编码,但是额外的样板对 GraphQL API 的长期灵活性来说是值得的。

 

我们使用的方法是基于“上下文高于控制”(“context over control”)的,这是Netflix文化的一个重要原则。我们没有试图严格控制整个 Graph,而是为产品团队提供指导和上下文,这样他们就可以应用自己的领域知识为自己的领域创建灵活的 API。随着该架构的成熟,我们将继续监控模式运行情况,并在需要的时候开发新的工具、流程和最佳实践。

可观测性

在我们之前的架构中,可观测性是通过人工分析和 API 团队的路由来实现的,它的可伸缩性很差。对于我们的联邦架构,我们以一种更具可伸缩性的方式来优先解决可观测性的需求。


我们优先考虑如下三个方面:

  • 报警——当发生错误时(when)报告

  • 发现——轻松确定哪些(what)是不工作的

  • 诊断——调试某些东西不工作的原因(why)

 

我们在这一领域的指导度量指标是平均修复时间(MTTR)以及服务等级目标和服务等级指标(SLO/SLI)。

 

我们与 Netflix 遥测团队的专家合作。我们将网关和 DGS 架构组件与Zipkin、内部分布式跟踪工具Edgar和应用程序监控工具TellTale 集成在一起。在 GraphQL 中,几乎每个响应都是 200,错误块中包含了自定义的错误。我们从响应中检查这些自定义的错误代码,并将它们发送到我们的度量服务Atlas中。这些集成为 GraphQL API 的使用者和开发人员奠定了丰富的可见性和洞察力的良好基础。

 

监控联邦请求生命周期的 Edgar Trace

 

联邦请求的时间轴视图


分布式日志关联(Distributed Log Correlation)有助于调试更复杂的服务问题。通过显示处理请求所涉及的所有系统的应用级日志详细信息,我们可以更深入地了解堆栈中发生的事情。开发人员可以很容易地看到与给定请求同时发生的事情,以排查可能影响交互的周边因素。

一个跨多个服务记录的联邦请求的日志

 

为了解决“我应该问谁(who)……”的路由问题,我们集成了从 GraphQL 类型和字段到它们所属团队的支持(support)渠道的深度链接。现在,寻找 support 只需单击跟踪中的链接,这有助于缩短 MTTR 并能减少网关团队所需的参与次数。

保护联邦 Graph 的安全

 我们的目标是在整个联邦架构中实现可靠且一致的安全实践。为了实现这一点,我们与 Netflix 的安全专家合作,将安全性构建到 Graph 中。让我们来看下我们安全解决方案的两个基本部分:AuthN 和 AuthZ。

身份认证

我们在 Studio 空间中的所有产品体验都需要一个经过认证的帐户,因此我们将 GraphQL 网关的访问权限限制为只允许受信任的经过身份验证的调用者。此外,Graph Introspection仅限于 Netflix 的内部开发人员。

授权

在 Studio Edge 之前,授权逻辑在各个团队之间是分散的。一些团队在他们的 BFF 中实现了授权,一些在微服务中实现了授权,而另一些团队则在这两处兼而有之。结果往往是,由于用户访问的 UI 不同,对于给定的数据,授权也不同。UI 团队还发现他们需要对每个新的前端实现(以及重新实现)进行授权检查。


在 Studio Edge 中,我们将授权责任委托给 DGS 的所有者。这就实现了在不同应用程序中对同一用户进行一致的授权。此外,产品经理、工程师和安全团队可以很容易地了解谁可以访问哪种数据类型以及如何访问。


我们在 Netflix 内部提供了多种授权服务:从基于用户身份授予访问权限的简单系统,到引入角色和功能概念的更细粒度的系统。DGS 开发人员可以根据自己的需要选择解决方案。然后,他们只需使用 @Secured 注解对解析器进行注解,并将其配置为使用一个可用的系统即可。如果需要,可以在解析器或下游系统中实现更复杂的授权。

授权的未来

我们目前正在构建一个支持 GraphQL 授权解决方案的原型。注册模式时,Schema Registry 会自动为每个字段及其相应类型生成访问控制组(Access Control Groups,ACGs)。产品经理和 DGS 工程师可以决策这些生成的 ACG 的成员资格和规则。由于 ACG 映射到 GraphQL 中的某个字段,因此 DGS 框架会在执行期间自动应用与 ACG 关联的规则。

面向失败的架构设计

GraphQL 网关是所有请求的单一入口点;网关上的故障可能会导致严重的中断。根据 Netflix 工程的最佳实践,我们假设会发生故障,并设计方法来减轻这些故障的影响。以下是我们用来确保网关层具有弹性的设计原则:

  1. 单一用途

  2. 无状态服务

  3. 需求控制

  4. 多区域

  5. 按功能分片

 

首先,我们将网关层的职责集中在一个用途上:解析客户端查询,然后构建和执行查询计划。通过缩小范围,我们限制了可能发生的问题的范围。我们的目标是即时执行日志记录和度量指标之外的任何其他资源密集型操作。在网关层中添加其他不相关的逻辑可能会增加在这一关键层中出现故障的表面积。

 

其次,我们运行网关服务的多个无状态实例。任何网关实例都能够为任何请求生成和执行查询计划。当我们对网关层进行代码变更时,我们会在正式投入生产之前对它们进行严格的测试。

 

第三,我们通过应用需求控制来平衡每个请求消耗的资源。我们限制调用方的速率,以避免底层数据库过载,这些数据库是大多数领域元素的来源。我们还对所有传入的查询运行静态查询开销计算,并拒绝昂贵的查询,以避免网关和 DGS 资源的阻塞。我们的合作伙伴了解这些折衷方案,并与我们一起来满足这些要求,重新处理昂贵的查询并减少大的调用。

 

第四,我们在世界各地的多个 AWS 区域中部署了网关层。这使我们能够限制爆炸半径以应对发生不可避免的问题。当问题发生时,我们可以将故障转移到另一个区域,以确保我们的客户受到的影响最小。

 

最后,我们部署多个网关层的功能分片(Shard),每个分片中的代码均相同,传入的请求根据类别进行路由。例如,GraphQL 订阅(Subscription)通常会导致长时间的连接,而查询(Query)和突变(Mutation)则是短暂的。我们使用一个单独的实例组来处理订阅,因此“连接耗尽”也不会影响查询和突变的可用性。

 

我们还可以做更多的事情来提高弹性。我们计划对网关部署以及最终的模式变更进行金丝雀(Canary)部署和分析。今天,我们的网关通过轮询模式注册来动态更新其模式。我们正在通过将联邦配置存储在一个版本化的 S3 存储桶中来实现这些功能的解耦,从而使网关能够抵御模式注册的故障。

结束语

GraphQL 和 Federation 已成为 Studio 应用程序的效率倍增器。基于此,我们最近使用 GraphQL Federation 为 iOS 和 Android 上的 Netflix 消费者应用程序搜索页面创建了原型。为了做到这一点,我们创建了三个 DGS 来为消费者 Graph 的最小部分提供数据。我们正在将一小部分用户切流到这个替代技术栈中,并测量其高层度量指标。我们很高兴看到结果,并进一步探讨其在 Netflix 消费者领域的适用空间。


尽管我们拥有积极的经验,但 GraphQL Federation 尚处于成熟期的早期,可能对每个团队和组织来说并不都是最合适的。学习 GraphQL 和 DGS 开发、运行联邦层并进行迁移需要合作团队的高度投入以及无缝的跨功能协作。如果你正在考虑朝这个方向发展,我们建议你查看 Apollo 为Federation提供的SaaS产品,以及许多学习 GraphQL 相关的在线资源。对于像我们这样拥有大量的微服务并需要其聚合在一起的生态系统来说,开发速度和可操作性的提高使得这种转变是值得的。


最后,我们想听听你的意见!如果你已经实现了 Federation,或者尝试用另一种方法来解决这个问题,我们很乐意向你了解更多信息。分享知识是我们这个行业快速学习和提高的方式之一。最后,如果你想参与解决诸如 Netflix 弹性相关的复杂而有趣的问题,请查看我们的招聘页面或直接联系我们。

 

英文原文链接:


https://netflixtechblog.com/how-netflix-scales-its-api-with-graphql-federation-part-2-bbe71aaec44a

2021-01-20 16:472390

评论

发布
暂无评论
发现更多内容

模块七-游戏商城异地多活架构设计

babos

#架构实战营

模块七作业

河马先生

架构实战营

14. DeepMind--会打游戏的人工智能

Databri_AI

人工智能

王者荣耀商城异地多活分析-模块7

小牧ah

架构实战营

架构实战营模块 7 作业

zlz

模块七作业

VE

架构实战营

月薪10K码农,跳槽到40K架构师,技术学习路线图汇总

小傅哥

Java 学习 运维 大前端 后端

【OpenIM原创】C/C++调用golang函数,golang回调C/C++函数

OpenIM

css中塌陷问题指的是什么,margin和padding应该怎么区分

你好bk

CSS html css3 大前端

计算机中遇到的单位该怎么换算

耳东@Erdong

计算机 9月日更 单位换算

架构实战营 模块七 作业

脉醉

#架构实战营

架构实战营模块 7 作业-王者荣耀商城异地多活架构设计

蔸蔸

《联想发布绿色智城解决方案,加速城市绿色低碳转型发展》

科技大数据

架构训练营 模块六

小卷儿

【架构训练营】模块七作业

zclau

springboot vue二手交易市场毕设源码

清风

毕业设计

手撸二叉树之二叉树的直径

HelloWorld杰少

九月

学读代码比学写代码更重要

baiyutang

编程 工程规约 9月日更

模块7作业

柱林

架构实战营模块七作业

子豪sirius

架构实战营

C#多线程开发-任务并行库04

Andy阿辉

C# asp.net 多线程 多线程并发

【Flutter 专题】52 图解可折叠状态栏

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 9月日更

聊聊 Jmeter 如何并发执行 Python 脚本

星安果

Python Jmeter 并发 文件上传

模块七作业

Mr.He

架构实战营

模块七作业

袁小芬

架构训练营模块七作业

喻高咏        

架构训练营 模块七

特洛伊木马-图解VXLAN容器网络通信方案

Lance

深入了解Spring之ConfigurationClassPostProcessor

邱学喆

@Bean @ComponentScan @PropertySource @Import @ImportResource

架构实战营 - 模块 7 - 王者荣耀商城异地多活架构设计

雪中亮

架构实战营 #架构实战营

【LeetCode】检查平衡性Java题解

Albert

算法 LeetCode 9月日更

Kubernetes生态系统与演进路线

博文视点Broadview

Netflix联邦GraphQL平台的实现过程及经验教训_架构_Netflix技术博客_InfoQ精选文章