RabbitMQ--延时队列

文章介绍了延时队列的概念及其应用场景,如订单自动取消等。在SpringBoot中使用RabbitMQ实现延时队列时,由于仅检查第一条消息是否过期,可能导致消息不按时处理的问题。为解决此问题,文章提出了使用RabbitMQ的死信队列和插件方式。详细阐述了如何配置队列、交换机、死信队列,并展示了生产者和消费者的代码示例,包括基于注解的方式和使用延迟消息插件的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概念

        延时队列就是死信队列中的TTL过期的一种。延时队列内部是有序的,延时队列中的元素是希望在指定时间到了以后或之前取出和处理。延时队列就是用来存放需要在指定时间被处理的元素的队列。

        应用场景:订单到时未支付自动取消、用户注册成功一定时间未登录提醒等。

       

        基于SpringBoot的延时队列可能会出现消息不按时过期的情况。RabbitMQ在检查消息时,只会检查第一条消息是否过期,如果过期则丢到死信队列,如果第一条消息的延时很长,第二条消息的延时很短,第二个消息也不会优先处理。解决办法用RabbitMQ插件

        

二、基于SpringBoot+注解的方式

1、代码架构图

        创建两个队列QA和QB,两者队列TTL分别设置为10s和40s,然后创建一个交换机X和一个死信交换机Y,他们类型都是direct,创建一个死信队列QD。                       

2、基于SpringBoot。依赖、配置文件。

         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
# rabiitmq 配置
spring:
  rabbitmq:
    host: 192.168.10.109
    port: 5672
    username: admin
    password: 123

3、配置类代码


    //信道A
    public static final String CHANNEL_A="CA";
    //信道B
    public static final String CHANNEL_B="CB";
    //信道死信
    public static final String CHANNEL_DIE="CDIE";
    //自定义时间
    public static final String CHANNEL_SELF="CSELF";

    //声明普通交换机
    @Bean("exchangeX")
    public DirectExchange exchangeX(){
       return new DirectExchange(NORMAL_EXCHANGE);
    }

    //声明死信交换机
    @Bean("exchangeY")
    public DirectExchange exchangeY(){
        return new DirectExchange(DIE_EXCHANGE);
    }

    //声明普通队列
    @Bean("queueA")
    public Queue queueA(){
        Map<String, Object> arguments =new HashMap<>();
        return QueueBuilder.durable(QUEUE_A)
//                //死信交换机
                .deadLetterExchange(DIE_EXCHANGE)
//                //死信routingkey
                .deadLetterRoutingKey(CHANNEL_DIE)
//                //设置ttl时间
                .ttl(2000)
                .build();
    }

    //声明普通队列
    @Bean("queueB")
    public Queue queueB(){
        return QueueBuilder.durable(QUEUE_B)
                //死信交换机
                .deadLetterExchange(DIE_EXCHANGE)
                //死信routingkey
                .deadLetterRoutingKey(CHANNEL_DIE)
                //设置ttl时间
                .ttl(5000)
                .build();
    }

    //声明死信队列
    @Bean("queueDie")
    public Queue queueDie(){
        return QueueBuilder.durable(QUEUE_DIE).build();
    }

    //声明自定义时间
    @Bean("queueSelf")
    public Queue queueSelf(){
        return QueueBuilder.durable(QUEUE_SELF)
                //死信交换机
                .deadLetterExchange(DIE_EXCHANGE)
                //死信routingkey
                .deadLetterRoutingKey(CHANNEL_DIE)
                .build();
    }

    //绑定A
    @Bean
    public Binding queueABindingX(@Qualifier("queueA")Queue queueA,
                                  @Qualifier("exchangeX")DirectExchange exchangeX){
        return BindingBuilder.bind(queueA).to(exchangeX).with(CHANNEL_A);
    }
    //绑定B
    @Bean
    public Binding queueBBindingX(@Qualifier("queueB")Queue queueA,
                                  @Qualifier("exchangeX")DirectExchange exchangeX){
        return BindingBuilder.bind(queueA).to(exchangeX).with(CHANNEL_B);
    }
    //绑定self
    @Bean
    public Binding queueSelfBindingX(@Qualifier("queueSelf")Queue queueA,
                                  @Qualifier("exchangeX")DirectExchange exchangeX){
        return BindingBuilder.bind(queueA).to(exchangeX).with(CHANNEL_SELF);
    }
    //绑定DIE
    @Bean
    public Binding queueDieBindingX(@Qualifier("queueDie")Queue queueA,
                                  @Qualifier("exchangeY")DirectExchange exchangeX){
        return BindingBuilder.bind(queueA).to(exchangeX).with(CHANNEL_DIE);
    }


}

4、生产者

@RestController
@RequestMapping("/product")
public class ProductController {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("/send")
    public String sendMsg(@RequestParam(value = "msg",required = true) String msg,
                        @RequestParam(value = "time",required = false)Integer time){
//        信道A
        System.out.println("信道A发送消息:"+msg);
        rabbitTemplate.convertAndSend(TtlConfig.NORMAL_EXCHANGE,TtlConfig.CHANNEL_A,"A的消息"+msg);
        //信道B
        System.out.println("信道B发送消息:"+msg);
        rabbitTemplate.convertAndSend(TtlConfig.NORMAL_EXCHANGE,TtlConfig.CHANNEL_B,"B的消息"+msg);
        if(null!=time && !"".equals(time)){
            //信道C
            System.out.println("信道自定义发送消息:"+msg+",时间:"+time+"秒");
            rabbitTemplate.convertAndSend(TtlConfig.NORMAL_EXCHANGE,TtlConfig.CHANNEL_SELF,"自定义的消息"+msg,message -> {
                //延时
                int i = time * 1000;
                message.getMessageProperties().setExpiration(String.valueOf(i));
                return message;
            });
        }
        return "发送成功";
    }
}

5、接收者

@Component
public class TtlConsumer {
    //接收消息
    @RabbitListener(queues = TtlConfig.QUEUE_DIE)
    public void getMsg(Message msg, Channel channel){
        String body = new String(msg.getBody());
        System.out.println(body);
    }
}

三、基于插件的RabbitMQ案例       

1、安装

        Community Plugins — RabbitMQ下载rabbitmq_delayed_message_exchange。

        放到/usr/lib/rabbitmq/lib/rabbitmq_server-3.11.3/plugins/目录中,

        执行命令:rabbitmq-plugins enable rabbitmq_delayed_message_exchange

        重启服务        

2、代码架构图

        

 3、配置类

        

//基于插件的延时队列
@Configuration
public class DelayConfig {
    //交换机
    public static final String DELAY_EXCHANGE="delayexchange";
    //队列
    public static final String DELAY_QUEUE="delayqueue";
    //routingkey
    public static final String DELAY_KEY="delaykey";

    //交换机
    @Bean
    public CustomExchange delayExchange(){
        Map<String, Object> arguments=new HashMap<>();
        arguments.put("x-delayed-type","direct");
        //1、交换机名称。2、交换机类型。3、是否持久化。4、是否需要自动删除。5、其他参数。
        return new CustomExchange(DELAY_EXCHANGE,"x-delayed-message",true,false,arguments);
    }

    //队列
    @Bean
    public Queue delayQueue(){
        return new Queue(DELAY_QUEUE);
    }

    //绑定
    @Bean
    public Binding delayBinding(@Qualifier("delayQueue")Queue queue,
                                @Qualifier("delayExchange") CustomExchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with(DELAY_KEY).noargs();
    }
}

4、生产者

@GetMapping("/delayMsg")
    public String delayMsg(@RequestParam(value = "msg") String msg,
                          @RequestParam(value = "time",required = false)Integer time){
        //信道C
        System.out.println("延时队列插件版消息:"+msg+",时间:"+time+"秒");
        rabbitTemplate.convertAndSend(DelayConfig.DELAY_EXCHANGE,DelayConfig.DELAY_KEY,"延时队列插件版消息:"+msg, message -> {
            //延时
            int i = time * 1000;
            message.getMessageProperties().setDelay(i);
            return message;
        });
        return "发送成功";
    }

5、接收消息

@Component
public class DelayConsumer {
    //接收消息
    @RabbitListener(queues = DelayConfig.DELAY_QUEUE)
    public void getDelayMsg(Message msg, Channel channel){
        String body = new String(msg.getBody());
        System.out.println(body);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值