事务消息
常见的事务处理方式有最终一致性,两阶段,XA,维护本地事务表等,而rocketmq消息中间件就是最终一致性的实现方式之一.
rocketmq是怎么实现的呢?
RocketMQ producer第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改消息的状态,如果确认消息发送失败了,RocketMQ会定期扫描消息集群中的事物消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认,A的钱到底是减了还是没减呢?如果减了是回滚还是继续发送确认消息呢?RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
==================================================================================
producer端:
/**
* 发送
*/
@Test
public void send1() {
TransactionCheckListener transactionCheckListener = new TransactionCheckListenerImpl();
TransactionMQProducer producer = new TransactionMQProducer("transaction_producer");
producer.setNamesrvAddr("xxxx");
producer.setTransactionCheckListener(transactionCheckListener);
try {
producer.start();
String[] tags = new String[]{"TagA", "TagB"};
TransactionExecuterImpl tranExecuter = new TransactionExecuterImpl();
for (int i = 0; i < 3; i++) {
Message msg = new Message("transactionTopic1", tags[i % 2], "uniqueId" + i,
("Hello RocketMQ " + i).getBytes());
SendResult sendResult = producer.sendMessageInTransaction(msg, tranExecuter, null);
System.out.println(sendResult);
}
while (true) {
}
} catch (Exception e) {
producer.shutdown();
e.printStackTrace();
}
}
源码:
/**
* This method will be removed in the version 5.0.0, method <code>sendMessageInTransaction(Message,Object)</code>}
* is recommended.
*/
@Override
@Deprecated
public TransactionSendResult sendMessageInTransaction(final Message msg,
final LocalTransactionExecuter tranExecuter, final Object arg) throws MQClientException {
if (null == this.transactionCheckListener) {
throw new MQClientException("localTransactionBranchCheckListener is null", null);
}
msg.setTopic(NamespaceUtil.wrapNamespace(this.getNamespace(), msg.getTopic()));
return this.defaultMQProducerImpl.sendMessageInTransaction(msg, tranExecuter, arg);
}
@Override
public TransactionSendResult sendMessageInTransaction(final Message msg,
final Object arg) throws MQClientException {
if (null == this.transactionListener) {
throw new MQClientException("TransactionListener is null", null);
}
msg.setTopic(NamespaceUtil.wrapNamespace(this.getNamespace(), msg.getTopic()));
return this.defaultMQProducerImpl.sendMessageInTransaction(msg, null, arg);
}
consumer端:
/**
* 消费
*/
@Test
public void consumer1() {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("transaction_consumer");
consumer.setNamesrvAddr("xxxx");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
try {
consumer.subscribe("transactionTopic1", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
try {
System.out.println("transactionInfo:" + new String(msgs.get(0).getBody()));
} catch (Exception e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;// 重试
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;// 成功
}
});
consumer.start();
System.out.println("transaction_consumer started.");
while (true) {
}
} catch (MQClientException e) {
e.printStackTrace();
}
}
本地事务:
public class TransactionExecuterImpl implements LocalTransactionExecuter {
@Override
public LocalTransactionState executeLocalTransactionBranch(final Message msg, final Object arg) {
System.out.println("执行本地事务msg = " + new String(msg.getBody()) + " ;arg = " + arg);
String tags = msg.getTags();
if (tags.equals("TagB")) {
System.out.println("进行-------ROLLBACK");
return LocalTransactionState.ROLLBACK_MESSAGE;//回滚
// return LocalTransactionState.UNKNOW;//未决事务
}
return LocalTransactionState.COMMIT_MESSAGE;//提交
}
}
未决事务:
public class TransactionCheckListenerImpl implements TransactionCheckListener {
public LocalTransactionState checkLocalTransactionState(MessageExt msg) {
try {
//TODO 根据业务处理
System.out.println("未决事务,服务器回查客户端msg =" + new String(msg.getBody(),"utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return LocalTransactionState.ROLLBACK_MESSAGE;
// return LocalTransactionState.COMMIT_MESSAGE;
// return LocalTransactionState.UNKNOW;
}
}
涉及的源码附上:
public interface TransactionListener {
/**
* When send transactional prepare(half) message succeed, this method will be invoked to execute local transaction.
*
* @param msg Half(prepare) message
* @param arg Custom business parameter
* @return Transaction state
*这个事务处理方法
*/
LocalTransactionState executeLocalTransaction(final Message msg, final Object arg);
/**
* When no response to prepare(half) message. broker will send check message to check the transaction status, and this
* method will be invoked to get local transaction status.
*rocket默认固定间隔60s调用这个方法来校验本地事务状态
* @param msg Check message
* @return Transaction state
*/
LocalTransactionState checkLocalTransaction(final MessageExt msg);
}