作者,罗泽轩

上个月马斯克评论 Twitter App 滥用 RPC 后,与一些 Twitter 的技术主管发生了矛盾 —— 直言马斯克不懂技术。那这个马斯克都不懂的 GraphQL 到底是什么?

什么是 GraphQL?它有多流行?

GraphQL 是一套由 Facebook 在 2015 年发布的一套面向 API 的查询操作语言。相比于其他的 API 设计方式,GraphQL 允许客户端根据事先约定的数据结构组建查询语句,由服务端解析这一语句并只返回所需的内容。这么一来,GraphQL 在提供丰富性和灵活性的同时,避免了冗余数据带来的性能损耗。

GraphQL 的这一特性,让它在需要跟许多复杂数据对象打交道的应用场景里大行其道,成为该环境下的不二之选。

2018 年 GraphQL 完成了规范的制定工作,并推出了稳定版本。同年,Facebook 将 GraphQL 项目捐献给了 Linux 基金会下属的 GraphQL 基金会。自那以后,GraphQL 已经在许许多多的开源项目和商业机构中落地。到目前为止,市面上已经有了多个 GraphQL 的主流客户端实现。而服务端的实现遍布各大服务端编程语言,甚至连一些小众编程语言如 D 和 R 都有对应的实现。

GraphQL 的一些真实场景和挑战

最为知名的采用 GraphQL 的例子,莫过于 GitHub 的 GraphQL API 了。

在拥抱 GraphQL 之前,GitHub 提供了 REST API 来暴露千千万万托管项目所产生的丰富数据。GitHub 的 REST API 是如此的成功,以致于它成为了人们设计 REST API 时竞相模仿的典范。

然而随着数据对象的变多和对象内字段的变大,REST API 开始暴露出越来越多的弊端。在服务端,由于每次调用都会产生大量的数据,GitHub 为了降低成本不得不对调用频率设置严格的限制。

而在开发者这边,他们则不得不与这一限制做斗争。因为虽然单次调用会返回繁多的数据,但是绝大部分都是无用的。开发者要想获取某一特定的信息,往往需要发起多个查询,然后编写许多胶水代码把查询结果中有意义的数据拼接成所需的内容。在这一过程中,他们还不得不带上“调用次数”的镣铐。

所以 GraphQL 的出现,立刻就让 GitHub 皈依了。GitHub 成为了 GraphQL 的使者保罗,为万千开发者传递福音。目前 GraphQL API 已经是 GitHub API 的首选。从第一次宣布对 GraphQL 的支持之后,GitHub 每一年都会发几篇关于 GraphQL 的文章。为了让开发者能够迁移到 GraphQL 上来,GitHub 专门写了个交互式查询应用,开发者可以通过这个应用学习怎么编写 GraphQL。

然而 GraphQL 并非灵丹妙药。就在最近,GitHub 废弃自己 package API 的 GraphQL 实现。许多人也开始热议 GraphQL 的一些缺点。

GraphQL 的许多问题源自于它跟 HTTP 标准的结构差别较大,没办法简单地将 GraphQL 的一些概念映射到诸如 HTTP path/header 这样的结构中。把 GraphQL 当作普通的 HTTP API 来处理,需要额外的开发工作。如此一来,开发者如果要管理自己的 GraphQL API,就必须采用支持 GraphQL 的 API 网关才行。

APISIX 现在对 GraphQL 的支持

Apache APISIX 是一个动态、实时、高性能的 API 网关,提供负载均衡、动态上游、灰度发布、精细化路由、限流限速、服务降级、服务熔断、身份认证、可观测性等数百项功能。作为 Apache 的顶级项目,APISIX 一直致力于周边生态的扩展与跟进。

APISIX 目前支持通过 GraphQL 的一些属性进行动态路由。通过该能力,我们可以只接受特定的 GraphQL 请求,或者让不同的 GraphQL 转发到不同的上游。

以下面的 GraphQL 语句为例:

  query getRepo {owner {name}repo {created}}

APISIX 会提取 GraphQL 以下三个属性,用在路由当中:

  • graphql_operation
  • graphql_name
  • graphql_root_fields

在上面的 GraphQL 语句中:

  • graphql_operation 对应 query
  • graphql_name 对应 getRepo
  • graphql_root_fields 对应 ["owner", "repo"]

让我们来创建一个路由,展示下 APISIX 对 GraphQL 的精细化路由能力。

curl http://127.0.0.1:9180/apisix/admin/routes/1 \-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '{"methods": ["POST"],"uri": "/graphql","vars": [["graphql_operation", "==", "query"],["graphql_name", "==", "getRepo"],["graphql_root_fields", "has", "owner"]],"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:2022": 1}}}'

接下来使用带有 GraphQL 语句的请求去访问:

curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {owner {name}repo {created}
}'
HTTP/1.1 200 OK
...

我们可以看到请求到达了上游,这是因为查询语句匹配了全部三个条件。 反之,如果我们使用不匹配的语句来访问,比如不包含 owner 字段:

curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {repo {created}
}'
HTTP/1.1 404 Not Found
...

则不会匹配对应的路由规则。

接下来,我们可以另外创建一个路由,让不包含 owner 字段的语句路由到别的上游:

curl http://127.0.0.1:9180/apisix/admin/routes/2 \-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '{"methods": ["POST"],"uri": "/graphql","vars": [["graphql_operation", "==", "query"],["graphql_name", "==", "getRepo"],["graphql_root_fields", "!", "has", "owner"]],"upstream": {"type": "roundrobin","nodes": {"192.168.0.1:2022": 1}}}'
curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {repo {created}
}'
HTTP/1.1 200 OK
..

展望 APISIX 未来对 GraphQL 的支持

除了动态路由之外,APISIX 在未来也可能会根据 GraphQL 的具体字段推出更多的操作。比如说,GitHub 的 GraphQL API 有专门一套针对限流的计算公式,我们也可以应用类似的规则来把单个 GraphQL 请求转成相应的“虚拟调用”次数,来完成 GraphQL 专属的限流工作。

当然,我们也可以换个思路解决问题。即应用自身还是提供 REST API,由网关在最外层把 GraphQL 请求转成 REST 请求,把 REST 响应转成 GraphQL 响应。这种方式提供的 GraphQL API 无需开发专门的插件,就可以完成诸如 RBAC、限流、缓存等功能。

从插件角度来看,它就是个平平无奇的 REST API。从技术角度上讲,这个思路并不难实现。毕竟在 2022 年的现在,REST API 也会提供 OpenAPI spec 来作为 schema,无非是 GraphQL schema 和 OpenAPI schema 之间的互转,外加 GraphQL 特有的字段筛选罢了(当然,我必须承认,我并没有亲自实践过,或者在一些细节上存在尚待克服的挑战)。

细心的读者会发现,这种方式转换得来的 GraphQL API,每次只能操作一个模型,显然无法满足 GraphQL 灵活性的要求,无非是披着 GraphQL 外衣的 REST API。且慢,我还没有把话说完呢!GraphQL 有一个叫 schema stitch 的概念,允许实现者把多个 schema 组合在一起。

举个例子,现在有两个 API。一个叫 GetEvent,另一个叫 GetLocation。他们返回的类型分别是 Event 和 Location。

type Event {id: stringlocation_id: string
}type Location {id: stringcity: string
}type Query {GetEvent(id: string): EventGetLocation(id: string): Location
}

我们可以加一个配置,由这两个 API 组合成新的 API 叫 GetEventWithLocation。新的 API 是这样的:

type EventWithLocation {id: stringlocation: Location
}type Query {GetEventWithLocation(id: string): EventWithLocation
}

整体 schema stitch 的过程都由网关来完成。在上面的例子中,网关会把 API 拆分成两个,先调用 GetEvent 得到 location_id,再调用 GetLocation 得到组合后的数据。

总而言之,通过 REST 转 GraphQL,每个 REST API 可以变成对应的 GraphQL 模型;再借助 schema stitch,可以把多个模型组合成一个 GraphQL API。

这样一来,我们就能在现有的 REST API 上构建起丰富灵活的 GraphQL API,且在 REST API 的粒度上完成具体的插件管理。这一设计顺带解决了部分 API 编排的问题。就像上面的例子中,我们把一个 API 的输出(Event.location_id)作为另一个 API 的输入(Location.id)。

马斯克都不懂的 GraphQL,API 网关又能对其如何理解?相关推荐

  1. 大公司为什么都有API网关?没你想的那么简单!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:rrd.me/g6eyJ 在这篇文章中将我们一起来探讨当前的 ...

  2. 大公司为什么都有API网关?聊聊API网关的作用

    一.API网关的用处 二.API网关在企业架构中的地位 三.企业中如何应用API网关 四.API网关有哪些竞争方案 五.API网关解决方案 六.企业怎么选择API网关 一.API网关的用处 API网关 ...

  3. 为什么都有API网关?聊聊API网关的作用

    点击上方「蓝字」关注我们 一.API网关的用处 API网关一般会用到以下三种场景上. Open API 企业需要将自身数据.能力等作为开发平台向外开放,通常会以rest的方式向外提供,最好的例子就是淘 ...

  4. 微服务架构之「 API网关 」

    点击上方"方志朋",选择"置顶公众号" 技术文章第一时间送达! 在微服务架构的系列文章中,前面已经通过文章<架构设计之「服务注册 」>介绍过了服务注 ...

  5. yii2 请求外部api_微服务架构之「 API网关 」

    作者:奎哥来自公众号:不止思考 在微服务架构的系列文章中,前面已经通过文章<架构设计之「服务注册 」>介绍过了服务注册的原理和应用,今天这篇文章我们来聊一聊「 API网关 」. 「 API ...

  6. code block怎样实现图形界面_微服务入门:Openresty实现API网关

    概念介绍 如果大家清楚"网关"这个概念,那就很容易理解"API网关",即所有API的入口. 从面向对象设计的角度看,它与外观模式类似,封装了系统内部架构.在单体 ...

  7. 美团技术:百亿规模API网关服务Shepherd的设计与实现

    在微服务架构下,服务拆分会让API的规模成倍增长,使用API网关来管理API逐渐成为一种趋势.美团统一API网关服务Shepherd就是在这种背景下应运而生,适用于美团业务且完全自研,用于替换传统的W ...

  8. 聊聊API网关的作用

    在这篇文章中将我们一起来探讨当前的API网关的作用. API网关的用处 API网关我的分析中会用到以下三种场景. Open API.企业需要将自身数据.能力等作为开发平台向外开放,通常会以rest的方 ...

  9. 聊聊 API 网关的作用

    在这篇文章中将我们一起来探讨当前的API网关的作用. 一.API网关的用处 API网关我的分析中会用到以下三种场景. 1. Open API 企业需要将自身数据.能力等作为开发平台向外开放,通常会以r ...

最新文章

  1. Linux学习笔记三
  2. display:none;与visibility:hidden;的区别
  3. 永恒之蓝漏洞原理 445_不死的EternalBlue(永恒之蓝)
  4. lua4.0中实现% 取余操作
  5. 第12章[12.4] 鼠标移入移除时弹出和关闭窗口
  6. 在Win7系统下, 使用VS2015 打开带有日文注释程序出现乱码的解决方案
  7. 如何批量查询手机号码归属地
  8. 中国石油大学(北京)-《 修井工程》第二阶段在线作业
  9. 新浪滚动新闻的json数据获取页面
  10. UE4 打包问题总结
  11. Redis 跳跃表实现原理 时间复杂度分析
  12. 浅析Chrome Packaged Apps
  13. 最新Hive/Hadoop高频面试点小集合
  14. python处理fasta文件_Python脚本:fasta文件单序列信息提取
  15. 零遁NAS伴侣实现WOL远程唤醒
  16. GIC Partitioning
  17. 曾国藩:从30岁起,脱胎换骨
  18. 10个优秀的Spring Boot开源项目整理分享
  19. 动态规划之钱币兑换问题
  20. 问道手游-0基础架设教程

热门文章

  1. 计算机考试音乐怎么粘贴,用手机或电脑怎样剪切和接音乐?
  2. 文献调研-存算一体的实现
  3. 毕业设计-基于微信小程序的医生随访系统
  4. [IOS初学]ios 第一篇 storyboard 与viewcontroller的关系 - Zoe_J
  5. JGG:肠道菌群与COVID-19重症风险密切关联
  6. 计算机文档翻页怎么设置,PDF文档翻页设置
  7. WSTMart多商户商城跟随thinkphp框架升级到5.0.3
  8. 1.1 css style 样式定义:行内 style 属性、单页 <style> 标签、多页 <style> 标签
  9. redis 在32位系统安装以及使用
  10. DEFCON携手百度安全落地中国,打造国际化网络安全交流平台