订单&支付系统最终一致性(RocketMQ)
·
一、下单支付流程图

二、rocketMQ流程
5.接收,发送
①微信回调,发送 Half 半消息(代码不全)
@RestController
@RequestMapping(value = "/wx")
@CrossOrigin
public class WeixinPayController {
@Autowired
private RocketMQTemplate rocketMQTemplate;
/****
* 支付结果回调
*/
@RequestMapping(value = "/result")
public String result() throws Exception{
//创建日志对象
PayLog payLog = new PayLog(xxxxxxxxxxxxxxxxxxxxxxxxxx);
//构建消息
Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payLog)).build();
//发消息
rocketMQTemplate.sendMessageInTransaction("rocket","log",message,null);
return RespResult.ok();
}
}
6.本地事务(支付存根)
②MQ 确认收到 Half 消息;
③MQ 回调监听器 TransactionListener ,(executeLocalTransaction)执行(支付)本地事务;
④这里的返回值,就是给 MQ 的最终指令:
COMMIT:本地事务成功,让 MQ 把 Half 消息变成正常消息,推给消费者;ROLLBACK:本地事务失败,让 MQ 直接删掉 Half 消息,永远不投递。
⑤⑥⑦超时回查(checkLocalTransaction)(代码不全面)
@Component
@RocketMQTransactionListener(txProducerGroup = "rocket")
public class TransactionListener implements RocketMQLocalTransactionListener{
@Autowired
private PayLogService payLogService;
/****
* 当向RocketMQ的Broker发送Half消息成功之后,调用该方法
* @param msg:发送的消息
* @param arg:额外参数
* @return
*/
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
try {
//========================本地事务控制===================
//消息
String result = new String((byte[]) msg.getPayload(),"UTF-8");
PayLog payLog = JSON.parseObject(result,PayLog.class);
payLogService.add(payLog);
//========================本地事务控制===================
} catch (Exception e) {
e.printStackTrace();
return RocketMQLocalTransactionState.ROLLBACK;
}
return RocketMQLocalTransactionState.COMMIT;
}
/***
* 超时回查
* @param msg
* @return
*/
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
return RocketMQLocalTransactionState.COMMIT;
}
}
Last:消费
消费者 OrderResultListener 收到消息,修改订单状态
@Component
@RocketMQMessageListener(topic = "log",consumerGroup = "resultgroup")
public class OrderResultListener implements RocketMQListener,RocketMQPushConsumerLifecycleListener {
@Autowired
private OrderService orderService;
@Override
public void onMessage(Object message) {
}
/****
* 消息监听
* @param consumer
*/
@Override
public void prepareStart(DefaultMQPushConsumer consumer) {
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
try {
for (MessageExt msg : msgs) {
String result = new String(msg.getBody(),"UTF-8");
PayLog payLog = JSON.parseObject(result,PayLog.class);
if(payLog.getStatus().intValue()==2){
//支付成功
int count = orderService.updateAfterPayStatus(payLog.getPayId());
System.out.println(payLog.getId()+"================"+count);
}else{
//支付失败
//1:修改订单状态
//2:库存回滚
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
}
}
简而言之就是,用户支付成功后,先把「支付成功」的消息押在 MQ 那里,等你把支付凭证存到自己数据库里了,再让 MQ 把消息发给订单系统改状态;哪怕中间服务宕机,MQ 也会回查结果,绝对不会出现数据不一致的问题。如果存凭证失败了,就让 MQ 把消息删了,订单系统永远收不到。
三、MQ 在这个场景下的 3 个核心作用
- 异步解耦:支付系统只负责存 PayLog 和发消息,不用管订单系统什么时候改状态,两者互不影响;
- 削峰填谷:大促峰值时,MQ 把流量扛下来,订单系统慢慢消费,不会被冲垮;
- 保证可靠性:消息持久化 + 回查机制,哪怕服务宕机、网络波动,最终也能保证订单状态改过来,数据一致。
四、最终一致性核心思想
- PayLog 是底线,绝对不能回滚:它是用户付钱的凭证,是对账的依据,动了它就会出大问题;
- 用重试保证大多数失败能自动恢复:网络波动、服务重启等临时问题,通过 RocketMQ 的自动重试就能解决;
- 用死信队列和定时任务做兜底:极端情况下,人工介入或定时任务核对,保证数据最终一致。
这就是分布式事务里「最终一致性」的经典实现:不追求强一致(不要求所有操作同时成功),但保证经过一段时间后,所有数据最终都能一致。
更多推荐


所有评论(0)