MQ解决分布式事务 原创

新一代程序猿
发布于 2022-1-22 00:37
浏览
1收藏

春节不停更,此文正在参加「星光计划-春节更帖活动

MQ解决什么问题?

要了解MQ的必要性,需要先了解一下微服务的产生与出现的问题。只有了解了问题产生的原因才能明白MQ的作用。
举个栗子:支付业务

在微服务的环境下,会将三个步骤拆分成三个服务,例如:支付服务,订单服务,物流服务。三者顾名思义只做好自己的一件事情。
MQ解决分布式事务-鸿蒙开发者社区
情况会变复杂,同样以支付业务为例,三个步骤需要有服务间的调用,如下图,有两次调用产生,服务间调用一般有两种方式:restful 的http与RPC。这里不讨论两者的区别。

微服务化的好处:化整为零,单一服务制作单一的事情,可以防止单服务无线膨胀。无论从开发,测试,运维方面单个服务都是占优势的。

微服务带来的问题:

  • 分布式事务
    服务间调用难以保持一致性。例如上图中的两次调用。因为三个步骤操作的不是同一个数据库,导致无法使用jdbc事务管理以达到一致性。而且两次服务调用,因为涉及到复杂的网络环境,很容易出现,服务调用失败。所以,微服务化之后出现的一个很严重的问题就是,分布式事务问题如何解决。

解决分布式事务

MQ作为解决分布式事务的一种,是如何解决分布式事务问题的,如下图,支付服务完成支付步骤后,往MQ发送一条已支付消息,订单服务收到消息后更改订单状态。
MQ解决分布式事务-鸿蒙开发者社区

MQ实战

  • 初始化MQ队列
@Configuration
public class MyRabbitMQConfig {

    /* 容器中的Queue、Exchange、Binding 会自动创建(在RabbitMQ)不存在的情况下 */

    /**
     * 死信队列
     *
     * @return
     */@Bean
    public Queue orderDelayQueue() {
        /*
            Queue(String name,  队列名字
            boolean durable,  是否持久化
            boolean exclusive,  是否排他
            boolean autoDelete, 是否自动删除
            Map<String, Object> arguments) 属性
         */
        HashMap<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", "order-event-exchange");
        arguments.put("x-dead-letter-routing-key", "order.release.order");
        arguments.put("x-message-ttl", 60000); // 消息过期时间 1分钟
        Queue queue = new Queue("order.delay.queue", true, false, false, arguments);

        return queue;
    }



    /**
     * storage队列
     *
     * @return
     */
    @Bean
    public Queue orderToStorageQueue() {

        Queue queue = new Queue("order.release.storage.queue", true, false, false);

        return queue;
    }


    /**
     * TopicExchange
     *
     * @return
     */
    @Bean
    public Exchange orderEventExchange() {
        /*
         *   String name,
         *   boolean durable,
         *   boolean autoDelete,
         *   Map<String, Object> arguments
         * */
        return new TopicExchange("order-event-exchange", true, false);

    }


    @Bean
    public Binding orderCreateBinding() {
        /*
         * String destination, 目的地(队列名或者交换机名字)
         * DestinationType destinationType, 目的地类型(Queue、Exhcange)
         * String exchange,
         * String routingKey,
         * Map<String, Object> arguments
         * */
        return new Binding("order.delay.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.create.order",
                null);
    }



    @Bean
    public Binding orderReleaseStorageBinding() {

        return new Binding("order.release.storage.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.release.order",
                null);
    }




}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 编写下单接口创建订单并锁定库存,通过发送消息给mq的等待队列

  @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(Orders order) {
        log.info("------->交易开始");
        //本地方法
        order.setStatus(Orderstatus.NOPAYMENT);
        super.save(order);
        //远程方法 锁定库存
        storageApi.decrease(order.getProductId(),order.getCount());
        log.info("订单号:"+order.getId());
        log.info("------->发送mq");
        rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", order);
    }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • StorageReleaseListener监听等待队列(根据实际情况设置等待时间)判断队列是否支付,如果没有支付关闭订单并回滚库存
@Slf4j
@Service
public class StorageReleaseListener {
    @Autowired
    private orderApi orderApi;
    @Autowired
    private IStorageService storageService;




    @RabbitListener(queues = "order.release.storage.queue")
    public void handleStockLockedRelease(Orders to, Channel channel, Message message) throws IOException {
        log.info("******order.release.account.queue---》收到回滚库存的信息******");
        try {
            Orders orders = orderApi.GetByOrders(to.getId().longValue());
           if("0".equals(orders.getStatus())){
               storageService.rollbackStorage(orders.getProductId(),orders.getCount());
           }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
1
收藏 1
回复
举报
1
1


回复
    相关推荐