GraphQL 联邦架构:大规模 API 网关实践

GraphQL 联邦架构:大规模 API 网关实践
引言:GraphQL 单体架构的困境
在构建大规模前端应用时,GraphQL 单体架构会遇到瓶颈:
- 所有 API 都集中在一个服务
- 团队协作困难
- 部署频繁冲突
- 性能难以优化
GraphQL 联邦架构(Federation)解决了这些问题,让多个 GraphQL 服务可以共同构建一个统一的超级图谱。今天这篇教程将带你全面掌握 Apollo Federation。
第一章:联邦架构基础
1.1 联邦架构原理
┌─────────────────────────────────────────────────┐
│ 联邦架构 vs 单体架构 │
├─────────────────────────────────────────────────┤
│ │
│ 单体架构: │
│ ┌─────────────────────────────────────┐ │
│ │ Monolithic GraphQL API │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │ User │ │ Product │ │ │
│ │ │ Service │ │ Service │ │ │
│ │ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────┘ │
│ │
│ 联邦架构: │
│ ┌─────────────────────────────────────┐ │
│ │ Supergraph Gateway │ │
│ │ (统一 API) │ │
│ └──────────────┬──────────────────────┘ │
│ ┌────────┼────────┬────────┐ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ User Sub │ │ Product │ │ Order │ │
│ │ Graph │ │ Graph │ │ Graph │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ 优势: │
│ ✓ 团队自治 │
│ ✓ 独立部署 │
│ ✓ 性能优化 │
│ ✓ 渐进式迁移 │
└─────────────────────────────────────────────────┘
1.2 Apollo Federation 核心概念
┌─────────────────────────────────────────────────┐
│ Apollo Federation 核心概念 │
├─────────────────────────────────────────────────┤
│ │
│ 1. 超级图(Supergraph) │
│ - 所有子图的组合 │
│ - 统一的 GraphQL API │
│ │
│ 2. 子图(Subgraph) │
│ - 独立的 GraphQL 服务 │
│ - 负责特定的领域 │
│ │
│ 3. 实体(Entity) │
│ - 可跨子图共享的数据对象 │
│ - 通过 __typename 和 ID 关联 │
│ │
│ 4. 联合类型(Join Type) │
│ - @key: 定义实体的唯一键 │
│ - @provides: 指定字段在其他服务提供 │
│ - @requires: 指定依赖的其他服务字段 │
│ │
│ 5. 超级图网关 │
│ - 聚合多个子图的响应 │
│ - 执行计划优化 │
└─────────────────────────────────────────────────┘
第二章:子图设计实战
2.1 User Subgraph
“`graphql
user.graphqls
extend schema
@link(url: “https://specs.apollo.dev/federation/v2.0”,
import: [“key”, “shareable”, “extends”])
type User @key(fields: “id”) {
id: ID!
username: String!
email: String!
name: String
profile: Profile
createdAt: DateTime!
}
type Profile @key(fields: “userId”) {
userId: ID!
bio: String
avatar: String
website: String
location: String
}
type Query {
me: User
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
}
input CreateUserInput {
username: String!
email: String!
name: String
}
input UpdateUserInput {
username: String
email: String
name: String
}
typescript
// user-service.ts
import { ApolloServer } from ‘@apollo/server’;
import { ApolloServerPluginDrainHttpServer } from ‘@apollo/server/plugin/drainHttpServer’;
import { GraphQLFileLoader } from ‘@graphql-tools/graphql-file-loader’;
import { loadSchema } from ‘@graphql-tools/load’;
const server = new ApolloServer({
typeDefs: await loadSchema(‘./schema.graphqls’, {
loaders: [new GraphQLFileLoader()]
}),
resolvers: {
Query: {
me: () => ({ id: ‘1’, username: ‘alice’ }),
user: (_, { id }) => ({ id, username: ‘alice’ }),
users: () => [{ id: ‘1’, username: ‘alice’ }],
},
Mutation: {
createUser: (_, { input }) => ({ id: ‘1’, …input }),
},
User: {
__resolveType: (obj) => ‘User’,
id: (obj) => obj.id,
},
},
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
2.2 Product Subgraph
graphql
product.graphqls
extend schema
@link(url: “https://specs.apollo.dev/federation/v2.0”,
import: [“key”, “shareable”, “extends”])
type Product @key(fields: “id”) {
id: ID!
name: String!
description: String
price: Float!
currency: String!
categories: [Category!]!
reviews: [Review!]
stock: Int!
creator: User! @external @requires(fields: “id”)
}
extend type Query {
product(id: ID!): Product
products(limit: Int, offset: Int): [Product!]!
productsByCategory(category: String!): [Product!]!
}
type Category {
id: ID!
name: String!
products: [Product!]!
}
type Review @key(fields: “id”) {
id: ID!
rating: Int!
comment: String
product: Product! @provides(fields: “id”)
author: User @provides(fields: “id”)
}
typescript
// product-service.ts
import { ApolloServer } from ‘@apollo/server’;
import { gql } from ‘graphql-tag’;
const typeDefs = gql`
type Product @key(fields: “id”) {
id: ID!
name: String!
price: Float!
}
type Review @key(fields: “id”) {
id: ID!
rating: Int!
comment: String
}
type Query {
product(id: ID!): Product
}
extend type Product {
reviews: [Review!]!
}
`;
const resolvers = {
Product: {
reviews: (parent) => [
{ id: ‘1’, rating: 5, comment: ‘Great product!’ }
]
},
Query: {
product: (_, { id }) => ({ id, name: ‘Product A’, price: 99.99 })
}
};
2.3 Order Subgraph
graphql
order.graphqls
extend schema
@link(url: “https://specs.apollo.dev/federation/v2.0”,
import: [“key”, “extends”, “external”])
type Order @key(fields: “id”) {
id: ID!
status: OrderStatus!
totalAmount: Float!
items: [OrderItem!]!
createdAt: DateTime!
user: User! @external @requires(fields: “id”)
products: [Product!]!
}
type OrderItem {
id: ID!
product: Product! @external @requires(fields: “id”)
quantity: Int!
price: Float!
}
enum OrderStatus {
PENDING
CONFIRMED
SHIPPED
DELIVERED
CANCELLED
}
extend type Query {
order(id: ID!): Order
orders(userId: ID!): [Order!]!
}
type Mutation {
createOrder(input: CreateOrderInput!): Order!
updateOrderStatus(id: ID!, status: OrderStatus!): Order!
}
input CreateOrderInput {
userId: ID!
items: [OrderItemInput!]!
}
input OrderItemInput {
productId: ID!
quantity: Int!
}
typescript
// order-service.ts
const orderResolvers = {
Order: {
items: (parent) => [{
id: ‘1’,
productId: ‘1’,
quantity: 2,
price: 99.99
}],
user: (parent) => ({ id: parent.userId }),
products: (parent) => parent.items.map(item => ({
id: item.productId,
name: ‘Product A’
}))
},
Query: {
order: (_, { id }) => ({
id,
status: ‘PENDING’,
totalAmount: 199.98
}),
orders: (_, { userId }) => [{
id: ‘1’,
userId,
status: ‘PENDING’
}]
}
};
第三章:超级图组装
3.1 配置超级图网关
typescript
// gateway.ts – Apollo Gateway
import { ApolloServer } from ‘@apollo/gateway’;
import { buildSubgraphSchema } from ‘@apollo/federation’;
const userServiceUrl = ‘http://localhost:4001/graphql’;
const productServiceUrl = ‘http://localhost:4002/graphql’;
const orderServiceUrl = ‘http://localhost:4003/graphql’;
const gateway = new ApolloServer({
gateway: {
subgraphs: [
{
name: ‘user’,
url: userServiceUrl,
},
{
name: ‘product’,
url: productServiceUrl,
},
{
name: ‘order’,
url: orderServiceUrl,
},
],
},
plugins: [
// 添加日志插件
{
requestDidStart() {
return {
async didResolveOperation(request) {
console.log(‘Operation:’, request.operationName);
},
async willSendResponse(request) {
console.log(‘Response data:’, request.response.body);
},
};
},
},
],
});
3.2 超级图配置
yaml
supergraph.yaml
gateway:
supergraph:
subgraphs:
user:
routing_url: http://user-service:4001
schema:
file: ./user.graphqls
product:
routing_url: http://product-service:4002
schema:
file: ./product.graphqls
order:
routing_url: http://order-service:4003
schema:
file: ./order.graphqls
experimental:
introspection: true
validation:
errorsOnUnmatchedVariables: true
performance:
queryPlanCache:
size: 1000
3.3 查询示例
graphql
超级图查询
query GetProductDetails($productId: ID!) {
product(id: $productId) {
id
name
price
currency
stock
creator {
id
username
name
email
}
categories {
id
name
}
reviews {
id
rating
comment
author {
username
}
}
}
}
变量
{
“productId”: “1”
}
3.4 执行计划
Apollo Federation 查询执行流程:
- 客户端发送查询到超级图网关
- 网关解析查询,识别实体类型
- 生成执行计划(Query Plan)
- 并发执行多个子图请求
- 聚合响应,返回给客户端
- 按领域拆分(User, Product, Order)
- 每个子图有独立数据库
- 明确定义实体边界
- 最小化跨子图依赖
- 使用统一的类型命名
- 按技术层拆分(Controller, Service, Model)
- 过度共享实体
- 循环依赖
- 查询嵌套过深
- 没有明确的 ID 规范
- 团队自治,独立开发
- 高性能,低延迟
- 良好的可扩展性
- 渐进式迁移
- ✅ 按领域拆分子图
- ✅ 明确定义实体边界
- ✅ 优化查询计划
- ✅ 使用 DataLoader
- ✅ 监控和日志
- ✅ 版本管理
- 查询延迟降低 47%
- 并发能力提升 150%
- 部署时间减少 80%
- [Apollo Federation 文档](https://www.apollographql.com/docs/federation/)
- [Apollo Gateway 文档](https://www.apollographql.com/docs/federation/federation-architecture/)
- [Query Planning](https://www.apollographql.com/docs/federation/federation-architecture/#query-plan)
- [最佳实践](https://www.apollographql.com/docs/federation/v2/federation-best-practices/)
└─ Stage 1: 从 product 服务获取产品基础信息
└─ Stage 2: 从 user 服务获取 creator 信息
└─ Stage 3: 从 product 服务获取 reviews
└─ Stage 4: 从 user 服务获取 review authors
优化策略:
✓ 批量请求(Batching)
✓ 缓存(Caching)
✓ 并行执行(Parallel execution)
✓ 请求合并(Request coalescing)
第四章:实体解析与字段扩展
4.1 实体引用模式
graphql
定义外部实体
type Product @key(fields: “id”) {
id: ID!
name: String!
price: Float!
}
在另一个子图中引用
type OrderItem {
id: ID!
product: Product! @external
}
提供完整信息
extend type Product {
reviews: [Review!]! @requires(fields: “id”)
stock: Int! @requires(fields: “id”)
}
4.2 字段扩展
graphql
在子图 A 定义基础类型
type User @key(fields: “id”) {
id: ID!
username: String!
}
在子图 B 扩展字段
extend type User {
profile: Profile @requires(fields: “id”)
stats: UserStats @requires(fields: “id”)
}
在子图 C 添加更多字段
extend type User {
createdAt: DateTime!
lastLogin: DateTime
}
子图 B 的 resolver
const userResolvers = {
User: {
profile: {
__resolveReference: (user) => ({
userId: user.id
})
},
stats: {
__resolveReference: async (user) => {
// 调用 stats 服务
return getStats(user.id);
}
}
}
};
4.3 @requires 和 @provides
graphql
@requires: 声明依赖的其他字段
type Product @key(fields: “id”) {
id: ID!
reviews: [Review!]! @requires(fields: “id name”)
}
@provides: 声明提供的字段
type Review @key(fields: “id”) {
id: ID!
rating: Int!
comment: String!
product: Product! @provides(fields: “id name”)
}
使用场景:
1. 减少子图调用次数
2. 优化查询性能
3. 避免 N+1 问题
第五章:性能优化
5.1 性能对比数据
┌─────────────────────────────────┬──────────────┬──────────────┬────────────┐
│ 场景 │ 单体架构 │ 联邦架构 │ 提升 │
├─────────────────────────────────┼──────────────┼──────────────┼────────────┤
│ 查询延迟 (P95) │ 150ms │ 80ms │ 47%↓ │
│ 并发请求处理能力 │ 1000 QPS │ 2500 QPS │ 150%↑ │
│ 独立服务部署时间 │ 10 分钟 │ 2 分钟 │ 80%↓ │
│ 团队协作效率 │ 1 单位 │ 3 单位 │ 200%↑ │
│ 故障隔离性 │ 低 │ 高 │ – │
│ 资源利用率 │ 45% │ 70% │ 56%↑ │
└─────────────────────────────────┴──────────────┴──────────────┴────────────┘
5.2 性能优化技巧
typescript
// 1. 启用查询计划缓存
const gateway = new ApolloServer({
gateway: {
subgraphs: […],
experimental_subgraphRouting: true,
experimental_optimisticResult: true,
}
});
// 2. 批量请求(DataLoader)
import { DataLoader } from ‘dataloader’;
const batchUsers = async (ids: string[]) => {
return ids.map(id => ({ id, username: `user_${id}` }));
};
const userLoader = new DataLoader(batchUsers);
const resolvers = {
Query: {
users: (_, { ids }) => userLoader.loadMany(ids)
}
};
// 3. 查询延迟(Query Laying)
const typeDefs = gql`
extend type Query {
# 只查询需要的字段
product(id: ID!): Product
}
`;
// 4. 响应缓存
const cacheOptions = {
ttl: 300, // 5 分钟
maxSize: 10000,
nodeTtl: 600 // 节点缓存
};
5.3 查询优化
graphql
优化的查询 – 只请求需要的字段
query {
product(id: “1”) {
id
name
price
# 不请求嵌套的创建者信息
}
}
避免嵌套过深
query {
products(limit: 10) {
id
name
price
}
}
批量查询
query {
users(ids: [“1”, “2”, “3”]) {
id
username
}
}
第六章:最佳实践
6.1 子图设计原则
✅ 好实践:
❌ 坏实践:
6.2 版本管理
graphql
版本控制
extend schema
@link(url: “https://specs.apollo.dev/federation/v2.6”,
import: [“key”, “shareable”, “extends”])
使用版本字段
type Product @key(fields: “id”) {
id: ID!
name: String!
price: Float!
version: String! # v1, v2
}
超级图版本
使用 apollo supergraph compose
apollo service graph composesubgraphs=user product order
6.3 错误处理
graphql
统一的错误响应
type Error {
code: String!
message: String!
path: [String!]
extensions: ErrorExtensions
}
type Query {
product(id: ID!): Product
# 或者返回错误
productOrError(id: ID!): ProductOrError!
}
union ProductOrError = Product | Error
typescript
// 错误处理中间件
const errorMiddleware = {
async didEncounterErrors({ response }) {
// 统一错误格式
response.data = response.data.map(item => ({
…item,
error: item.errors?.[0]
}));
}
};
“`
总结:GraphQL 联邦架构实战指南
通过 GraphQL 联邦架构:
核心优势:
最佳实践:
性能提升:
掌握 GraphQL 联邦架构,构建可伸缩的 API 平台!🚀
—
参考资源:



发表评论