rabbitmq实现延时队列(死信队列、延时插件)

前言
rabbitmq本身是没有实现延时队列的,但是我们可以通过死信队列或延时插件来实现延时队列。

死信队列

什么是死信?

如果在队列中的消息没有消费者消费,那么该消息就会成为一个死信。如果这个消息被发送到另外一个exchange的话,那么后面的exchange就是死信队列

消息变成死信的几种情况

  • 消息被消费者拒绝(basic.reject/ basic.nack),且requeue属性设置为false,即不重回队列
  • 消息过期:消息设置了过期时间,过期后成为死信
  • 队列达到最大长度,放不下了,溢出的消息将被删除或送到死信队列上

死信队列也是一个正常的exchange,也可以通过routingkey绑定到具体的队列上。

死信队列实现延时队列原理

在上面我们知道一个消息过期后会成为死信,而死信可以被转发到另外一个exchange也就是死信队列。死信队列也是一个正常的exchange,它可以将消息发送到绑定的队列上,该队列被消费者消费监听。

举个生活中的栗子,我们买东西下单后,一般会要求我们半小时内支付。半小时后,如果没有支付将取消订单,换句话说,就是我们需要在半小时后去检查订单的状态。那么如何用死信队列实现该功能?

流程图如下:
在这里插入图片描述
上图中,生产者下单后,发送消息到orderQueue,30分钟后,消息后期,将被发送到dlxQueue中,此时消费者监听到消息,去查看订单的状态,如果订单没有支付,取消订单,回滚库存。

代码实现

知道了理论后,接下来就是代码实践了,这里采用SpringBoot实现:

生产者

1、rabbitmq配置类,声明交换机和队列

@Configuration
public class DlxRabbitmqConfig {
	//====================1.声明正常队列====================
    @Bean
    public DirectExchange orderExchange(){
        return new DirectExchange("springboot.orderExchange", true, false);
    }

    @Bean
    public Queue orderQueue(){
        Map<String, Object> queueArgs = new HashMap<>();
        //10秒中后消息过期
        queueArgs.put("x-message-ttl",10000);
        //过期后,将消息转发给"springboot.OrderDlxExchange"交换机
        queueArgs.put("x-dead-letter-exchange","springboot.OrderDlxExchange");

        return new Queue("springboot.orderQueue",true,false,false,queueArgs);
    }

    @Bean
    public Binding orderBinding(){
        return BindingBuilder.bind(orderQueue()).to(orderExchange()).with("order.key");
    }

	//====================2.声明死信队列====================
    @Bean
    public DirectExchange OrderDlxExchange(){
        return new DirectExchange("springboot.OrderDlxExchange",true,false);
    }

    @Bean
    public Queue orderDlxQueue(){
        return new Queue("springboot.orderDlxQueue",true,false,false);
    }

    @Bean
    public Binding dlxBinding(){
        return BindingBuilder.bind(orderDlxQueue()).to(OrderDlxExchange()).with("order.key");
    }
}

2、订单实体类

@Data
@ToString
public class Order implements Serializable {
    private String orderNo;

    private Date createDt;

    private double payMoney;

    private String userName;
}

3、发送消息组件

@Component
public class MsgSender {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    //利用死信队列实现延时消息的发送
    public void sendMsgDlx(Object order , Map<String,Object> msgProp) throws JsonProcessingException {
        //1. 构建消息头
        MessageHeaders messageHeaders = new MessageHeaders(msgProp);

        //2. 构建消息体
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(order);

        Message message = MessageBuilder.createMessage(json.getBytes(), messageHeaders);
        
 		//3. 构建correlationData 用于做可靠性投递得,ID:必须为全局唯一的 根据业务规则
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());

        //4. 开启确认模式
        rabbitTemplate.setConfirmCallback(new MyConfirmCallBack());

        //5. 开启消息可达监听
        rabbitTemplate.setReturnCallback(new MyReturnCallback());
        
        //6. 发送消息
  		rabbitTemplate.convertAndSend("springboot.orderExchange","order.key",message,correlationData);
    }
  }

4、测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqProductTest {
    @Autowired
    private MsgSender msgSender;
  @Test
    public void testSendMsgDlx() throws JsonProcessingException {
        Order order = new Order();
        order.setOrderNo(UUID.randomUUID().toString());
        order.setUserName("fcp");
        order.setPayMoney(10000.00);
        order.setCreateDt(new Date());

        msgSender.sendMsgDlx(order,null);
    }
  }

代码已经上传到码云上https://gitee.com/F_promise/rabbitmq,生产者的代码在springboot-rabbitmq-producer模块上。另外代码中设计了消息确认机制,这我会在可靠性投递中讲到。

消费者

@Component
@Slf4j
public class MsgReceiver {
    //监听订单的死信队列
    @RabbitListener(queues = {"springboot.orderDlxQueue"})
    @RabbitHandler
    public void consumerOrderMsg(Message message, Channel channel) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        Order order = objectMapper.readValue((byte[]) message.getPayload(), Order.class);
        log.info("消费消息:{}",order);

        //手工签收
        Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
        log.info("接受deliveryTag:{}",deliveryTag);
        channel.basicAck(deliveryTag, false);
    }
}

代码已上传到码云,在springboot-rabbitmq-consumer模块上

延时插件实现延时队列

下载插件

插件地址:https://www.rabbitmq.com/community-plugins.html
在这里插入图片描述
下载与自己版本一致的插件后,解压到指定的目录下:

cp rabbitmq_delayed_message_exchange-20171215-3.6.x.ez /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/plugins

在这里插入图片描述
启动延时插件:rabbitmq-plugins enable rabbitmq_delayed_message_exchange

在这里插入图片描述

代码实现

生产者

1、配置类(声明延迟队列)

@Configuration
public class DelayRabbitmqConfig {
    @Bean
    public CustomExchange OrderDelayExchange(){
        HashMap<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange("orderDelayExchange", "x-delayed-message",true, false,args);
    }

    @Bean
    public Queue OrderDelayQueue(){
        return new Queue("orderDelayQueue",true,false,false);
    }

    @Bean
    public Binding orderDelayBinding(){
        return BindingBuilder.bind(OrderDelayQueue()).to(OrderDelayExchange()).with("order.key").noargs();
    }
}

2、发送消息组件

@Component
public class MsgSender {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
  	//利用插件发送延迟消息
    public void sendDelayMsg(Object order , Map<String,Object> msgProp) throws JsonProcessingException {
        //1. 构建消息头
        MessageHeaders messageHeaders = new MessageHeaders(msgProp);

        //2. 设置消息转换器
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());

        //3. 构建correlationData 用于做可靠性投递得,ID:必须为全局唯一的 根据业务规则
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());

        //4. 开启确认模式
        rabbitTemplate.setConfirmCallback(new MyConfirmCallBack());

        //5. 开启消息可达监听
        rabbitTemplate.setReturnCallback(new MyReturnCallback());

        //6. 发送消息(这里是关键)
        rabbitTemplate.convertAndSend("orderDelayExchange", "order.key",order, new MessagePostProcessor() {
            @Override
            public org.springframework.amqp.core.Message postProcessMessage(org.springframework.amqp.core.Message message) throws AmqpException {
                message.getMessageProperties().setDelay(5000);  //设置延迟时间
                return message;
            }
        },correlationData);
    }
}

3、测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqProductTest {
 	@Test
    public void testSendDelayMsg() throws JsonProcessingException {
        Order order = new Order();
        order.setOrderNo(UUID.randomUUID().toString());
        order.setUserName("fcp");
        order.setPayMoney(10000.00);
        order.setCreateDt(new Date());

        msgSender.sendDelayMsg(order,null);
    }
}

消费者代码不变

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值