RocketMQ进阶-事务消息

本文详细解释了在SpringBoot应用中如何使用RocketMQ的sendMessageInTransaction方法发送半消息,以及如何实现执行本地事务和回查的过程,涉及RocketMQLocalTransactionListener接口的使用。
摘要由CSDN通过智能技术生成
  • 添加配置

within rocketmq

rocketmq:

name-server: xxx.xx.x.xx:9876; xxx.xx.x.xx:9876

producer:

group: cloud-group

发送半消息

order-service在执行删除订单操作时发送一条半消息给MQServer,发送半消息主要是使用rocketMQTemplate.sendMessageInTransaction() 方法,发送事务消息。

@Override

public void delete(String orderNo) {

Order order = orderMapper.selectByNo(orderNo);

//如果订单存在且状态为有效,进行业务处理

if (order != null && CloudConstant.VALID_STATUS.equals(order.getStatus())) {

String transactionId = UUID.randomUUID().toString();

//如果可以删除订单则发送消息给rocketmq,让用户中心消费消息

rocketMQTemplate.sendMessageInTransaction(“add-amount”,

MessageBuilder.withPayload(

UserAddMoneyDTO.builder()

.userCode(order.getAccountCode())

.amount(order.getAmount())

.build()

)

.setHeader(RocketMQHeaders.TRANSACTION_ID, transactionId)

.setHeader(“order_id”,order.getId())

.build()

,order

);

}

}

首先先校验一下订单状态,然后发送消息给MQServer,这个逻辑大家都看得懂,主要是关注sendMessageInTransaction() 方法,源码如下:

public TransactionSendResult sendMessageInTransaction(String destination, Message<?> message, Object arg) throws MessagingException {

try {

if (((TransactionMQProducer)this.producer).getTransactionListener() == null) {

throw new IllegalStateException(“The rocketMQTemplate does not exist TransactionListener”);

} else {

org.apache.rocketmq.common.message.Message rocketMsg = this.createRocketMqMessage(destination, message);

return this.producer.sendMessageInTransaction(rocketMsg, arg);

}

} catch (MQClientException var5) {

throw RocketMQUtil.convert(var5);

}

}

该方法有三个参数:

  • destination:目的地(主题),这里发送给add-amount 这个主题

  • message:发送给消费者的消息体,需要使用 MessageBuilder.withPayload() 来构建消息

  • arg:参数

注意,这里我们生成了一个transactionId,并放在header中跟消息一起发送(这里实际也可以构造成一个对象,放在arg里进行发送),作用后面再讲!

执行本地事务与回查

MQServer收到半消息后会告诉生产者order-service确认收到半消息,这时候order-service需要执行本地事务,执行完本地事务后再告诉MQServer本地事务的执行状态,确认消息究竟是Commit还是Rollback。如果在告诉MQServer本地执行状态的时候出异常了还需要让MQServer能够回查到,怎么实现这一些列操作呢?

RocketMQ提供了 RocketMQLocalTransactionListener 接口,本地事务监听器,这个接口类的实现如下:

第一个方法executeLocalTransaction 为执行本地事务;

第二个方法checkLocalTransaction 为检查本地事务的执行状态,也就是回查动作。

有了这个接口类我们的执行逻辑清楚了,但是还有个问题:本地事务已经执行完成了,怎么去回查本地事务的执行结果呢?

我们可以在执行本地事务的时候同时生成一个事务日志,让本地事务与日志事务在同一个方法中,同时添加@Transactional 注解,保证两个操作事务是一个原子操作。这样如果事务日志表中有这个本地事务的信息,那就代表本地事务执行成功,需要Commit,相反如果没有对应的事务日志,则表示没执行成功,需要Rollback

思路既然理顺了,咱们就开撸。

  • 首先创建一个日志表

很简单的三个字段,主要是这个事务id,需要根据这个事务id回查事务,还记得我们在发送半消息时生成的事务id吗,就是干这个用的!

  • 在生产者编写方法实现RocketMQLocalTransactionListener

@Slf4j

@RocketMQTransactionListener

@RequiredArgsConstructor(onConstructor = @__(@Autowired))

public class AddUserAmountListener implements RocketMQLocalTransactionListener {

private final OrderService orderService;

private final RocketMqTransactionLogMapper rocketMqTransactionLogMapper;

/**

  • 执行本地事务

*/

@Override

public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object arg) {

log.info(“执行本地事务”);

MessageHeaders headers = message.getHeaders();

//获取事务ID

String transactionId = (String) headers.get(RocketMQHeaders.TRANSACTION_ID);

Integer orderId = Integer.valueOf((String)headers.get(“order_id”));

log.info(“transactionId is {}, orderId is {}”,transactionId,orderId);

try{

//执行本地事务,并记录日志

orderService.changeStatuswithRocketMqLog(orderId, CloudConstant.INVALID_STATUS,transactionId);

//执行成功,可以提交事务

return RocketMQLocalTransactionState.COMMIT;

}catch (Exception e){

return RocketMQLocalTransactionState.ROLLBACK;

}

}

/**

  • 本地事务的检查,检查本地事务是否成功

*/

@Override

public RocketMQLocalTransactionState checkLocalTransaction(Message message) {

MessageHeaders headers = message.getHeaders();

//获取事务ID

String transactionId = (String) headers.get(RocketMQHeaders.TRANSACTION_ID);

log.info(“检查本地事务,事务ID:{}”,transactionId);

//根据事务id从日志表检索

QueryWrapper queryWrapper = new QueryWrapper<>();

queryWrapper.eq(“transaction_id”,transactionId);

RocketmqTransactionLog rocketmqTransactionLog = rocketMqTransactionLogMapper.selectOne(queryWrapper);

if(null != rocketmqTransactionLog){

return RocketMQLocalTransactionState.COMMIT;

}

return RocketMQLocalTransactionState.ROLLBACK;

}

}

  • 执行本地事务的方法

@Transactional(rollbackFor = RuntimeException.class)

@Override

public void changeStatuswithRocketMqLog(Integer id,String status,String transactionId){

//将订单状态置位无效

orderMapper.changeStatus(id,status);

//插入事务表

rocketMqTransactionLogMapper.insert(

RocketmqTransactionLog.builder()

.transactionId(transactionId)

.log(“执行删除订单操作”)

.build()

);

}

这一块的代码逻辑都是在生产端,即Order-Server,大家不要搞错了

消费消息

Rollback的消息MQServer会给我们处理,我们只要关注Commit状态时消费端可以正常消费即可。在account-service监听消息,如果收到消息则给用户账户增加余额。

总结

这个月马上就又要过去了,还在找工作的小伙伴要做好准备了,小编整理了大厂java程序员面试涉及到的绝大部分面试题及答案,希望能帮助到大家

在这里插入图片描述

在这里插入图片描述

;

}

这一块的代码逻辑都是在生产端,即Order-Server,大家不要搞错了

消费消息

Rollback的消息MQServer会给我们处理,我们只要关注Commit状态时消费端可以正常消费即可。在account-service监听消息,如果收到消息则给用户账户增加余额。

总结

这个月马上就又要过去了,还在找工作的小伙伴要做好准备了,小编整理了大厂java程序员面试涉及到的绝大部分面试题及答案,希望能帮助到大家

[外链图片转存中…(img-M5SsUCsG-1714443570462)]

[外链图片转存中…(img-qzj9SZPw-1714443570462)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 13
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值