分布式事务
1.基本概念CAP原理 P一定存在,一般追求CP强一致性或者AP高可用。 AP追求高可用,软一致,base原来,软一致性,或者最终一致性。 BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩展 CP 强一致性,基于XA协议,事务协调者和事务参与者,有两阶段提交(seata模式) 三阶段提交。无论两阶段还是三阶段都不能完全保证数据一致性。异常都需要人工干预。
两阶段基于数据库的undolog 和redolog日志实现,第一阶段 AP:消息队列的最终一致性。 rocketMq 消息队列的最终一致性,允许短暂超卖。 不需要强一致性的场景,订单-短信服务/订单-物流服务 生产者先发送一个半消息,对消费者不可见,本地事务执行完成后,对消息进行确认,消费者消费消息,会有重试机制。
-
半消息发送:消息先发送到MQ,但对消费者不可见。
-
本地事务执行:生产者执行本地事务(如创建订单)。
-
提交/回滚消息:
- 若事务成功 → MQ将消息标记为可消费。
- 若事务失败 → MQ删除消息。
-
消息投递:仅当事务成功时,消费者才能处理消息。 消费者需要注意幂等性校验,消费者重试机制,重试多次后进入死信队列,人工介入。
`#### 1. 生产者初始化
csharp
Java
1public class OrderService {
2 private TransactionMQProducer producer;
3
4 public void init() throws MQClientException {
5 // 创建事务消息生产者
6 producer = new TransactionMQProducer("order_producer_group");
7 producer.setNamesrvAddr("localhost:9876");
8
9 // 绑定事务监听器(核心事务逻辑入口)
10 producer.setTransactionListener(new OrderTransactionListener(this));
11
12 // 启动生产者
13 producer.start();
14 }
15}
- 关键点 :通过
setTransactionListener
绑定事务监听器OrderTransactionListener
,用于后续处理本地事务和状态回查。
2. 发送事务消息(半消息)
java
Java
1public void createOrderTransaction(OrderDTO order) throws MQClientException {
2 // 构建消息(Topic、Tag、Key、Body)
3 Message msg = new Message(
4 "order_topic",
5 "order_tag",
6 order.getOrderId(),
7 JSON.toJSONBytes(order)
8 );
9
10 // 发送事务消息(触发半消息机制)
11 TransactionSendResult result = producer.sendMessageInTransaction(msg, order);
12
13 // 检查事务状态(此处为同步调用结果,非最终消息状态)
14 if (result.getLocalTransactionState() != LocalTransactionState.COMMIT_MESSAGE) {
15 throw new RuntimeException("事务可能已回滚!");
16 }
17}
-
关键点 :
producer.sendMessageInTransaction(msg, order)
触发以下流程:- 半消息发送 :消息被发送到 RocketMQ Broker,但对消费者不可见(状态为
HALF_MESSAGE
)。 - 执行本地事务 :自动调用
OrderTransactionListener.executeLocalTransaction()
。
- 半消息发送 :消息被发送到 RocketMQ Broker,但对消费者不可见(状态为
3. 本地事务执行与消息确认(核心逻辑)
typescript
Java
1public class OrderTransactionListener implements TransactionListener {
2 private OrderService orderService;
3
4 @Override
5 public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
6 // 解析业务参数(arg 即发送消息时传入的 order 对象)
7 OrderDTO order = (OrderDTO) arg;
8
9 try {
10 // --------------------------------
11 // 本地事务提交(如订单入库)
12 // --------------------------------
13 boolean success = orderService.createOrder(order);
14
15 // 根据事务结果返回状态(提交或回滚消息)
16 return success ? LocalTransactionState.COMMIT_MESSAGE
17 : LocalTransactionState.ROLLBACK_MESSAGE;
18 } catch (Exception e) {
19 return LocalTransactionState.ROLLBACK_MESSAGE;
20 }
21 }
22
23 @Override
24 public LocalTransactionState checkLocalTransaction(MessageExt msg) {
25 // 事务状态回查(兜底逻辑)
26 String orderId = msg.getKeys();
27 boolean exists = orderService.orderExists(orderId);
28 return exists ? LocalTransactionState.COMMIT_MESSAGE
29 : LocalTransactionState.ROLLBACK_MESSAGE;
30 }
31}`
-
关键点:
-
executeLocalTransaction
:- 触发时机 :在
sendMessageInTransaction
发送半消息后立即执行。 - 作用 :执行本地事务(如订单入库),并根据结果决定提交(
COMMIT_MESSAGE
)或回滚(ROLLBACK_MESSAGE
)消息。
- 触发时机 :在
-
checkLocalTransaction
:- 触发时机:若生产者崩溃未返回事务状态,RocketMQ 会主动调用此方法回查状态。
- 作用:检查本地事务是否已提交(如查询订单是否存在),确保最终一致性。
-
4. RocketMQ Broker 处理消息
根据 LocalTransactionState
的返回值,Broker 执行以下操作:
COMMIT_MESSAGE
:将半消息标记为可消费状态(对消费者可见)。ROLLBACK_MESSAGE
:删除半消息,避免无效投递。UNKNOW
:等待下一次回查(默认间隔 1 分钟)
CAP定理,又被叫作布鲁尔定理。对于设计分布式系统来说(不仅仅是分布式事务)的架构师来说,CAP就是你的入门理论。
- C (一致性):对某个指定的客户端来说,读操作能返回最新的写操作。对于数据分布在不同节点上的数据上来说,如果在某个节点更新了数据,那么在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。
- A (可用性):非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。可用性的两个关键一个是合理的时间,一个是合理的响应。合理的时间指的是请求不能无限被阻塞,应该在合理的时间给出返回。合理的响应指的是系统应该明确返回结果并且结果是正确的,这里的正确指的是比如应该返回50,而不是返回40。
- P (分区容错性):当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。
熟悉CAP的人都知道,三者不能共有,如果感兴趣可以搜索CAP的证明,在分布式系统中,网络无法100%可靠,分区其实是一个必然现象,如果我们选择了CA而放弃了P,那么当发生分区现象时,为了保证一致性,这个时候必须拒绝请求,但是A又不允许,所以分布式系统理论上不可能选择CA架构,只能选择CP或者AP架构。
对于CP来说,放弃可用性,追求一致性和分区容错性,我们的zookeeper其实就是追求的强一致。
对于AP来说,放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,这是很多分布式系统设计时的选择,后面的BASE也是根据AP来扩展。