阶段五:实战项目
微服务架构下,API不再是单一服务的接口,而是多个自治服务协作的结果。服务间通信、数据一致性、API组合、Saga模式、服务发现——微服务API设计面临全新挑战。本课从架构视角设计微服务间的API契约。
| 服务 | 职责 | API前缀 | 通信方式 |
|---|---|---|---|
| 用户服务 | 注册、认证、用户资料 | /api/users | REST |
| 商品服务 | 商品CRUD、库存管理 | /api/products | REST + gRPC |
| 订单服务 | 下单、支付、订单状态 | /api/orders | REST + 事件 |
| 支付服务 | 支付处理、退款 | /api/payments | gRPC |
| 通知服务 | 邮件、短信、推送 | - | 事件驱动 |
// 同步通信: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:跨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'));
// 服务间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版本管理
// 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) },
},
});
设计CancelOrderSaga:取消订单→释放库存→退款→发送通知。注意补偿操作的顺序。
将订单状态变更记录为事件流,支持从事件重建订单状态。