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