091:RocketMQ-RocketMQ解决分布式事务问题
1 rocketmq解决分布式事务问题概述
课程安排:
- 什么是分布式事务?分布式事务最大思想
- RocketMQ与Rabbitmq解决分布式事务区别
- RocketMQ解决分布式事务最大的核心思想
- 基于RocketMQ实战解决分布式事务问题
2 rocketmq分布式事务问题简单回顾
分布式事务产生背景:rpc远程调用 多个不同服务之间通讯需要保证数据一致性的问题;存在多个数据源,每个数据源事务互不影响。
3 分布式事务最终一致性的核心思想
分布式领域中名词:
强一致性:读取数据不允许出现脏读;要求同步速度非常快或者采用锁的机制;
弱一致性:允许读取的数据为原来的脏数据;
最终一致性:在分布式系统中,因为数据之间同步通过网络实现通讯,短暂的数据延迟是允许的,但是最终数据必须要一致。数据同步延迟通过补偿(手动或者自动)解决。
举例:充值200电信话费,支付宝显示该笔订单已经扣款成功200,在电信app客户端订单中显示未支付。
解决方法:人工补偿,用订单号调用支付宝接口查询付款结果,手动补偿
4 回顾rabbitmq解决分布式事务的bug
在订单与派单系统中,因为订单系统可能发生各种变化,派单系统相对稳定,解决分布系统核心主要在订单系统。
rabbitmq解决分布式事务方案:
如果补单队列也挂的情况下,订单数据可能会丢失,但是能够成功派单,这种情况只能手动补偿。
5 rocketmq解决分布式事务思路
解决分布式事务的核心,必须先确保订单系统的事务能够百分百成功执行,如果该事务执行成功,就开始发送派单消息;如果该事务回滚的情况下,就不发送派单消息。
解决分布式事务核心思路:确保第一个事务一定先执行成功。
Rocketmq解决分布式事务的核心思路:
- 生产者向Broker(MQ服务器端)发送派单消息设置为半消息,该消息不可以被消费者消费;
- 执行本地的事务,将本地执行事务结果提交或者回滚告诉Broker;
- Broker获取到本地事务的结果,如果是提交的话,将该半消息设置为允许被消费者消费,如果本地事务执行失败的情况下,将该半消息直接从Broker中移除;
- 如果本地事务没有将结果及时通知给Broker,这时候Broker会主动定时(默认60s)查询本地事务结果(最多重试15次);
- 本地事务结果实际上就是一个回调方法,根据自己业务场景封装本地事务结果;
为什么设置半消息:因为没有被订单系统的本地事务确认
6 实战手写rocketmq核心代码
核心代码
@Service
public class ProducerService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
public String saveOrder() {
// 提前生成订单userId
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;
}
}
7 rocketmq事务监听处理
@Slf4j
@Component
@RocketMQTransactionListener(txProducerGroup = "mayiktProducer")
public class SyncProducerListener implements RocketMQLocalTransactionListener {
@Resource
private OrderMapper orderMapper;
@Autowired
private TransationalUtils transationalUtils;
/**
* 执行订单的事务
*
* @param msg
* @param arg
* @return
*/
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
MessageHeaders headers = msg.getHeaders();
Object object = headers.get("msg");
if (object == null) {
return null;
}
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;
}
// 告诉Broker可以消费该消息
return RocketMQLocalTransactionState.COMMIT;
} catch (Exception e) {
if (begin != null) {
transationalUtils.rollback(begin);
return RocketMQLocalTransactionState.ROLLBACK;
}
}
return null;
}
/**
* 提供给Broker定时检查
*
* @param msg
* @return
*/
@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;
}
}