RabbitMQ-高级特性:消费端限流

294 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第21天,点击查看活动详情

RabbitMQ-高级特性:消费端限流

MQ有一个非常重要的作用:削峰填谷

如果请求并发量特别大会让系统宕机,这时我们把并发量全部存到MQ中,然后再每秒从MQ中拉取一些请求进行处理,保证了系统的安全性,从MQ中拉的请求数就是所谓的限流处理,在消费消息的时候做了限流操作

还有一种业务场景,如果系统有一天要维护,由于生产者一直在发消息,但是系统在维护,那么等系统维护好了之后,所有生产者生产的消息会一直堆积在MQ中,那么等系统维护好了之后,之前生产的消息一次性打到系统,系统也会宕机,所以为了保证系统稳定性,做限流是非常有必要性的!

实现方式

1 确保ack的机制为手动确认(只有手动确认才会生效) acknowledge="manual"

2 配置属性 在配置文件监听listener-container中配置属性prefetch=100

循环发送消息

注释签收代码

没有限流数的情况下会全部消费完全的消息

相关代码

配置xml

 <? xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"

 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

 xmlns:context="http://www.springframework.org/schema/context"

 xmlns:rabbit="http://www.springframework.org/schema/rabbit"

 xsi:schemaLocation="http://www.springframework.org/schema/beans

 http://www.springframework.org/schema/beans/spring-beans.xsd

 http://www.springframework.org/schema/context

 https://www.springframework.org/schema/context/spring-context.xsd

 http://www.springframework.org/schema/rabbit

 http://www.springframework.org/schema/rabbit/spring-rabbit.xsd" >

    <!--加载配置文件-->

 <context:property-placeholder location="classpath:application.properties" />



    <!-- 定义rabbitmq connectionFactory -->

 <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"

 port="${rabbitmq.port}"

 username="${rabbitmq.username}"

 password="${rabbitmq.password}"

 virtual-host="${rabbitmq.virtual-host}"



 />



    <!--配置监听器bean 扫描这个包的路径 配置类需要加注解-->

 <context:component-scan base-package="com.wyh.listener" />

    <!--定义监听器    acknowledge="" 设置签收方式 -->

 <rabbit:listener-container  connection-factory="connectionFactory"  acknowledge="manual" >

        <!--加载对应监听器的类 具体的类名和监听的队列名-->

 <rabbit:listener ref="qasCKListener" queue-names="test_queue_confirm" />

 <!--

 <rabbit:listener ref="rabbitMQACKListener" queue-names="test_queue_confirm"  />

-->

 </rabbit:listener-container>



</beans>

生产者生产消息



package com.producer.test;



import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.amqp.core.Message;

import org.springframework.amqp.rabbit.connection.CorrelationData;

import org.springframework.amqp.rabbit.core.RabbitTemplate;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;



 /**

 *  @prog ram: SpringBoot_RabbitMQ_Advanced

 *  @description:  测试确认模式消息是否发送成功

 *  @author:  魏一鹤

 *  @createDate:  2022-04-04 23:10

 **/



 //spring配置文件

@RunWith(SpringJUnit4ClassRunner.class)

 //加载文件的路径

@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml" )

public class ProducerTest {

    //注入 RabbitTemplate

 @Autowired

    private RabbitTemplate rabbitTemplate;



    //专门测试限流循环发送消息

 @Test

    public void testSend() {

        for (int i = 0; i < 10; i++) {

            //发送消息

  rabbitTemplate.convertAndSend( "test_exchange_confirm" , "confirm" , "message confirm..." );

        }

    }

}

消费者限流消费监听

package com.wyh.listener;



import com.rabbitmq.client.Channel;

import org.springframework.amqp.core.Message;

import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;

import org.springframework.stereotype.Component;



import java.util.concurrent.TimeUnit;



 /**

 *  @program:  SpringBoot_RabbitMQ_Advanced

 *  @description:  RabbitMQ QAS消费端限流

 *  @author:  魏一鹤

 *  @createDate:  2022-04-06 20:30

 **/





 /**

 * Consumer消费端限流机制

 *  1 确保ack的机制为手动确认(只有手动确认才会生效) acknowledge="manual"

 *  2 配置属性 在配置文件监听listener-container中配置属性prefetch=100

 *  prefetch=100 表示消费端每次从mq拉取100条消息来消费 直到它手动确认消费完毕后,才会继续拉取下一条消息

 **/



 //包扫描注解 把bean加载到spring容器中

@Component

 //实现MessageListener接口并重写onMessage方法

public class QasCKListener implements ChannelAwareMessageListener {





    @Override

    public void onMessage(Message message, Channel channel) throws Exception {

        TimeUnit.SECONDS.sleep(1);

        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        //接收并且打印消息

 System.out.println(new String(message.getBody()));

        //处理业务逻辑



 //签收

 channel.basicAck(deliveryTag,true);

    }

}

消费限流小结

1 在<rabbit:listener-container>配置acknowledge="manual"(手动确认) prefetch="5"(限流数.设置消费端一次拉去多少消息)
2 消费端的确认模式一定改为手动确认acknowledge="manual"(手动确认),因为只有手动确认玩之后,才能继续拉取