Various RabbitMQ modes and their implementation of confirm mode, return mode, ack mechanism description, current limiting, TTL, dead letter queue and delay queue

Posted by cs-web on Sat, 18 Dec 2021 05:54:47 +0100

Rabbitmq

1. Download and install

**Step 1: * * Download and install Erlang. The RabbitMQ server code is written in parallel language Erlang. The prerequisite for installing RabbitMQ is to install Erlang.

Download address: http://www.erlang.org/downloads

Select the appropriate version to download. Remember to remember the installation path during installation. After installation, remember to configure the environment variables of the system

This computer – > right click "properties" – > advanced system settings – > environment variables – > new system environment variable

Variable name: ERLANG_HOME, the variable value is the installation address of erlang just now. Click OK.

Double click the system variable path and click "new" to open% Erlang_ Add home% \ bin to path.

Finally, press the windows key + R key, enter cmd, and then enter erl. Seeing the version number indicates that erlang has been successfully installed.

Step 2: install rabbitmq

Download address: http://www.rabbitmq.com/download.html

  • Double click the downloaded exe file. The installation process is the same as that of erlang.
  • After RabbitMQ is installed, install RabbitMQ plugins. Open the command line cd and enter the sbin directory of RabbitMQ.

cmd to the sbin directory of rabbitmq server. Open the cmd command line tool

cd C:\Program Files\RabbitMQ Server\rabbitmq_server-3.7.4\sbin

Execute command:

rabbitmq-plugins.bat enable rabbitmq_management

Restart rabbitmq server:

This computer – "right click management interface" – "double click services and Applications" – "double click services" – "click restart button

Enter localhost:15672 in the browser and enter guest/guest

2. Concept description

The concepts include:

  • Producer: the producer who sends the message
  • Consumer: the consumer who receives the message
  • broker: a component used to store logical concepts of information
  • Virtual hosts: virtual hosts, which are used to distinguish different business components
  • Queue: the component (queue) that actually stores messages

3. Interface introduction

The admin tab represents the user interface:

add a user as follows:

We created a user named zhangsan

Role description:

1. Super administrator

You can log in to the management console, view all information, and operate user policies.

2. Monitoring

You can log in to the management console and view the related information of rabbitmq node (number of processes, memory usage, disk usage, etc.)

3. Policy maker

You can log in to the management console and manage policies at the same time. However, you cannot view the relevant information of the node (the part identified by the red box in the above figure).

4. General manager

You can only log in to the management console. You cannot see node information or manage policies.

5. Other

Unable to log in to the management console, usually ordinary producers and consumers.

Then a new User will appear in the User list above:

It can be seen that zhangsan users do not have their own virtual host. At this time, we can assign a virtual host to zhangsan, whose name is pay, that is, payment related:

Click pay to set permissions:

After setting:

Parameter Description:

  • User: user name
  • Configure: a regular expression. The user has the permission to configure all resources that match the regular expression
  • Write: a regular expression. The user has write permission on all resources that match the regular expression
  • Read: a regular expression. The user has read permission on all resources that match the regular expression

After configuration, zhangsan will have a pay virtual host:

4. RabbitMQ getting started

Add dependency:

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.6.0</version>
</dependency>

4.1 consumers

The creation of producers is divided into the following steps:

//Create linked factory object
//Set the RabbitMQ service host address. The default is localhost
//Set the RabbitMQ service port. The default value is 5672
//Set virtual host name, default/
//Set the user connection name. The default is guest
//Set link password, default guest
//create link
//Create channel
//Declaration queue
//Create message
//message sending
//close resource

Follow the above steps to create a message producer,

package com.yxinmiracle.simple;

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

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 10:22
 */

public class Producer {

    // send message
    public static void main(String[] args) throws IOException, TimeoutException {
        // Create factory object
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // Set up RabbitMq server host
        connectionFactory.setHost("localhost");
        // Set port
        connectionFactory.setPort(5672);
        // Set virtual host name
        connectionFactory.setVirtualHost("/pay");
        connectionFactory.setUsername("zhangsan");
        connectionFactory.setPassword("zhangsan");
        Connection connection = connectionFactory.newConnection();
        // Create channel
        Channel channel = connection.createChannel();

        // Create queue
        /*
        * 1. Parameter 1: make queue name
        * 2. Parameter 2: determines whether to persist. It is generally true
        * 3. Parameter 3: whether to monopolize this channel. Generally, it is not monopolized
        * 4. Parameter 4: specify whether to delete automatically
        * 5. Parameter 5: specify additional parameters
        * */
        channel.queueDeclare("simple_queue1",true,false,false,null);
        // Create message
        String msg = "hello i am from simple producer";

        // message sending
        /*
        * 1. Parameter 1: Specifies that the default empty string is used in switch simple mode
        * 2. Parameter 2: simple mode is queue name
        * 3. Parameter 3: Specifies that the extra parameter carried is null
        * 4. Sent message itself
        * */
        channel.basicPublish("","simple_queue1",null,msg.getBytes());

        channel.close();
        connection.close();
    }
}

After performing the above message transmission; You can log in to rabbitMQ's administrative console to discover the queue and its messages:

To view messages, click queue name - > get messages, as shown in the following figure:

4.2 consumers

Consumer creation can be realized as follows:

//Create linked factory object
//Set the RabbitMQ service host address. The default is localhost
//Set the RabbitMQ service port. The default value is 5672
//Set virtual host name, default/
//Set the user connection name. The default is guest
//Set link password, default guest
//create link
//Create channel
//Create queue
//Create a consumer and set up message processing
//Message listening
//Close the resource (it is not recommended to close, and it is recommended to listen to messages all the time)

Follow the steps above to create a message consumer:

package com.yxinmiracle.simple;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 10:23
 */

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // Create factory object
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // Set up RabbitMq server host
        connectionFactory.setHost("localhost");
        // Set port
        connectionFactory.setPort(5672);
        // Set virtual host name
        connectionFactory.setVirtualHost("/pay");
        connectionFactory.setUsername("zhangsan");
        connectionFactory.setPassword("zhangsan");
        Connection connection = connectionFactory.newConnection();
        // Create channel
        Channel channel = connection.createChannel();
        // listen queue 
        channel.queueDeclare("simple_queue1",true,false,false,null);

        // Create a consumer and set up to process messages
        DefaultConsumer consumer = new DefaultConsumer(channel){
            // Override the parent class method to process the message after receiving the message
            /***
             * @param consumerTag   Message sender tag, in channel Basicconsume can be specified
             * @param envelope      The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * @param properties    Attribute information
             * @param body           news
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                String routingKey = envelope.getRoutingKey();
                //Get switch information
                String exchange = envelope.getExchange();
                //Get message ID
                long deliveryTag = envelope.getDeliveryTag();
                //Get message information
                String message = new String(body,"UTF-8");
                System.out.println("routingKey:"+routingKey+",exchange:"+exchange+",deliveryTag:"+deliveryTag+",message:"+message);

            }
        };

        // Message listening
        channel.basicConsume("simple_queue1",true,consumer);

    }
}

After running console printing:

4.3 extraction of tools

Tool class extraction

Whether consumers or producers as like as two peas, we found that the steps taken before were almost the same, so we could extract a tool class and extract the following code.

Create a tool class to create a Connection. The code is as follows:

package com.yxinmiracle.utils;

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

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 11:23
 */
public class ConnectionUtil {

    /***
     * Create linked objects
     * @return
     * @throws IOException
     * @throws TimeoutException
     */
    public static Connection getConnection() throws IOException, TimeoutException {
        //Create linked factory object
        ConnectionFactory connectionFactory = new ConnectionFactory();

        //Set the RabbitMQ service host address. The default is localhost
        connectionFactory.setHost("localhost");

        //Set the RabbitMQ service port. The default value is 5672
        connectionFactory.setPort(5672);

        //Set virtual host name, default/
        connectionFactory.setVirtualHost("/pay");

        //Set the user connection name. The default is guest
        connectionFactory.setUsername("zhangsan");

        //Set link password, default guest
        connectionFactory.setPassword("zhangsan");

        //create link
        Connection connection = connectionFactory.newConnection();
        return connection;
    }
}

5. Working mode

Link: https://rabbitmq.com/getstarted.html , you can view all the official modes through this website:

5.1 Work queues

5.1. 1 mode description

Compared with the simple mode of the entry program, Work Queues has one or more consumers, and multiple consumers consume messages in the same queue together.

Application scenario: when tasks are too heavy or there are many tasks, using work queue can improve the speed of task processing.

5.1. 2 code

The code of Work Queues is almost the same as that of the simple mode of the entry program; You can completely copy and copy one more consumer to test the simultaneous consumption of messages by multiple consumers.

(1) Producer

Create a message producer object with the following code:

package com.yxinmiracle.work;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.yxinmiracle.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 11:27
 */

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        // Create channel
        Channel channel = connection.createChannel();

        // Create queue
        /*
         * 1. Parameter 1: make queue name
         * 2. Parameter 2: determines whether to persist. It is generally true
         * 3. Parameter 3: whether to monopolize this channel. Generally, it is not monopolized
         * 4. Parameter 4: specify whether to delete automatically
         * 5. Parameter 5: specify additional parameters
         * */
        channel.queueDeclare("simple_queue2",true,false,false,null);

        for (int i = 0; i < 20; i++) {
            // Create message
            String msg = "hello i am from simple producer";

            // message sending
            /*
             * 1. Parameter 1: Specifies that the default empty string is used in switch simple mode
             * 2. Parameter 2: simple mode is queue name
             * 3. Parameter 3: Specifies that the extra parameter carried is null
             * 4. Sent message itself
             * */
            channel.basicPublish("","simple_queue2",null,msg.getBytes());
        }

        channel.close();
        connection.close();
    }
}

(2) Consumer One

Create the first Work consumer with the following code:

public class WorkConsumerOne {

    /***
     * Message consumer
     * @param args
     * @throws IOException
     * @throws TimeoutException
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        //create link
        Connection connection = ConnectionUtil.getConnection();

        //Create channel
        Channel channel = connection.createChannel();

        //Create queue
        channel.queueDeclare("work_queue",true,false,false,null);

        //Create a consumer and set up message processing
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            /***
             * @param consumerTag   Message sender tag, in channel Basicconsume can be specified
             * @param envelope      The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * @param properties    Attribute information
             * @param body           news
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                String routingKey = envelope.getRoutingKey();
                //Get switch information
                String exchange = envelope.getExchange();
                //Get message ID
                long deliveryTag = envelope.getDeliveryTag();
                //Get message information
                String message = new String(body,"UTF-8");
                System.out.println("Work-One:routingKey:"+routingKey+",exchange:"+exchange+",deliveryTag:"+deliveryTag+",message:"+message);
            }
        };

        /**
         * Message listening
         * Parameter 1: queue name
         * Parameter 2: whether to confirm automatically. If it is set to true, it means that the message is received and replied to mq automatically. If mq receives the reply, it will delete the message. If it is set to false, it needs to be confirmed manually
         * Parameter 3: callback after receiving message
         */
        channel.basicConsume("work_queue",true,defaultConsumer);

        //Close the resource (it is not recommended to close, and it is recommended to listen to messages all the time)
        //channel.close();
        //connection.close();
    }
}

(3) Consumer Two

Create the second Work consumer with the following code:

public class WorkConsumerTwo {

    /***
     * Message consumer
     * @param args
     * @throws IOException
     * @throws TimeoutException
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        //create link
        Connection connection = ConnectionUtil.getConnection();

        //Create channel
        Channel channel = connection.createChannel();

        //Create queue
        channel.queueDeclare("work_queue",true,false,false,null);

        //Create a consumer and set up message processing
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            /***
             * @param consumerTag   Message sender tag, in channel Basicconsume can be specified
             * @param envelope      The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * @param properties    Attribute information
             * @param body           news
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                String routingKey = envelope.getRoutingKey();
                //Get switch information
                String exchange = envelope.getExchange();
                //Get message ID
                long deliveryTag = envelope.getDeliveryTag();
                //Get message information
                String message = new String(body,"UTF-8");
                System.out.println("Work-Two:routingKey:"+routingKey+",exchange:"+exchange+",deliveryTag:"+deliveryTag+",message:"+message);
            }
        };

        /**
         * Message listening
         * Parameter 1: queue name
         * Parameter 2: whether to confirm automatically. If it is set to true, it means that the message is received and replied to mq automatically. If mq receives the reply, it will delete the message. If it is set to false, it needs to be confirmed manually
         * Parameter 3: callback after receiving message
         */
        channel.basicConsume("work_queue",true,defaultConsumer);

        //Close the resource (it is not recommended to close, and it is recommended to listen to messages all the time)
        //channel.close();
        //connection.close();
    }
}

After the test, you can see that the two consumers received 10 messages respectively

(1) First consumer

(2) Second consumer

6. Publish subscribe mode - broadcast mode

In this subscription mode, there is an exchange role. The role of this role is to forward messages to the queue. It does not store messages, but only forward messages. If the queue wants to receive the information forwarded by the switch, it needs to make a binding.

In the first two cases, there are only three roles:

P: The producer is the program that sends the message
C: Consumer: the receiver of the message will always wait for the message to arrive.
Queue: Message queue, shown in red

In the subscription model, there is an exchange role, and the process changes slightly:

P: The producer, that is, the program that sends the message, but no longer sends it to the queue, but to X(Switch)
C: Consumers, the recipients of news, will always wait for the news to come.
Queue: Message queue, receiving messages and caching messages.
Exchange: Switch, in Figure X. On the one hand, it receives messages sent by producers. On the other hand, you know how to process messages, such as delivering to a special queue, delivering to all queues, or discarding messages. How to operate depends on Exchange Type of. Exchange There are three common types:
	Fanout: Broadcast and deliver the message to all queues bound to the switch
	Direct: Directional, deliver the message to the qualified user routing key Queue of
	Topic: Wildcard, give the message to the routing pattern(Routing mode) queue

Exchange (switch) is only responsible for forwarding messages and does not have the ability to store messages. Therefore, if there is no queue bound to exchange or no queue that meets the routing rules, the messages will be lost!

6.1 producer code

package com.yxinmiracle.famout;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.yxinmiracle.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 11:27
 */

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        // Create channel


        Channel channel = connection.createChannel();



        // Create switch
        channel.exchangeDeclare("exchange_fanout", BuiltinExchangeType.FANOUT);

        // Reputation two queues
        channel.queueDeclare("fanout_queue1",true,false,false,null);
        channel.queueDeclare("fanout_queue2",true,false,false,null);

        // Bind the queue to the specified switch
        // The third parameter is null by default in broadcast mode
        channel.queueBind("fanout_queue1","exchange_fanout","");
        channel.queueBind("fanout_queue2","exchange_fanout","");


        // Create message
        String msg = "hello i am from fanout producer";

        // message sending
        /*
         * 1. Parameter 1: Specifies that the default empty string is used in switch simple mode
         * 2. Parameter 2: simple mode is queue name
         * 3. Parameter 3: Specifies that the extra parameter carried is null
         * 4. Sent message itself
         * */
        channel.basicPublish("exchange_fanout","",null,msg.getBytes());

        channel.close();
        connection.close();
    }
}

6.2 consumer code

package com.yxinmiracle.famout;

import com.rabbitmq.client.*;
import com.yxinmiracle.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 11:30
 */

public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        // Create channel
        Channel channel = connection.createChannel();


        // listen queue 
        channel.queueDeclare("fanout_queue1",true,false,false,null);

        // Create a consumer and set up to process messages
        DefaultConsumer consumer = new DefaultConsumer(channel){
            // Override the parent class method to process the message after receiving the message
            /***
             * @param consumerTag   Message sender tag, in channel Basicconsume can be specified
             * @param envelope      The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * @param properties    Attribute information
             * @param body           news
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                String routingKey = envelope.getRoutingKey();
                //Get switch information
                String exchange = envelope.getExchange();
                //Get message ID
                long deliveryTag = envelope.getDeliveryTag();
                //Get message information
                String message = new String(body,"UTF-8");
                System.out.println("routingKey:"+routingKey+",exchange:"+exchange+",deliveryTag:"+deliveryTag+",message:"+message);

            }
        };

        // Message listening
        channel.basicConsume("fanout_queue1",true,consumer);
    }
}

6.3 consumer 2 code

package com.yxinmiracle.famout;

import com.rabbitmq.client.*;
import com.yxinmiracle.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 11:30
 */

public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        // Create channel
        Channel channel = connection.createChannel();


        // listen queue 
        channel.queueDeclare("fanout_queue2",true,false,false,null);

        // Create a consumer and set up to process messages
        DefaultConsumer consumer = new DefaultConsumer(channel){
            // Override the parent class method to process the message after receiving the message
            /***
             * @param consumerTag   Message sender tag, in channel Basicconsume can be specified
             * @param envelope      The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * @param properties    Attribute information
             * @param body           news
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                String routingKey = envelope.getRoutingKey();
                //Get switch information
                String exchange = envelope.getExchange();
                //Get message ID
                long deliveryTag = envelope.getDeliveryTag();
                //Get message information
                String message = new String(body,"UTF-8");
                System.out.println("routingKey:"+routingKey+",exchange:"+exchange+",deliveryTag:"+deliveryTag+",message:"+message);

            }
        };

        // Message listening
        channel.basicConsume("fanout_queue2",true,consumer);
    }
}

6.4 testing

After the producer runs:

consumer:

7. Publish subscribe mode - routing mode

7.1 mode description

Routing mode features:

1.The binding between the queue and the switch cannot be arbitrary, but one must be specified RoutingKey(route key)
2.The sender of the message is sending Exchange When sending a message, you must also specify the name of the message RoutingKey. 
3.Exchange Instead of giving the message to each bound queue, it is based on the number of messages Routing Key To judge, only the queue Routingkey Message related Routing key The message will not be received until it is completely consistent

Illustration:

P: Producer, to Exchange Send a message. When you send a message, you specify a routing key. 
X: Exchange(Switch) to receive the producer's message, and then submit the message to routing key Exact match queue
C1: Consumer whose queue specifies the required routing key by error News of
C2: Consumer whose queue specifies the required routing key by info,error,warning News of

7.2 codes

(1) Producer

Producers should pay attention to the following three points:

1.Claim switch
2.Declaration queue
3.The queue needs to be bound to the specified switch

Create a message producer with the following code:

package com.yxinmiracle.routing;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.yxinmiracle.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 16:15
 */

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        // Create channel


        Channel channel = connection.createChannel();



        // Create switch
        channel.exchangeDeclare("direct_exchange", BuiltinExchangeType.DIRECT);

        // Reputation two queues
        channel.queueDeclare("direct_queue1",true,false,false,null);
        channel.queueDeclare("direct_queue2",true,false,false,null);

        // Bind the queue to the specified switch
        // The third parameter is null by default in broadcast mode
        // A routing key must be specified in the routing mode
        channel.queueBind("direct_queue1","direct_exchange","order.insert");
        channel.queueBind("direct_queue2","direct_exchange","order.select");


        // Create message
        String msg1 = "hello i am from fanout producer insert";
        String msg2 = "hello i am from fanout producer select";

        // message sending
        /*
         * 1. Parameter 1: Specifies that the default empty string is used in switch simple mode
         * 2. Parameter 2: simple mode is queue name
         * 3. Parameter 3: Specifies that the extra parameter carried is null
         * 4. Sent message itself
         * */
        channel.basicPublish("direct_exchange","order.insert",null,msg1.getBytes());
        channel.basicPublish("direct_exchange","order.select",null,msg2.getBytes());

        channel.close();
        connection.close();
    }
}

(2) Consumer One

package com.yxinmiracle.routing;

import com.rabbitmq.client.*;
import com.yxinmiracle.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 11:30
 */

public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        // Create channel
        Channel channel = connection.createChannel();


        // listen queue 
        channel.queueDeclare("direct_queue1",true,false,false,null);

        // Create a consumer and set up to process messages
        DefaultConsumer consumer = new DefaultConsumer(channel){
            // Override the parent class method to process the message after receiving the message
            /***
             * @param consumerTag   Message sender tag, in channel Basicconsume can be specified
             * @param envelope      The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * @param properties    Attribute information
             * @param body           news
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                String routingKey = envelope.getRoutingKey();
                //Get switch information
                String exchange = envelope.getExchange();
                //Get message ID
                long deliveryTag = envelope.getDeliveryTag();
                //Get message information
                String message = new String(body,"UTF-8");
                System.out.println("routingKey:"+routingKey+",exchange:"+exchange+",deliveryTag:"+deliveryTag+",message:"+message);

            }
        };

        // Message listening
        channel.basicConsume("direct_queue1",true,consumer);
    }
}

(3) Consumer Two

package com.yxinmiracle.routing;

import com.rabbitmq.client.*;
import com.yxinmiracle.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 11:30
 */

public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        // Create channel
        Channel channel = connection.createChannel();


        // listen queue 
        channel.queueDeclare("direct_queue2",true,false,false,null);

        // Create a consumer and set up to process messages
        DefaultConsumer consumer = new DefaultConsumer(channel){
            // Override the parent class method to process the message after receiving the message
            /***
             * @param consumerTag   Message sender tag, in channel Basicconsume can be specified
             * @param envelope      The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * @param properties    Attribute information
             * @param body           news
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                String routingKey = envelope.getRoutingKey();
                //Get switch information
                String exchange = envelope.getExchange();
                //Get message ID
                long deliveryTag = envelope.getDeliveryTag();
                //Get message information
                String message = new String(body,"UTF-8");
                System.out.println("routingKey:"+routingKey+",exchange:"+exchange+",deliveryTag:"+deliveryTag+",message:"+message);

            }
        };

        // Message listening
        channel.basicConsume("direct_queue2",true,consumer);
    }
}

7.3 testing

8. Wildcard pattern

Compared with Direct, Topic can route messages to different queues according to RoutingKey. However, Topic Exchange allows the queue to use wildcards when Binding routing keys!

A Routingkey is usually composed of one or more words with "." between them Split, for example: item insert

Wildcard rule:

#: matches one or more words

*: no more, no less, exactly 1 word

give an example:

item.#: Can match item insert. ABC or item insert

item.*: Only item. Can be matched insert

Illustration:

  • Red Queue: the bound is usa. #, so all routing key s starting with usa. will be matched
  • Yellow Queue: bound to # News, so usually with The routing key s at the end of news will be matched

9. Spring boot integration rabbitmq

Add dependency

<!--Parent project-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
    </parent>

    <!--rely on-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

Startup class:

package com.yxinmiracle;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 18:32
 */

@SpringBootApplication
public class RabbitMqApplication {
    public static void main(String[] args) {
        SpringApplication.run(RabbitMqApplication.class,args);
    }

    @Bean
    public Queue queue(){
        return new Queue("springboot_topic_queue");
    }

    @Bean
    public TopicExchange exchange(){
        return new TopicExchange("topic_exchange");
    }

    @Bean
    public Binding binding(){
        return BindingBuilder.bind(queue()).to(exchange()).with("order.*");
    }


    // send message
    @RequestMapping("/order")
    @RestController
    class OrderController{

        @Autowired
        private RabbitTemplate rabbitTemplate;

        @GetMapping("/add")
        public String addOrder(){
            System.out.println("======Under order======");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("======checkout success ======");

            rabbitTemplate.convertAndSend("topic_exchange","order.insert","Message itself,insert");
            rabbitTemplate.convertAndSend("topic_exchange","order.delete","Message itself,delete");
            return "success";
        }
    }
}

Configure rabbitMq

spring:
  rabbitmq:
    host: localhost
    port: 5672
    virtual-host: /szitheima
    username: admin
    password: admin

Message listening and processing class

package com.yxinmiracle;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 18:32
 */

@SpringBootApplication
public class RabbitMqApplication {
    public static void main(String[] args) {
        SpringApplication.run(RabbitMqApplication.class,args);
    }


    @Bean
    public Queue queue(){
        return new Queue("springboot_topic_queue");
    }

    @Bean
    public TopicExchange exchange(){
        return new TopicExchange("topic_exchange");
    }

    @Bean
    public Binding binding(){
        return BindingBuilder.bind(queue()).to(exchange()).with("order.*");
    }


    // send message
    @RequestMapping("/order")
    @RestController
    class OrderController{

        @Autowired
        private RabbitTemplate rabbitTemplate;

        @GetMapping("/add")
        public String addOrder(){
            System.out.println("======Under order======");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("======checkout success ======");

            rabbitTemplate.convertAndSend("topic_exchange","order.insert","Message itself,insert");
            rabbitTemplate.convertAndSend("topic_exchange","order.delete","Message itself,delete");
            return "success";
        }
    }
}

Test:

10. rabbitmq advanced features

There are some problems in the use of messages. For example, how can we ensure the reliability of message delivery? How to ensure the reliability of consumer information? If there is no guarantee, losses may occur in some cases. For example, can we simulate some business scenarios according to the characteristics of messages when sending and receiving messages? The order expires in 30 minutes, etc., and the confirmation of system communication, etc.

10.1 producer reliability message delivery

Reliability message

When using RabbitMQ, the message sender wants to eliminate any message loss or delivery failure scenarios. RabbitMQ provides us with two modes to control the delivery reliability of messages. mq provides the following two modes:

+ confirm pattern
	The timing at which the producer sends messages to the switch 
+ return pattern
    Switch forwards message to queue Timing

The process of MQ message delivery is as follows:

1.The producer sends a message to the switch
2.Switch according to routingkey Forward message to queue
3.The consumer monitors the queue and obtains the information in the queue
4.Consumption successfully deleted the message in the queue
  • If the message goes from product to exchange, a confirmCallback will be returned.
  • If the message fails to be delivered from exchange to queue, a returnCallback will be returned.
10.1. 1. Code implementation of confirmcallback

Engineering structure:

Add dependency

   <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>      
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

Startup class:

package com.yxinmiracle;


import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 20:17
 */
@SpringBootApplication
public class SpringRabbitApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringRabbitApplication.class,args);
    }

    @Bean
    public Queue queue(){
        return new Queue("queue_demo01");
    }

    @Bean
    public DirectExchange exchange(){
        return new DirectExchange("exchange_direct_demo1");
    }

    @Bean
    public Binding createBinding(){
        return BindingBuilder.bind(queue()).to(exchange()).with("item.insert");
    }
}

Create application The YML file is configured as follows. The configuration enables the confirm mode, which is false by default

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    publisher-confirms: true
server:
  port: 8080

Create controller to send message

package com.yxinmiracle.controller;

import com.yxinmiracle.confirm.MyConfirmCallback;
import com.yxinmiracle.confirm.MyReturnCallBack;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 20:17
 */
@RestController
@RequestMapping("/test")
public class TestController {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private MyConfirmCallback myConfirmCallback;

    @RequestMapping("/send")
    public String send1(){
        rabbitTemplate.setConfirmCallback(myConfirmCallback);
        rabbitTemplate.setReturnCallback(myReturnCallBack);
        rabbitTemplate.convertAndSend("exchange_direct_demo01","item.insert","hello,insert");
        return "success";
    }
}

Create callback function:

package com.yxinmiracle.confirm;

import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 20:33
 */
@Component
public class MyConfirmCallback implements RabbitTemplate.ConfirmCallback {
    /**
     *
     * @param correlationData
     * @param ack Confirm that the sending is successful. If true, it is successful
     * @param cause If it is a success, cause is null. If it is a failure, there is a failure message
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if(ack){
            System.out.println("===success===");
        }else {
            System.out.println(cause);
        }
    }
}

Test send message:

Change to nonexistent switch:

Test again:

report errors!

Summary:

1.Send and play according to confrim Mechanism to ensure whether messages have been sent to the switch
2.confirm The mechanism can ensure that there is a callback when the message is sent to the switch, but can not ensure that the message is forwarded to the switch queue With callback
10.1. 2. Implementation of returncallback code

As described above, the content of the message sent to the switch has been realized. If yes, the switch sends the message successfully, but the message is sent incorrectly when the route is forwarded to the queue. At this time, the returncallback mode needs to be used. Next, let's implement it.

The implementation steps are as follows:

1.open returncallback pattern
2.Set callback function
3.send message

Configure yml to enable returncallback

Write the returncallback Code:

package com.yxinmiracle.confirm;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 20:57
 */
@Component
public class MyReturnCallBack implements RabbitTemplate.ReturnCallback {
    /**
     * This method is called as soon as an error occurs
     * @param message
     * @param replyCode
     * @param replyText Error message expression
     * @param exchange Switch
     * @param routingKey
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        System.out.println(new String(message.getBody()));
        System.out.println("returned  replyCode Yes:"+replyCode);
        System.out.println("returned  replyText Yes:"+replyText);
        System.out.println("returned  exchange Yes:"+exchange);
        System.out.println("returned  routingKey Yes:"+routingKey);
    }
}

send message

We send the correct switch, but send the wrong routingkey test

controller:

package com.yxinmiracle.controller;

import com.yxinmiracle.confirm.MyConfirmCallback;
import com.yxinmiracle.confirm.MyReturnCallBack;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 20:17
 */
@RestController
@RequestMapping("/test")
public class TestController {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private MyConfirmCallback myConfirmCallback;

    @Autowired
    private MyReturnCallBack myReturnCallBack;

    @RequestMapping("/send")
    public String send1(){
        rabbitTemplate.setConfirmCallback(myConfirmCallback);
        rabbitTemplate.setReturnCallback(myReturnCallBack);
        rabbitTemplate.convertAndSend("exchange_direct_demo01","itemxxx.insert","hello,insert");
        return "success";
    }
}

Test:

You can see that the report is wrong.

10.1. 3 summary of the two modes

The confirm mode is used when messages are sent to the switch, and the return mode is used when messages are routed to the queue by the switch to send errors.

However, in general, we can use confirm, because the routing key is specified by the developer, and generally no error occurs. If you want to ensure that messages are in the switch and routingkey, you need to combine the two methods to set them.

10.2 consumer confirmation mechanism (ACK)

Above, we learned about the sender's reliable delivery, but there may also be problems with the consumer, such as not accepting the message, for example, an exception occurs during code execution after receiving the message. In this case, we need additional processing, so we need to manually confirm and sign in the message. rabbtimq provides us with a mechanism: ACK mechanism.

ACK mechanism: there are three ways

  • Auto acknowledge acknowledge = "none"
  • Manually confirm acknowledge = "manual"
  • Confirm according to abnormal conditions (not used for the time being) acknowledge = "auto"

Explanation:

Automatic confirmation refers to:
	When the message is Consumer If it is received, it will automatically confirm the receipt and send the corresponding message message from RabbitMQ Removed from the message cache. However, in the actual business processing, it is likely that the message will be lost if an exception occurs in the business processing after the message is received.
Manual confirmation means:
	It needs to be called after the business process is successful. channel.basicAck(),Manually sign in. If an exception occurs, call channel.basicNack()And other methods to process it according to business functions, such as resending, refusing to sign in, entering the dead letter queue, etc.
10.2. 1. Ack code implementation

Implementation steps:

1.Create a normal listener to listen for messages
2.modify controller Send correct message test
3.Set profile on ack Manual confirmation. The default is automatic confirmation
4.Modify the message listener to manually confirm the business judgment logic

To create a normal message listener:

package com.yxinmiracle.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-30 21:11
	 Sign in if it is normal, and discard if it is abnormal.
 */
@Component
@RabbitListener(queues = "queue_demo01")
public class MyRabbitListener {

    @RabbitHandler // Different methods can be triggered according to different types
    public void msg(Message message, Channel channel, String msg) {
        System.out.println("The message received is:" + msg);

        try {
            System.out.println("start=============");
            Thread.sleep(1000);
            int i = 1 / 0;
            System.out.println("end==============");
            // succeed
            // Parameter 1: make the sequence number of the message
            // Parameter 2: determine whether to sign in in batch
            
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),true); // accept
        } catch (Exception e) {
            e.printStackTrace();
            // If there is an exception, you should refuse to sign in
            try {
                // Parameter 3: Yes, return to the queue
                //If an exception occurs, the reject message can be returned to the queue or discarded, which can be changed according to the business scenario
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),true,false);
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }
}
First: sign in
channel.basicAck()
The second method: reject signing in batch processing 
channel.basicNack() 
The third method: refuse to sign in and do not batch process
channel.basicReject()

Modify the Testcontroller method to test sending the correct message:

 @RequestMapping("/send")
    public String send1(){
        rabbitTemplate.setConfirmCallback(myConfirmCallback);
        rabbitTemplate.setReturnCallback(myReturnCallBack);
        rabbitTemplate.convertAndSend("exchange_direct_demo01","itemxxx.insert","hello,insert");
        return "success";
    }

Set yml to manual confirmation mode

Test results:

10.3 current limiting at consumer end

If there is a large amount of concurrency, the manufacturer keeps sending messages, which may not be able to process so many messages. At this time, there are a lot of messages in the queue. When the consumer starts, many messages will flow in instantly, and the consumer may collapse instantly. At this time, we can limit the current at the consumer and release how many messages per second. In this way, the concurrency can be controlled, the load of the system can be reduced, and the availability of the system can be provided. This effect can often be used in second kill and rush purchase. Some configurations of finite flows are also in rabbitmq.

The configuration is as follows:

The default is 250.

Test: send 10 messages concurrently. At this time, as shown in the figure below, each message is processed one by one. The processing can be continued only after the processing is completed.

You can also simulate multithreading:

10.4 TTL

The full name of TTL is Time To Live. When the message reaches the lifetime, it has not been consumed and will be automatically cleared.

RabbitMQ sets the expiration time in two ways:

  • Set the expiration time for a queue; After the expiration time, all messages in the queue will be cleared if they are not consumed
  • Set the expiration time for a specific message; After setting the expiration time for a message in the queue, if the message is not deleted, it will be cleared.
It should be noted that:
 	When setting the expiration time for a specific message, it must be calculated when the message is at the head of the queue. If a message A Set the expiration time to 5 seconds, and the message B At the head of the team, the news B No expiration time is set, B At this time, it has been 5 seconds and has not been consumed. Note that at this time A The message is not deleted because it no longer has a queue header.
Generally, it is used alone in work TTL There are fewer cases.

To demonstrate TTL code steps:

1.Create configuration classes to configure expired queue switches and bindings
2.establish controller Test send message

To create a configuration class:

package com.yxinmiracle.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class TtlConfig {

    //Create expiration queue
    @Bean
    public Queue createqueuettl1(){
        //Set the queue expiration time to 10000 10S
        return QueueBuilder.durable("queue_demo02").withArgument("x-message-ttl",10000).build();
    }

    //Create switch

    @Bean
    public DirectExchange createExchangettl(){
        return new DirectExchange("exchange_direct_demo02");
    }

    //Create binding
    @Bean
    public Binding createBindingttl(){
        return BindingBuilder.bind(createqueuettl1()).to(createExchangettl()).with("item.ttl");
    }
}

To create a controller test:

@RequestMapping("/send2")
    public String send2(){
        rabbitTemplate.setConfirmCallback(myConfirmCallback);
        rabbitTemplate.setReturnCallback(myReturnCallBack);
        rabbitTemplate.convertAndSend("exchange_direct_demo02","item.ttl","hello,insert");
        return "success";
    }

Test:

After 10s:

10.5 dead letter queue

Dead letter queue: when the message becomes Dead message, it can be re sent to another switch, which is Dead Letter Exchange (Dead Letter Exchange abbreviation: DLX).

The process is as follows:

Three conditions for becoming a dead letter:

  1. The queue message length reaches the limit;
  2. The consumer rejects the consumption message, basicNack/basicReject, and does not put the message back into the original target queue, request = false;
  3. The message expiration setting exists in the original queue, and the message arrival timeout is not consumed;
10.5. 1. Processing of dead letter

DLX is also a normal Exchange. It is no different from general Exchange. It can be specified on any queue. In fact, it is to set the properties of a queue.

When there is a dead letter in the queue, RabbitMQ will automatically republish the message to the set Exchange and then route it to another queue.

You can listen to the messages in this queue and process them accordingly.

10.5. 2 setting of dead letter queue

Just now, the dead letter queue is also a normal exchange Just set some parameters.

Set parameters for the queue: x-dead-letter-exchange and x-dead-letter-routing-key.

Steps:

1.establish queue1 Receive forwarded dead letter
2.establish queue2 Used to receive messages sent by producers
3.Create dead letter switch
4.binding queue1 To dead letter switch

Configuration class code:

package com.yxinmiracle.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DlxConfig {


    //Create queue 1 to receive the forwarded dead letter queue1
    @Bean
    public Queue createqueuetdlq(){
        return QueueBuilder.durable("queue_demo03").build();
    }

    //Queue 2 is created to receive messages sent by the producer, and then to expire, it becomes a dead letter and is forwarded to queue 1
    @Bean
    public Queue createqueuetdelq2(){
        return QueueBuilder
            .durable("queue_demo03_deq") // name
            .withArgument("x-max-length",1)//Set the length of the queue
            .withArgument("x-message-ttl",10000)//Set the message expiration time of the queue to 10S
            .withArgument("x-dead-letter-exchange","exchange_direct_demo03_dlx")//Set dead letter switch name
            .withArgument("x-dead-letter-routing-key","item.dlx")//Set the dead letter route key item Dlx is routingkey
            .build();
    }

    //Create dead letter switch

    @Bean
    public DirectExchange createExchangedel(){
        return new DirectExchange("exchange_direct_demo03_dlx");
    }

    // queue1 is bound to the routing key of the dead letter exchange and the dead letter routing key specified when the queue forwards messages should be consistent
    @Bean
    public Binding createBindingdel(){
        return BindingBuilder.bind(createqueuetdlq()).to(createExchangedel()).with("item.dlx");
    }


}

controller:

 @RequestMapping("/send3")
    public String send3(){
        rabbitTemplate.convertAndSend("","queue_demo03_deq","hello,insert");
        return "success";
    }
10.5. 2.1 entering dead letter after test timeout:

After becoming a dead letter queue:

10.5. 2.2 the test refuses to sign in the dead letter
package com.yxinmiracle.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @version 1.0
 * @author:  YxinMiracle
 * @date:  2021-08-31 18:24
 */
@Component
@RabbitListener(queues = "queue_demo03_deq")
public class DlxListner {
    @RabbitHandler
    public void jieshouMsg(Message message, Channel channel,String msg){
        System.out.println(msg);
        try {
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Check the console and forward it to the dead letter queue immediately without waiting for 10S

10.5. 2.3 test setting length into dead letter

Modify the configuration and add the queue length parameter

Messages exceeding the length will directly enter the dead letter queue

10.6 delay queue

Delay queue, that is, messages will not be consumed immediately after entering the queue, but only after reaching the specified time. In rabbitmq, there is no concept of delay queue, but we can use ttl and dead letter queue to achieve the effect of delay. This requirement often appears in some application scenarios. Of course, you can also use plug-ins.

As shown in the figure:

1.The producer generates a message and sends it to queue1
2.queue1 Messages in are forwarded to when they expire queue2
3.Consumer in queue2 Get messages for consumption

The typical case in the above scenario: if the payment has not been made within 30 minutes after the order is placed, the order will be cancelled and the inventory will be rolled back. Let's simulate the following requirements:

Create configuration class

package com.yxinmiracle.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DelayConfig {
    //The normal queue receives messages transferred from the dead letter queue
    @Bean
    public Queue createQueue2(){
        return QueueBuilder.durable("queue_order_queue2").build();
    }

    //Dead letter queue -- > when messages are sent here in the future, the expiration time is not set here. We should set the expiration time of a message (ordered by a user) when sending messages
    @Bean
    public Queue createQueue1(){
        return QueueBuilder
                .durable("queue_order_queue1")
                .withArgument("x-dead-letter-exchange","exchange_order_delay")//Set up dead letter switch
                .withArgument("x-dead-letter-routing-key","item.order")//Set dead letter routing key
                .build();
    }

    //Create switch
    @Bean
    public DirectExchange createOrderExchangeDelay(){
        return new DirectExchange("exchange_order_delay");
    }

    //Create a binding to bind the normal queue to the dead letter switch
    @Bean
    public Binding createBindingDelay(){
        return BindingBuilder.bind(createQueue2()).to(createOrderExchangeDelay()).with("item.order");
    }
}

Modify controller

 @RequestMapping("/send5")
    public String send5(){
        // Simulated order
        System.out.println("checkout success ");
        // Simulated inventory reduction
        System.out.println("Inventory reduction succeeded");
        // Producer sends message
        rabbitTemplate.convertAndSend("queue_order_queue1", (Object) "Delayed queued messages: orderId The value of is 12334545", new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setExpiration("10000");// Set expiration time
                return message;
            }
        });
        return "success";
    }

Note: send the message to queue1 and listen to queue2

/**
 * Send order
 *
 * @return
 */
@RequestMapping("/send6")
public String send6() {
    //To send a message to the dead letter queue, you can use the default switch and specify ourtingkey as the name of the dead letter queue
    System.out.println("The user orders successfully. If there is no payment after 10 seconds, it will expire and roll back the order");
    System.out.println("Time:"+new Date());
    rabbitTemplate.convertAndSend("queue_order_queue1", (Object) "Ha ha, I want to check whether you have paid", new MessagePostProcessor() {
        @Override
        public Message postProcessMessage(Message message) throws AmqpException {
            message.getMessageProperties().setExpiration("10000");//Set the expiration time of the message
            return message;
        }
    });
    return "The user orders successfully. If there is no payment after 10 seconds, it will expire and roll back the order";
}

Set listening class

Note: listen to queue2 for listening messages and send queue1 for sending messages

@Component
@RabbitListener(queues = "queue_order_queue2")
public class OrderListener {

    @RabbitHandler
    public void orderhandler(Message message, Channel channel, String msg) {
        System.out.println("Get message:" + msg + ":Time is:" + new Date());
        try {
            System.out.println("Start of simulation check=====start");
            Thread.sleep(1000);
            System.out.println("End of simulation check=====end");
            System.out.println("If the user fails to pay, the check fails, and the rollback inventory processing is entered");
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Test successful

Code structure:

Topics: Java RabbitMQ Back-end