rabbitmq实现延迟队列

延迟队列

延迟队列,顾名思义就是消息进入消息队列后不会被立即消费,而是等一段时间后消费,这个时间可以自己设定,比如用户登录5分钟后向其推送消息。

rabbitmq并未提供延时队列的直接实现,但是rabbitmq提供了死信队列和TTL(消息过期时间)的实现方式,我们可以借助死信队列+TTL到达实现延迟队列的目的。下面先实现死信队列和TTL。

实现TTL

这里使用springboot实现TTL,通过ttl方法设置队列过期时间,配置类:

@Configuration
public class RabbitmqConfig {
    public static final String  EXCHANGE = "springboot-exchange";

    public static final String QUEUE = "springboot-queue";

   
    /**
     * 这里使用topic模式
     */
    @Bean
    public Exchange topicExchange() {
        return ExchangeBuilder.topicExchange(EXCHANGE).durable(true).build();
    }

    @Bean
    public Queue itemQueue() {
    	//过期时间设置为10000毫秒
       return QueueBuilder.durable(QUEUE).ttl(10000).build();
    }

    @Bean
    public Binding itemQueueExchange(@Qualifier("itemQueue") Queue queue, @Qualifier("topicExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("item.#").noargs();
    }
}

也可以单独设置消息的过期时间,最终以时间短的为准。

 @Test
    void test01() {
        MessagePostProcessor postProcessor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //设置5秒后过期,队列是10秒后过期,消息过期时间比队列过期时间短,所以消息最终是5秒后过期
                message.getMessageProperties().setExpiration("5000");
                return message;
            }
        };
        rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANGE, "item.insert", "商品新增, routing key为item.insert");
    }

实现死信队列

当消息成为死信后,可以被重新发送到另一个交换机,与该交换机绑定的消息队列可以暂时存放该死信消息。一个消息成为死信可能是由于 队列消息长度到达限制, 消费者拒接消费消息,并且没把消息重新放入原目标队列,或者原队列消息到达超时时间还未被消费。

队列绑定死信交换机,只需要给队列设置参数: x-dead-letter-exchange 和 x-dead-letter-routing-key,这里使用springboot实现死信队列。

配置类:

@Configuration
public class RabbitmqConfig {
    public static String DEAD_EXCHANGE = "dead-exchange";

    public static String DEAD_QUEUE = "dead-queue";

    public static String NORMAL_EXCHANGE = "normal-exchange";

    public static String NORMAL_QUEUE = "normal_queue";
    /**
     * 配置死信交换机
     */
    @Bean
    public Exchange deadExchange() {
        return ExchangeBuilder.directExchange(DEAD_EXCHANGE).durable(true).build();
    }

    /**
     * 配置死信队列
     */
    @Bean
    public Queue deadQueue() {
        return QueueBuilder.durable(DEAD_QUEUE).build();
    }

    /**
     * 死信交换机与死信队列绑定
     */
    @Bean
    public Binding deadExchangeQueue(@Qualifier("deadQueue") Queue queue, @Qualifier("deadExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("dead").noargs();
    }

    /**
     * 配置正常交换机
     */
    @Bean
    public Exchange normalExchange() {
        return ExchangeBuilder.directExchange(NORMAL_EXCHANGE).durable(true).build();
    }

    /**
     * 配置正常队列
     */
    @Bean
    public Queue normalQueue() {
        // 设置过期时间和队列长度,并且设置 x-dead-letter-exchange 和 x-dead-letter-routing-key参数
        Map<String, Object> map = new HashMap();
        map.put("x-dead-letter-exchange", DEAD_EXCHANGE);
        map.put("x-dead-letter-routing-key", "dead");
        return QueueBuilder.durable(NORMAL_QUEUE).ttl(10000).maxLength(10).withArguments(map).build();
    }

    /**
     * 队列与交换机绑定
     */
    @Bean
    public Binding normalExchangeQueue(@Qualifier("normalQueue") Queue queue, @Qualifier("normalExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("normal").noargs();
    }
}

测试:

    @Test
    public void testDlx() {
        //1. 测试消息拒收,死信消息,需要在消费端拒收
        //rabbitTemplate.convertAndSend(RabbitmqConfig.NORMAL_EXCHANGE,"normal","我是一条消息,我会死吗?");
        //2. 测试长度限制后,消息死信
//        for (int i = 0; i < 20; i++) {
//            rabbitTemplate.convertAndSend(RabbitmqConfig.NORMAL_EXCHANGE, "normal", "我是一条消息,我会死吗?");
//        }
        //3. 测试过期时间,死信消息
        rabbitTemplate.convertAndSend(RabbitmqConfig.NORMAL_EXCHANGE,"normal","我是一条消息,我会死吗?");
    }

实现延迟队列

利用TTL+死信队列就可以轻松实现延迟队列了,正常队列设置过期时间,过期后放到死信队列,监听该死信队列的消费者便可消费。

上面配置好的死信队列就可以用来做延迟队列,使用测试中的第三个,即:

 		//3. 测试过期时间,死信消息
        rabbitTemplate.convertAndSend(RabbitmqConfig.NORMAL_EXCHANGE,"normal","我是一条消息,我会死吗?");

该消息过期后,便会转到死信队列,消费端监听该死信队列:

@Component
public class MyListener implements ChannelAwareMessageListener {
    @Override
    @RabbitListener(queues = "dead-queue")
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            System.out.println("message: " + message);
            channel.basicAck(deliveryTag, true);
        } catch (Exception e) {
            e.printStackTrace();
            channel.basicNack(deliveryTag, true, true);
        }
        Thread.sleep(1000);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值