RabbitMQ實現訂單30分鐘超時自動關閉

demo地址:鏈接:https://pan.baidu.com/s/1kGaSCHlfhm6UvbPcRUYp4g 
提取碼:11hs

訂單30分鐘未支付,系統自動超時關閉有哪些實現方案?
    1.基於任務調度實現,效率是非常低,耗服務器性能
    2.基於redis過期key實現.用戶下單的時候,生成一個令牌(有效期)30分鐘,存放到我們redis;
        redis.set(orderToken ,orderID) 下單時候存放到redis,並存儲id入庫,30分鐘過期,
        redis客戶端監聽,過期獲取到orderId,拿orderId去查訂單,沒有支付則,訂單關閉,庫存增加
        缺點:非常冗餘 ,會在表中存放一個冗餘字段 https://blog.csdn.net/zhangshengqiang168/article/details/104925649

    3.基於redis延遲隊列   https://blog.csdn.net/zhangshengqiang168/article/details/100130523
    4.基於MQ的延遲隊列實現(最佳)    死信隊列(延遲隊列)
        原理:當我們在下單的時候,往MQ投遞一個消息設置有效期爲30分鐘,但該消息失效的時候(沒有被消費的情況下),
             執行我們客戶端一個方法告訴我們該消息已經失效,這時候查詢這筆訂單是否有支付.     

實現原理(消息過期投遞到死信隊列)

原理:下單投放消息A交換機(過期時間30分鐘),消息到aa隊列(綁定死信交換機),不設置aa隊列的消費者(故此消息一直未消費).
30分鐘後,過期消息投遞到死信交換機,死信隊列,由死信消費者消費,判斷訂單id是否支付,執行業務邏輯,支付->return 
未支付->關閉訂單,返還庫存

demo演示:

 1.聲明訂單和死信交換機、 訂單和死信隊列 、關鍵步驟訂單隊列綁定訂單交換機和死信交換機 、死信隊列綁定死信交換機

@Component
public class DeadLetterMQConfig {
    /**
     * 訂單交換機
     */
    @Value("${mayikt.order.exchange}")
    private String orderExchange;
    /**
     * 訂單隊列
     */
    @Value("${mayikt.order.queue}")
    private String orderQueue;
    /**
     * 訂單路由key
     */
    @Value("${mayikt.order.routingKey}")
    private String orderRoutingKey;
    /**
     * 死信交換機
     */
    @Value("${mayikt.dlx.exchange}")
    private String dlxExchange;
    /**
     * 死信隊列
     */
    @Value("${mayikt.dlx.queue}")
    private String dlxQueue;
    /**
     * 死信路由
     */
    @Value("${mayikt.dlx.routingKey}")
    private String dlxRoutingKey;
    /**
     * 聲明死信交換機
     */
    @Bean
    public DirectExchange dlxExchange() {
        return new DirectExchange(dlxExchange);
    }
    /**
     * 聲明死信隊列
     */
    @Bean
    public Queue dlxQueue() {
        return new Queue(dlxQueue);
    }
    /**
     * 聲明訂單業務交換機
     */
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange(orderExchange);
    }
    /**
     * 聲明訂單隊列 核心操作一
     */
    @Bean
    public Queue orderQueue() {
        Map<String, Object> arguments = new HashMap<>(2);
        // 綁定我們的死信交換機
        arguments.put("x-dead-letter-exchange", dlxExchange);
        // 綁定我們的路由key
        arguments.put("x-dead-letter-routing-key", dlxRoutingKey);
        return new Queue(orderQueue, true, false, false, arguments);
    }
    /**
     * 綁定訂單隊列到訂單交換機
     */
    @Bean
    public Binding orderBinding() {
        return BindingBuilder.bind(orderQueue()).to(orderExchange()).with(orderRoutingKey);
    }
    /**
     * 綁定死信隊列到死信交換機
     */
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with(dlxRoutingKey);
    }
}

2.application.properties文件配置 交換機隊列相關名稱和其他配置

server.port=8082
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#開啓駝峯命名  譬如數據庫create_time 自動映射pojo屬性createTime
mybatis.configuration.map-underscore-to-camel-case=true

#配置virtual-host虛擬主機
spring.rabbitmq.virtual-host=/zhang_rabbit
#ip地址
spring.rabbitmq.host=127.0.0.1
#用戶名  密碼
spring.rabbitmq.username=zhang
spring.rabbitmq.password=zhang
#連接端口號
spring.rabbitmq.port=5672
#spring.rabbitmq.publisher-confirm-type=

#模擬演示死信隊列
mayikt.dlx.exchange=mayikt_order_dlx_exchange
mayikt.dlx.queue=mayikt_order_dlx_queue
mayikt.dlx.routingKey=dlx

##備胎交換機
mayikt.order.exchange=mayikt_order_exchange
mayikt.order.queue=mayikt_order_queue
mayikt.order.routingKey=mayikt.order

3.創建producer生產者.暫時設置消息10秒過期,驗證消息是否加入死信隊列

@RestController
public class OrderController {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Value("${mayikt.order.exchange}")
    private String orderExchange; //訂單交換機

    @Value("${mayikt.order.routingKey}")
    private String orderRoutingKey; //訂單路由key

    @GetMapping("/addOrder")
    public String addOrder(){
        String orderId=System.currentTimeMillis()+"";
        OrderEntity orderEntity=new OrderEntity("訂單30分鐘過期",orderId,0);
        //訂單入庫
        int result= orderMapper.addOrder(orderEntity);
        if(result<=0){
            return "fail";
        }
        //rabbit投遞消息
    rabbitTemplate.convertAndSend(orderExchange,orderRoutingKey,orderId,messagePostProcessor());
        return "success";
    }

    //處理待發送消息
    private MessagePostProcessor messagePostProcessor(){
        return  new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //設置有效期30分鐘
                //message.getMessageProperties().setExpiration("1800000");
                message.getMessageProperties().setExpiration("10000");
                return message;
            }
        };
    }
}

4.死信消費者消費過期消息

@Component //死信隊列
public class OrderDlxConsumer {
    
    @Autowired
    private OrderMapper orderMapper;

    /**
     * 監聽我們的死信隊列
     */
    @RabbitListener(queues = "mayikt_order_dlx_queue")
    public void orderConsumer(String orderId) {
        System.out.println("死信隊列獲取消息:" + orderId);
        if (StringUtils.isEmpty(orderId)) {
            return;
        }
        //根據id查詢
        OrderEntity orderEntity = orderMapper.getOrder(orderId);
        if (null == orderEntity) {
            return;
        }
        //獲取狀態
        Integer orderStatus=orderEntity.getOrderStatus();
        //判斷未支付 , 關閉訂單
        if(0==orderStatus){
            orderMapper.updateStatus(orderId,2);
            //庫存返還
        }
    }
}

5.下單測試  http://localhost:8082/addOrder.訂單入庫成功.狀態0未支付

10秒後,死信消費者處理 狀態0未支付 變爲2 已關閉

 

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