什么是 GraphQL ?第一次看到这个名词未免让人联想到数据库查询语言 SQL 。但本质上,这是两个完全不同的东西, GraphQL 在官方文档里的定义如下:

GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data.

即 GraphQL 既是一个 API 查询语言,也指其服务端实现。但 GraphQL 不只是为了在 API 领域搞个类似数据库的查询语言,它的诞生更涉及到 API 设计的思路转变。

REST 模式的难题

通常,一项新技术的产生都会伴随着两个背景,一个是该技术所在的领域出现了新趋势、二是原有的技术难以应对新趋势。而近几年, API 领域有几个趋势愈发值得关注:

首先是日益增多的移动端应用,和移动端性能本身较低下的矛盾,要求数据加载过程更高效。

再者,要满足客户端和前端快速开发、快速添加特性的需求, API 必须能快速拓展。

第三则是各种不同的前端框架和平台层出不穷,而后端 API 服务面对众多的前端框架、乃至前端和客户端共享 API 的情况,其能否按需提供数据,会影响接口复用度和开发效率。

而现如今在 API 领域被广泛使用的 REST 模式,面对上述愈发复杂的客户端和服务端交互,问题也渐渐浮现:

首先是接口灵活性差。由于设计接口粒度较粗或历史遗留原因,接口中有时会存在当前数据交互不需要的字段,导致取到无用且多余的数据;而另一方面,有时前端需要一份数据,却需要手动访问多个接口才能完整获取。

第二是接口操作流程繁琐,回想下前端获取数据的过程,通常我们要先构造 HTTP 请求,然后接收和解析服务端响应。有时还要对收到的或处理后的数据另作本地数据转储,最后才进行 UI 展示。

第三,随着客户端功能拓展,服务端不断增加接口。这样维护众多接口,不仅服务端维护成本高,此外也不能按需提供数据、阻碍了客户端的快速迭代和拓展。

还有 REST 模式实质上是基于 HTTP 协议的,这虽让其易于被 Web 开发人员理解和上手,但也决定它不能灵活选择网络协议来解决问题。

GraphQL 的解决方案

面对 REST 模式的上述不足, Facebook 提出了他们的解决方案 – GraphQL :

前面提到 GraphQL 既是一个 API 查询语言,也指其服务端实现,所以 GraphQL 本身也由两部分组成,Facebook 将它们分别开源:

  • 语言标准: Open Web Foundation Agreement (OWFa) v1.0 协议
  • GraphQL.js、客户端工具Relay: MIT 协议

我们来逐条了解下 GraphQL 的特性:

声明式的数据获取

如下图所示,声明式的数据查询带来了接口的精确返回,服务器会按数据查询的格式返回同样结构的 JSON 数据、真正照顾了客户端的灵活性:

另外,这种数据获取方式也带来更简洁的数据查询流程。 GraphQL 认为,客户端只需描述查询结构发起查询,再把服务端响应数据用于 UI 展示即可。中间构造请求和转储数据的操作可以交由 GraphQL 客户端辅助完成。

一个服务仅暴露一个 GraphQL 层

上图是一个 GraphQL 应用的基本架构,其中客户端只和 GraphQL 层进行 API 交互,而 GraphQL 层再往后接入各种数据源。这样一来,只要是数据源有的数据, GraphQL 层都可以让客户端按需获取,不必专门再去定接口了。

传输层无关、数据库技术无关

带来了更灵活的技术栈选择,比如我们可以选择对移动设备友好的协议,将网络传输数据量最小化,实现在网络协议层面优化应用。

GraphQL 接入概览

既然 GraphQL 有诸多优点,那又该如何接入呢?大体上,有三种接入的方式:

直连数据库的GraphQL服务

最为简洁的服务配置,直接操作数据库也能减少中间环节的性能损耗。

集成现有服务的GraphQL层

这种配置适合于旧服务的改造,尤其是在涉及第三方服务时、依然可以通过原有接口进行交互。

直连数据库和集成服务的混合模式

前两种方式的混合:

GraphQL 核心概念浅析

GraphQL 的一大特点便是声明式的 API Schema ,GraphQL 的 Schema 是一个声明式的查询规范(可认为是服务器和客户端间的一个查询协议),它主要由两部分组成:

  • 类型系统
  • 编写语法:SDL(视图定义语言)

GraphQL 的类型系统包含了各编程语言中通用的一些数据类型,具体可参考规范文档了解。

接下来简单介绍下 GraphQL 的 SDL 语法:

定义 API Schema

自定义类型的定义主要是在服务端完成的,语法如下:

type 类型名 {字段名: 类型
}
复制代码

此外, GraphQL 还有 Query, Mutation, Subscription 等特殊的根类型,用于定义 API Schema 。我们可以定义一个用户:

type User {id: Int!name: String
}
复制代码

然后定义几个用于数据操作的 API Schema :

type Query { // 基本查询 Schemauser(id: Int!): User // 传入一个 id ,返回具体用户
}type Mutation { // 操作数据的 SchemacreateUser( // 传入用户名自动创建一个用户name: String): User
}type Subscription { // 监听数据变更的 SchemauserChanged: User
}
复制代码

数据操作

有了这些定义好的 API Schema ,我们就可以此来发起数据操作了。 GraphQL 的数据操作也分为 Query, Mutation, Subscription 三种类型。简单来讲, Query 就是获取数据的基本查询;Mutation 支持对数据的增、删、改等操作;而 Subscription 则用于监听数据变动、并靠 Websocket 等协议推送变动的消息给订阅方。

基于前面的定义的用户 Schema ,我们可以写出如下的数据操作:

query {user(id:3) { // 查询用户 id 为3的用户name}
}mutation {createUser(name: "Tom") { // 新增一个名为 "Tom" 的用户nameid}
}subscription {userChanged { // 监听用户数据变动nameid}
}
复制代码

上面这些查询,根字段之后的所有内容称为查询的 payload 。服务端会按查询格式,在 data 字段返回 payload 中指定的数据,比如 createUser 这个操作就会返回如下的数据:

{"data": {"createUser": {"name": "Tom","id": 9}}
}
复制代码

GraphQL 生态圈

通过 API Schema,我们既可指定 API 功能、同时也能定义客户端如何请求数据。但前面介绍的只是个规范,而这个 GraphQL 的规范又是如何落地实现的呢?接下来会围绕服务端、客户端、调试工具,介绍下 GraphQL 应用开发的 “生态圈”。

服务端实现

在服务端, GraphQL 服务器可用任何可构建 Web 服务器的语言实现。除 JavaScript 之外, Ruby , Python , Scala , Java , Clojure , Go 和 .NET 都有实现供参考。

服务端查询执行的核心算法也很简单:就是查询逐字段遍历,并为各字段执行一个 resolver 以处理数据操作。下图举了一个例子:

最左边为一个 GraphQL 查询,该语句查询了 id 为 'abc' 的作者所有文章的标题和内容。中间一副图展示了每个查询字段对应的数据类型,然后在最右边可看到每个字段的解析过程:首先查询 id 为 'abc' 的作者,再从该作者处获取其所有文章;而由于文章是一个列表,最后我们还要遍历这个列表以获取各文章对应的标题、内容。

这个逐字段解析的流程清晰易懂,但如果服务器只是这么实现的话,就会面临性能问题。见下图的例子,若用户要查询文章列表下各个作者的信息,由于文章列表中可能有大量重复的作者,当处理到同一作者的文章时就要重复查询该作者信息,甚至当“查询作者信息”这操作本身就包含大量子操作的话、对服务器性能的消耗就非常可观:

对这种一个查询触发大量相同的数据操作的问题,一种解决思路是将数据操作改为批量处理。还是用上面的例子,下图中我们把查询作者信息的操作改为存入一个队列,待合适的时机再批量发起查询,这时查询的数量就只是队列里的一个最小子集,避免了重复操作。 Facebook 推出的 DataLoder 就是一个这样的数据批量处理和缓存的方案。

上面讨论了 GraphQL 服务端的基本实现思路,而针对 Node.js 的实现,我基于前文示例中的 API Schema 写了一个简单的 Demo ,读者可了解下 GraphQL 的服务端具体是如何实现和使用的。

客户端实现

常见的 GraphQL 客户端库有:

  • Relay:Facebook 官方的 GraphQL 客户端,它大大优化了性能,但只能在 Web 上可用
  • Apollo:一个开源社区项目,旨在为所有开发平台(Web, 安卓, iOS , React Native 等)构建强大而灵活的 GraphQL 客户端

至于如何使用这两个客户端库,可以参考官方文档,这里不再赘述。而对于 Apollo 的入门, Full-stack React + GraphQL Tutorial 一文提供了深入浅出的示例,建议动手尝试下,构建自己第一个 GraphQL 应用吧。

开发工具

GraphQL 有大量实用的开发工具,基本都是基于 introspection 查询实现的。所谓 introspection 查询,就是指客户端向服务器询问 API Schema 信息的查询。比如,我们可以通过查询 __schema 等元字段来获取完整的类型信息:

query {__schema {types {name // 获取根字段名fields {name // 获取字段名}}}
}
复制代码

有了这样一个查询 Schema 信息的功能,就使得 GraphQL 的文档浏览器,自动补全,代码生成等开发工具非常容易实现。而开发工具中,最有名的就是 GraphiQL 了,其本质上可认为是个 GraphQL 客户端,但配有编辑、自动补全、文档浏览等功能,常用于服务端的调试。

前面我们那个服务端 Demo 也以中间件形式引入了基于 GraphiQL 的调试工具 GraphQL PlayGround 。运行 Demo 后,你可以访问 localhost:3000/playground 试试上面列举的所有查询~

GraphQL 存在的问题

当然, GraphQL 也不是完美无缺的,现在 GraphQL 主要存在安全性和服务端缓存能力两方面的问题。

安全问题

GraphQL 声明式的的数据查询提供了灵活、易拓展的接口;但如果我们发起的一次查询包含了过多的数据操作,那么这一次查询就会给数据服务器的带来巨大的压力,提升了被 DDOS 的风险。

此外,每次发起的查询语句,实质上也反映了查询文档的结构,如果被攻击者截取了我们的请求、拼凑出完整的接口内容,这也不利于接口的安全。

面对查询压力,我们可以通过服务端限流、客户端限流等措施来进行缓解,具体限流的措施可参见这篇文章。

而对于 API 结构公开传输的问题,有人提出一个持久化查询的方案。简单来讲,就是客户端和服务端分别将约定好查询内容转换为查询ID,转而使用查询ID进行查询。这样一来既解决了查询语句公开传输的问题,而只传 ID 还顺便减少了传输的数据量、提升了传输速度。

服务端缓存能力

GraphQL 能让客户端灵活地请求数据,这就样一来客户端请求内容就是不确定的,服务端难以根据同一个连接来维护查询缓存。

关于这个问题,前面提到 Facebook 有一个 DataLoader 的技术,可用于实现查询的批量处理和缓存,但其文档中描述的缓存也只是针对单个请求进行、粒度还是较粗。

总结

GraphQL 作为一个新的 API 标准,通过声明式的数据获取方式,给客户端提供了简洁、灵活、高效的数据查询。适应了移动互联网时代客户端技术的快速发展和需求的快速迭代,是当前 REST 模式的有力竞争者。

同时其活跃的社区和日渐成熟的生态圈也证明了这是一个很有生命力的技术,目前 GraphQL 已被许多的公司( Facebook , GitHub , Twitter 等等)用于生产环境中,相信其未来还有很大的发展前景。

但 GraphQL 自身存在的安全性等问题也不容忽视;此外引入 GraphQL 势必存在学习成本,在 API 设计思想上的变化页还会影响到相应的开发模式、开发流程。所以只有权衡好引入成本和收益,才能让这项新技术用在刀刃上。

Ref

官方文档
[译] 怎样使用GraphQL(文中架构图引用自该教程)

GraphQL 初探—面向未来 API 及其生态圈相关推荐

  1. 2021技术文大盘点 | 打包过去,​面向未来

    先用四句诗词快速描述 一下我的写作心得 1. 只在此山中,云深不知处 作为开发人员,常执着于机器0,1代码,非假既真:真实世界是很主观的,需要精致细节,更多时候需要全局把控. 带着问题写作,对事物理解 ...

  2. .NET Core:面向未来的开源跨平台开发技术

    作为一种全新的开源和跨平台的开发平台,.NET Core 历经两年多的开发,终于在于2016年6月27日针对所有主流服务器和桌面操作系统发布 1.0 RTM 版本..NET Core 是一种通用开发平 ...

  3. drupal8 表单_Drupal 8如何实现面向未来

    drupal8 表单 托马斯·爱迪生(Thomas Edison)著名地说道:"实现任何有价值的事情的三大要素是:第一,努力工作:第二,坚守自我:第三,常识." 这句话让我怀疑&q ...

  4. GraphQL初探:Java服务示例及Yahoo/Elide

    GraphQL初探:Java服务示例及Yahoo/Elide 周末想理一理一件事可不可以干,大致上就是:假设存储各种各样的数据,如何保证读取是足够灵活的--用户只需声明式地说明需要什么内容,而接口就可 ...

  5. 面向未来,我们来聊一聊什么是现代化数据架构

    在不那么遥远的旧 IT 时代,有这样一个段子--假如把数据库们"聚在一起"开会". Oracle: 我们需要企业级数据库. MySQL: Oracle 不开源. Post ...

  6. 面向未来的智能驾驶关键设计要素:融合定位

    面向未来的智能驾驶关键设计要素:融合定位 (360doc.com) 汽车在自动驾驶领域将面临前所未有的变革,这一变革不仅与驾驶过程中要求的功能更新有巨大的联系,也包含了对于整个驾驶系统开发过程中的架构 ...

  7. 落地零售智能体,百联集团全力打造面向未来的数字化商业体

    "十四五"期间,百联集团将通过深化数字化商业系统的建设,围绕客户价值,数字化重构商业"人.货.场". 出品 | 常言道 作者 | 丁常彦 作为商品流通的终端环节 ...

  8. 面向未来的跨界开发技术(下)

    作者简介:余果,腾讯社交用户体验设计部高级UI工程师,前端开发组负责人,UI开发通道评委,腾讯云特邀布道师,<Web全栈工程师的自我修养>作者 在上一篇文章中,我们定义了"未来& ...

  9. 王飞跃 | 面向未来的AI人才应该具备这3种思维

    来源:湛庐阅读 作者:王飞跃  苇草智酷学术委员.中科院自动化研究所复杂系统与智能科学重点实验室主任.中国科学院社会计算与平行系统研究中心主任 当今最好的人工智能程序到底有多智能?它们是如何工作的?能 ...

最新文章

  1. 修改linux的时间可以使用date指令
  2. Web网站架构演变—高并发、大数据
  3. 【万字长文】详解Python时间处理模块-datetime
  4. 将数组按指定大小分组
  5. 为什么需要MapReduce?
  6. 12位18Bb20计算公式c语言,C语言的一些简单题目,没有答案,哪位大神帮忙做一下!!!...
  7. 生成javaDoc文档MyEclipse 0914
  8. CPU取指到发出控制、微程序控制原理详细过程
  9. 我的开发笔记spring等常见问题
  10. 简练软考知识点整理-管理项目团队
  11. el-form-item 如何限制只能输入数字_中教云数字课程教材云平台操作答疑
  12. Louvain算法介绍
  13. ssh secure 安装mysql_centos7 安装mysql
  14. 深入理解bootloader_1----- bootloader的初步概念
  15. 火影Vulcan笔记本金钢T1安装Win7系统图文教程
  16. 使用C++,Python,Matlab进行facial 68 points landmarks
  17. Hi3531D加载TVP7002驱动模块——新手全过程记录
  18. 普通程序员如何走出困境?
  19. Entity Framework 使用教程
  20. Robust Consistent Video Depth Estimation_具有鲁棒一致性的视频深度估计

热门文章

  1. office默认设置
  2. 20080330 - strong-typing and weak-typing
  3. 面向对象之内置方法(简单)、组合。以及接口归一化设计与抽象类
  4. VBS基础篇 - wscript 对象
  5. response.getWriter().write 传递中文乱码
  6. javascript回车完美实现tab切换功能
  7. 从指定文件夹里COPY指定的一批文件列表(TXT文件)
  8. Vue:利用Vue生成的网页,在浏览器中的标签页中的图标与标题怎么修改为自己的?
  9. 人脸特征值能存放在sql server中吗_钥匙丢了进不了门,Out了!只要自己没丢就能进门...
  10. matlab将图片转换为灰度图_视频处理之灰度图