事务消息是分布式事务的一种解决方案,RocketMQ 有成熟的事务消息模型,今天就来聊一聊 RocketMQ 事务消息实现机制。
假如有一个电商场景,用户下单后,账户服务从用户账户上扣减金额,然后通知库存服务给用户发货,这两个服务需要在一个分布式事务内完成。
这时,账户服务作为 Producer,库存服务作为 Consumer,见下面消息流程:
- 账户服务作为 Producer 向 Broker 发送一条 half 消息。
- half 消息发送成功后,执行本地事务,执行成功则向 Broker 发送 commit 请求,否则发送 rollback 请求。
- 如果 Broker 收到的是 rollback 请求,则删除保存的 half 消息。
- 如果 Broker 收到的是 commit 请求,则保存扣减库存消息(这里的处理是把消息从 half 队列投递到真实的队列),然后 删除保存的 half 消息 。
- 如果 Broker 没有收到请求,则会发送请求到 Producer 查询本地事务状态,然后根据 Producer 返回的本地状态做 commit/rollback 相关处理。
1、half 消息
上面电商的案例中,RocketMQ 解决分布式事务的第一步是账户服务发送 half 消息。
首先看官网一个发送事务消息的示例:
public static void main(String[] args) throws MQClientException, InterruptedException {
TransactionListener transactionListener = new TransactionListenerImpl();
TransactionMQProducer producer = new TransactionMQProducer("please_rename_unique_group_name");
ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2000), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("client-transaction-msg-check-thread");
return thread;
}
});
producer.setExecutorService(executorService);
producer.setTransactionListener(transactionListener);
producer.start();
String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"};
for (int i = 0; i < 10; i++) {
try {
Message msg =
new Message("TopicTest1234", tags[i % tags.length], "KEY" + i,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.sendMessageInTransaction(msg, null);
System.out.printf("%s%n", sendResult);
Thread.sleep(10);
} catch (MQClientExceptio