RabbitMQ 详解(三)------消息过期、死信、延迟和流控

一.TTL (消息的过期时间)

1.两种设置方式:

(1)在生产者中通过队列属性设置消息过期时间

      所有队列中的消息超过时间未被消费时,都会过期。

       Map<String, Object> argss = new HashMap<String, Object>();
        argss.put("x-message-ttl",6000);
        //在生产者中声明队列
        channel.queueDeclare("队列名", false, false, false, argss);

  (2)在生产者中消息属性设置消息的过期时间

​
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .deliveryMode(2) // 持久化消息
                .contentEncoding("UTF-8")
                .expiration("10000") // TTL
                .build();
 channel.basicPublish("", "队列名", properties, msg.getBytes());

​

      如果同时指定了 Message TTL 和 Queue TTL,则小的那个时间生效。

二.死信队列

 1.什么情况下消息会变成死信?
   1)消息被消费者拒绝并且未设置重回队列:(NACK || Reject ) && requeue== false
   2)消息过期
   3)队列达到最大长度,超过了最大消息数或字节数,最先入队的消息会被发送到死信交换机。

2.死信队列如何使用?

1)生产者发送消息通过原交换机到原队列。

2)声明原队列,并指定原队列的死信交换机。队列中的过期消息,因为没有消费者,会变成死信进入到死信交换机中。

3)声明死信交换机、死信队列,并通过#绑定(无条件路由)

4)消费者监听死信队列。

      生产者

  
String msg = "Hello world, Rabbit MQ, DLX MSG";

        // 设置属性,消息10秒钟过期
        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .deliveryMode(2) // 持久化消息
                .contentEncoding("UTF-8")
                .expiration("10000") // TTL
                .build();

        // 发送消息
        channel.basicPublish("", "TEST_QUEUE", properties, msg.getBytes());

   消费者


  
   // 设置队列的死信交换机
   Map<String,Object> arguments = new HashMap<String,Object>();
  arguments.put("x-dead-letter-exchange","DEAD_EXCHANGE");
    // arguments.put("x-expires",9000L); // 设置队列的TTL
   // arguments.put("x-max-length", 4); // 如果设置了队列的最大长度

   // 声明队列(默认交换机AMQP default,Direct)
   channel.queueDeclare("TEST_QUEUE", false, false, false, arguments);

        // 声明死信交换机
   channel.exchangeDeclare("DEAD_EXCHANGE","topic", false, false, false, null);

        // 声明死信队列
   channel.queueDeclare("DEAD_QUEUE", false, false, false, null);
       
       // 绑定,此处路由设置为 #
   channel.queueBind("DEAD_QUEUE","DEAD_EXCHANGE","#");
       
   //监听死信队列,并消费
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("Received message : '"+ new Date() + msg + "'");
            }
        };
        
        channel.basicConsume("DEAD_QUEUE", true, consumer);

 

  3.使用死信队列就可以实现消息延时需考虑到以下问题:

       1)通过设置队列实现消息过期,在面对多种过期时间时需创建多个交换机

       2)通过设置设置消息实现消息过期,由于队列中先进先出,可能造成消息阻塞。(前一天消息还未过期,后一条消息已过期,但由于第一条还未出队,所以无法投递)

       3)可能存在一定的时间误差

       如果使用场景需避免以上问题,可考虑下载延迟队列插件。

三.延迟队列

       1.下载安装

           1)下载地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases

           2)将文件复制到你RabbitMQ的安装目录下的plugins文件夹下

           3)在\sbin目录下运行命令,启用插件: rabbitmq-plugins enable rabbitmq_delayed_message_exchange ;停用插件:rabbitmq-plugins disable rabbitmq_delayed_message_exchange

       2.使用(可类比死信队列)

          1)用x-delayed-message声明延时交换机,然后声明一个队列,将队列与交换机绑定


    @Bean("delayExchange")
    public TopicExchange exchange() {
        Map<String, Object> argss = new HashMap<String, Object>();
        argss.put("x-delayed-type", "direct");
        return new TopicExchange("DELAY_EXCHANGE", true, false, argss);
    }

    @Bean("delayQueue")
    public Queue deadLetterQueue() {
        return new Queue("DELAY_QUEUE", true, false, false, new HashMap<>());
    }

    @Bean
    public Binding bindingDead(@Qualifier("delayQueue") Queue queue, @Qualifier("delayExchange") TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("#"); // 无条件路由
    }

           2)生产者设置消息过期,并发送到延时交换机

 Map<String, Object> headers = new HashMap<String, Object>();
        headers.put("x-delay", delayTime.getTime() - now.getTime());
        AMQP.BasicProperties.Builder props = new AMQP.BasicProperties.Builder().headers(headers);
        channel.basicPublish("DELAY_EXCHANGE", "DELAY_KEY", props.build(), msg.getBytes());

          3)消费者通过通道从延时交换机正常获取消息

  Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                System.out.println("收到消息:[" + msg + "]\n接收时间:" +sf.format(new Date()));
            }
        };
        // 开始获取消息
        // String queue, boolean autoAck, Consumer callback
        channel.basicConsume("DELAY_QUEUE", true, consumer);

四.服务端流控

        队列有两个控制长度的属性:x-max-length:队列中最大存储最大消息数,超过这个数量,队头的消息会被丢弃。x-max-length-bytes:队列中存储的最大消息容量,超过这个容量,队头的消息会被丢弃。通过设置队列长度在消息堆积的情况下会删除先入队的消息。但如何在消息的产生速度远大于消费速度的情况下实现服务限流呢。

         1.内存控制

       RabbitMQ 会在启动时检测机器的物理内存数值。默认当 MQ 占用 40% 以上内存时,MQ 会主动抛出一个内存警告并阻塞所有连接。可以通过修改rabbitmq.config 文件来调整内存阈值,默认值是 0.4,如下所示: ​{vm_memory_high_watermark, 0.4}。如果设置成0,则所有消息都不能发布。

          2.磁盘控制

         通过磁盘来控制消息的发布。当磁盘空间低于指定的值时(默认50MB),触发流控措施。

         disk_free_limit.relative = 3.0
         disk_free_limit.absolute = 2GB

五.消费端限流

      默认情况下,如果不进行配置,RabbitMQ会尽可能快速地把队列中的消息发送消费者,在消费者处理消息的能力有限:消费者数量太少,或者单条消息的处理时间过长。我们需要在一定数量的消息消费完之前,不再推送消息过来,就要对消费端进行流量限制措施,设置消费端最大的可缓存消息数目,超过这个数值的消息不被确认,RabbitMQ会停止投递新的消息给消费者。

第一种:基于channel设置

channel.basicQos(2); // 如果超过 2 条消息没有发送 ACK,当前消费者不再接受队列消息
channel.basicConsume(QUEUE_NAME, false, consumer);

第二种:Spring Boot 配置:

spring.rabbitmq.listener.simple.prefetch=2

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值