微服务架构-Outbox发件箱模式

背景:在现代微服务架构中,服务之间常常需要通过异步消息传递来解耦。例如,一个订单服务创建订单后,需要通知库存服务扣减库存。如何在不同服务间传递事件的同时保持数据一致性,成为了分布式系统设计的核心挑战之一。

本篇将介绍一种经典的一致性设计模式:Outbox(发件箱)模式。它能有效解决“写库成功但消息发送失败”所带来的数据不一致问题。

1. 为什么需要Outbox模式?

考虑一个场景的场景:订单服务先写入数据库,再发送消息的MQ通知库存服务扣减库存。

public void createOrder(Order order) {
    orderRepository.save(order);                 // 写数据库
    messageQueue.send("order_created", order);   // 发消息
}

如果第2步消息发送失败(比如MQ挂掉了、网络波动),订单已经创建了,但是库存不会减少,就会导致业务不一致,可能这时我们就需要进行回滚或者补偿机制,但是我们这次介绍的Outbox设计模式提供了一个全新的思路,确保在第一次处理时就保证“本地事务+跨服务通信”下的数据一致性问题。

2. Outbox模式架构详解

2.1. 核心思想

  • 在数据库中新增一个outbox表,用于保存待发送的事件。
  • 在本地事务中,同时写入业务数据和outbox事件。
  • 通过后台轮询工具或者定时脚本读取outbox表中的事件,找到状态为待发送的事件,发送消息。

2.2. Outbox模式流程图

3. 代码示例

3.1. 创建订单代码(写入Outbox)

@Transactional
public void createOrder(Order order) {
    // 1. 创建订单记录
    orderRepository.save(order);

    // 2. 写入发件箱表
    OutboxEvent event = new OutboxEvent("ORDER_CREATED", JsonUtil.toJson(order));
    outboxRepository.save(event);
}

3.2. 消息发送器(定时轮询版)

@Scheduled(fixedDelay = 5000)
public void dispatchEvents() {
    List<OutboxEvent> events = outboxRepository.findPendingEvents();
    for (OutboxEvent event : events) {
        try {
            messageQueue.send(event.getType(), event.getPayload());
            event.markAsSent();
            outboxRepository.save(event);
        } catch (Exception e) {
            // 这里可以放补偿方法或者最终的回滚机制,也可以放重试机制
        }
    }
}

4. 适用场景与总结

Outbox模式适用于:

  • 核心业务链路必须保证事件可靠传递
  • 有本地数据库,且可控制写入事务(如 MYSQL)
  • 可以接受最终一致性,不追求强一致性

不适合:

  • 无本地数据库的场景(如纯计算服务)
  • 要求毫秒级响应的一致性事务

总结

Outbox模式是解决分布式事务一致性的一个经典方法。通过“写数据库 + 发消息”组合的原子化改造,把事件的可靠投递交由后台发送器完成,极大地提升了系统的容错性和可靠性。

在电商订单、支付系统、库存调整等典型的微服务场景中,Outbox都是一个值得考虑的模式。

最后修改:2025 年 04 月 25 日
如果觉得我的文章对你有用,请随意赞赏