正在加载,请稍候…

API 网关模式:BFF、聚合与横切关注点

设计和实现 API 网关。学习面向前端的后端(BFF)模式、请求聚合、身份验证、速率限制、熔断和协议转换。

API 网关模式:BFF、聚合与横切关注点

API 网关模式

API 网关的作用

客户端请求
      |
      v
  API 网关
   |   |   |   |
   |   |   |   +-- 速率限制
   |   |   +------ 身份验证/授权
   |   +---------- 请求/响应转换
   +-------------- 路由、负载均衡、熔断
   |
   v
微服务(用户服务、订单服务、产品服务...)

API 网关模式:BFF、聚合与横切关注点 插图

面向前端的后端(BFF)模式

// Web BFF:聚合针对桌面 Web 优化的数据
app.get('/dashboard', authenticate, async (req, res) => {
  const userId = req.user.id;

  // 并行从多个服务聚合
  const [user, orders, recommendations, notifications] = await Promise.all([
    userService.getProfile(userId),
    orderService.getRecentOrders(userId, { limit: 5 }),
    recommendationService.getForUser(userId, { limit: 6 }),
    notificationService.getUnread(userId),
  ]);

  // 为 Web UI 需求塑造数据
  res.json({
    user: { name: user.name, avatar: user.avatar, plan: user.plan },
    recentOrders: orders.map(o => ({ id: o.id, status: o.status, total: o.total, date: o.createdAt })),
    recommendations: recommendations.map(r => ({ id: r.id, name: r.name, price: r.price, image: r.imageUrl })),
    unreadCount: notifications.filter(n => !n.read).length,
  });
});

// Mobile BFF:更少的数据,移动端优化的字段
mobileBFF.get('/dashboard', authenticate, async (req, res) => {
  const [user, notifications] = await Promise.all([
    userService.getProfile(req.user.id),
    notificationService.getUnread(req.user.id),
  ]);

  // 移动端需要更少的数据(注重带宽)
  res.json({
    name: user.name,
    avatar: user.avatarSmall, // 更小的图片
    unreadCount: notifications.length,
  });
});

API 网关模式:BFF、聚合与横切关注点 插图

请求聚合

// 将多个服务调用聚合为一个 API 调用
app.get('/products/:id/full', async (req, res) => {
  const productId = req.params.id;

  const [product, inventory, reviews, seller] = await Promise.allSettled([
    productService.getProduct(productId),
    inventoryService.getStock(productId),
    reviewService.getSummary(productId),
    sellerService.getSeller(productId),
  ]);

  // 优雅处理部分失败
  res.json({
    product: product.status === 'fulfilled' ? product.value : null,
    inStock: inventory.status === 'fulfilled' ? inventory.value.quantity > 0 : null,
    rating: reviews.status === 'fulfilled' ? reviews.value.average : null,
    seller: seller.status === 'fulfilled' ? seller.value : null,
  });
});

API 网关模式:BFF、聚合与横切关注点 插图

横切关注点

// 身份验证中间件
const authenticate = async (req: Request, res: Response, next: NextFunction) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) return res.status(401).json({ error: 'Authentication required' });

  try {
    req.user = await verifyJWT(token);
    next();
  } catch {
    res.status(401).json({ error: 'Invalid token' });
  }
};

// 用于追踪的请求 ID
app.use((req, res, next) => {
  req.id = req.headers['x-request-id'] as string || uuid();
  res.setHeader('x-request-id', req.id);
  next();
});

// 超时中间件
app.use((req, res, next) => {
  const timeout = setTimeout(() => {
    res.status(504).json({ error: 'Gateway Timeout' });
  }, 30000);

  res.on('finish', () => clearTimeout(timeout));
  next();
});

// 每个下游服务的熔断器
const orderCircuit = new CircuitBreaker({ failureThreshold: 5, timeout: 60000 });

app.get('/orders', authenticate, async (req, res) => {
  try {
    const orders = await orderCircuit.execute(() => orderService.list(req.user.id));
    res.json(orders);
  } catch (err) {
    if (err.message === 'Circuit breaker is open') {
      return res.status(503).json({ error: 'Service temporarily unavailable' });
    }
    throw err;
  }
});

协议转换

// gRPC 到 REST 的网关
import grpc from '@grpc/grpc-js';

const userServiceClient = new UserServiceClient('user-service:50051', grpc.credentials.createInsecure());

app.get('/users/:id', async (req, res) => {
  // REST 请求 -> gRPC 调用 -> REST 响应
  userServiceClient.getUser({ id: req.params.id }, (err, user) => {
    if (err) return res.status(err.code === 5 ? 404 : 500).json({ error: err.message });
    res.json({ id: user.id, name: user.name, email: user.email });
  });
});

API 网关是微服务的前门——保持其轻量并专注于路由关注点。