rabbitMQ最終一致性處理分佈式事務簡單demo

demo地址:鏈接:https://pan.baidu.com/s/1kGaSCHlfhm6UvbPcRUYp4g 
提取碼:11hs
分佈式事務產生的背景?
   1.RPC通訊中每個服務都有自己獨立的數據源,每個數據源都互不影響.
   2.在單個項目中存在多個不同jdbc連接(多數據源)
   
如何基於我們的MQ解決我們的分佈式事務的問題(最終一致性)
   1.確保我們的生產者往我們的MQ投遞消息一定要成功.(生產者消息確認機制confirm),實現重試.
   2.確保我們的消費者能夠消費成功(手動ack機制),如果消費失敗情況下,MQ自動幫消費者重試.
   3.確保我們的生產者第一事務先執行成功,如果執行失敗採用補單隊列.

生產者

配置交換機 隊列 綁定  和創建 

package com.zhang.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class OrderRabbitMQConfig {


    @Autowired
    RabbitAdmin rabbitAdmin;

    /**
     * 派單隊列
     */
    public static final String ORDER_DIC_QUEUE = "order_dic_queue";
    /**
     * 補單對接
     */
    public static final String ORDER_CREATE_QUEUE = "order_create_queue";
    /**
     * 訂單交換機
     */
    private static final String ORDER_EXCHANGE_NAME = "order_exchange_name";

    /**
     * 定義派單隊列
     */
    @Bean
    public Queue directOrderDicQueue() {
        return new Queue(ORDER_DIC_QUEUE);
    }

    /**
     * 定義補派單隊列
     */
    @Bean
    public Queue directCreateOrderQueue() {
        return new Queue(ORDER_CREATE_QUEUE);
    }


    /**
     * 定義訂單交換機
     */
    @Bean
    DirectExchange directOrderExchange() {
        return new DirectExchange(ORDER_EXCHANGE_NAME);
    }

    /**
     * 派單隊列與交換機綁定
     */
    @Bean
    Binding bindingExchangeOrderDicQueue() {
        return BindingBuilder.bind(directOrderDicQueue()).to(directOrderExchange()).with("orderRoutingKey");
    }

    /**
     * 補單隊列與交換機綁定
     */
    @Bean
    Binding bindingExchangeCreateOrder() {
        return BindingBuilder.bind(directCreateOrderQueue()).to(directOrderExchange()).with("orderRoutingKey");
    }

    //創建初始化RabbitAdmin對象
    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        String string = connectionFactory.toString();
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        // 只有設置爲 true,spring 纔會加載 RabbitAdmin 這個類
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }
    //創建交換機和對列
    @Bean
    public void createExchangeQueue (){
        rabbitAdmin.declareExchange(directOrderExchange());
        rabbitAdmin.declareQueue(directOrderDicQueue());
        rabbitAdmin.declareQueue(directCreateOrderQueue());
    }
}

 發送訂單消息

@Component
@Slf4j
public class OrderProducer implements RabbitTemplate.ConfirmCallback{

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Transactional
    public String sendOrder() {
        // 1.先創建我們訂單信息
        String orderId = System.currentTimeMillis() + "";
        OrderEntity orderEntity = createOrder(orderId);
        // 2.添加到我們的數據庫中
        int result = orderMapper.addOrder(orderEntity);
        if (result <= 0) {
            return null;
        }
        // 3.訂單數據庫插入成功的情況下, 使用MQ異步發送派單信息
        String msgJson = JSONObject.toJSONString(orderEntity);
        System.out.println("<<<<<<<1線程名稱是>>>>>:"+Thread.currentThread().getName());
        sendMsg(msgJson);
        int i=1/0;
        return orderId;
    }

    //發送消息
    public void sendMsg(String msgJson) {
        // 設置生產者消息確認機制
        this.rabbitTemplate.setMandatory(true);
        this.rabbitTemplate.setConfirmCallback(this);
        CorrelationData correlationData = new CorrelationData();
        correlationData.setId(msgJson);
        String orderExchange = "order_exchange_name"; //訂單交換機
        String orderRoutingKey = "orderRoutingKey";  //訂單路由key
        rabbitTemplate.convertAndSend(orderExchange, orderRoutingKey, msgJson, correlationData);
    }

    //創建訂單信息
    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;
    }

    /**
     * 實現ConfirmCallback接口 重寫confirm方法
     * @param correlationData 投遞失敗回調消息
     * @param ack true 投遞到MQ成功  false投遞消息失敗
     * @param cause
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        String msg = correlationData.getId();
        if(!ack){
            log.info("<<<往MQ投遞消息失敗>>>>: {}" , msg);
            //採用遞歸重試
            sendMsg(msg);
            return;
        }
        log.info("<<<往MQ投遞消息成功>>>>: {}" , msg);
        // 生產者投遞多次還是is的情況下應該 人工記錄
    }
}

消費者

派單消費者

@Component
@Slf4j
public class DispatchConsumer {

    @Autowired
    private DispatchMapper dispatchMapper;

    @RabbitListener(queues = "order_dic_queue")
    public void dispatchConsumer(Message message , Channel channel) throws IOException {
        // 1.獲取消息
        String msg = new String(message.getBody());
        // 2.轉換json
        JSONObject jsonObject = JSONObject.parseObject(msg);
        String orderId = jsonObject.getString("orderId");
        //主動根據orderId查詢派單是否已經派單過,如果派單,不走下面插入代碼
        // 計算分配的快遞員id
        DispatchEntity dispatchEntity = new DispatchEntity(orderId, 1234L);
        // 3.插入我們的數據庫
        int result = dispatchMapper.insertDistribute(dispatchEntity);
        if (result > 0) {
            // 手動將該消息刪除
            // 手動ack 刪除該消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            log.info("<<<消費者派單消費成功>>> 時間: {} " ,new Date());
        }
    }
}

補單消費者

@Component
@Slf4j
public class OrderConsumer {

    @Autowired
    private OrderMapper orderMapper;

    /***
     * 補單的消費不應該和 訂單生產者放到一個服務器節點
     * 補單消費者如果不存在的情況下 隊列緩存補單消息
     * 補償分佈式事務解決框架 思想最終一致性
     */
    @RabbitListener(queues = "order_create_queue")
    public void dispatchConsumer(Message message , Channel channel) throws IOException {
        // 1.獲取消息
        String msg = new String(message.getBody());
        // 2.轉換json
        OrderEntity orderEntity = JSONObject.parseObject(msg,OrderEntity.class);
        String orderId = orderEntity.getOrderId();
        OrderEntity result = orderMapper.findOrderId(orderId);
        if (null!=result ) {
            // 手動將該消息刪除
            // 手動ack 刪除該消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            log.info("<<<消費者消費成功>>> 時間: {}",new Date());
            return;
        }
        //補單
        int i = orderMapper.addOrder(orderEntity);
        if(i>0){
            // 手動將該消息刪除
            // 手動ack 刪除該消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            log.info("<<<消費者補單消費成功>>> 時間: {}",new Date());
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章