Spring boot RabbitMq 实现分布式事务

分布式事务:

  • 指事务的操作位于不同的节点上,需要保证事务的ACID特性。
  • eg: 下单场景下,库存和订单如果不在同一节点上,就涉及分布式事务

大致思路流程:
1
测试表:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码:

死信交互机

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author xiaoshu
 * @description
 * @date 2022年03月19日 16:53
 */
@Configuration
public class DeadMq {

    @Bean
    public FanoutExchange fanoutDeadExchange(){
        return new FanoutExchange("fanout_dead_exchange");
    }

    @Bean
    public Queue deadQueue(){
        return new Queue("fanout_dead_order_queue");
    }

    @Bean
    public Binding deadBind(){
        return BindingBuilder.bind(deadQueue()).to(fanoutDeadExchange());
    }

}

声明订单交换机,订单队列 -> 死信交换机

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description MQ分布书事务,消息可靠生产
 * @Author Hexiaoshu
 * @Date 2021/7/10
 * @modify
 */
@Configuration
public class TransactionalRabbitMqConfig {
    /**
     * 发布订阅型,order交互机
     * @return FanoutExchange
     */
    @Bean
    public FanoutExchange transactionFanoutExchange(){
        return new FanoutExchange("fanout_transaction_order_exchange");
    }

    /**
     * 绑定订单交换机
     * @return Queue
     */
    @Bean
    public Queue transactionOrderQueue(){
        Map<String,Object> args = new HashMap<>();
        //args.put("x-message-ttl",10000);//过期时间int 类型
        //绑定死信交换机
        //args.put("x-max-length",5);//最大长度5条,超过进入死信队列
        args.put("x-dead-letter-exchange","fanout_dead_exchange");//指定死信交换机
        //args.put("x-dead-letter-routing-key","");//fanout 不用配置
        return new Queue("fanout_transaction_order_queue",true,false,false,args);
    }

    @Bean
    public Binding fanoutOrderBinding(){
        return BindingBuilder.bind(transactionOrderQueue()).to(transactionFanoutExchange());
    }
}

订单服务:推送订单-》MQ, 处理 -死信订单

import com.model.MyOrder;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;

/**
 * @Description
 * @Author Hexiaoshu
 * @Date 2021/7/3
 * @modify
 */
@RabbitListener(queues = {"fanout_dead_order_queue"})
public interface OrderService {

    /**
     * 分布式事务 订单处理示例
     * @param myOrder
     */
    void transactionOrder(MyOrder myOrder);

    /**
     * 死信,问题订单处理
     * @param meeage
     * @param channel
     * @param message
     */
    @RabbitHandler
    void deadOrderHandler(String meeage, Channel channel, Message message);
}
	/**
     * TODO 这里加事务是,rabbitTemplate.convertAndSend(),这个方法在消息推送配置上可能报错,不会触发消息投递确认。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transactionOrder(MyOrder myOrder) {
        String exchangeName="fanout_transaction_order_exchange";
        MyOrderTmr curTmrOrder = new MyOrderTmr().setOrderId(myOrder.getOrderId()).setCreateTime(new Date()).setStatus(0);
        //添加订单
        int orderFlag = orderMapper.insert(myOrder);
        if (orderFlag>0){
            //设置订单MQ消息,冗余表;表示订单消息处理状态; status 0,默认未投递成功 ,1投递成功。 2产品服务,库存数量扣减成功
            int orderTmrFlag = orderTmrMapper.insert(curTmrOrder);
            if (orderTmrFlag>0){
                //投递消息到交换机
                rabbitTemplate.convertAndSend(exchangeName,"", JsonUtil.toStr(myOrder),new CorrelationData(String.valueOf(myOrder.getOrderId())));
            }
        }
    }

    @Override
    public void deadOrderHandler(String msg, Channel channel, Message message) {
        try {
            System.out.println("处理失败消息");
            System.out.println("消息内容:"+msg);
            MyOrder order = JsonUtil.toObj(msg, MyOrder.class);
            orderMapper.updateByPrimaryKeySelective(order.setStatus(2));
            System.out.println("修改订单状态:2");
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            //处理异常,拒绝确认消息, 推送到死信交换机
            try {
                System.out.println("处理失败,拒绝消息");
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),
                        false, false);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            System.err.println("get msg1 failed msg = "+msg);
            e.printStackTrace();
        }
    }

    /**
     * 订单消息投递确认,投递成功修改消息冗余状态
     * 分布式订单业务例子, 订单服务 -> RabbitMq (Exchange) -> 订单配送服务消费订单
     */
    @PostConstruct
    public void regCallBack(){
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            System.out.println("cause:"+cause);
            assert correlationData != null;
            String orderId = correlationData.getId();
            //ack 为true代表收到消息
            if (!ack){
                System.out.println("MQ队列应答--失败,OrderId:"+orderId+",删除订单相关数据");
                orderMapper.deleteByPrimaryKey(new MyOrder().setOrderId(Long.parseLong(orderId)));
                orderTmrMapper.deleteByPrimaryKey(new MyOrderTmr().setOrderId(Long.parseLong(orderId)));
                return;
            }
            try {
                System.out.println("MQ队列应答--成功, 修改订单冗余状态 status=1,OrderId:"+orderId);
                //修改冗余消息状态,确认收到消息
                orderTmrMapper.updateByPrimaryKeySelective(new MyOrderTmr().setOrderId(Long.parseLong(orderId)).setStatus(1));
            }catch (Exception ex){
                System.out.println("本地消息出现异常"+ex.getMessage());
            }
        });
    }

另一节点库存服务:

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;

/**
 * @Description 订单消息接收
 * @Author Hexiaoshu
 * @Date 2021/7/11
 * @modify
 */
@RabbitListener(queues = {"fanout_transaction_order_queue"})
public interface TransactionalOrderConsumer {

    @RabbitHandler
    void orderHandler(String meeage, Channel channel, Message message);

}
import com.consumer.TransactionalOrderConsumer;
import com.mapper.ProductMapper;
import com.model.MyOrder;
import com.model.Product;
import com.rabbitmq.client.Channel;
import com.util.JsonUtil;
import org.springframework.amqp.core.Message;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;

/**
 * @Description
 * @Author Hexiaoshu
 * @Date 2021/7/11
 * @modify
 */
@Service("transactionalOrderConsumer")
public class TransactionalOrderConsumerImpl implements TransactionalOrderConsumer {

    @Resource
    ProductMapper productMapper;

    /**
     * 处理派送订单
     */
    @Override
    public void orderHandler(String msg, Channel channel, Message message) {
        //处理状态,默认成功,false
        boolean handlerFlag=false;
        long msgTag = message.getMessageProperties().getDeliveryTag();
        try {
            //确认消息处理,返回ack
            System.out.println("消息内容:"+msg);
            //报错,死信测试
            //int i=1/0;
            MyOrder order = JsonUtil.toObj(msg, MyOrder.class);
            Product curProduct = productMapper.selectByPrimaryKey( new Product().setId(order.getProductId()));
            //修改产品数量
            Product product1 = curProduct.setCount(curProduct.getCount()-1);
            productMapper.updateByPrimaryKey(product1);
            System.out.println("产品数量修改成功:"+product1);
        } catch (Exception e) {
            handlerFlag=true;
            e.printStackTrace();
        }finally {
            if (handlerFlag){
                //确认消息
                toDo(channel, msgTag);
            }else {
                //拒绝消息
                toReject(channel, msgTag);
            }
        }
    }

    /**
     * 拒绝消息
     * @param channel 通道
     * @param msgTag  消息标识
     */
    private void toReject(Channel channel,Long msgTag){
        try {
            channel.basicNack(msgTag, false,false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 确认消息
     * @param channel 通道
     * @param msgTag  消息标识
     */
    private void toDo(Channel channel,Long msgTag){
        try {
            channel.basicAck(msgTag, false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

测试入口

@RestController
public class OrderController {
    @Resource
    private OrderService orderService;

    @GetMapping("/sendOrder")
    public String order(){
        String orderName="购买白象方便面";
        long orderId = SnowflakeIdWorker.id();
        long productId = 1L;
        MyOrder curMyOrder = new MyOrder().setOrderId(orderId).setProductId(productId).setOrderName(orderName).setCreateTime(new Date());
        orderService.transactionOrder(curMyOrder);
        return "订单生产成功";
    }

}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot RabbitMQ是一个基于Spring Boot框架的消息队列服务,它使用RabbitMQ作为消息中间件,提供了一种简单、灵活、可靠的消息传递机制。通过Spring Boot RabbitMQ,开发者可以轻松地实现消息的生产、消费、路由、过滤等功能,从而构建高效、可靠的分布式应用系统。同时,Spring Boot RabbitMQ还提供了丰富的配置选项和监控工具,方便开发者进行调试和优化。 ### 回答2: Spring Boot RabbitMQ是一个消息队列中间件框架,用于实现高效可靠的消息传递和异步处理。它基于AMQP协议(Advanced Message Queuing Protocol)来实现消息的发布与订阅。 Spring Boot RabbitMQ的优点包括: 1. 简单易用:使用Spring Boot可以快速搭建RabbitMQ应用程序,并且具有自动配置和默认值的特性。开发人员只需简单配置一些参数,即可进行消息的发送和接收操作。 2. 可靠性:RabbitMQ提供持久化机制,确保消息在发送和接收过程中不会丢失。同时,RabbitMQ也支持消息的事务处理,保证消息的可靠性。 3. 异步处理:RabbitMQ使用消息队列的方式进行消息的传递,实现了消息的异步处理。发送方将消息发送到队列中后,不需要等待接收方的响应,可以继续处理其他任务。 4. 解耦合:使用消息队列可以实现系统的解耦合。发送方只需要将消息发送到队列中,接收方则可以通过订阅队列获取消息,实现了发送方和接收方之间的解耦合。 5. 可扩展性:RabbitMQ支持集群部署,可以根据实际业务需求进行水平扩展和负载均衡,提高系统的吞吐量和并发能力。 总而言之,Spring Boot RabbitMQ为开发人员提供了一种方便高效的消息传递和异步处理方式,降低了系统之间的耦合度,并且具备可靠性和可扩展性。在分布式系统中广泛应用于解决异步通信、任务调度和日志传输等场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值