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

news2025/1/16 3:53:54

作者,罗泽轩

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

image (3).png

什么是 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: string
  location_id: string
}

type Location {
  id: string
  city: string
}

type Query {
    GetEvent(id: string): Event
    GetLocation(id: string): Location
}

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

type EventWithLocation {
  id: string
  location: 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)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/93973.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【javascript】值,类型,变量,函数,noi103题目,if语句,调试

❤️ Author: 老九 ☕️ 个人博客:老九的CSDN博客 🙏 个人名言:不可控之事 乐观面对 😍 系列专栏: 文章目录基本类型NumberStringboolean例子变量求二元一次方程函数1.3题目if例子1.4题目调试下面我们会随着…

SpringMVC:SpringMVC五种类型参数传递(4)

请求参数1. 环境准备2. 参数传递2.1 GET请求2.1.1 GET发送一个参数2.1.2 GET发送多个参数2.1.3 GET请求中文乱码2.2 POST请求2.2.1 POST发送一个参数2.2.2 POST发送多个参数2.2.3 POST请求中文乱码问题3. 五种类型参数传递3.1 普通参数3.2 POJO类型参数3.3 嵌套POJO类型参数3.4…

HashMap(二)扩容

想要了解HashMap的扩容机制你要有这两个问题 1、什么时候才需要扩容 2、HashMap的扩容是什么 1、什么时候才需要扩容 当HashMap中的元素个数超过数组大小(数组长度)* loadFactor(负载因子)时,就会进行数组扩容,loadFactor的默认值…

Pytest框架运行常用参数解析

-s:表示输出调试信息,用于显示测试函数中print()打印的信息。我们在用例中加上一句 print(driver.title),我们再运行一下我们的用例看看,调试信息输出-v:未加前只打印模块名,加v后--verbose打印类名、模块名…

Java中的八大包装类(Wrapper)

目录 一、八大包装类 1、八大包装类的体系图: 二、装箱和拆箱 三、intValue()和valueOf()方法 1、intValue() 2、valueOf() 四、包装类型和String类型的相互转换 五、包装类的常用方法(以Integer和Character为例) 一、八大包装类 包装…

Scala环境搭建

目录1)安装步骤2)测试3)IDEA安装Scala 插件1)安装步骤 1.首先确保 JDK1.8 安装成功 2.下载对应的 Scala 安装文件 scala-2.x.zip 3.解压 scala-2.12.11.zip,我这里解压到 F:\software 4.配置 Scala 的环境变量 …

使用Python和GDAL处理遥感影像数据超详细教程

提示:文章末尾有强化学习代码资源 : ) 前言 在本教程中,我们将学习使用 Python 和地理空间数据抽象库 GDAL 自动处理栅格数据的基本技术。 栅格文件通常用于存储地形模型和遥感数据及其衍生产品,例如植被指数和其他环境数据集。 栅格文件往往…

windows 连接蓝牙耳机失败 解决方法

windows 连接蓝牙耳机失败 解决方法 如果我们在windows7或windows10电脑中连接蓝牙却出现了连接失败的状况,这要怎么办呢,可能是我们没有打开电脑的蓝牙功能,这时我们点击打开蓝牙网络的属性,勾选Bluetooth设置的选项即可&#x…

安卓某通讯协议环境算法浅谈

所有的tlv组包都在 oicq.wlogin_sdk.tlv_type加密算法可以hook oicq.wlogin_sdk.tools Tlv144 是由5个tlv组成 然后用TGTkey进行 TEA加密 tlv_109 AndroidIDtlv_52d 系统内核信息tlv_124 平台网络信息tlv_128 手机设备信息tlv_16e 手机品牌TLV544 是设备id&#xff0…

MySQL-复合查询

文章目录复合查询基础查询多表查询自连接子查询单行子查询多行子查询多列子查询合并查询uion会自动去重union all就是不去重union all就是不去重复合查询 基础查询 查询工资高于500或者岗位为MANAGER的员工,同时名字首字母是J select * from emp where (sal>500…

ADI Blackfin DSP处理器-BF533的开发详解54:CVBS输出(含源码)

硬件准备 ADSP-EDU-BF533:BF533开发板 AD-HP530ICE:ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 CVBS OUT 视频输出 硬件实现原理 CVBS_OUT 子卡板连接在 ADSP-EDU-BF53x 开发板的扩展端口 PORT3 和 PORT4 上,板卡插入时&#xff0…

pytest + yaml 框架 -14.钉钉机器人通知测试结果

前言 当用例执行完成后,希望能给报告反馈,常见的报告反馈有:邮箱/钉钉群/飞书/企业微信 等。 pip 安装插件 pip install pytest-yaml-yoyo钉钉机器人通知测试结果功能在v1.1.1版本实现 钉钉机器人设置 钉钉机器人的设置请参考官方API文档…

实验2 VLAN的划分及VLAN间通信的配置

实验2 VLAN的划分及VLAN间通信的配置一、实验目的二、实验要求三、实验步骤,数据记录及处理四、实验总结一、实验目的 掌握VLAN的划分及VLAN间通信的配置方法 二、实验要求 交换机在没有划分虚拟网络时,都默认属于VLAN1,可以相互通信。通过…

【链表面试题】——剑指 Offer : 复杂链表(带随机指针)的复制

文章目录前言1.题目介绍2. 题目分析3. 思路讲解思路1思路2步骤1步骤2步骤34. 分析图及源码展示前言 这篇文章,我们一起来解决一道与链表相关的经典面试题:复杂链表(带随机指针)的复制。 1.题目介绍 我们先来一起了解一下这道题&…

Java的继承到底是怎么回事?看这篇让你明明白白

一. 引言 在学习面向对象后,我们就可以使用类来描述对象共有的特征(属性)和行为举止(方法),如果我们用类来描述猫、狗和企鹅,可以进行如下编码: public class Cat {private String name;//名字private int age;//年龄private St…

操作系统,计算机网络,数据库刷题笔记11

操作系统,计算机网络,数据库刷题笔记11 2022找工作是学历、能力和运气的超强结合体,遇到寒冬,大厂不招人,可能很多算法学生都得去找开发,测开 测开的话,你就得学数据库,sql&#xf…

kubelet源码分析 syncLoopIteration(一) configCh

kubelet源码分析 syncLoopIteration syncLoopIteration里有四个chan管道。分别是configCh、plegCh、syncCh、housekeepingCh。这篇主要聊一下这四个管道的由来。 一、configCh configCh是通过list&watch的API SERVER获得的数据。然后在本地进行比对,推送到c…

Qt-Web混合开发-QtWebChannel实现Qt与Web通信交互-进阶功能(6)

Qt-Web混合开发-QtWebChannel实现Qt与Web通信交互-进阶功能🥬 文章目录Qt-Web混合开发-QtWebChannel实现Qt与Web通信交互-进阶功能🥬1、概述🌽2、实现效果🍆3、实现功能🍒4、关键代码🥝5、源代码&#x1f9…

Android基础学习(二十二)—— View的事件分发(1)

一、View的层级关系 二、View的事件分发机制 1、MotionEvent ——点击事件 点击事件用MotionEvent来表示 ACTION_DOWN:手指刚接触屏幕 ACTION_MOVE:手指在屏幕上移动 ACTION_UP:手指从屏幕上松开的一瞬间 点击事件的事件分发&#xff0…

OM6621系列国产M4F内核低功耗BLE5.1大内存SoC蓝牙芯片

目录OM6621系列简介OM6621P系列芯片特性应用领域OM6621系列简介 随着5G与物联网时代的到来,智慧城市、电动出行、智能家居、可穿戴设备等应用高速发展,低功耗蓝牙技术在近几年智能化浪潮中的地位也尤为重要。OM6621系列的开发即是为解决国内低功耗蓝牙应…