通过一个下单以后扣减库存的数据一致性场景来演示RocketMQ的分布式事务特性
TransactionProducer
public class TransactionProducer {
public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, InterruptedException {
TransactionMQProducer transactionMQProducer=new
TransactionMQProducer("tx_producer");
transactionMQProducer.setNamesrvAddr("192.168.13.102:9876");
ExecutorService executorService= Executors.newFixedThreadPool(10);
transactionMQProducer.setExecutorService(executorService);
transactionMQProducer.setTransactionListener(new TransactionListenerLocal()); //本地事务的监听
transactionMQProducer.start();
for(int i=0;i<20;i++){
String orderId= UUID.randomUUID().toString();
String body="{'operation':'doOrder','orderId':'"+orderId+"'}";
Message message=new Message("order_tx_topic",
"TagA",orderId,body.getBytes(RemotingHelper.DEFAULT_CHARSET));
transactionMQProducer.sendMessageInTransaction(message,orderId+"&"+i);
Thread.sleep(1000);
}
}
}
TransactionListenerLocal
public class TransactionListenerLocal implements TransactionListener {
private Map<String,Boolean> results=new ConcurrentHashMap<>();
//执行本地事务
@Override
public LocalTransactionState executeLocalTransaction(Message message, Object o) {
System.out.println("开始执行本地事务:"+o.toString()); //o
String orderId=o.toString();
//模拟数据库保存(成功/失败)
boolean result=Math.abs(Objects.hash(orderId))%2==0;
results.put(orderId,result); //
return result?LocalTransactionState.COMMIT_MESSAGE:LocalTransactionState.UNKNOW;
}
//提供给事务执行状态检查的回调方法,给broker用的(异步回调)
//如果回查失败,消息就丢弃
@Override
public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
String orderId=messageExt.getKeys();
System.out.println("执行事务回调检查: orderId:"+orderId);
boolean rs=results.get(orderId);
System.out.println("数据的处理结果:"+rs); //只有成功/失败
return rs?LocalTransactionState.COMMIT_MESSAGE:LocalTransactionState.ROLLBACK_MESSAGE;
}
}
TransactionConsumer
public class TransactionConsumer {
//rocketMQ 除了在同一个组和不同组之间的消费者的特性和kafka相同之外
//RocketMQ可以支持广播消息,就意味着,同一个group的每个消费者都可以消费同一个消息
public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer defaultMQPushConsumer=
new DefaultMQPushConsumer("tx_consumer");
defaultMQPushConsumer.setNamesrvAddr("192.168.13.102:9876");
defaultMQPushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//subExpression 可以支持sql的表达式. or and a=? ,,,
defaultMQPushConsumer.subscribe("order_tx_topic","*");
defaultMQPushConsumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
list.stream().forEach(message->{
//扣减库存
System.out.println("开始业务处理逻辑:消息体:"+new String(message.getBody())+"->key:"+message.getKeys());
});
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; //签收
}
});
defaultMQPushConsumer.start();
}
}