一、下单支付流程图

        

二、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 个核心作用

  1. 异步解耦:支付系统只负责存 PayLog 和发消息,不用管订单系统什么时候改状态,两者互不影响;
  2. 削峰填谷:大促峰值时,MQ 把流量扛下来,订单系统慢慢消费,不会被冲垮;
  3. 保证可靠性:消息持久化 + 回查机制,哪怕服务宕机、网络波动,最终也能保证订单状态改过来,数据一致。

四、最终一致性核心思想

  1. PayLog 是底线,绝对不能回滚:它是用户付钱的凭证,是对账的依据,动了它就会出大问题;
  2. 用重试保证大多数失败能自动恢复:网络波动、服务重启等临时问题,通过 RocketMQ 的自动重试就能解决;
  3. 用死信队列和定时任务做兜底:极端情况下,人工介入或定时任务核对,保证数据最终一致。

这就是分布式事务里「最终一致性」的经典实现:不追求强一致(不要求所有操作同时成功),但保证经过一段时间后,所有数据最终都能一致。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐