1.RocketMQ解决分布式事务核心思路:
1.生产者向我们的Broker(MQ服务器端)发送一条消息设置为半消息,该消息不可以被消费者消费.
2.在执行我们本地的事务,将本地执行事务结果提交或者回滚告诉Broker
3.Broker获取本地事务的结果如果为提交的话,将该半消息设置为允许被消费者消费,如果本地事务执行失败的情况下,
将该半消息直接从Broker中移除.
4.如果我们的本地事务没有将结果及时通知给我们的Broker,这时候我们Broker会主动定时(默认60S)查询本地事务结果.
5.本地事务结果实际上就是一个回调方法,根据自己业务场景封装本地事务结果.
项目代码demo: 链接:https://pan.baidu.com/s/11EHZtOw4JFBZ7O1zXMqKuw
提取码:ybtn
来源:源自蚂蚁课堂学习整理笔记
核心代码
1.向RocketMQ发送半消息
public String saveOrder() {
// 提前生成我们的订单id
String orderId = System.currentTimeMillis() + "";
/*
* 1.提前生成我们的半消息
*
* 2.半消息发送成功之后,在执行我们的本地事务
*/
OrderEntity orderEntity = createOrder(orderId);
String msg = JSONObject.toJSONString(orderEntity);
MessageBuilder<String> stringMessageBuilder = MessageBuilder.withPayload(msg);
stringMessageBuilder.setHeader("msg", msg);
Message message = stringMessageBuilder.build();
// 该消息不允许被消费者消费
rocketMQTemplate.sendMessageInTransaction("mayiktProducer",
"orderTopic", message, null);
return orderId;
}
public OrderEntity createOrder(String orderId) {
OrderEntity orderEntity = new OrderEntity();
orderEntity.setName("每特教育第六期平均就业薪资破10万");
orderEntity.setOrderCreatetime(new Date());
// 价格是300元
orderEntity.setOrderMoney(300d);
// 状态为 未支付
orderEntity.setOrderState(0);
Long commodityId = 30L;
// 商品id
orderEntity.setCommodityId(commodityId);
orderEntity.setOrderId(orderId);
return orderEntity;
}
2.创建类实现接口RocketMQLocalTransactionListener, 重写本地事务方法和Broker定时检查 .并监听Rocket分组
@Slf4j
@Component
@RocketMQTransactionListener(txProducerGroup = "mayiktProducer")
public class SyncProducerListener implements RocketMQLocalTransactionListener {
@Autowired
private OrderMapper orderMapper;
@Autowired
private TransationalUtils transationalUtils;
/**
* 执行我们订单的事务
*/
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
MessageHeaders headers = msg.getHeaders();
Object object = headers.get("msg");
if (object == null) {
return RocketMQLocalTransactionState.ROLLBACK;
}
String orderMsg = (String) object;
OrderEntity orderEntity = JSONObject.parseObject(orderMsg, OrderEntity.class);
TransactionStatus begin = null;
try {
begin = transationalUtils.begin();
int result = orderMapper.addOrder(orderEntity);
transationalUtils.commit(begin);
if (result <= 0) {
return RocketMQLocalTransactionState.ROLLBACK;
}
// 告诉我们的Broke可以消费者该消息
return RocketMQLocalTransactionState.COMMIT;
} catch (Exception e) {
if (begin != null) {
transationalUtils.rollback(begin);
return RocketMQLocalTransactionState.ROLLBACK;
}
}
return null;
}
/**
* 提供给我们的Broker定时检查
*/
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
MessageHeaders headers = msg.getHeaders();
Object object = headers.get("msg");
if (object == null) {
return RocketMQLocalTransactionState.ROLLBACK;
}
String orderMsg = (String) object;
OrderEntity orderEntity = JSONObject.parseObject(orderMsg, OrderEntity.class);
String orderId = orderEntity.getOrderId();
// 直接查询我们的数据库
OrderEntity orderDbEntity = orderMapper.findOrderId(orderId);
if (orderDbEntity == null) {
return RocketMQLocalTransactionState.UNKNOWN;
}
return RocketMQLocalTransactionState.COMMIT;
}
}
3.配置文件
server.port=8088
###连接地址nameServer
rocketmq.name-server=192.168.0.110:9876
rocketmq.producer.group=mayikt_producer
spring.datasource.url=jdbc:mysql://localhost:3306/order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver