第八章:生产实践¶
性能优化¶
查询复杂度限制¶
// 限制查询复杂度
const { createComplexityLimitRule } = require('graphql-validation-complexity');
const complexityLimit = createComplexityLimitRule(1000, {
onCost: (cost) => console.log('Query cost:', cost),
formatErrorMessage: (cost) => `Query complexity (${cost}) exceeds limit`,
});
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [complexityLimit],
});
查询深度限制¶
// 限制查询深度
const depthLimit = require('graphql-depth-limit');
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [depthLimit(5)],
});
DataLoader 批量加载¶
const DataLoader = require('dataloader');
// 创建 DataLoader
const userLoader = new DataLoader(async (ids) => {
const users = await User.findAll({ where: { id: ids } });
return ids.map((id) => users.find((user) => user.id === id));
});
// 在 Resolver 中使用
const resolvers = {
Post: {
author: (post, args, context) => {
return context.userLoader.load(post.authorId);
},
},
};
缓存¶
// Apollo 缓存
const { ApolloServerPluginResponseCache } = require('apollo-server-plugin-response-cache');
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
ApolloServerPluginResponseCache({
sessionId: (requestContext) => requestContext.request.http.headers.get('session-id'),
}),
],
});
安全加固¶
查询白名单¶
// 只允许预定义的查询
const { persistedQueries } = require('apollo-server');
const server = new ApolloServer({
typeDefs,
resolvers,
persistedQueries: {
hash: 'query-hash-1',
query: 'query { user(id: "1") { name } }',
},
});
速率限制¶
// 速率限制
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分钟
max: 100, // 每个 IP 最多 100 次请求
});
app.use('/graphql', limiter);
输入验证¶
// 输入验证
const { UserInputError } = require('apollo-server');
const resolvers = {
Mutation: {
createUser: (parent, args) => {
// 验证输入
if (!args.name || args.name.length < 2) {
throw new UserInputError('Name must be at least 2 characters');
}
if (!args.email || !args.email.includes('@')) {
throw new UserInputError('Invalid email format');
}
// 创建用户
return createUser(args);
},
},
};
监控告警¶
Apollo Studio¶
// 启用 Apollo Studio
const { ApolloServerPluginInlineTrace } = require('apollo-server');
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
ApolloServerPluginInlineTrace(),
],
});
Prometheus 监控¶
// Prometheus 指标
const promClient = require('prom-client');
const graphqlRequestDuration = new promClient.Histogram({
name: 'graphql_request_duration_seconds',
help: 'GraphQL request duration',
labelNames: ['operation', 'field'],
buckets: [0.1, 0.3, 0.5, 1, 3, 5, 10],
});
const resolvers = {
Query: {
user: (parent, args, context, info) => {
const end = graphqlRequestDuration.startTimer({
operation: info.operation.operation,
field: info.fieldName,
});
const result = getUser(args.id);
end();
return result;
},
},
};
告警规则¶
groups:
- name: graphql-alerts
rules:
- alert: GraphQLHighErrorRate
expr: rate(graphql_errors_total[5m]) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "GraphQL 错误率过高"
- alert: GraphQLHighLatency
expr: histogram_quantile(0.99, rate(graphql_request_duration_seconds_bucket[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "GraphQL 延迟过高"
故障处理¶
常见问题¶
1. 查询超时¶
// 设置超时
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
timeout: 30000, // 30 秒超时
}),
});
2. 内存溢出¶
// 限制查询大小
const server = new ApolloServer({
typeDefs,
resolvers,
bodyParserConfig: {
limit: '1mb', // 限制请求体大小
},
});
3. N+1 查询问题¶
// 使用 DataLoader 解决 N+1 问题
const resolvers = {
Post: {
author: (post, args, context) => {
// 使用 DataLoader 批量加载
return context.userLoader.load(post.authorId);
},
},
};
最佳实践¶
1. Schema 设计¶
2. Resolver 设计¶
3. 安全设计¶
4. 运维管理¶
小结¶
生产实践要点:
- 性能优化:复杂度限制、深度限制、DataLoader、缓存
- 安全加固:查询白名单、速率限制、输入验证
- 监控告警:Apollo Studio、Prometheus、告警规则
- 故障处理:查询超时、内存溢出、N+1 问题
- 最佳实践:Schema 设计、Resolver 设计、安全设计、运维管理
完成本教程后,你应该能够在生产环境中部署和管理 GraphQL 服务。