一段逻辑中有这样的技术需求:
1、需要发送多个不同topic的消息(一般可以避免);
2、需要进行本地数据库事务操作;
3、需要保证多条消息的发送跟数据库事务的事务一致性;
这里使用RocketMQ4.3及以后版本的事务消息进行尝试实现。
这段代码实现了 RocketMQ 的事务消息监听器 MyTransactionListener
,它实现了 RocketMQ 的 TransactionListener
接口,其中包含了两个方法:
-
executeLocalTransaction(Message msg, Object arg)
方法用于执行本地事务。当发送事务消息时,RocketMQ 会调用此方法来执行本地事务。在方法中,首先从参数arg
中获取事务消息相关的数据,然后从队列中取出下一条待发送的消息nextMsg
,如果存在下一条消息,则使用事务生产者发送该消息,并返回发送结果对应的本地事务状态;如果队列中已经没有下一条消息,则说明当前消息是最后一条消息,模拟数据库连接并保存订单信息,最后返回提交消息的状态。如果在任何步骤中发生异常,则返回回滚消息的状态。 -
checkLocalTransaction(MessageExt msg)
方法用于检查本地事务的执行状态。RocketMQ 在发送事务消息后会定期调用此方法来检查本地事务的执行状态。在方法中,首先解析消息体获取用户信息,然后模拟数据库连接并查询用户信息的状态,如果用户信息状态为正常,则返回提交消息的状态,否则返回回滚消息的状态。如果在查询过程中发生异常,则记录错误日志,并返回状态为未知的状态。
需要注意的是,在 checkLocalTransaction
方法中,如果发生异常,则返回 LocalTransactionState.UNKNOW
表示本地事务状态未知,这可能会导致 RocketMQ 在无法确定本地事务状态时采取一定的策略(如重试)来保证消息最终一致性。因此,在实际生产环境中,应尽量确保本地事务的状态能够被正确地查询和确认,以确保消息的可靠传递和数据的一致性。
如下代码,只是自己按对RocketMQ事务消息的理解设计的简单的demo,并未在生产上进行实践,如有疑问欢迎交流指正。
public class NestedTransactionDemo {
// 模拟数据库连接
private static Connection getConnection() throws SQLException {
// 这里使用简单的内存数据库 H2
return DriverManager.getConnection("jdbc:h2:mem:test");
}
// 业务db操作
priv