RabbitMQ from entry to Mastery Series: details of work queues

Keywords: RabbitMQ less

(1) RabbitMQ work queue model structure

The work queue model increases the number of consumers compared to simple queues.

The producer provides the message to the message queue, and the consumer can get the message in the queue. By default, polling distribution is used to distribute messages to consumers in work queues. The so-called polling distribution means that no matter how fast or slow the consumer processes the messages, they will send the messages to the consumer in turn according to the order.

(two) work queue practice (polling distribution)

The code using work queues is basically the same as that of simple queues, with only a few more consumers

2.1 create tool class

The code for creating the tool class has been mentioned in the previous blog. Here is the code:

public class ConnectionUtil {
    public static Connection getConnection() throws IOException, TimeoutException {
        //Define a connection factory
        ConnectionFactory factory=new ConnectionFactory();
        //Set service address
        factory.setHost("127.0.0.1");
        //Set AMQP port
        factory.setPort(5672);
        //Set VHOSTS
        factory.setVirtualHost("/vhosts_sdxb");
        //Set user name
        factory.setUsername("user_sdxb");
        factory.setPassword("123456");
        return factory.newConnection();
    }
}

2.2 create producers

public class Send {
    private static final String QUEUE_NAME="work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //1. Get connection
        Connection connection = ConnectionUtil.getConnection();
        //2. Create channel
        Channel channel = connection.createChannel();
        //3. Create queue declaration
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        for (int i=0;i<50;i++){
            String msg="i="+i;
            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
        }
        channel.close();
        connection.close();
    }
}

2.3 create consumer one

In order to reflect the speed of message processing by consumers, I set thread sleep for 1s and 2s respectively in the two consumers

public class Receive1 {
    private static final String QUEUE_NAME="work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //Get connection
        Connection connection = ConnectionUtil.getConnection();
        //Create channel
        Channel channel = connection.createChannel();
        //Create queue declaration
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //Create consumer monitoring method
        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(msg);
                try {
                    //Set sleep practice for 1s
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //listen queue
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

2.4 create consumer II

public class Receive2 {
    private static final String QUEUE_NAME="work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        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(msg);
                try {
                    //Set sleep time for 2s
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

Run the two consumers separately, and then run the producer to send 50 messages. It can be found that although the ability of the two consumers to process messages is fast or slow, they get 25 messages. The screenshot of the message obtained by consumer 1 is shown below.

(3) Fair dispatch

In some scenarios, polling distribution is unreasonable, so the work queue also has a fair distribution method. The so-called fair distribution is that people with more advantages and more efforts get more messages when they are fast, and people who are slow get less messages when they are slow. The implementation of fair distribution only needs to make some changes to the code:

3.1 modify the producer

For producers, only one restriction should be added to the channel to restrict the channel from sending more than one message to the same consumer, that is, the second message will be sent to the consumer only after the consumer processes a message. Using the channel.basicQos(); method, set the parameter to 1 to limit no more than 1 message at a time.

public class Send {
    private static final String QUEUE_NAME="work_queue_fair";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //Limit channel to send no more than one message to the same consumer
        int prefenchCount=1;
        channel.basicQos(prefenchCount);
        for (int i=0;i<50;i++){
            String msg="i="+i;
            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
        }
        channel.close();
        connection.close();
    }
}

3.2 modifying consumers

For consumers, there are three places that need to be modified: the first place is to modify the restriction information of the channel like the producer; the second place is to turn off the automatic response of consumers; the third place is to set the manual receipt, that is, to send the completed instructions to the queue after processing a message manually.

//Ensure only one distribution at a time
channel.basicQos(1);
//Set manual receipt
channel.basicAck(envelope.getDeliveryTag(),false);
//Turn off auto answer
boolean autoAck=false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);

Here is the revised consumer code

public class Receive1 {
    private static final String QUEUE_NAME="work_queue_fair";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //Ensure only one distribution at a time
        channel.basicQos(1);
        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(msg);
                //Set manual receipt
                channel.basicAck(envelope.getDeliveryTag(),false);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //Turn off auto answer
        boolean autoAck=false;
        channel.basicConsume(QUEUE_NAME,autoAck,consumer);
    }
}

After setting the work queue, it becomes a fair distribution method. Test results:

3.3 about automatic response

When modifying the consumer code, we turned off auto answer

boolean autoAck=false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);

This is the second parameter of basicConsume

When autoAck=true, it means auto answer is turned on. Once rabbitmq sends the message in the queue to the consumer, the message will disappear from the queue. But if the consumer hangs up at this time, the news will disappear completely.

When autoAck=false, turn off auto answer, rabbitmq will send the message in the queue to the consumer, and the message in the queue will be deleted only after the consumer returns to confirm.

68 original articles published, 961 praised, 120000 visitors+
Private letter follow

Posted by neveriwas on Fri, 21 Feb 2020 05:52:43 -0800