• Springboot+死信实现RabbitMQ延迟队列


    原理


      生产者把带有 ttl(Time-To-Live过期时间) 的消息发送到一个临时队列(DelayQueue),该队列没有消费者;

      该消息在DelayQueue中停留直至过期,同时该消息没有ReQueue(重新入队),就变成了死信(Dead-letter或Dead-message),死信自动地被发送给了配置好的DLX(Dead-Letter-Exchange);

      DLX根据路由规则把消息路由到了配置好的队列中(DeadLetterQueue),队列中的消息被消费者消费。

    maven依赖

    引入amqp的依赖, 生产者和消费者都需要

    <!--amqp 适用rabbitmq-->
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    

    boostrap配置

    同样,生产者和消费者都需要
    properties.yaml:

    spring:
      rabbitmq:
        host: localhost
        port: 5672
        username: admin
        password: 123456
    

    使用配置类创建各组件

    rabbitmq的基本的组件是Exchange(交换机)、Queue(队列)、Binding(绑定)。实现延时队列需要定义如下组件:

    1. 一个带ttl的Queue临时队列;
    2. 一个普通的用于业务的Queue,即死信队列;
    3. 定义一个Exchange,即死信交换器DLX(Dead-Letter-Exchange);
    4. 再定义一个Bingding把死信队列和死信交换器绑定在一起。
    import org.springframework.amqp.core.*;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class DelayQueueConfig {
    
        /**为了更贴合业务,参数名不使用DeadQueue之类的*/
        /**延迟队列名*/
        private static String DELAY_QUEUE = "delay.queue";
        /**延迟队列(死信队列)交换器名*/
        private static String DELAY_EXCHANGE = "delay.exchange";
        /**处理业务的队列(死信队列)*/
        private static String PROCESS_QUEUE = "process.queue";
        /**ttl(10秒)*/
        private static int DELAY_EXPIRATION = 10000;
    
        /**
        * 创建延迟队列
        * "x-dead-letter-exchange"参数定义死信队列交换机
        * "x-dead-letter-routing-key"定义死信队列中的消息重定向时的routing-key
        * "x-message-ttl"定义消息的过期时间
        */
        @Bean
        public Queue delayQueue(){
            return QueueBuilder.durable(DELAY_QUEUE)
                    .withArgument("x-dead-letter-exchange", DELAY_EXCHANGE)
                    .withArgument("x-dead-letter-routing-key", PROCESS_QUEUE)
                    .withArgument("x-message-ttl", DELAY_EXPIRATION)
                    .build();
        }
    
        /**创建用于业务的队列*/
        @Bean
        public Queue processQueue(){
            return QueueBuilder.durable(PROCESS_QUEUE)
                    .build();
        }
    
        /**创建一个DirectExchange*/
        @Bean
        public DirectExchange delayExchange(){
            return new DirectExchange(DELAY_EXCHANGE);
        }
    
        /**绑定Exchange和queue,把消息重定向到业务queue*/
        @Bean
        Binding dlxBinding(DirectExchange directExchange, Queue processQueue){
            return BindingBuilder.bind(processQueue)
                    .to(directExchange)
                    .with(PROCESS_QUEUE);
                    //绑定,以PROCESS_QUEUE为routing key
        }
    }
    

    生产者创建发送消息的组件

    @Component
    public class MessageSender {
        @Autowired
        private AmqpTemplate amqpTemplate;
    
        public void send(String routingKey, String msg) {
            //amqpTemplate.convertAndSend("process.queue", msg);
            amqpTemplate.convertAndSend(routingKey, msg);
        }
    }
    

    消费者自动接收并处理消息的组件

    @Component
    //注意监听的Queue是用于业务的ProcessQueue, 而不是临时存放消息的DelayQueue
    @RabbitListener(queues = "process.queue")
    public class MessageReceiver {
    
        @RabbitHandler()
        public void doSth(String msg) {
            //TODO
        }
    }
    

    拓展

    该方案存在的缺点:消息会堆积在队列中,如果队列已满,新的消息会变为死信,会直接重发送到死信队列,此时就没有“延迟”的效果。

  • 相关阅读:
    hdu 1568 Fibonacci
    hdu 1286 找新朋友
    mysql错误之2014
    mysql查看语句执行状态的常见函数
    mysql里制造一个错误
    css对html中表格单元格td文本过长的处理
    写js时常见错误
    DOM中的节点属性
    button的默认type居然是submit
    ubuntu手贱改了sudoers权限之后的恢复
  • 原文地址:https://www.cnblogs.com/life-of-coding/p/13222443.html
Copyright © 2020-2023  润新知