性能
优化 GraphQL 响应的执行与交付
乍一看,鉴于 API 是通过单个端点提供服务的,且你无法预先知道客户端会在操作中包含哪些字段,GraphQL 请求的缓存似乎极具挑战性。
然而在实践中,GraphQL 与任何支持参数化请求的 API(例如允许客户端为特定端点指定不同查询参数的 REST API)一样具有可缓存性。有很多方法可以在客户端、服务器端以及传输层优化 GraphQL 请求,而且不同的 GraphQL 服务端和客户端库通常会直接内置通用的缓存功能。
在本页面中,我们将探讨可在 GraphQL 客户端和服务器中利用的几种不同策略,以优化从 API 获取数据的方式。
客户端缓存
GraphQL 客户端可能会实现一系列客户端缓存策略,这不仅有助于提升性能,还能确保向用户呈现一致且响应迅速的界面。阅读更多关于客户端缓存的内容。
查询使用 GET 请求
遵循 GraphQL over HTTP 规范的 GraphQL 实现默认支持 POST HTTP 方法,但也可能支持查询(query)操作的 GET 请求。
使用 GET 可以提高查询性能,因为使用此 HTTP 方法发出的请求通常被默认视为可缓存的。当服务器响应中提供了缓存相关的 HTTP 头(Headers)时,它有助于促进 HTTP 缓存或内容分发网络(CDN)的使用。
然而,由于浏览器和 CDN 对 URL 有长度限制,在 URL 的查询字符串中发送复杂操作的大型文档可能并不可行。使用“持久化查询”(Persisted Queries)——无论是采用“可信文档”(Trusted Documents)还是“自动持久化查询”(Automatic Persisted Queries)的形式——都允许客户端改为发送查询的哈希值。服务器可以在验证和执行操作之前,通过在服务端存储中查找该哈希值来获取文档的完整版本。
发送哈希后的查询而非其明文版本的另一个好处是,减少了客户端在网络请求中发送的数据量。
N+1 问题
GraphQL 的设计方式允许你在服务器上编写简洁的代码,其中每个类型的每个字段都有一个专注于解析该值的单一用途函数。然而,如果不加额外考虑,一个简单的 GraphQL 服务可能会变得非常“啰唆”,或者反复从数据库加载数据。
考虑以下查询——为了获取一个英雄及其朋友列表,我们可以想象随着字段解析器的执行,会产生一个发往底层数据源的请求来获取角色对象,另一个请求来获取他们的朋友,然后紧接着产生三个后续请求来为每个类人朋友加载星际飞船列表。
这就是所谓的 N+1 问题:对底层数据源的初始请求(获取英雄的朋友)导致了 N 个后续请求来解析所有请求字段的数据(获取朋友的星际飞船数据)。
这通常通过批处理(Batching)技术来解决。通过使用类似 Facebook 的 DataLoader 等工具,可以在短时间内收集多个后端数据请求,然后在单个请求中分发到底层数据库或微服务。此外,某些 GraphQL 实现可能具有内置功能,允许你将操作的选择集(Selection Sets)转换为针对底层数据源的优化查询。
需求控制
取决于 GraphQL 架构的设计方式,客户端可能会请求高度复杂的操作,从而在执行期间给底层数据源带来过重负荷。这些操作可能是由已知客户端无意中发送的,但也可能由恶意攻击者发送。
某些需求控制机制可以帮助保护 GraphQL API 免受此类操作的影响,例如对列表字段进行分页、限制操作的深度和广度,以及查询复杂度分析。你可以在安全页面阅读更多关于需求控制的内容。
JSON (结合 GZIP)
尽管 GraphQL 规范并未强制要求,但 GraphQL 服务通常使用 JSON 进行响应。对于承诺提供更好网络性能的 API 层来说,JSON 似乎是一个奇怪的选择,然而,因为它主要是文本,使用 GZIP、deflate 和 brotli 等算法可以实现极佳的压缩效果。
建议任何生产环境的 GraphQL 服务都启用 GZIP,并鼓励其客户端发送相应的 Header。
Accept-Encoding: gzipJSON 对客户端和 API 开发人员来说也非常熟悉,且易于阅读和调试。事实上,GraphQL 的语法在一定程度上受到了 JSON 语法的启发。
性能监控
随着时间的推移监控 GraphQL API 可以深入了解某些操作如何影响 API 性能,并帮助你确定应做出哪些调整以维持其健康状态。例如,你可能会发现某些字段由于对后端数据源的请求未优化而需要很长时间才能解析,或者发现其他字段在执行期间经常报错。
可观测性工具通过允许你对 API 服务进行插桩,以在请求期间收集指标、追踪和日志,从而深入了解某些 GraphQL 操作执行中的瓶颈所在。例如,OpenTelemetry 提供了一套与供应商无关的工具,可用于多种不同语言,以支持 GraphQL API 的插桩。
总结
回顾这些提高 GraphQL API 性能的建议:
- 对 GraphQL 查询操作使用
GET可以支持 HTTP 缓存和 CDN 使用,尤其是与哈希查询文档结合使用时。 - 由于操作的选择集可以表达不同类型对象之间的关系,N+1 问题可以通过对底层数据源的请求进行批处理和缓存,在字段执行期间得到缓解。
- 字段分页、限制操作深度和广度以及 API 请求限流,可以防止单个 GraphQL 操作对服务器资源造成过度负担。
- 当服务器和客户端支持时,可以使用 GZIP 来压缩 GraphQL JSON 格式响应的大小。
- 通过使用 OpenTelemetry 等性能监控工具收集与请求执行相关的指标、日志和追踪,可以长期维持 GraphQL API 的整体健康。
安全
通过查询限制、输入验证、内省控制等最佳实践来保护你的 GraphQL API。