rocketmq事务消息可以用来实现分布式事务,其核心思想为两阶段提交,回调复查,下面是rocketmq事务消息执行流程图。
1.生产者向mq发送半消息。
2.mq收到半消息返回半消息发送成功。
3.执行本地事务。
4.根据本地事务执行结果判断半消息提交或回滚。
5.如果没有收到第四步通知则定时回调。
6.处理回调消息,检查本地事务执行结果。
7.根据回调消息结果判断事务消息提交或回滚。
事务消息实现代码如下:
创建事务监听
public class TransactionListenerImpl implements TransactionListener {
private AtomicInteger transactionIndex = new AtomicInteger(0);
private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<>();
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
// 执行本地事务,状态都返回未知,等待回查
int value = transactionIndex.getAndIncrement();
int status = value % 3;
localTrans.put(msg.getTransactionId(), status);
return LocalTransactionState.UNKNOW;
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
Integer status = localTrans.get(msg.getTransactionId());
if (null != status) {
switch (status) {
case 0:
// 未知状态
return LocalTransactionState.UNKNOW;
case 1:
// 成功
return LocalTransactionState.COMMIT_MESSAGE;
case 2:
// 回滚
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
return LocalTransactionState.COMMIT_MESSAGE;
}
}
创建发送对象
public class TransactionProducer {
public static void main(String[] args) throws MQClientException, InterruptedException {
// 创建监听器
TransactionListener transactionListener = new TransactionListenerImpl();
// 创建mq对象
TransactionMQProducer producer = new TransactionMQProducer("transaction_producer_test");
producer.setNamesrvAddr("ip:9876");
producer.setInstanceName(UUID.randomUUID().toString());
// 创建线程池处理回调
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("TopicTest", tags[i % tags.length], "KEY" + i,
("rocketmq" + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.sendMessageInTransaction(msg, null);
System.out.printf("%s%n", sendResult);
Thread.sleep(10);
} catch (MQClientException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 100000; i++) {
Thread.sleep(1000);
}
producer.shutdown();
}
}
开启消费者验证是否发送成功,按现有逻辑rocketmq1、rocketmq4、rocketmq7会成功投递。
public class Consumer {
public static void main(String[] args) throws InterruptedException, MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("transaction_consumer_test");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.setNamesrvAddr("ip:9876");
consumer.setInstanceName(UUID.randomUUID().toString());
consumer.subscribe("TopicTest", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
for (Message msg : msgs) {
System.out.println(new String(msg.getBody()) + " === date:" + new Date());
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.println("start success");
}
}