【RabbitMQ】消费端限流(Prefetch)

消息中间件有一个重要的功能是削峰填谷,在访问量高的时候,可以限制消费端消费消息的数量,当访问高峰期过去以后,消费端再慢慢消费队列中的消息。
在RabbitMQ中,要实现削峰填谷的功能,是通过消费端限流实现的。
在消费端中设置 prefetch=n ,表示一次从队列中获取 n 条消息。其余消息存放在队列中。

做个小实验:
当不设置 prefetch 时,生产者一次性向交换机中投递10条消息,消费者确认消息的方式设置为手动确认,在确认消息之前线程休眠5秒(因为管控台数据更新是5秒更新一次,这里设置为5秒比较方便观察),可以观察到队列是一次性将10条数据全部发送给了消费者,然后消费者再进行处理:

生产者一次发送10条消息到交换机:

public void test02(){
	for(int i=0;i<10;i++){
		rabbitTemplate.convertAndSend(ProducerConfig.FANOUT_EXCHANGE,ProducerConfig.FANOUT_QUEUE_01,"消息0"+i);
	}
}

消费者在确认之前休眠5秒:

@Component
public class MyListener {
    @RabbitListener(queues = "springboot_fanout_queue_01")
    public void myListener(Message message, Channel channel) throws IOException, InterruptedException {
        Thread.sleep(5000);
        System.out.println("收到的消息是:"+new String(message.getBody(),"utf-8"));
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
    }
}

管控台的数据如下:
消费者还没开始接收消息:
【RabbitMQ】消费端限流(Prefetch)_第1张图片
消费者开始接收消息:
【RabbitMQ】消费端限流(Prefetch)_第2张图片
从管控台中可以看到,一开始ready的数量是10,unacked为0,当消费者开始接收消息后,ready数量变为0,unacked数量变为了10,说明队列一次性将消息发送给了消费者。如果在访问量很大并且持续有消息进入队列的情况下,队列会将所有消息发送给消费者,导致消费者资源占用过多,可能会引起消费者服务器宕机。
所以消费者需要设置prefetch进行消费端限流。

现在把消费者的prefetch设置为2,就是一次从队列中取两条数据,再观察结果:

消费者开始消费消息前:
【RabbitMQ】消费端限流(Prefetch)_第3张图片
消费者开始消费消息:
【RabbitMQ】消费端限流(Prefetch)_第4张图片
可以看到消息不是一次性从队列中获取的,而是根据prefetch设置的数量获取的。这样即使在高峰期,消费者也可以根据自己的服务器的能力从队列中获取消息,达到削峰填谷的目的。

prefetch的设置:

(1)spring:在绑定监听器和队列的时候配置


<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:rabbitmq.yaml"/>
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}" port="${rabbitmq.port}"
                               username="${rabbitmq.username}" password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"
    />
    <bean id="ackListener" class="com.wxf.listener.AckListener"/>
    <bean id="prefetchListener" class="com.wxf.listener.PrefetchListener"/>
    <rabbit:admin connection-factory="connectionFactory"/>
    
    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true" acknowledge="manual" prefetch="1">
        <rabbit:listener ref="prefetchListener" queue-names="spring_direct_queue_confirm" />
    rabbit:listener-container>
beans>

(2)springboot:在springboot配置文件里配置

spring:
  rabbitmq:
    host: 192.168.127.131
    port: 5672
    username: admin
    password: 123456
    virtual-host: /
    listener:
      simple:
        prefetch: 2
        acknowledge-mode: manual

(3)原生
在原生rabbitMQ中,是根据 channel.basicQos 来进行限流,它有三个参数,源码如下:

Request specific "quality of service" settings. These settings impose limits on the amount of data the server will deliver to consumers before requiring acknowledgements. Thus they provide a means of consumer-initiated flow control.
Params:
prefetchSize – maximum amount of content (measured in octets) that the server will deliver, 0 if unlimited
prefetchCount – maximum number of messages that the server will deliver, 0 if unlimited
global – true if the settings should be applied to the entire channel rather than each consumer
Throws:
IOException – if an error is encountered
See Also:
AMQP.Basic.Qos
void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;

prefetchSize:传递给消费者的消息的最大字节数;
prefetchCount:传递给消费者的消息的最大条数
global:是否对所有channel都有效

你可能感兴趣的:(RabbitMQ,rabbitmq)