DDD的一些随笔感想V

紧接着上一篇,这篇主要结合事件驱动架构和 Saga 模式进一步探索。

事件驱动架构和Saga模式的结合能够充分利用两者的优点,提供了一种高效、松耦合且具有最终一致性的分布式事务处理方式。结合这些架构,可以在微服务系统中处理复杂的业务逻辑和跨服务事务,确保系统具有弹性、扩展性和可维护性。

1. 事件驱动架构与Saga模式的结合思路

事件驱动架构通过发布和订阅事件来实现服务之间的异步通信,而Saga模式通过一系列独立的局部事务和补偿机制来保证分布式系统的最终一致性。将这两者结合起来时,Saga的每个局部事务执行完后可以通过发布事件通知下一个服务或进行补偿操作,达到分布式事务管理的目的。

2. 结合的优势

  • 松耦合:通过事件驱动架构,Saga中的各个服务可以解耦,通过异步事件通知其他服务。服务只需监听感兴趣的事件,避免了服务之间的直接调用,减少了耦合。
  • 可扩展性:由于事件驱动架构天然支持扩展,结合Saga模式后,服务可以独立扩展,随着业务需求增长,不需要大规模修改现有系统。
  • 异步处理与弹性:事件驱动架构的异步特性可以让Saga中的事务不会因为单一服务的延迟而阻塞整个流程,增强系统的弹性和容错能力。
  • 最终一致性:Saga模式通过局部事务和补偿机制确保最终一致性,事件驱动架构则可以保证事务事件的可靠传递和处理,增强了事务的稳健性。

3. 结合的工作流程

在结合事件驱动架构和Saga模式时,系统中每个微服务执行局部事务后都会发布事件。其他微服务通过监听这些事件来执行下一步的操作或补偿逻辑。如果某个局部事务失败,系统会通过补偿事件来撤销之前的操作。

示例:订单处理流程(结合事件驱动和Saga模式)

假设我们有一个电商系统,用户下单后涉及多个微服务:

  1. 订单服务创建订单。
  2. 库存服务扣减库存。
  3. 支付服务扣减用户余额。

如果任何一个步骤失败,系统需要回滚前面的操作,如恢复库存、退还余额。

具体流程如下:

  1. 订单服务:用户创建订单后,订单服务将订单存入数据库并发布“订单创建成功”的事件。
  2. 库存服务:收到订单创建成功的事件后,库存服务扣减商品库存。如果库存扣减成功,库存服务发布“库存扣减成功”的事件;如果扣减失败,发布“订单创建失败”的补偿事件。
  3. 支付服务:收到库存扣减成功的事件后,支付服务扣减用户账户余额。如果支付成功,发布“支付成功”的事件;如果支付失败,发布补偿事件。
  4. 补偿逻辑:如果任何一个步骤失败,相关服务会通过补偿事件撤销之前的事务操作(如恢复库存、取消订单等)。
订单服务代码示例(发布事件):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
public class OrderService {

@Autowired
private KafkaTemplate<String, OrderCreatedEvent> kafkaTemplate;

public void createOrder(OrderRequest request) {
Order order = new Order(request.getUserId(), request.getProductId(), request.getQuantity());
// 保存订单
orderRepository.save(order);

// 发布订单创建事件
kafkaTemplate.send("OrderCreatedTopic", new OrderCreatedEvent(order));
}

@KafkaListener(topics = "OrderCreationFailedTopic")
public void handleOrderCreationFailed(OrderCreationFailedEvent event) {
// 取消订单
orderRepository.deleteById(event.getOrderId());
}
}
库存服务代码示例(监听事件并发布补偿事件):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Service
public class InventoryService {

@Autowired
private InventoryRepository inventoryRepository;

@KafkaListener(topics = "OrderCreatedTopic")
public void handleOrderCreated(OrderCreatedEvent event) {
try {
// 扣减库存
inventoryRepository.deduct(event.getProductId(), event.getQuantity());

// 发布库存扣减成功事件
kafkaTemplate.send("InventoryDeductedTopic", new InventoryDeductedEvent(event.getOrderId()));
} catch (Exception e) {
// 扣减库存失败,发布订单创建失败补偿事件
kafkaTemplate.send("OrderCreationFailedTopic", new OrderCreationFailedEvent(event.getOrderId()));
}
}
}
支付服务代码示例(补偿逻辑):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Service
public class PaymentService {

@Autowired
private PaymentRepository paymentRepository;

@KafkaListener(topics = "InventoryDeductedTopic")
public void handleInventoryDeducted(InventoryDeductedEvent event) {
try {
// 扣减用户账户余额
paymentRepository.deductBalance(event.getUserId(), event.getTotalAmount());

// 支付成功
kafkaTemplate.send("PaymentSuccessTopic", new PaymentSuccessEvent(event.getOrderId()));
} catch (Exception e) {
// 支付失败,发布补偿事件,通知恢复库存
kafkaTemplate.send("OrderCreationFailedTopic", new OrderCreationFailedEvent(event.getOrderId()));
}
}

@KafkaListener(topics = "OrderCreationFailedTopic")
public void handleOrderCreationFailed(OrderCreationFailedEvent event) {
// 补偿逻辑,恢复用户余额
paymentRepository.revertBalance(event.getUserId(), event.getTotalAmount());
}
}

4. 结合架构中的补偿策略

补偿操作是Saga模式与事件驱动架构结合的核心,主要有以下几种常见的补偿策略:

1. 反向操作(Reversal)

对于大多数事务,补偿逻辑可以简单地通过反向操作来撤销之前的成功操作。例如,库存扣减失败时恢复库存,用户支付失败时退还余额。

2. 幂等性(Idempotency)

在事件驱动架构中,事件可能会被重复发送或消费,因此补偿操作和事务操作都应该是幂等的,即同一事务或补偿操作多次执行的结果相同,不会产生额外影响。

3. 手动干预(Manual Intervention)

对于某些复杂的补偿场景,自动补偿可能不容易实现。这时可以设置监控和告警机制,让运维人员或业务人员手动处理补偿操作,确保事务一致性。

5. 最佳实践与落地建议

在结合事件驱动架构和Saga模式的过程中,确保系统的可靠性和最终一致性是关键。以下是一些最佳实践和落地建议:

1. 消息可靠性

确保事件驱动架构中消息的可靠传递,避免消息丢失或重复。可以使用消息中间件(如Kafka、RabbitMQ)提供的持久化机制和消息幂等性支持,保证事件不会被丢失或多次处理。

2. 事务一致性

通过Saga模式的局部事务和补偿机制保证系统的最终一致性。在每个服务中实现幂等性,确保重复的事件处理不会导致数据不一致。

3. 监控与可观察性

由于事件驱动架构和Saga模式中的事务是异步和分散的,监控和日志记录是至关重要的。需要跟踪每个事件的处理状态,监控事务的成功和失败,并设置告警机制及时发现和处理异常。

4. 超时管理

在Saga模式中,某些局部事务可能会因为外部依赖导致延迟。通过设置超时机制和事件来处理这种场景,确保系统能够在事务超时时执行相应的补偿操作。

5. 流程模拟与测试

由于结合事件驱动和Saga模式的系统涉及多个异步操作和补偿逻辑,进行模拟和集成测试非常重要。测试可以帮助发现流程中的潜在问题,确保系统在各种边界情况下的正确性。

6. 结合模式的应用场景

  • 金融系统:处理资金转移时需要跨多个账户或银行的事务管理,Saga模式可以保证每个账户的操作是独立的,且失败时能够回滚资金。
  • 电子商务系统:如订单处理、库存管理、支付流程等,需要跨多个服务的操作可以通过事件驱动与Saga模式确保事务的一致性和恢复能力。
  • 物流系统:处理复杂的订单配送流程时,可以使用Saga模式跟踪每个配送步骤的状态,确保配送失败时可以退回原状态。

总结

通过将事件驱动架构与Saga模式结合,系统可以处理分布式环境下的复杂事务,同时保持高效、松耦合和弹性。事件驱动架构的异步通信特性和Saga模式的事务补偿机制使得系统能够在面对网络分区、延迟或服务故障时仍然保持一致性。


DDD的一些随笔感想V
https://yangtzeshore.github.io/2024/10/15/PartVOfDDD/
作者
Chen Peng
发布于
2024年10月15日
许可协议