Bootstrap

Ribbitmq使用TTL+死信队列实现延时队列

TTL+死信队列实现延时队列:正常消息过期没有被消费掉,进入死信队列后立即消费。

1.pom.xml引用rabbitMQ

        <!--rabbitMQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

2.application.yml引入rabbitMQ

  rabbitmq:
    port: 5672
    host: localhost
    username: admin
    password: wdj123456
    listener:
      direct:
        acknowledge-mode: manual
      simple:
        acknowledge-mode: manual
        retry:
          enabled: true
          max-attempts: 5
          initial-interval: 3000
    virtual-host: newCore
    publisher-confirm-type: correlated
    publisher-returns: true

3.实现需求:到拼餐时间,没有任何用户参与进来,共享订单取消

    RabbitMQ延时消息配置类

package com.hxnwm.ny.diancan.common.config;

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;

/**
 * RabbitMQ延时消息配置
 *
 * @time 2022/3/11 11:40
 */
@Configuration
public class RabbitMQLazyConfig {

    // ----- 共享订单未拼单成功 wdj-----

    /**
     * 共享订单立即消费队列
     */
    public static final String ACT_SHAREORDER_QUEUE="act.shareOrder.queue";

    /**
     * 共享订单死信消费队列
     */
    public static final String ACT_SHAREORDER_DELAY_QUEUE="act.shareOrder.delay.queue";

    /**
     * 共享订单交换器
     */
    public static final String ACT_SHAREORDER_EXCHANGE="act.shareOrder.exchange";

    /**
     * 共享订单死信交换器
     */
    public static final String ACT_SHAREORDER_DEAD_EXCHANGE="act.shareOrder.dead.exchange";

    /**
     * 共享订单立即消费路由键
     */
    public static final String ACT_SHAREORDER_ROUTING_KEY = "act.shareOrder.routing.key";

    /**
     * 共享订单延迟路由键
     */
    public static final String ACT_SHAREORDER_DELAY_ROUTING_KEY = "act.shareOrder.delay.routing.key";

    /**
     * 共享订单立即消费队列
     */
    @Bean
    public Queue actShareOrderQueue() {
        return new Queue(ACT_SHAREORDER_QUEUE, true);
    }

    /**
     * 延迟队列: 创建一个延迟队列, 此队列中的消息没有消费者去消费, 到了过期时间之后变成死信, 变死信之后会根据
     * 绑定的DLX和routingKey重新发送到指定交换机再到指定队列。
     */
    @Bean
    public Queue delayShareOrderQueue() {
        Map<String, Object> map = new HashMap<>();
        map.put("x-dead-letter-exchange", ACT_SHAREORDER_EXCHANGE);
        map.put("x-dead-letter-routing-key", ACT_SHAREORDER_ROUTING_KEY);
        return new Queue(ACT_SHAREORDER_DELAY_QUEUE, true, false, false, map);
    }

    /**
     * 声明立即消费队列交换机-direct类型
     * 注:把消息投递到那些binding key与routing key完全匹配的队列中。
     */
    @Bean
    public DirectExchange actShareOrderExchange() {
        return new DirectExchange(ACT_SHAREORDER_EXCHANGE, true, false);
    }

    /**
     * 声明死信队列交换机-direct类型
     */
    @Bean
    public DirectExchange deadShareOrderLetterExchange() {
        return new DirectExchange(ACT_SHAREORDER_DEAD_EXCHANGE, true, false);
    }

    /**
     * 把立即消费的队列和立即消费交换机绑定, immediate_exchange, 路由键:immediate_routing_key
     */
    @Bean
    public Binding actShareOrderBinding() {
        return BindingBuilder.bind(actShareOrderQueue()).to(actShareOrderExchange()).with(ACT_SHAREORDER_ROUTING_KEY);
    }

    /**
     * 死信队列的交换机绑定:把延迟消费的队列和死信交换机绑定, immediate_dead_exchange, 路由键:delay_routing_key
     */
    @Bean
    public Binding queueShareOrderDeadBinding() {
        return BindingBuilder.bind(delayShareOrderQueue()).to(deadShareOrderLetterExchange()).with(ACT_SHAREORDER_DELAY_ROUTING_KEY);
    }

}

4.发送消息工具类

 /**
 * 订单工具类
 *
 * @author knight
 * @time 2022/3/11 11:42
 */
@Component
@Slf4j
//设置scope为:prototype
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class OrderUtils {

    @Resource
    private AmqpTemplate amqpTemplate;

   /**
     *到拼餐时间,没有任何用户参与进来,共享订单取消
     *
     * @param shareOrder
     * @return void
     * @Author wdj
     * @date 2023/3/21 16:23
     */
    public void delayCancelShareOrder(ShareOrder shareOrder, long millisecond) {
        this.amqpTemplate.convertAndSend(RabbitMQLazyConfig.ACT_SHAREORDER_DEAD_EXCHANGE, RabbitMQLazyConfig.ACT_SHAREORDER_DELAY_ROUTING_KEY, shareOrder, message -> {
            //给消息设置延迟毫秒值
            message.getMessageProperties().setExpiration(String.valueOf(millisecond));
            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            return message;
        });
        log.info("把共享订单{}取消,发送到rabbitmq队列中", shareOrder.getShareOrderSn());
    }

}

5.业务层:创建订单时,加入延时队列,设置消息过期时间

        //将共享用餐订单添加到延时对列中
        this.orderUtils.delayCancelShareOrder(shareOrder,(shareOrder.getShareTime()-now)*1000);

6.消息监听,并且消费

package com.hxnwm.ny.diancan.common.listener;
/**
 * 延时任务监听
 *
 * @time 2022/3/11 10:40
 */
@Slf4j
@Component
public class LazyOrderListener {

    @Resource
    private OrderService orderService;

    @Resource
    private OrderManage orderManage;

    /**
     * 监听共享用餐成功后如果到了拼餐时间,还没有任何用户参与进来,自动给此共享订单详情页状态变为已取消状态
     *
     * @author wdj
     * @time 2023/3/21 10:48
     */
    @RabbitListener(queues = RabbitMQLazyConfig.ACT_SHAREORDER_QUEUE)
    @Transactional(rollbackFor = Exception.class)
    public void listenDelayCancelShareOrder(ShareOrder shareOrder1) {
        log.info("监听到共享订单shareOrderSn:{} ", shareOrder1.getShareOrderSn());
        try {
 
            log.info("监听共享订单处理成功");
        }catch (Exception e){
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

}

;