📖 第23课:微服务API设计

阶段五:实战项目

微服务架构下,API不再是单一服务的接口,而是多个自治服务协作的结果。服务间通信、数据一致性、API组合、Saga模式、服务发现——微服务API设计面临全新挑战。本课从架构视角设计微服务间的API契约。

🏗️ 微服务API架构

电商微服务拆分

服务职责API前缀通信方式
用户服务注册、认证、用户资料/api/usersREST
商品服务商品CRUD、库存管理/api/productsREST + gRPC
订单服务下单、支付、订单状态/api/ordersREST + 事件
支付服务支付处理、退款/api/paymentsgRPC
通知服务邮件、短信、推送-事件驱动

🔗 服务间通信模式

同步通信 vs 异步通信

// 同步通信:REST/gRPC
// 优点:简单、实时响应
// 缺点:耦合紧、故障传播

// 异步通信:消息队列
// 优点:解耦、弹性、削峰
// 缺点:复杂、最终一致性

// ===== 服务间API设计 =====

// 1. REST API - 对外暴露
// 用户服务对外API
GET    /api/users/:id         → 用户资料
POST   /api/auth/login       → 登录
POST   /api/auth/register    → 注册

// 商品服务对外API
GET    /api/products          → 商品列表
GET    /api/products/:id      → 商品详情
POST   /api/products          → 创建商品
PATCH  /api/products/:id      → 更新商品

// 2. 内部API - 服务间调用
// 商品服务内部API(给订单服务调用)
GET    /internal/products/:id     → 商品详情(含库存)
POST   /internal/products/reserve → 预留库存
POST   /internal/products/release → 释放库存

// 用户服务内部API(给其他服务调用)
GET    /internal/users/:id        → 用户基本信息(精简版)
GET    /internal/users/batch      → 批量获取用户信息

// 3. gRPC - 高性能内部通信
// 商品服务gRPC
service ProductInternal {
  rpc GetProduct(GetProductRequest) returns (Product);
  rpc ReserveInventory(ReserveRequest) returns (ReserveResponse);
  rpc ReleaseInventory(ReleaseRequest) returns (ReleaseResponse);
  rpc CheckInventory(CheckRequest) returns (CheckResponse);
}

🔄 Saga模式——分布式事务

// 订单创建Saga:跨4个服务的协调流程

// 编排式Saga(Orchestration)—— 推荐方式
class CreateOrderSaga {
  constructor(orderData) {
    this.orderData = orderData;
    this.compensations = [];  // 补偿操作栈
  }

  async execute() {
    try {
      // 步骤1: 创建订单(草稿状态)
      const order = await this.createOrder();
      this.compensations.push(() => this.cancelOrder(order.id));

      // 步骤2: 预留库存
      const reservation = await this.reserveInventory(order);
      this.compensations.push(() => this.releaseInventory(reservation.id));

      // 步骤3: 扣款
      const payment = await this.processPayment(order);
      this.compensations.push(() => this.refundPayment(payment.id));

      // 步骤4: 确认订单
      await this.confirmOrder(order.id);

      // 步骤5: 发送通知(非关键,失败不影响)
      await this.sendNotification(order).catch(() => {});

      return { success: true, order };
    } catch (error) {
      // 执行补偿操作(逆序)
      console.error(`Saga失败: ${error.message},开始补偿...`);
      for (const compensate of this.compensations.reverse()) {
        try { await compensate(); }
        catch (e) { console.error('补偿失败:', e.message); }
      }
      return { success: false, error: error.message };
    }
  }

  async createOrder() {
    const response = await fetch('http://order-service/api/orders', {
      method: 'POST',
      body: JSON.stringify({ ...this.orderData, status: 'DRAFT' }),
    });
    if (!response.ok) throw new Error('创建订单失败');
    return response.json();
  }

  async reserveInventory(order) {
    const response = await fetch('http://product-service/internal/products/reserve', {
      method: 'POST',
      body: JSON.stringify({ items: order.items, orderId: order.id }),
    });
    if (!response.ok) throw new Error('库存预留失败');
    return response.json();
  }

  async processPayment(order) {
    const response = await fetch('http://payment-service/api/payments', {
      method: 'POST',
      body: JSON.stringify({ orderId: order.id, amount: order.total, method: order.paymentMethod }),
    });
    if (!response.ok) throw new Error('支付失败');
    return response.json();
  }

  async confirmOrder(orderId) {
    const response = await fetch(`http://order-service/api/orders/${orderId}/confirm`, {
      method: 'POST',
    });
    if (!response.ok) throw new Error('确认订单失败');
    return response.json();
  }

  async cancelOrder(orderId) {
    await fetch(`http://order-service/api/orders/${orderId}`, { method: 'DELETE' });
  }

  async releaseInventory(reservationId) {
    await fetch('http://product-service/internal/products/release', {
      method: 'POST',
      body: JSON.stringify({ reservationId }),
    });
  }

  async refundPayment(paymentId) {
    await fetch(`http://payment-service/api/payments/${paymentId}/refund`, { method: 'POST' });
  }

  async sendNotification(order) {
    await fetch('http://notification-service/api/notify', {
      method: 'POST',
      body: JSON.stringify({ type: 'ORDER_CREATED', orderId: order.id, userId: order.userId }),
    });
  }
}

📡 事件驱动架构

// 事件总线(简化实现)
class EventBus {
  constructor() {
    this.handlers = new Map();
  }

  subscribe(eventType, handler) {
    if (!this.handlers.has(eventType)) this.handlers.set(eventType, []);
    this.handlers.get(eventType).push(handler);
  }

  async publish(eventType, data) {
    const handlers = this.handlers.get(eventType) || [];
    console.log(`📢 事件: ${eventType}`, data);
    for (const handler of handlers) {
      try { await handler(data); }
      catch (e) { console.error(`事件处理错误 [${eventType}]:`, e.message); }
    }
  }
}

const eventBus = new EventBus();

// 订单服务:发布事件
// 创建订单后
eventBus.publish('order.created', { orderId: 101, userId: 1, items: [...], total: 798 });

// 商品服务:订阅事件
eventBus.subscribe('order.created', async (event) => {
  // 扣减库存
  for (const item of event.items) {
    await fetch(`http://product-service/internal/products/${item.productId}/stock`, {
      method: 'PATCH',
      body: JSON.stringify({ delta: -item.quantity }),
    });
  }
  console.log(`库存已扣减: 订单${event.orderId}`);
});

// 通知服务:订阅事件
eventBus.subscribe('order.created', async (event) => {
  // 发送订单确认邮件
  console.log(`发送订单确认邮件: 用户${event.userId}, 订单${event.orderId}`);
});

// 支付服务:订阅事件
eventBus.subscribe('order.created', async (event) => {
  // 发起支付
  console.log(`发起支付: 订单${event.orderId}, 金额${event.total}`);
});

// 事件类型定义
const EVENT_TYPES = {
  'order.created': { description: '订单创建', fields: ['orderId', 'userId', 'items', 'total'] },
  'order.cancelled': { description: '订单取消', fields: ['orderId', 'reason'] },
  'order.shipped': { description: '订单发货', fields: ['orderId', 'trackingNumber'] },
  'payment.completed': { description: '支付完成', fields: ['paymentId', 'orderId', 'amount'] },
  'payment.failed': { description: '支付失败', fields: ['paymentId', 'orderId', 'reason'] },
  'product.stock.low': { description: '库存不足', fields: ['productId', 'currentStock'] },
  'user.registered': { description: '用户注册', fields: ['userId', 'email'] },
};

// ===== 完整的微服务订单API =====

const express = require('express');
const app = express();
app.use(express.json());

const orders = new Map();
let nextOrderId = 1;

// 创建订单(触发Saga)
app.post('/api/orders', async (req, res) => {
  const { userId, items, paymentMethod, shippingAddress } = req.body;

  // 创建订单记录
  const orderId = nextOrderId++;
  const order = {
    id: orderId,
    userId,
    items,
    paymentMethod,
    shippingAddress,
    status: 'PENDING',
    total: items.reduce((sum, item) => sum + item.price * item.quantity, 0),
    createdAt: new Date().toISOString(),
  };
  orders.set(orderId, order);

  // 执行Saga
  const saga = new CreateOrderSaga(order);
  const result = await saga.execute();

  if (result.success) {
    // 发布事件
    eventBus.publish('order.created', { orderId, userId, items, total: order.total });
    res.status(201).json({ success: true, data: orders.get(orderId) });
  } else {
    order.status = 'FAILED';
    res.status(422).json({
      success: false,
      error: { code: 'ORDER_FAILED', message: result.error },
    });
  }
});

// 获取订单
app.get('/api/orders/:id', (req, res) => {
  const order = orders.get(parseInt(req.params.id));
  if (!order) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND' } });
  res.json({ success: true, data: order });
});

// 列出订单
app.get('/api/orders', (req, res) => {
  const { userId, status } = req.query;
  let result = [...orders.values()];
  if (userId) result = result.filter(o => o.userId === parseInt(userId));
  if (status) result = result.filter(o => o.status === status);
  res.json({ success: true, data: result });
});

app.listen(3003, () => console.log('📦 订单服务运行在 :3003'));

📝 本课小结

  1. 微服务API分为对外API(REST)和内部API(gRPC/REST)
  2. Saga模式解决分布式事务——编排式优于协调式
  3. 事件驱动架构实现服务解耦
  4. 内部API使用/internal前缀区分
  5. 每个服务拥有独立的数据存储(数据库每服务独立)
  6. 服务发现和负载均衡是基础设施关注点

📐 微服务API契约设计

服务间API契约示例

// 服务间API契约文档
//
// 商品服务 → 订单服务 提供的接口
// 版本: v1
// 负责人: 商品团队
//
// GET /internal/products/:id
//   描述: 获取商品详情(含库存)
//   调用方: 订单服务、推荐服务
//   SLA: P99 < 50ms
//   限流: 1000 req/s
//
// POST /internal/products/reserve
//   描述: 预留库存
//   请求: { items: [{productId, quantity}], orderId, ttl }
//   响应: { reservationId, items: [{productId, reserved}] }
//   SLA: P99 < 100ms
//   注意: 预留30分钟过期
//
// POST /internal/products/release
//   描述: 释放预留库存
//   请求: { reservationId }
//   SLA: P99 < 50ms

// 错误码
// PRODUCT_NOT_FOUND: 商品不存在
// INSUFFICIENT_STOCK: 库存不足
// RESERVATION_EXPIRED: 预留已过期
// RESERVATION_NOT_FOUND: 预留不存在

🔄 API版本与兼容性策略

// 微服务API版本管理

// 1. 内部API使用语义版本
// v1.2.3 → major.minor.patch
// major: 破坏性变更
// minor: 新增功能(向后兼容)
// patch: Bug修复

// 2. 消费者驱动的契约测试
const { Pact } = require('@pact-foundation/pact');

// 订单服务作为消费者,定义它期望的商品服务响应
const provider = new Pact({
  consumer: 'OrderService',
  provider: 'ProductService',
});

// 定义契约
await provider.addInteraction({
  state: '商品1有库存',
  uponReceiving: '获取商品详情',
  withRequest: { method: 'GET', path: '/internal/products/1' },
  willRespondWith: {
    status: 200,
    headers: { 'Content-Type': 'application/json' },
    body: { id: 1, name: like('机械键盘'), stock: integer(120), price: decimal(599.0) },
  },
});

💪 练习

练习1:实现取消订单Saga

设计CancelOrderSaga:取消订单→释放库存→退款→发送通知。注意补偿操作的顺序。

练习2:实现事件溯源

将订单状态变更记录为事件流,支持从事件重建订单状态。

🏆 本课成就:微服务架构师