How does RabbitMQ limit the flow at the consumer end?

Posted by j_smith123 on Tue, 11 Jan 2022 08:48:58 +0100

Author: Hai Xiang\
Source: www.cnblogs.com com/haixiang/p/10905189. html

1. Why limit the current at the consumer end

Suppose a scenario. First, our Rabbitmq server has a backlog of tens of thousands of unprocessed messages. If we open a consumer client casually, this will happen: a huge amount of messages will be pushed in an instant, but our single client cannot process so much data at the same time!

When the amount of data is particularly large, it is certainly unscientific for us to limit the flow at the production end, because sometimes the amount of concurrency is particularly large, and sometimes the amount of concurrency is particularly small. We cannot restrict the production end, which is the behavior of users. Therefore, we should limit the flow at the consumer end to maintain the stability of the consumer end. When the number of messages increases sharply, it is likely to cause resource depletion, affect the performance of the service, and lead to the jamming or even direct collapse of the system.

2. API explanation of current limiting

RabbitMQ provides a QoS (quality of service assurance) function, that is, on the premise of non automatic message confirmation, if a certain number of messages (by setting the QoS value based on consumer or channel) are not confirmed, new messages will not be consumed.

/**
* 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.
* @param prefetchSize maximum amount of content (measured in
* octets) that the server will deliver, 0 if unlimited
* @param prefetchCount maximum number of messages that the server
* will deliver, 0 if unlimited
* @param global true if the settings should be applied to the
* entire channel rather than each consumer
* @throws java.io.IOException if an error is encountered
*/
void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;
  • prefetchSize: 0, the size limit of a single message. 0 means no limit
  • prefetchCount: number of messages consumed at one time. It will tell RabbitMQ not to push more than N messages to a consumer at the same time, that is, once there are N messages without ACK, the consumer will block until there is a message ack.
  • global: true, false whether to apply the above settings to the channel. In short, whether the above restrictions are channel level or consumer level. It takes effect when we set it to false. When it is set to true, there is no current limiting function, because the channel level has not been implemented.
  • Note: rabbitmq does not implement prefetchSize and global, so we will not study them for the time being. Note that prefetchCount is in No_ It takes effect only when ask = false, that is, the two values do not take effect in the case of automatic response.

3. How to limit the current at the consumer end

  • First, since we want to use the consumer end to limit the flow, we need to turn off the automatic ack and set the autoAck to false channel basicConsume(queueName, false, consumer);
  • The second step is to set the specific current limit size and quantity. channel.basicQos(0, 15, false);
  • The third step is to manually ack in the consumer's handleDelivery consumption method, and set the batch processing ack response to truechannel basicAck(envelope.getDeliveryTag(), true);

This is the production side code. There is no change from the production side code in the previous chapters. The main operations are concentrated on the consumer side.

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class QosProducer {
    public static void main(String[] args) throws Exception {
        //1. Create a ConnectionFactory and set it
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");

        //2. Create a connection through the connection factory
        Connection connection = factory.newConnection();

        //3. Create a Channel through Connection
        Channel channel = connection.createChannel();

        //4. Declaration
        String exchangeName = "test_qos_exchange";
        String routingKey = "item.add";

        //5. Sending
        String msg = "this is qos msg";
        for (int i = 0; i < 10; i++) {
            String tem = msg + " : " + i;
            channel.basicPublish(exchangeName, routingKey, null, tem.getBytes());
            System.out.println("Send message : " + tem);
        }

        //6. Close the connection
        channel.close();
        connection.close();
    }
}

Here, we create a consumer to verify the current limiting effect through the following code and that it does not work when the global parameter is set to true. We passed thread sleep(5000); To make the process of ack processing messages slower, so that we can clearly observe the flow restriction from the background management tool.

import com.rabbitmq.client.*;
import java.io.IOException;
public class QosConsumer {
    public static void main(String[] args) throws Exception {
        //1. Create a ConnectionFactory and set it
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");
        factory.setAutomaticRecoveryEnabled(true);
        factory.setNetworkRecoveryInterval(3000);

        //2. Create a connection through the connection factory
        Connection connection = factory.newConnection();

        //3. Create a Channel through Connection
        final Channel channel = connection.createChannel();

        //4. Declaration
        String exchangeName = "test_qos_exchange";
        String queueName = "test_qos_queue";
        String routingKey = "item.#";
        channel.exchangeDeclare(exchangeName, "topic", true, false, null);
        channel.queueDeclare(queueName, true, false, false, null);

        channel.basicQos(0, 3, false);

        //Generally, code binding is not required, and it is manually bound in the management interface
        channel.queueBind(queueName, exchangeName, routingKey);

        //5. Create consumers and receive messages
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body)
                    throws IOException {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String message = new String(body, "UTF-8");
                System.out.println("[x] Received '" + message + "'");

                channel.basicAck(envelope.getDeliveryTag(), true);
            }
        };
        //6. Set Channel consumer binding queue
        channel.basicConsume(queueName, false, consumer);
        channel.basicConsume(queueName, false, consumer1);
    }
}

From the figure below, we find that unacketed value is always 3. Every 5 seconds, the consumption of a message, that is, Ready and Total are reduced by 3. Here, unacketed value represents the message that consumers are processing. Through our experiments, we found that consumers can process up to 3 messages at one time, reaching the expected function of consumer flow restriction.

When we set the global in void basicQos(int prefetchSize, int prefetchCount, boolean global) to true, we find that there is no current limiting effect.

Recent hot article recommendations:

1.1000 + Java interview questions and answers (2022 latest version)

2.Hot! The Java collaboration is coming...

3.Spring Boot 2.x tutorial, too complete!

4.Spring Boot 2.6 was officially released, a wave of new features..

5.Java development manual (Songshan version) is the latest release. Download it quickly!

Feel good, don't forget to like + forward!

Topics: Java