消息中间件第一性原理:为什么需要异步通信?
为什么一个订单创建需要900ms?为什么下游服务故障会导致订单无法创建?为什么秒杀活动会让系统崩溃? 本文从一个真实的电商订单场景出发,用第一性原理思维深度拆解:为什么我们需要消息中间件? 一、引子:订单处理的两种实现 让我们从一个最常见的业务场景开始:用户下单。 场景背景 一个典型的电商订单创建流程包含以下步骤: 创建订单:保存订单到数据库 扣减库存:调用库存服务扣减商品库存 增加积分:调用积分服务为用户增加积分 发送通知:调用通知服务发送订单确认短信/邮件 创建物流:调用物流服务创建配送单 看起来很简单,但在实际生产环境中,这个流程会遇到很多挑战。让我们对比两种实现方式。 1.1 场景A:同步实现(直接调用) 这是大多数初学者的第一反应:依次调用所有服务。 /** * 订单服务 - 同步实现 * 问题:串行等待,响应时间长,系统强耦合 */ @Service @Slf4j public class OrderServiceSync { @Autowired private OrderRepository orderRepository; @Autowired private InventoryServiceClient inventoryService; @Autowired private PointsServiceClient pointsService; @Autowired private NotificationServiceClient notificationService; @Autowired private LogisticsServiceClient logisticsService; /** * 创建订单 - 同步方式 * 总耗时:900ms */ @Transactional public Order createOrder(OrderRequest request) { long startTime = System.currentTimeMillis(); try { // 1. 创建订单(核心业务逻辑,50ms) log.info("开始创建订单,用户ID: {}", request.getUserId()); Order order = buildOrder(request); orderRepository.save(order); log.info("订单创建成功,订单号: {}", order.getOrderNo()); // 2. 同步调用库存服务(网络调用,200ms) log.info("开始扣减库存"); long inventoryStart = System.currentTimeMillis(); try { InventoryDeductRequest inventoryRequest = InventoryDeductRequest.builder() .productId(request.getProductId()) .quantity(request.getQuantity()) .orderId(order.getId()) .build(); inventoryService.deduct(inventoryRequest); log.info("库存扣减成功,耗时: {}ms", System.currentTimeMillis() - inventoryStart); } catch (FeignException e) { log.error("库存扣减失败", e); throw new BusinessException("库存不足或服务不可用"); } // 3. 同步调用积分服务(网络调用,150ms) log.info("开始增加积分"); long pointsStart = System.currentTimeMillis(); try { PointsAddRequest pointsRequest = PointsAddRequest.builder() .userId(request.getUserId()) .points((int) (order.getAmount() / 10)) // 消费10元得1积分 .orderId(order.getId()) .build(); pointsService.add(pointsRequest); log.info("积分增加成功,耗时: {}ms", System.currentTimeMillis() - pointsStart); } catch (FeignException e) { // 积分服务失败不影响订单创建,只记录日志 // 但用户还是要等待150ms log.error("积分增加失败,将稍后重试", e); } // 4. 同步调用通知服务(网络调用 + 第三方API,300ms) log.info("开始发送通知"); long notificationStart = System.currentTimeMillis(); try { NotificationRequest notificationRequest = NotificationRequest.builder() .userId(request.getUserId()) .type(NotificationType.ORDER_CREATED) .content("您的订单" + order.getOrderNo() + "已创建成功") .build(); notificationService.send(notificationRequest); log.info("通知发送成功,耗时: {}ms", System.currentTimeMillis() - notificationStart); } catch (FeignException e) { // 通知失败不影响订单创建,但用户还是要等待300ms log.error("通知发送失败,将稍后重试", e); } // 5. 同步调用物流服务(网络调用,250ms) log.info("开始创建物流单"); long logisticsStart = System.currentTimeMillis(); try { LogisticsCreateRequest logisticsRequest = LogisticsCreateRequest.builder() .orderId(order.getId()) .address(request.getAddress()) .build(); logisticsService.create(logisticsRequest); log.info("物流单创建成功,耗时: {}ms", System.currentTimeMillis() - logisticsStart); } catch (FeignException e) { // 物流失败不影响订单创建,但用户还是要等待250ms log.error("物流单创建失败,将稍后重试", e); } // 记录总耗时 long totalTime = System.currentTimeMillis() - startTime; log.info("订单创建完成,总耗时: {}ms", totalTime); return order; } catch (Exception e) { log.error("订单创建失败", e); throw e; } } private Order buildOrder(OrderRequest request) { Order order = new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(request.getUserId()); order.setProductId(request.getProductId()); order.setQuantity(request.getQuantity()); order.setAmount(calculateAmount(request)); order.setStatus(OrderStatus.PENDING_PAYMENT); order.setCreateTime(LocalDateTime.now()); return order; } private String generateOrderNo() { return "ORD" + System.currentTimeMillis(); } private BigDecimal calculateAmount(OrderRequest request) { // 简化处理,实际应该查询商品价格 return new BigDecimal("100.00").multiply(new BigDecimal(request.getQuantity())); } } 日志输出示例: ...