写点什么

超越 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:023557

评论

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

当心这些“坑”!阿里云服务器部署前后端分离项目

麦洛

nginx 前后端分离 跨域

未读消息(小红点),前端与 RabbitMQ实时消息推送实践,贼简单~

程序员小富

Java RabbitMQ mqtt

合约跟单app,永续合约交易所带单交易软件

MySQL高性能架构设计原则

洛神灬殇

CSS常用样式——绘制单(双)箭头的多种方法(2)

程序员学院

CSS html 程序员

新基建夯实粤港澳大湾区高质量发展基础

CECBC

区块链 人工智能 大数据

传统产业数字化转型的思考与建议

CECBC

经济转型 企业经济

模板方法模式——看看 JDK 和 Spring 是如何优雅复用代码的

Java架构师迁哥

使用开源软件构建工业互联网的平台

刘旭东

工业互联网 Odoo thingsboard

边缘计算,如何啃下集群管理这块硬骨头?

BoCloud博云

云计算 容器 边缘计算 PaaS 博云

呕心沥血,整理200+最新面试题,面试之前看一下,事半功倍

小Q

Java 学习 程序员 架构 面试

去中心化交易所搭建,虚拟币去中心化交易系统

交易所开发

【万字长文】探讨可信构架之道

华为云开发者联盟

架构 服务端

教师节特别活动:第四范式多项自研技术及其应用实践分享

天枢数智运营

人工智能 推荐系统 第四范式 个性化推荐

20张图,大学四年都没整明白的操作系统就这么学会了

小Q

Java 程序员 架构 操作系统

云图说 | GPU共享型AI容器,让AI开发更普及

华为云开发者联盟

AI 容器

大厂运维必备技能:PB级数据仓库性能调优

华为云开发者联盟

架构 数据

协同新机遇:让研发敏捷起来

人称T客

Docker 容器编排利器 Docker Compose

哈喽沃德先生

Docker 容器 微服务 Docker-compose

The Go Blog-Article index

卓丁

Redis 哨兵模式

是老郭啊

redis redis哨兵模式 redis哨兵 redis哨兵集群

LeetCode题解:225. 用队列实现栈,两个队列,压入 -O(1), 弹出 -O(n),JavaScript,详细注释

Lee Chen

大前端 LeetCode

Polkadot初识,不止于跨链

QTech

polkadot 跨链

一步搞定任意圆角背景

mengxn

android xml 圆角

未来经济 数字优先 | 大数据专场

腾讯云大数据

大数据

indexOf原理,Java,javascript,python实现

叫练

算法

[翻译]The Go scheduler[Go调度]

卓丁

Go scheduler Go 语言

深度解析!--阿里开源分布式事务框架Seata

攀岩飞鱼

分布式 分布式事务 微服务 分布式锁 Seate

物联网通信技术最全科普!你一定要了解的NB-IoT

华为云开发者联盟

物联网

XSKY ClickHouse如何实现存算分离

XSKY星辰天合

[翻译]Go Concurrency Patterns[Go 并发模式]

卓丁

Rob Pike Go Concurrency Patterns Concurrency Go 语言

超越 REST_架构_Dane Avilla_InfoQ精选文章