在分布式系统中,确保消息与本地事务的一致性是一个关键问题。RocketMQ通过事务消息提供了对这种需求的支持,其核心思想是通过两阶段提交来确保消息和本地事务的原子性。本文将深入探讨RocketMQ事务消息的实现机制,包括基本流程、事务回查机制以及消息状态的处理。
一、事务消息的基本流程
(一)第一阶段:半消息的发送
当生产者发送事务消息时,RocketMQ会将消息存储在一个特殊的队列RMQ_SYS_TRANS_HALF_TOPIC中,并将其标记为“半消息”,此时消费者无法看到该消息。随后,生产者会执行本地事务,并根据事务的结果决定提交或回滚。
- 事务成功:生产者提交事务,通知RocketMQ将半消息转为正式消息。
- 事务失败:生产者回滚事务,RocketMQ会删除该半消息。
(二)第二阶段:事务提交与回滚
- 提交操作:生产者确认事务成功时,发送COMMIT_MESSAGE操作,RocketMQ将半消息转为正式消息,消费者可以消费该消息。
- 回滚操作:如果事务失败,生产者发送ROLLBACK_MESSAGE操作,RocketMQ会丢弃该半消息,消费者无法消费。
二、事务回查机制
在实际场景中,生产者可能因为网络问题、程序崩溃等原因无法及时提交或回滚事务。为了确保消息与本地事务的一致性,RocketMQ引入了事务回查机制。
当RocketMQ检测到某条半消息长时间未提交或回滚时,会主动发起回查请求。生产者需要实现checkLocalTransaction方法,以返回事务的执行状态:
- 事务成功:返回COMMIT_MESSAGE,RocketMQ将半消息提交。
- 事务失败:返回ROLLBACK_MESSAGE,RocketMQ会丢弃消息。
- 状态未知:返回UNKNOWN,RocketMQ会再次回查。
三、消息的三种状态
RocketMQ事务消息有三种状态:
1. COMMIT_MESSAGE:本地事务成功,消息可以投递给消费者。
2. ROLLBACK_MESSAGE:本地事务失败,消息被丢弃。
3. UNKNOWN:事务状态无法确定,RocketMQ会继续回查。
四、源码解析
(一)sendMessageInTransaction方法解析
生产者通过sendMessageInTransaction方法发送事务消息。该方法首先将消息标记为事务消息,并发送到RocketMQ。消息被存储在RMQ_SYS_TRANS_HALF_TOPIC队列中,直到本地事务完成。
public SendResult sendMessageInTransaction(Message msg, LocalTransactionExecuter transactionExecuter, Object arg) throws MQClientException {
// 设置事务标志
msg.putUserProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");
// 发送消息至 Broker
SendResult sendResult = producer.send(msg);
// 执行本地事务
transactionExecuter.executeTransaction(msg, arg);
return sendResult;
}
(二)Broker处理事务消息
RocketMQ的Broker会接收到事务消息,并将其存储在特殊队列RMQ_SYS_TRANS_HALF_TOPIC中。此时,消费者无法看到这些消息。Broker会检查消息是否为事务消息,如果是事务消息,则会将其状态设置为“半消息”,并等待生产者提交或回滚事务。
public void sendMessage(SendMessageRequestHeader requestHeader, Message msg) {
// 检查消息是否为事务消息
if (msg.getProperties().containsKey(MessageConst.PROPERTY_TRANSACTION_PREPARED)) {
// 事务消息处理
messageQueue = new MessageQueue(RMQ_SYS_TRANS_HALF_TOPIC, "0");
msg.setTopic(RMQ_SYS_TRANS_HALF_TOPIC);
}
// 存储消息
brokerController.getMessageStore().putMessage(msg);
}
(三)事务提交与回滚
当生产者决定提交或回滚事务时,RocketMQ的Broker会通过EndTransactionProcessor处理请求,根据事务状态将消息移出半消息队列,放入正式队列或丢弃。
public void processRequest(ChannelHandlerContext ctx, EndTransactionRequestHeader requestHeader) {
if (requestHeader.getTransactionStatus() == TransactionStatus.CommitMessage) {
// 提交事务,移动消息至正式队列
brokerController.getMessageStore().putMessage(requestHeader.getMessage());
} else if (requestHeader.getTransactionStatus() == TransactionStatus.RollbackMessage) {
// 回滚事务,删除半消息
brokerController.getMessageStore().deleteMessage(requestHeader.getMessage());
}
}
(四)事务回查
如果半消息未被确认,RocketMQ会定期扫描RMQ_SYS_TRANS_HALF_TOPIC队列,并发起回查请求。生产者通过checkLocalTransaction方法返回事务状态。
public TransactionStatus checkLocalTransactionState(Message msg) {
// 检查本地事务状态
boolean success = localTransactionService.checkTransactionStatus(msg);
if (success) {
return TransactionStatus.CommitMessage;
} else {
return TransactionStatus.RollbackMessage;
}
}
五、总结
RocketMQ通过两阶段提交机制实现了事务消息,确保了消息与本地事务的一致性。事务消息的实现包括以下关键步骤:
1. 生产者发送半消息至RocketMQ。
2. 生产者执行本地事务,并根据结果提交或回滚事务。
3. 如果生产者未能及时确认,RocketMQ会通过事务回查机制确保消息状态的一致性。
事务消息的三种状态(COMMIT_MESSAGE、ROLLBACK_MESSAGE、UNKNOWN)以及事务回查机制,使得RocketMQ能够在分布式环境中可靠地处理事务消息。
2804




被折叠的 条评论
为什么被折叠?



