探索 GraphQL Federation 和 Schema Stitching 作为前端 API 网关解决方案的强大功能。学习如何在现代 Web 应用中统一微服务、提高性能并简化数据获取。
前端 API 网关:GraphQL Federation 与 Schema Stitching
在现代 Web 应用开发的世界中,管理来自多个数据源的数据可能是一项重大挑战。随着应用程序的复杂性不断增加并采用微服务架构,对统一高效的数据访问方式的需求变得至关重要。前端 API 网关作为客户端应用程序的中心入口点,聚合来自各种后端服务的数据,为开发人员和最终用户提供简化的体验。本博客文章探讨了两种用于构建前端 API 网关的强大技术:GraphQL Federation 和 Schema Stitching。
什么是前端 API 网关?
前端 API 网关是一种架构模式,其中专用服务器充当前端客户端(例如 Web 浏览器、移动应用)和多个后端服务之间的中介。它通过以下方式简化数据获取:
- 聚合数据: 将来自多个来源的数据合并到单个响应中。
- 转换数据: 调整数据格式以适应前端的需求。
- 抽象复杂性: 向客户端隐藏后端服务的复杂性。
- 强制执行安全: 实施身份验证和授权策略。
- 优化性能: 缓存频繁访问的数据并减少网络请求。
从本质上讲,它大规模地实现了前端专用后端 (BFF) 模式,并使前端团队能够更多地控制他们所消费的 API。在大型组织中,让前端管理和策划自己的 API 可以加快交付速度并减少对后端团队的依赖。
为什么使用 GraphQL 构建前端 API 网关?
GraphQL 是一种用于 API 的查询语言,也是一个用您现有数据来执行这些查询的运行时。与传统的 REST API 相比,它具有多项优势,使其非常适合构建前端 API 网关:
- 高效的数据获取: 客户端只请求所需的数据,减少了数据过度获取并提高了性能。
- 强类型: GraphQL schema 定义了数据的结构,从而实现了更好的工具和验证。
- 内省: 客户端可以通过 schema 内省发现可用的数据和操作。
- 实时能力: GraphQL subscriptions 支持实时数据更新。
通过利用 GraphQL,前端 API 网关可以为访问来自多个后端服务的数据提供一个灵活、高效且对开发人员友好的接口。这与使用多个 REST 端点的传统方法形成鲜明对比,后者需要单独查询每个端点,并且通常返回的数据超出所需。
GraphQL Federation:一种分布式方法
什么是 GraphQL Federation?
GraphQL Federation 是一种强大的技术,通过将多个 GraphQL 服务(称为“subgraphs”)组合成一个统一的 schema 来构建分布式 GraphQL API。每个 subgraph 负责一个特定的领域或数据源,而 Federation 网关则协调跨这些 subgraph 的查询。
核心概念围绕着一个 supergraph,这是一个单一、统一的 GraphQL schema,代表整个 API。这个 supergraph 是通过组合多个较小的 GraphQL schema(称为 subgraphs)构建的,每个 subgraph 代表一个特定的微服务或数据源。Federation 网关负责将传入的 GraphQL 查询路由到适当的 subgraph,并将结果合并成一个单一的响应。
GraphQL Federation 的工作原理
- Subgraph 定义: 每个微服务都公开一个 GraphQL API (一个 subgraph),定义其自己的数据和操作。这些 schema 包含指令,告诉 Federation 网关如何解析类型和字段。关键指令包括 `@key`、`@external` 和 `@requires`。
- Supergraph 组合: Federation 网关(例如 Apollo Gateway)从每个 subgraph 中检索 schema,并将它们组合成一个单一、统一的 schema (supergraph)。此过程涉及解决类型和字段冲突,并建立跨不同 subgraph 的类型之间的关系。
- 查询计划和执行: 当客户端向网关发送 GraphQL 查询时,网关会分析查询并确定需要查询哪些 subgraph 来满足请求。然后它将查询分发到适当的 subgraph,收集结果,并将它们合并成一个单一的响应返回给客户端。
示例:使用 GraphQL Federation 的电子商务平台
考虑一个电子商务平台,其产品、客户和订单分别由不同的微服务管理。
- 产品 Subgraph: 管理产品信息(名称、描述、价格等)。
- 客户 Subgraph: 管理客户数据(姓名、地址、电子邮件等)。
- 订单 Subgraph: 管理订单信息(订单 ID、客户 ID、产品 ID、总金额等)。
每个 subgraph 都公开一个 GraphQL API,Federation 网关将这些 API 组合成一个 supergraph。然后,客户端可以查询 supergraph,在单个请求中检索有关产品、客户和订单的信息。
例如,一个检索客户姓名及其订单历史的查询可能如下所示:
query GetCustomerAndOrders($customerId: ID!) {
customer(id: $customerId) {
id
name
orders {
id
orderDate
totalAmount
}
}
}
Federation 网关会将此查询路由到客户和订单 subgraph,检索必要的数据,并将其合并成一个单一的响应。
GraphQL Federation 的优势
- 简化的数据访问: 无论底层数据源如何,客户端都与单个 GraphQL 端点交互。
- 提高性能: 通过仅从每个 subgraph 检索必要的数据来优化数据获取。
- 增强的可扩展性: 每个 subgraph 都可以独立扩展,从而更好地利用资源。
- 去中心化开发: 团队可以独立开发和部署 subgraph,从而促进敏捷性和创新。
- Schema 治理: Federation 网关在所有 subgraph 之间强制执行 schema 的一致性和兼容性。
GraphQL Federation 工具
- Apollo Federation: 一种流行的开源 GraphQL Federation 实现,提供网关、schema 注册表以及用于构建和管理联合 GraphQL API 的工具。Apollo Federation 以其可扩展性和强大的错误处理而闻名。
- GraphQL Hive: 此工具为 GraphQL 联合服务提供 schema 注册和治理,提供变更检测、使用情况分析和 schema 检查等功能。它增强了对 supergraph 的可见性和控制力。
Schema Stitching:另一种方法
什么是 Schema Stitching?
Schema Stitching 是另一种将多个 GraphQL schema 合并成一个统一 schema 的技术。与 Federation 不同,Schema Stitching 通常涉及一个更手动的过程来定义不同 schema 的类型和字段如何连接。虽然 Federation 被认为是更现代、更强大的解决方案,但对于更简单的用例或从现有的 GraphQL API 迁移时,Schema Stitching 可能是一个可行的选择。
Schema Stitching 的工作原理
- Schema 定义: 每个微服务都公开一个带有自己 schema 的 GraphQL API。
- Stitching 逻辑: 一个 stitching 层(通常使用像 GraphQL Tools 这样的库实现)定义了不同 schema 的类型和字段如何连接。这包括编写解析器函数,从底层服务获取数据并将其映射到统一的 schema。
- 统一 Schema: stitching 层将各个 schema 组合成一个单一、统一的 schema,并向客户端公开。
示例:Stitching 产品和评论
想象一下两个独立的 GraphQL 服务:一个用于产品,另一个用于评论。
- 产品服务: 提供有关产品的信息(ID、名称、描述、价格)。
- 评论服务: 提供产品的评论(ID、产品 ID、评分、评论)。
使用 Schema Stitching,您可以创建一个统一的 schema,允许客户端在单个查询中检索产品信息和评论。
您将在 stitching 层中定义一个解析器函数,该函数从评论服务中获取给定产品 ID 的评论,并将它们添加到统一 schema 的 Product 类型中。
// 示例 (概念性):使用 GraphQL Tools 的 stitching 逻辑
const { stitchSchemas } = require('@graphql-tools/stitch');
const productsSchema = ... // 定义您的产品 schema
const reviewsSchema = ... // 定义您的评论 schema
const stitchedSchema = stitchSchemas({
subschemas: [
{
schema: productsSchema,
},
{
schema: reviewsSchema,
transforms: [
{
transformSchema: (schema) => schema,
transformRequest: (originalRequest) => {
return originalRequest;
},
transformResult: (originalResult) => {
return originalResult;
}
}
],
},
],
typeDefs: `
extend type Product {
reviews: [Review]
}
`,
resolvers: {
Product: {
reviews: {
resolve: (product, args, context, info) => {
// 从评论服务中获取该产品的评论
return fetchReviewsForProduct(product.id);
},
},
},
},
});
此示例演示了将 schema 拼接在一起的核心概念。请注意,需要自定义解析器来获取 `reviews` 字段。为每个关系编码解析器的额外开销可能使开发过程比使用 Federation 慢。
Schema Stitching 的优势
- 统一的 API: 客户端访问单个 GraphQL 端点,简化了数据访问。
- 增量采用: Schema Stitching 可以逐步实施,允许您逐渐迁移到统一的 API。
- 灵活性: Schema Stitching 对 schema 的组合方式提供了更多的控制,允许您自定义 stitching 逻辑以满足特定需求。
Schema Stitching 的缺点
- 手动配置: Schema Stitching 需要手动配置 stitching 逻辑,这可能复杂且耗时。
- 性能开销: 解析器函数可能会引入性能开销,尤其是在涉及复杂数据转换时。
- 可扩展性有限: Schema Stitching 的扩展性可能比 Federation 更难,因为 stitching 逻辑通常是集中的。
- Schema 所有权: 可能会导致 schema 所有权模糊不清,特别是如果不同的团队管理拼接的服务。
Schema Stitching 工具
- GraphQL Tools: 一个用于构建和操作 GraphQL schema 的流行库,包括对 Schema Stitching 的支持。
- GraphQL Mesh: GraphQL Mesh 允许您使用 GraphQL 查询语言访问来自各种来源(如 REST API、数据库和 gRPC)的数据。它可以将这些 API 拼接成统一的 GraphQL schema。
GraphQL Federation vs. Schema Stitching:比较
GraphQL Federation 和 Schema Stitching 都提供了将多个 GraphQL schema 组合成单个 API 的方法,但它们在方法和功能上有所不同。
| 特性 | GraphQL Federation | Schema Stitching |
|---|---|---|
| 方法 | 分布式、自动化组合 | 集中式、手动配置 |
| 复杂性 | 维护和扩展的复杂性较低 | 由于手动解析器逻辑,复杂性较高 |
| 可扩展性 | 专为大规模分布式系统设计 | 可扩展性较差,通常用于较小的应用程序 |
| Schema 治理 | 内置的 schema 治理和验证 | 需要手动的 schema 管理和协调 |
| 工具 | 强大的工具和库生态系统(例如 Apollo Federation) | 需要更多的自定义工具和配置 |
| 用例 | 微服务架构、大规模 API、去中心化开发 | 较小的应用程序、增量迁移、特定的自定义需求 |
何时使用 GraphQL Federation: 当您拥有复杂的微服务架构、需要扩展 API 并希望授权独立团队管理自己的 subgraph 时,请选择 Federation。它还简化了 schema 管理和治理。
何时使用 Schema Stitching: 当您的 API 比较简单、需要更多地控制 stitching 逻辑,或者正在从现有的 GraphQL API 迁移时,可以考虑 Schema Stitching。但是,请注意潜在的复杂性和可扩展性限制。
实现身份验证与授权
无论您选择 GraphQL Federation 还是 Schema Stitching,实施身份验证和授权对于保护您的前端 API 网关都至关重要。您可以采用以下几种方法:
- 网关级身份验证: API 网关在将请求路由到后端服务之前处理身份验证和授权。这种方法集中了安全逻辑并简化了后端服务。常用方法包括 JWT (JSON Web Token) 验证和 OAuth 2.0。
- 服务级身份验证: 每个后端服务处理自己的身份验证和授权。这种方法提供了对安全性的更精细控制,但管理起来可能更复杂。
- 混合方法: 结合网关级和服务级身份验证。网关处理初始身份验证,后端服务执行更精细的授权检查。
示例:使用 Apollo Federation 进行 JWT 身份验证
使用 Apollo Federation,您可以配置网关以验证请求头中包含的 JWT 令牌。然后,网关可以将从令牌中提取的用户信息传递给 subgraph,后者可以使用此信息进行授权。
// 示例 (概念性):使用 JWT 验证的 Apollo Gateway 配置
const { ApolloGateway } = require('@apollo/gateway');
const gateway = new ApolloGateway({
serviceList: [
// ... 您的 subgraph 配置
],
buildService: ({ name, url }) => {
return new MyCustomService({
name, // subgraph 的名称
url, // subgraph 的 URL
});
},
});
class MyCustomService extends RemoteGraphQLDataSource {
willSendRequest({ request, context }) {
// 从上下文中获取用户信息
const user = context.user;
// 将用户 ID 添加到请求头中
if (user) {
request.http.headers.set('user-id', user.id);
}
}
}
在此示例中,创建了一个自定义服务来修改传出的请求,以包含从 JWT 派生的用户 ID。下游服务然后可以使用此 ID 进行授权检查。
性能优化的缓存策略
缓存对于提高前端 API 网关的性能至关重要。通过缓存频繁访问的数据,您可以减少后端服务的负载并改善客户端的响应时间。以下是一些缓存策略:
- HTTP 缓存: 利用 HTTP 缓存机制(例如 `Cache-Control` 头)在浏览器和中间代理中缓存响应。
- 内存缓存: 使用内存缓存(例如 Redis、Memcached)在网关上缓存频繁访问的数据。
- CDN 缓存: 利用内容分发网络 (CDN) 将静态资产和 API 响应缓存在离客户端更近的地方。
- GraphQL 查询缓存: 根据查询字符串和变量缓存 GraphQL 查询的结果。这对于频繁执行的查询尤其有效。Apollo Server 提供了对查询缓存的内置支持。
在实施缓存时,请考虑缓存失效策略,以确保客户端收到最新的数据。常见的策略包括:
- 基于时间的过期: 为缓存数据设置固定的过期时间。
- 基于事件的失效: 当后端服务中的数据发生变化时,使缓存失效。这可以通过使用 webhooks 或消息队列来实现。
监控与可观测性
监控和可观测性对于确保前端 API 网关的健康和性能至关重要。实施全面的监控以跟踪关键指标,例如:
- 请求延迟: 处理请求所需的时间。
- 错误率: 导致错误的请求百分比。
- 吞吐量: 单位时间内处理的请求数。
- 资源利用率: 网关和后端服务的 CPU、内存和网络使用情况。
使用跟踪来追踪请求在系统中的流转过程,识别瓶颈和性能问题。日志记录为网关和后端服务的行为提供了宝贵的见解。
用于监控和可观测性的工具包括:
- Prometheus: 一个开源的监控和警报系统。
- Grafana: 一个数据可视化和监控工具。
- Jaeger: 一个开源的分布式跟踪系统。
- Datadog: 一个用于云应用程序的监控和安全平台。
- New Relic: 一个用于监控和改善软件性能的数字智能平台。
通过实施强大的监控和可观测性,您可以主动识别和解决问题,确保前端 API 网关的可靠性和性能。
结论
使用 GraphQL Federation 或 Schema Stitching 构建的前端 API 网关可以显著简化数据访问、提高性能并增强现代 Web 应用程序中的开发人员体验。GraphQL Federation 为组合分布式 GraphQL API 提供了一个强大且可扩展的解决方案,而 Schema Stitching 则为组合现有 schema 提供了一种更灵活的方法。通过仔细考虑您应用程序的具体要求以及这些技术之间的权衡,您可以选择构建强大而高效的前端 API 网关的最佳方法。
请记住实施适当的身份验证和授权、缓存策略以及监控和可观测性,以确保网关的安全性、性能和可靠性。通过采纳这些最佳实践,您可以释放 GraphQL 的全部潜力,并构建提供卓越用户体验的现代 Web 应用程序。