【译】2023年GraphQL网关的状况

原文链接,如有侵权,请联系删除。

推出 GraphQL Hive,完整的 GraphQL API 管理器

GraphQL 生态系统正在快速发展,并且,针对复杂问题的解决方案也比以往任何时候更容易找到。

关于 GraphQL 最常见的问题之一是如何扩展或者丰富它。这可以通过添加自定义工作流,比如保护你的 /graphql 端点,通过缓存提升性能,编排分布式 GraphQL 的上游调用,或者仅仅是监控和跟踪功能。

无论是特定的临时解决方案,还是专为 GraphQL 设计的网关都可以解决如下所有问题。

在这篇文章中,我们将介绍 The Guild(以及其他公司)为期六个月的研究和探索,并产生一个 2023 年有关 GraphQL 网关的状况的调查报告。我们对各种开源解决方案进行了比较、测试和基准测试,我们想分享到目前为止我们的发现。

我们要感谢参与这段旅程的合作伙伴和客户!

但首先,什么是 GraphQL 网关

GraphQL 网关遵循一个工作流,该工作流可以允许添加功能,并充当消费者和提供 GraphQL 模型(Schemas)的实际 GraphQL 服务(Servers)之间的代理。此设置的架构图如下所示:

在上面的架构图中,GraphQL 网关充当代理服务器角色,负责向外部公开您的 GraphQL 服务并向其添加功能。另一方面,GraphQL 服务器实现实际的 GraphQL 模型并运行解析器、数据加载器,以及获取和连接数据或实体所需的任何其他自定义代码。

在你的架构图中引入 GraphQL 网关层可以帮助你减轻 GraphQL 服务器需要的一些常见的通用特性和功能。以下是 GraphQL 网关可以提供的能力:

  1. 缓存,GraphQL 中的缓存可以通过多种方式实现,但它通常不需要与你的模型实现紧密耦合。一个 GraphQL 网关可以处理 GraphQL 服务器的缓存,以减少实际服务器的网络流量。这种方式还可以保证一个更轻量级且更易于维护的 GraphQL 服务器实现。
  2. 身份验证和授权,可以由 GraphQL 网关管理。如果您使用 JWT 或任何其他标准,则可以在网关层面验证令牌、权限和范围。然后,您可以将身份验证元数据(例如用户 ID)传递到 GraphQL 服务器。这样,您的 GraphQL 服务器不需要处理身份验证,而可以只专注于实现应用程序的业务逻辑。
  3. 安全 ,当谈到 GraphQL 服务器时,安全性也是一个问题。通过在 GraphQL 服务器前面放置 GraphQL 网关,您可以轻松保护 GraphQL API 免遭恶意查询并防范常见攻击向量
  4. 加固,GraphQL 网关还可以帮助你加固 API ,防止敏感信息、错误细节甚至个人身份信息(PII)的泄漏。
  5. 策略验证,策略验证也是 GraphQL 中的一个常见功能。例如速率限制、深度限制或复杂性限制都可以在您的网关中实现,而不是在服务器上。
  6. 类似实时或订阅的现代化功能,GraphQL 网关还可以解决 GraphQL 服务器中缺乏实时功能或订阅等现代功能的问题。网关可以向消费者公开实时网络传输,例如 WebSocket 或 SSE,同时向上游 GraphQL 服务器发送常规 HTTP 请求。
  7. 模式过滤,也可以在网关层面实现,允许在每个端点上公开不同的功能、字段和类型集。
  8. 其他 ,它还可以做一些简单的事情,例如为您渲染最新版本的 GraphiQL ,并配置所有启用的传输(WebSocket/SSE/HTTP)。

GraphQL 网关的基准测试和比较

为了了解和比较 GraphQL 网关领域的情况,我们首先收集了有关现有解决方案的信息。这包括:

  1. 支持哪些分布式 GraphQL 规范。
  2. 如何使用产品/库(作为代码或作为产品)。
  3. 支持哪些功能。

对于我们当前的基准设置,我们做出了以下决定,以确保 GraphQL 网关之间的公平比较:

  1. 我们使用分布式 GraphQL 规范来检查复杂设置的性能,该设置需要网关承担执行流的重要部分。我们选择了 Federation (v1) 规范,因为它被广泛使用并受到许多网关的支持。
  2. 我们禁用响应结果缓存以确保执行完整的请求流。
  3. 我们比较响应结果以确保所有网关以相同的方式响应。
  4. 我们追踪了所有可用的关键指标和度量数据,如网络流量、CPU 和内存。
  5. 我们测试了不同的用例,模拟了不同流程的真实情景,如高峰时段或上游服务器延迟。
  6. 我们对每一次更改运行了所有的场景,并对每个网关的结果和统计数据进行了全面的概述。
  7. 我们正在运行基于 Rust 的服务器来实现子图(Subgraph),以确保它永远不会成为瓶颈。

对于基准测试,我们选择了以下网关:

  1. Apollo-Server:用于实现 GraphQL 服务器或网关的 JS/TS 库。
  2. Apollo-Router:用于运行 Apollo Federation 的基于 Rust 的产品。
  3. Wundergraph:基于 Go 的 GraphQL 网关和平台。
  4. GraphQL-Mesh:基于 GraphQL-Yoga 的 JS/TS GraphQL 网关,还支持将任何内容转化为 GraphQL,同时也支持将任何其他 API 协议(REST、OpenAPI、gRPC、SOAP 等)作为子图(Subgraphs)进行支持。

除此之外,我们还为 JavaScript/TypeScript 解决方案使用了各种运行时:NodeJS (18 and 20), and Bun

此外,所有场景都在 Docker 容器环境中运行,使用稳定的运行程序(专用的 GitHub Actions 运行程序,每次运行 1 个并发作业,以避免竞态条件或资源丢失),并且有内存和 CPU 的限制。

上述决策可能会发生变化,主要是因为我们希望引入更多的选项和更多的场景。

整个源代码是开源的,我们鼓励开发人员协助我们进行以下工作:

  • 分享更多用例和现实场景;可以通过更改使用的规范、测试的网关、插件、执行流程或参数来实现。
  • 改进实际网关的代码,并帮助改进 GraphQL 生态系统。

数据表现

constant-vus-over-time

您可以在这里找到最新的结果、报告和统计数据
译者注:constant-vus-over-time 是一个性能测试中常见的术语,它表示在一段时间内以恒定的虚拟用户VUs,Virtual Users)负载进行测试。在这种测试中,系统会模拟一定数量的虚拟用户,这些用户以固定的速率发送请求,以测试系统在持续负载下的性能表现。

这是最简单、最朴素的设置,没有额外的调整;网关运行 Federation 规范 v1,使用一个大型查询(2个顶级字段,以及4~7个嵌套级别,跨多个子图的不同实体,以及一些片段扩展)。

  • VUs: 300
  • Time: 10 minutes
  • CPU limit: 2
  • Memory limit: 4GB
Gateway RPS ⬇️ Requests Duration Notes
apollo-router 175 105605 total, 0 failed avg: 923ms, p95: 2598ms
wundergraph 168 100951 total, 0 failed avg: 900ms, p95: 2583ms
mesh-supergraph-bun 120 72820 total, 0 failed avg: 2400ms, p95: 4116ms
mesh-bun 111 67229 total, 0 failed avg: 2606ms, p95: 4439ms
mesh 94 57124 total, 0 failed avg: 3092ms, p95: 3723ms
mesh-supergraph 94 56839 total, 0 failed avg: 3109ms, p95: 3815ms
apollo-server 69 41971 total, 980 failed avg: 4282ms, p95: 3174ms ❌ 980 failed requests, 980 non-200 responses, 980 unexpected GraphQL errors
apollo-server-node16 67 40578 total, 0 failed avg: 4432ms, p95: 6252ms
mercurius 50 30432 total, 50 failed avg: 5912ms, p95: 6121ms ❌ 50 failed requests

以下是一些值得一提的见解:

✅ 几乎测试的网关能够处理所有请求,没有任何失败(旁注:当 CPU 限制较低时,某些 JS 网关无法响应请求,并且请求会因超时或连接丢失而失败,如您所见NodeJS 18 上的 apollo-server )。

🏆 Apollo-RouterWundergraph 是最快的网关,在传入请求的压力下,平均响应时间为 900 毫秒,p95 为 2500 毫秒。

🚅 最快的基于 JS 的网关是 GraphQL-Mesh(几乎是其他基于 JS 的网关的两倍),并且在 Bun 运行时中运行时,速度甚至更快 (x1.5)。

📈 Apollo-Router 的峰值内存使用量约为 400MB,而 Wundergraph 需要更多资源(x3,1.3GB)来处理相同数量的请求。上述测试中 GraphQL-Mesh 的内存消耗在 Bun 上为 550MB,在 NodeJS 18 上为 1.4GB。

📊 mercurius (基于 Fastify)无法处理约 50 个请求。

constant-vus-subgraphs-delay

您可以在这里找到最新的结果、报告和统计数据

constant-vus-over-time 相同,但所有上游 HTTP 调用具有随机延迟(20~150ms)。此场景迫使网关将更多正在进行的请求保留在内存中,并创建更真实的场景。

  • VUs: 300
  • Time: 10 minutes
  • CPU limit: 2
  • Memory limit: 4GB
Gateway RPS ⬇️ Requests Duration Notes
wundergraph 192 115808 total, 0 failed avg: 1311ms, p95: 2017ms
apollo-router 186 112402 total, 0 failed avg: 1123ms, p95: 2335ms
mesh-supergraph-bun 109 65876 total, 0 failed avg: 2664ms, p95: 4510ms
mesh-bun 103 62060 total, 0 failed avg: 2832ms, p95: 4791ms
mesh-supergraph 101 61344 total, 0 failed avg: 2875ms, p95: 3437ms
mesh 93 56112 total, 0 failed avg: 3154ms, p95: 3838ms
apollo-server 64 38683 total, 92 failed avg: 4652ms, p95: 6050ms ❌ 92 failed requests, 92 non-200 responses, 92 unexpected GraphQL errors
mercurius 12 7838 total, 0 failed avg: 23393ms, p95: 24457ms

以下是一些值得一提的见解:

🏆 与之前的测试类似,Apollo-RouterWundergraph 是最快的网关,在传入请求的压力下,平均响应时间为 1100ms,p95 为 2300ms。

🚅 最快的基于 JS 的网关是 GraphQL-Mesh(几乎是其他基于 JS 的网关的两倍),并且在 Bun 运行时中运行时,速度甚至更快 (x1.5)。

📈 传输中请求时间的增加以及 Go 运行时的结合,导致 Wundergraph 使用 2.6GB RAM,而 Apollo-Router 能够使用 600MB RAM 处理相同数量的请求。

📈 在 Bun 上运行的 GraphQL-Mesh 也能够仅用 600MB RAM 处理大量请求。

constant-vus-subgraphs-delay-resources

您可以在这里找到最新的结果、报告和统计数据

constant-vus-subgraphs-delay 相同,但具有额外的资源(CPU 和 RAM)和更多并发 VU。

  • VUs: 500
  • Time: 10 minutes
  • CPU limit: 4
  • Memory limit: 8GB
Gateway RPS ⬇️ Requests Duration Notes
wundergraph 192 115980 total, 0 failed avg: 1808ms, p95: 3277ms
apollo-router 185 111874 total, 0 failed avg: 1674ms, p95: 3630ms
mesh-supergraph-bun 109 65985 total, 0 failed avg: 4451ms, p95: 7530ms
mesh-bun 102 61716 total, 0 failed avg: 4756ms, p95: 7949ms
mesh-supergraph 102 61806 total, 0 failed avg: 4746ms, p95: 5971ms
mesh 95 57722 total, 0 failed avg: 5109ms, p95: 5981ms
apollo-server 67 40608 total, 2610 failed avg: 7387ms, p95: 59998ms ❌ 2610 failed requests, 2610 non-200 responses, 2610 unexpected GraphQL errors
mercurius 12 7941 total, 0 failed avg: 38437ms, p95: 41293ms

以下是一些值得一提的见解:

📊 更多的 CPU 和更多的 RAM 能够推动测试的网关取得更好的结果。 Apollo-Router 似乎没有使用大部分提供的 RAM(只需要 700MB),而 Wundergraph 的内存消耗很高(几乎 3GB)。

📈 当有更多 CPU 可供使用时,GraphQL-Mesh 使用 NodeJS 的 cluster 功能的能力会得到回报。

📊 在压力下, apollo-server (JS)无法完成一定比例的请求。

ramping-vus

您可以在这里找到最新的结果、报告和统计数据

与之前的设置相同,但此场景旨在通过逐渐增加 VU 来将网关推向极限。一些网关在这种规模上落后或中断(由于资源的限制),并且在压力下更容易发现它可以处理的请求的最大容量。

  • VUs: 50 -> 2000 (ramping(逐渐增加))
  • Time: 10 minutes
  • CPU limit: 4
  • Memory limit: 8GB

下图衡量了请求持续时间的 p95(越低越好)。

Gateway duration(p95)⬇️ RPS Requests Durations Notes
wundergraph 6798ms 168 102752 total, 0 failed avg: 2665ms, p95: 6799ms, max: 18164ms, med: 2218ms
apollo-router 6826ms 172 104975 total, 0 failed avg: 2565ms, p95: 6827ms, max: 18279ms, med: 2098ms
mesh-supergraph-bun 16168ms 120 74289 total, 0 failed avg: 8354ms, p95: 16169ms, max: 40017ms, med: 7648ms
mesh-supergraph 18031ms 105 64859 total, 0 failed avg: 9598ms, p95: 18032ms, max: 25650ms, med: 9483ms
mesh-bun 18066ms 109 67768 total, 0 failed avg: 9245ms, p95: 18067ms, max: 44697ms, med: 8680ms
mesh 19777ms 98 60981 total, 0 failed avg: 10275ms, p95: 19777ms, max: 27667ms, med: 10122ms
apollo-server 60000ms 75 48009 total, 7489 failed avg: 13363ms, p95: 60001ms, max: 60717ms, med: 4289ms ❌ 7489 failed requests, 7489 non-200 responses, 7489 unexpected GraphQL errors

以下是一些值得一提的见解:

🏆 Apollo-RouterWundergraph 是最快的网关,能够处理所有请求,平均持续时间为 6500ms。

Apollo-Server 和 mercurius (JS-based) 无法处理负载,并且都无法响应请求(超时或连接丢失)。从基于 JS 的解决方案来看,GraphQL-Mesh 是唯一能够处理所有请求的解决方案。

注意事项

以下是关于在此基准测试中测试的网关的一些重要事项:

  • Federation(联合) 规范兼容性
    • 在测试的网关列表中,只有 apollo-serverapollo-routergraphql-mesh 完全支持 Federation 规范 v1,无需任何调整。其他服务,如 wundergraph,不具备全面的支持(目前只有 apollo-routerapollo-servergraphql-mesh 完全支持 Federation 规范 v2)。
    • 在服务器运行期间,我们使用了 Federation Supgraph 规范,它是通过成功组合子图而生成的产物。然后,某些网关,如 wundergraphmercurius,并不支持此规范,因此提供了一个基于 GraphQL 自省的实时组合的服务列表。
    • 我们选择使用 Federation 规范 v1 而不是 v2,因为它被广泛采用,并且更多的网关支持它。
  • NodeJS 运行时
    • 对于 NodeJS,我们使用了该引擎的 18(LTS) 版本。
    • 在上述某些情景中,我们使用了 Bun(最近发布的 v1 版本)。
  • 公平比较
    • 所有网关都运行相同的 GraphQL 模型,并执行相同的 GraphQL 查询。
    • 所有网关都以 Docker 容器的形式运行,使用最新的可用版本(并使用 Renovate 保持更新)
    • 所有场景中所有网关的资源仅限于 1 个 CPU 和 1GB 内存。
  • Subgraphs(子图) 指标
    • 在提到的所有情景中,所有子图大致消耗了 3% 的 CPU 和 10MB 的内存。我们对此进行了测量,以确保子图不会成为瓶颈,并影响结果。
  • 不仅仅是 Federation
    • 我们对 GraphQL 网关的期望不仅仅是作为查询器,而是能够执行更多的任务 ------ 当前虽然 GraphQL-Mesh 和 Wundergraph 具有更广泛的范围并支持更多用例,但 Apollo 工具专为 Apollo 生态系统定制,用于特定目的而构建。

我们学到了什么?

  • 兼容性是 GraphQL 生态系统中的一个大问题。GraphQL 网关缺乏标准规范,因此很难对不同的解决方案进行比较和基准测试。我们希望未来的规范能够得到社区更广泛的采用和支持。
  • 性能是 GraphQL 网关的一个重要关注点。处理大量请求并轻松扩展的能力对于任何 GraphQL 网关都是必不可少的。资源消耗也是一个重要关注点,重要的是要保持较低的内存和 CPU 使用率,以避免瓶颈并保持较低的成本。
  • 运行环境很重要:NodeJS 很好,但我们也在关注其他运行时,例如 Bun。我们也期待看到更多使用这些运行时构建的解决方案。当正确编写时,Rust 会保持较低的内存使用;而 Go 也是构建 GraphQL 网关的绝佳选择,但内存消耗可能会更高。

保持此报告的更新

我们欢迎社区的贡献。如果您注意到任何不正确的配置、设置或影响结果或比较的其他问题,请与我们联系并报告一个 GitHub 问题。这将帮助我们保持此基准测试的最新性,并确保比较的公平性。

我们还鼓励公司和开发人员改进其技术栈。当结果发生重大变化时,我们将很乐意更新此博文(代码仓库中的结果将始终是最新的!)。

相关推荐
网络点点滴24 分钟前
声明式和函数式 JavaScript 原则
开发语言·前端·javascript
禁默29 分钟前
【学术会议-第五届机械设计与仿真国际学术会议(MDS 2025) 】前端开发:技术与艺术的完美融合
前端·论文·学术
binnnngo34 分钟前
2.体验vue
前端·javascript·vue.js
LCG元35 分钟前
Vue.js组件开发-实现多个文件附件压缩下载
前端·javascript·vue.js
索然无味io38 分钟前
组件框架漏洞
前端·笔记·学习·安全·web安全·网络安全·前端框架
╰つ゛木槿1 小时前
深入探索 Vue 3 Markdown 编辑器:高级功能与实现
前端·vue.js·编辑器
yqcoder1 小时前
Commander 一款命令行自定义命令依赖
前端·javascript·arcgis·node.js
前端Hardy1 小时前
HTML&CSS :下雪了
前端·javascript·css·html·交互
醉の虾1 小时前
VUE3 使用路由守卫函数实现类型服务器端中间件效果
前端·vue.js·中间件
码上飞扬2 小时前
Vue 3 30天精进之旅:Day 05 - 事件处理
前端·javascript·vue.js