跳转至

第八章:生产实践

性能优化

查询复杂度限制

// 限制查询复杂度
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 设计

Schema 设计原则:
- 类型命名清晰
- 字段命名一致
- 使用枚举代替魔法值
- 合理使用 Input 类型

2. Resolver 设计

Resolver 设计原则:
- 单一职责
- 错误处理
- 日志记录
- 性能优化

3. 安全设计

安全设计原则:
- 认证授权
- 输入验证
- 查询限制
- 敏感数据保护

4. 运维管理

运维管理原则:
- 监控告警
- 日志收集
- 性能优化
- 故障演练

小结

生产实践要点:

  • 性能优化:复杂度限制、深度限制、DataLoader、缓存
  • 安全加固:查询白名单、速率限制、输入验证
  • 监控告警:Apollo Studio、Prometheus、告警规则
  • 故障处理:查询超时、内存溢出、N+1 问题
  • 最佳实践:Schema 设计、Resolver 设计、安全设计、运维管理

完成本教程后,你应该能够在生产环境中部署和管理 GraphQL 服务。