写点什么

超越 REST

  • 2021-03-31
  • 本文字数:4140 字

    阅读完需:约 14 分钟

超越 REST

娱乐业一直在努力应对 COVID-19 对全球制作的影响冲击。自 2020 年初以来,Netflix 一直在迭代开发系统,以向内部利益相关方和企业领导者提供有关疫情最新信息的最新工具和仪表盘。这些软件解决方案使得管理层可以就给定的实体产品是否以及何时能够安全地开始在全球范围内创建引人注目的内容而做出最明智的决策。在 Netflix Studio Engineering 内部,一种备受关注的方法是将 GraphQL 微服务(GQLMS)作为后端平台来促进应用程序的快速开发。


许多组织都在拥抱 GraphQL,以其作为统一企业范围内数据模型的一种方式,并提供了一个用其相关实体网络来导航大量结构化数据的单一入口点。这种努力值得称赞,但往往需要内部组织之间历经几个季度的协调,然后将所有相关实体开发并集成到一个单一的单体图中。


与“用一张图来管理所有对象”的方法不同,GQLMS 只是利用 GraphQL 来作为构建 CRUD 应用程序的丰富 API 规范。我们使用 GQLMS 进行了快速的概念验证应用,其经验证实了 GraphQL 宣传其好处时所提出两个理论:


  • GraphiQLIDE 在模式(schema)旁边显示任何可用的 GraphQL 文档,从而极大地改善了 API 使用者的人机工程学(与同类中最好的 Swagger UI 相比)

  • GraphQL 的强类型系统和多语言客户端支持,意味着 API 提供者无需关心特定于语言的 API 客户端的生成、版本控制和维护(比如,那些使用优秀的 Swagger Codegen 生成的客户端)。GraphQL API 的使用者可以简单地利用自己喜欢的开源 GraphQL 客户端。



GraphiQL:为 《星球大战》API 自动生成的测试 GUI


我们的经验已经为对 GQLMS 作为快速开发平台感兴趣的团队带来了一个具有许多最佳实践的架构。


Graphile


在早期的 GraphQL 探索过程中,Netflix 的工程师意识到 Graphile 库可以将 PostgreSQL 数据库对象(表、视图和函数)作为 GraphQL API 来呈现。Graphile 支持 智能注解,支持通过使用特定格式的 PostgreSQL 注解标记数据库的表、视图、列和类型来控制各种特性。文档甚至可以嵌入到数据库注解中,以便在 Graphile 生成的 GraphQL 模式中显示。


我们假设有一个 Docker 容器,其上运行了一个带有 Graphile 库的非常简单的 NodeJS Web 服务器(以及一些用于安全、日志、度量和监控的 Netflix 内部组件),可以为快速开发工作提供“比 REST 更好的 REST”或“REST++”平台。使用 Docker,我们定义了一个轻量级的独立容器,它允许我们将 Graphile 库及其支持的代码打包成一个独立的包,任何团队都可以在 Netflix 上使用它,而无需额外的编码。只需下拉定义 Docker 的基础镜像,并使用适当的数据库连接符运行它即可。这种方法被证明是非常成功的,并且对 Graphile 的使用产生了一些深刻洞察。


具体来说:


  • 使用数据库视图作为“API 层”来保持灵活性,以允许在不变更现有 GraphQL 模式(构建在数据库视图上)的情况下修改表。

  • 使用 PostgreSQL 聚合函数 时,请使用 PostgreSQL 复合类型。

  • 通过允许 GraphQL 客户端“所用权限”(“full access”)自动生成的 GraphQL 查询和 Graphile 生成的突变(在所有表和视图上公开的 CRUD 操作)来提高灵活性;然后在开发过程的后期,删除在应用程序投产之前未被 UI 使用到的模式元素。

数据库视图作为 API


我们决定将数据表放在一个 PostgreSQL 模式中,然后在另一个模式中定义这些表的视图,同时 Graphile Web 应用程序使用专用的 PostgreSQL 用户角色连接到数据库。这最终能实现几个不同的目标:


  • 可以独立于 GraphQL 模式中公开的视图来更改底层表。

  • 视图可以进行基本的格式化(比如将 TIMESTAMP 字段呈现为 ISO8601 字符串)。

  • 底层表上的所有权限必须显式地授权给 Web 应用程序的 PostgreSQL 用户,以避免意外的写操作。

  • 表和视图可以在同一个事务中进行修改,这样就可以原子地对公开的 GraphQL 模式进行更改。


关于最后一点:更改表中列的类型将会打破关联的视图,但是通过封装在事务中的更改,可以删除视图、更新该列,然后可以在提交事务之前重新创建视图。我们在启用 pgWatch 的情况下运行 Graphile,只要对数据库做任何更新,GraphQL 模式就会立即更新以反映所做的更改。

PostgreSQL 复合类型


Graphile 在读取 PostgreSQL 数据库模式以及将表和基本视图转换为 GraphQL 模式方面做得非常出色,但我们的经验表明,当视图中存在 PostgreSQL 聚合函数 或 JSON 函数 时,Graphile 在如何描述嵌套类型方面存在局限性。原生 PostgreSQL 函数,比如json_build_object,将被转换成 GraphQLJSON类型,该类型只是一个String,没有任何内部结构。例如,以这个返回JSON对象的简单视图为例:


postgres_test_db=# create view postgraphile.json_object_example as  select json_build_object(‘hello world’::text, 1, ‘2’::text, 3)  as json;postgres_test_db=# select * from postgraphile.json_object_example;          json— — — — — — — — — — — — -{“hello world”: 1, “2”: 3}(1 row)
复制代码


在生成的模式中,数据类型为JSON



json字段的内部结构(hello world2这两个子字段)在生成的 GraphQL 模式中是不透明的。


为了进一步描述json字段的内部结构(将其在生成的模式中公开),定义一个复合类型,并创建一个返回该类型的视图:


postgres_test_db=# CREATE TYPE postgraphile.custom_type AS (  "hello world" integer,  "2" integer);
复制代码


接下来,创建一个返回该类型的函数:


postgres_test_db=# CREATE FUNCTION postgraphile.custom_type(  "hello world" integer,  "2" integer)RETURNS postgraphile.custom_typeAS 'select $1, $2'LANGUAGE SQL;
复制代码


最后,创建一个返回该类型的视图:


postgres_test_db=# create view postgraphile.json_object_example2 as  select postgraphile.custom_type(1, 3)  as json;postgres_test_db=# select * from postgraphile.json_object_example2; json— — — -(1,3)(1 row)
复制代码


乍一看,这似乎没有什么用,但要记住:在查看生成的模式之前,请在视图、自定义类型和自定义类型的字段上定义注解,以利用 Graphile 的智能注解:


postgres_test_db=# comment on  type postgraphile.custom_type  is E’A description for the custom type’;postgres_test_db=# comment on  view postgraphile.json_object_example2  is E’A description for the view’;postgres_test_db=# comment on  column postgraphile.custom_type.”hello world”  is E’A description for hello world’;postgres_test_db=# comment on  column postgraphile.custom_type.field_2  is E’@name field_two\nA description for the second field’;
复制代码


现在,当查看模式时,json字段不再显示为不透明的类型JSON,而是显示为CustomType



(还要注意,对视图所做的注解(A description for the view)显示在查询字段的文档中)。


单击CustomType将显示自定义类型的字段及其注解:



请注意,在自定义类型中,第二个字段被命名为field_2,但 Graphile 智能注解将该字段重命名为field_two,通过 Graphile 将驼峰式大小写转换为fieldTwo。另外,对这两个字段的描述都被显示在生成的 GraphQL 模式中。

允许 Graphile 生成的模式具有“所有权限”(在开发期间)


最初,当讨论使用 Graphile 作为“一种模式来管理所有模式”架构中的一个选项时,该提议遭到了强烈的反对。关于安全性(如何将其与我们的 IAM 基础设施集成,以及如何在数据库中实施行级访问控制?)和性能(如何限制查询以避免一次选择所有行来对数据库进行 DDoS 攻击?)的合法性问题引起了人们的关注,提出了使用类似于 SQL 的查询接口以提供对数据库表的打开权限(open access)。然而,在小团队快速开发内部应用程序的 GQLMS 环境中,默认的 Graphile 行为是让所有列都可用来过滤,这允许 UI 团队可以快速迭代大量新特性,而无需后端团队的参与。这与其他开发模型不同,在其他模型中,UI 和后端团队首先就初始 API 契约达成一致,后端团队实现 API,UI 团队使用 API,然后 API 契约随着 UI 需求在开发生命周期中的变化而演变。


最初,整个应用程序的性能很差,因为 UI 通常需要多次查询才能获取所需的数据。然而,一旦应用程序的行为被充实起来,我们就可以快速创建新视图,以满足每个 UI 交互的需求,这样每次交互只需要一个调用即可。因为这些请求是以本机代码运行在数据库上,所以我们可以通过适当地使用索引、去规范化、集群等来执行复杂的查询并获得高性能。


一旦 UI 和后端之间的“公共 API”(“public API”)固化,我们就“加固”了 GraphQL 模式,通过使用智能注解@omit标记表和视图来删除所有不必要的查询(由 Graphile 的默认设置创建)。另外,Graphile 的默认行为是为表和视图生成突变,但是智能注解@omit create,update,delete将从模式中删除突变。

结论


对于那些采用模式优先方法进行 GraphQL API 开发中的用户来说,Graphile 的自动 GraphQL 模式生成功能可能会对模式设计者造成难以接受的限制。如果需要细粒度的访问控制,Graphile 可能很难集成到现有的企业 IAM 基础设施中。向 Graphile 生成的模式中添加自定义查询和突变(即公开 UI 所需的 gRPC 服务调用)是我们目前在 Docker 镜像中不支持的。然而,我们最近注意到 Graphile 的 makeExtendSchemaPlugin,它允许将自定义类型、查询和突变合并到 Graphile 生成的模式中。


也就是说,在初始需求有限并且有一个临时的分布式团队(之前没有合作过)的情况下,一个内部应用在 4-6 周内就能成功实现,这引起了整个 Netflix Studio 的极大兴趣。Netflix 的其他团队也正在寻找对应的 GQLMS 方法:


  1. 使用标准的 GraphQL 构造函数和实用程序将数据库公开为 API

  2. 利用自定义的 PostgreSQL 类型构建 GraphQL 模式

  3. 通过从数据库自动生成大型 API 来提高灵活性

  4. 并在 Graphile 生成的业务逻辑和数据类型之外,额外公开其他自定义的业务逻辑和数据类型


这是一个替代之前使用 REST 实现内部 CRUD 工具的可行解决方案。拥有托管 Graphile 的标准化 Docker 容器为团队提供了必要的基础设施,通过这些基础设施,他们可以快速迭代新工具的原型以及快速开发应用程序,从而解决全球媒体工作室在这个充满挑战时期内不断变化的需求。


原文链接:


https://netflixtechblog.com/beyond-rest-1b76f7c20ef6

2021-03-31 15:023646

评论

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

前端leetcde算法面试套路之回溯

js2030code

JavaScript LeetCode

适合小白Web前端入门JS基础知识梳理汇总

千锋IT教育

【计算讲谈社】第十二讲|数据中心那些事儿

大咖说

数据中心 碳中和

深度解读|NebulaGraph x 阿里云计算巢,云上构建超大规模图数据库

阿里云弹性计算

图数据库 计算巢

3.1版本【HarmonyOS 第一课】正式上线!参与学习赢官方好礼>>

HarmonyOS开发者

HarmonyOS

MyBatis 延迟加载代码详解

千锋IT教育

Baklib知识分享 | 搭建企业在线帮助中心的最佳攻略

Baklib

5分钟实现「视频检索」:基于内容理解,无需任何标签

Zilliz

人工智能 Towhee 视频检索

银行APP用户体验外滩峰会圆满落幕!易观分析赋能用户体验体系,助力体验提升

易观分析

App 银行 易观

谁能破解客户数字化困局?

ToB行业头条

如何通过Java代码给Word文档添加水印?

在下毛毛雨

Java word 水印

【案例】数字化浪潮中,云科通明湖如何助力能源行业弯道超车?

通明湖

负载均衡

mysql经典面试题

@下一站

MySQL 编程 dba 11月月更

Linux之用户管理、权限管理、程序安装卸载

C++后台开发

Linux 后端开发 linux开发 Linux服务器开发 C++开发

让 Serverless 更普惠,阿里云函数计算 FC 宣布全面降价,最大幅度达 37.5%

Serverless Devs

阿里云 Serverless

Apisix网关快速入门实践

IT巅峰技术

阿里P8整理的新版手抄本,所有Java知识操作

钟奕礼

Java java程序员 java面试 java编程

让 Serverless 更普惠,阿里云函数计算 FC 宣布全面降价,最大幅度达 37.5%

阿里巴巴云原生

阿里云 Serverless 云原生 函数计算

日志异常检测准确率低?一文掌握日志指标序列分类

云智慧AIOps社区

深度学习 日志分析 时间序列 指标预测 日志异常

Wallys/DR7915/MT7915/MT7975/industrial mini pcie card 2T2R

Cindy-wallys

MT7915 MT7975 / 2.4G&5G

Wallys/ industrial mini pcie card/2x 2 5G /High power Radio card

Cindy-wallys

QCA9882 2x 2 5G high power

如何做好成熟完整的企业团队知识管理?

Baklib

团队管理 知识管理

信息论与编码:信道编码的基本概念

timerring

11月月更 信息论

PHP反序列化漏洞解析

网络安全学海

网络安全 安全 信息安全 渗透测试 漏洞挖掘

FAQ是什么?该如何编辑FAQ?

Baklib

调用链路上千条,如何观测 Nacos 的运行状态

阿里巴巴云原生

阿里云 微服务 云原生 naocs

国标解读|从关键信息基础设施安全国标看软件供应链安全

墨菲安全

SBOM 软件供应链安全

Spring Bean的生命周期(详细解读)

千锋IT教育

《大厂面试》之JVM篇21问与答

钟奕礼

Java java程序员 java面试 java编程

为什么你的敏捷总是不成功?

敏捷开发

开源共建 | Dinky 扩展批流统一数据集成框架 ChunJun 的实践分享

袋鼠云数栈

超越 REST_架构_Dane Avilla_InfoQ精选文章