RabbitMQ graphic explanation of nearly 90000 words

Posted by Jeannie109 on Mon, 07 Mar 2022 04:58:12 +0100

I am Liao Zhiwei, a java development engineer, the founder of the behind the scenes big man community, a high-quality creator in the Java field and a CSDN blog expert. He has many years of front-line R & D experience, has studied the underlying source code of various common frameworks and middleware, and has practical architecture experience in large-scale distributed, micro service and three high architecture (high performance, high concurrency and high availability).

Blogger: java_wxid community: Big man behind the scenes

General content of this article:

1, The basic concept of RabbitMQ, as well as six working modes and message confirmation mechanism

About RabbitMQ: RabbitMQ is a message oriented middleware based on AMQP standard and Erlang language.

Basic concepts:

  • Producer: as the generator of messages.
  • Consumer: the consumer of the message.
  • Connection: the TCP connection between the message publisher or message consumer and the broker.
  • Channel: channel is a logical connection established inside the connection. If the application supports multithreading, usually each thread creates a separate channel for communication. AMQP method includes channel id to help the client and message broker identify the channel. Therefore, the channels are completely isolated, reducing the overhead of the operating system in establishing TCP connection.
  • Broker: an application that receives and distributes messages. RabbitMQ service is Message Broker.
  • Virtual host: virtual machine, designed for multi tenancy and security factors, divides the basic components of AMQP into a virtual group. It can be compared with mysql database, which will create many libraries, and the libraries are independent. When multiple different users use the services provided by the same RabbitMQserver, they can be divided into multiple vhosts. Each user creates exchange / queue in his own vhost.
  • Queue: queue, message queue, which receives messages and caches messages. Messages are finally sent here to wait for consumer s to take them away.
  • Binding: binding, virtual connection between exchange and queue. Binding can contain routing key. The binding information is saved in the query table in exchange for the distribution basis of message s
  • Exchange: the switch. The message arrives at the first stop of the broker, matches the routing key in the query table according to the distribution rules, and distributes the message to the queue. The commonly used types of switches are: Fanout: broadcast, deliver messages to all queues bound to the switch, Direct: Direct, and deliver messages to queues that meet the specified routing key Topic: wildcard, which gives the message to the queue conforming to the routing pattern

2, 6 working modes

1, Theory

RabbitMQ provides six working modes: simple mode, work queues, Publish/Subscribe publish and subscribe mode, Routing mode, Topics topic mode and RPC remote call mode (remote call, not too much message queue)

Simple mode: a producer sends the production message to the queue, and a consumer takes the message from the queue to consume the message. One producer and one consumer do not need to set the switch (use the default switch)

Note: similar to a mailbox, messages can be cached; Producers deliver messages to them, and consumers take messages out of them.

Work queues mode: a producer sends production messages to the queue, and one or more consumers take messages from the queue to consume messages. One producer and multiple consumers (competitive relationship) do not need to set the switch (use the default switch)

Note: in the case of heavy tasks or more tasks, using work queue can improve the speed of task processing. Application scenario: during the Spring Festival, 12306 grabs tickets and sends text messages to users. You can access multiple text message services to send, providing the processing speed of tasks.

Pub/Sub subscription mode: a producer sends a message to the switch, which processes the message. The queue is arbitrarily bound to the switch, assigns the message to a queue, and one or more consumers take the message from the queue to consume the message. The switch with the type of fanout needs to be set, and the switch is bound to the queue. When the message is sent to the switch, the exchange opportunity sends the message to the bound queue.

Note: 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!

Routing mode: a producer sends messages to the switch and specifies a routing key. The queue is bound to the switch through the routing key. When consuming, consumers need to take messages from the switch according to the routing key to consume messages. You need to set the switch with the type of direct, bind the switch to the queue, and specify the routing key. When a message is sent to the switch, the switch will send the message to the corresponding queue according to the routing key.

Note: the Routing mode requires the queue to specify the routing key when binding the switch, and the message will be forwarded to the queue that meets the routing key.

Topics wildcard mode: a producer sends the production message to the switch and uses the wildcard form (similar to the fuzzy query in mysql, for example, to obtain a batch of data with item prefix). The binding between the queue and the switch is through the wildcard. When consuming, consumers need to get the message from the switch according to the wildcard, Carry out consumption messages. It is necessary to set the switch with the type of topic, bind the switch to the queue, and specify the routing key in the wildcard mode. When a message is sent to the switch, the switch will send the message to the corresponding queue according to the routing key

Description: wildcard rule: # match one or more words, * match exactly one word. For example: lazy# Can match lazy insert. Content or lazy insert,Lazy.* Can only match lazy insert.

2, Code

Create a Maven project and introduce pom dependency:

    <dependencies>
        <!--rabbitmq client-->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.3.0</version>
        </dependency>
        <!--json Conversion kit-->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>
    </dependencies>

Create a tool class connecting Rabbitmq:

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

public class RabbitUtils {
    private static ConnectionFactory connectionFactory = new ConnectionFactory();
    static {
        connectionFactory.setHost("Yours rabbitmq of ip address");
        connectionFactory.setPort(5672);//The default port number of RabbitMQ, which can be modified according to the actual situation
        connectionFactory.setUsername("Yours rabbitmq User name for");
        connectionFactory.setPassword("Yours rabbitmq User password for");
        connectionFactory.setVirtualHost("Yours rabbitmq Virtual machine for");
    }
    public static Connection getConnection(){
        Connection conn = null;
        try {
            conn = connectionFactory.newConnection();
            return conn;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

Simple mode:

In order to distinguish and understand, I create a virtual machine for each mode. Here, I go to the rabbitMq control page to create a virtual machine

Modify the virtual machine of the tool class:

producer:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class Producer {

    public static void main(String[] args) throws Exception {
        //Get TCP long connection
        Connection conn = RabbitUtils.getConnection();
        //Create a communication "channel", which is equivalent to a virtual connection in TCP
        Channel channel = conn.createChannel();
        //Create a queue, declare and create a queue, and use it if it already exists
        //channel. Five parameters of queuedeclare
        //First parameter: queue name ID
        //The second parameter: whether to persist. false corresponds to non persistent data. If MQ stops, the data will be lost
        //The third parameter: whether to privatize the queue. false means that all consumers can access it. true means that only consumers who own it for the first time can always use it. Other consumers are not allowed to access it
        //Fourth: whether to delete automatically. false means that the queue will not be deleted automatically after the connection is stopped
        //Additional parameters, null
        channel.queueDeclare(RabbitConstant.QUEUE_TEST,false, false, false, null);
        String message = "To send message";
        //channel. Four parameters of basicpublish
        //The exchange switch is temporarily unavailable and will only be used later when publishing and subscribing
        //Queue name
        //Additional settings properties
        //The last parameter is the array of message bytes to be passed
        channel.basicPublish("", RabbitConstant.QUEUE_TEST, null,message.getBytes());
        channel.close();
        conn.close();
        System.out.println("===Sent successfully===");
    }
}

consumer:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

public class Consumer {

    public static void main(String[] args) throws Exception{
        //Get TCP long connection
        Connection conn = RabbitUtils.getConnection();
        //Create a communication "channel", which is equivalent to a virtual connection in TCP
        Channel channel = conn.createChannel();
        //Create a queue, declare and create a queue, and use it if it already exists
        //First parameter: queue name ID
        //The second parameter: whether to persist. false corresponds to non persistent data. If MQ stops, the data will be lost
        //The third parameter: whether to privatize the queue. false means that all consumers can access it. true means that only consumers who own it for the first time can always use it. Other consumers are not allowed to access it
        //Fourth: whether to delete automatically. false means that the queue will not be deleted automatically after the connection is stopped
        //Other additional parameters, null
        channel.queueDeclare(RabbitConstant.QUEUE_TEST,false, false, false, null);
        //Get data from MQ server
        //Create a message consumer
        //First parameter: queue name
        //The second parameter represents whether to automatically confirm the receipt of the message, and false represents manual programming to confirm the message, which is the recommended practice of MQ
        //The third parameter is passed into the implementation class of DefaultConsumer
        channel.basicConsume(RabbitConstant.QUEUE_TEST, false, new Reciver(channel));
    }
}


class  Reciver extends DefaultConsumer {

    private Channel channel;
    
    //Rewrite the constructor. The Channel object needs to be passed in from the outer layer and used in handleDelivery
    public Reciver(Channel channel) {
        super(channel);
        this.channel = channel;
    }

    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
         String message = new String(body);
         System.out.println("Message received by consumer:"+message);
         System.out.println("Informative TagId: "+envelope.getDeliveryTag());
        //false only confirms to sign in the current message. When set to true, it means to sign in all unsigned messages of the consumer
        channel.basicAck(envelope.getDeliveryTag(), false);
    }

I start the consumer first and then the producer, so that as soon as the producer produces the news, the consumer can consume immediately.

Work queues work queue mode:

In order to distinguish and understand, I create a virtual machine for each mode. Here, I go to the rabbitMq control page to create a virtual machine

Modify virtual machine of tool class

In order to simulate some businesses, I use a user-defined entity class to send messages, so I created a new user-defined entity class

/**
 * Custom entity class: send content
 */
public class SenderContent {

    private String name;
    private String content;

    public SenderContent(String name, String content) {
        this.name = name;
        this.content = content;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

producer:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.google.gson.Gson;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * Generator
 */
public class Producer {

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SENDER_CONTENT, false, false, false, null);
        for(int i = 1 ; i <= 100 ; i++) {
            SenderContent senderContent = new SenderContent("full name:" + i, "Content:" + i);
            String jsonSMS = new Gson().toJson(senderContent);
            channel.basicPublish("" , RabbitConstant.QUEUE_SENDER_CONTENT , null , jsonSMS.getBytes());
        }
        System.out.println("Sending data succeeded");
        channel.close();
        connection.close();
    }
}

Consumer 1:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;


/**
 * Consumer 1
 */
public class ConsumerOne {

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SENDER_CONTENT, false, false, false, null);
        //If basicQos (1) is not written, automatic MQ will send all requests to all consumers on average
        //basicQos,MQ no longer sends multiple requests to consumers at one time, but after the consumer processes a message (after confirmation), it obtains a new request from the queue
        channel.basicQos(1);//Take one after processing
        channel.basicConsume(RabbitConstant.QUEUE_SENDER_CONTENT , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String jsonSMS = new String(body);
                System.out.println("ConsumerOne-Sent successfully:" + jsonSMS);
                try {
                    Thread.sleep(10);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //Confirm receipt
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }

}

Consumer 2:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

/**
 * Consumer 2
 */
public class ConsumerTwo {

    public static void main(String[] args) throws IOException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SENDER_CONTENT, false, false, false, null);
        //If basicQos (1) is not written, automatic MQ will send all requests to all consumers on average
        //basicQos,MQ no longer sends multiple requests to consumers at one time, but after the consumer processes a message (after confirmation), it obtains a new request from the queue
        channel.basicQos(1);//Take one after processing
        channel.basicConsume(RabbitConstant.QUEUE_SENDER_CONTENT , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String jsonSMS = new String(body);
                System.out.println("ConsumerTwo-Sent successfully:" + jsonSMS);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //Confirm receipt
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }
}

Consumer III:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

/**
 * Consumer 3
 */
public class ConsumerThree {


    public static void main(String[] args) throws IOException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SENDER_CONTENT, false, false, false, null);
        //If basicQos (1) is not written, automatic MQ will send all requests to all consumers on average
        //basicQos,MQ no longer sends multiple requests to consumers at one time, but after the consumer processes a message (after confirmation), it obtains a new request from the queue
        channel.basicQos(1);//Take one after processing
        channel.basicConsume(RabbitConstant.QUEUE_SENDER_CONTENT , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String jsonSMS = new String(body);
                System.out.println("ConsumerThree-Sent successfully:" + jsonSMS);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //Confirm receipt
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }
}

Here, we set different dormancy time for each consumer, and demonstrate that each consumer has different time to process business, so as to check the message consumption

It can be seen that consumer one consumes the most and consumer three consumes the least, because this is set in the code

channel.basicQos(1);//Take one after processing

After the consumer has processed a message (after confirmation), it gets a new message from the queue.

Pub/Sub subscription mode:

In order to distinguish and understand, I create a virtual machine for each mode. Here, I go to the rabbitMq control page to create a virtual machine

Create a switch: here, the broadcast mode is used as the type of switch for demonstration

Modify virtual machine of tool class

producer:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.util.Scanner;

/**
 * Publisher
 */
public class Producer {

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitUtils.getConnection();
        //keyboard entry
        String input = new Scanner(System.in).next();
        Channel channel = connection.createChannel();
        //The first parameter is the switch name, and other parameters are the same as before
        channel.basicPublish(RabbitConstant.EXCHANGE_CONTENT,"" , null , input.getBytes());
        channel.close();
        connection.close();
    }
}

Consumer 1:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

/**
 * Consumer 1
 */
public class ConsumerOne {

    public static void main(String[] args) throws IOException {
        //Get TCP long connection
        Connection connection = RabbitUtils.getConnection();
        //Get virtual connection
        final Channel channel = connection.createChannel();
        //Declare queue information
        channel.queueDeclare(RabbitConstant.QUEUE_ONE, false, false, false, null);
        //queueBind is used to bind queues to switches
        //Parameter 1: queue name parameter 2: interactive machine name parameter 3: routing key (temporarily unavailable)
        channel.queueBind(RabbitConstant.QUEUE_ONE, RabbitConstant.EXCHANGE_CONTENT, "");
        channel.basicQos(1);
        channel.basicConsume(RabbitConstant.QUEUE_ONE , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("As soon as the consumer receives the message:" + new String(body));
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }

}

Consumer 2:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

/**
 * Consumer 2
 */
public class ConsumerTwo {

    public static void main(String[] args) throws IOException {
        //Get TCP long connection
        Connection connection = RabbitUtils.getConnection();
        //Get virtual connection
        final Channel channel = connection.createChannel();
        //Declare queue information
        channel.queueDeclare(RabbitConstant.QUEUE_TWO, false, false, false, null);
        //queueBind is used to bind queues to switches
        //Parameter 1: queue name parameter 2: interactive machine name parameter 3: routing key (temporarily unavailable)
        channel.queueBind(RabbitConstant.QUEUE_TWO, RabbitConstant.EXCHANGE_CONTENT, "");
        channel.basicQos(1);
        channel.basicConsume(RabbitConstant.QUEUE_TWO , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 2 receives information:" + new String(body));
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }
}

Demonstration effect:

Routing mode:

In order to distinguish and understand, I create a virtual machine for each mode. Here, I go to the rabbitMq control page to create a virtual machine

Modify virtual machine of tool class

Create switch: the switch type here must be changed to routing mode. If it is still fanout in broadcast mode, the effect will be the same as that in publish and subscribe mode above. Error instance:

Correct example:

producer:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Publisher
 */
public class Producer {

    public static void main(String[] args) throws Exception {
        Map area = new LinkedHashMap<String, String>();
        area.put("routing.one.a.20201127", "Changsha, Hunan, China 20201127 private data");
        area.put("routing.one.d.20201128", "China Hebei Shijiazhuang 20201128 private data");
        area.put("routing.two.b.20201127", "Wuhan, Hubei, China 20201127 private data");
        area.put("routing.two.e.20201128", "Wuhan, Hubei, China 20201128 private data");
        area.put("routing.three.c.20201127", "China Hunan Zhuzhou 20201128 private data");
        area.put("routing.three.f.20201128", "Zhengzhou, Henan, China 20201128 private data");
        area.put("us.one.a.20201127", "Private data in Los Angeles, California 20201127");
        area.put("us.two.b.20201128", "Private data in Los Angeles, California 20201128");
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();
        Iterator<Map.Entry<String, String>> itr = area.entrySet().iterator();
        while (itr.hasNext()) {
            Map.Entry<String, String> me = itr.next();
            //The first parameter is the name of the switch, and the second parameter is the routing key of the message
            channel.basicPublish(RabbitConstant.EXCHANGE_CONTENT_ROUTING,me.getKey() , null , me.getValue().getBytes());
        }
        channel.close();
        connection.close();
    }
}

Consumer 1:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

/**
 * Consumer 1
 */
public class ConsumerOne {

    public static void main(String[] args) throws IOException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_ONE, false, false, false, null);
        //queueBind is used to bind queues to switches
        //Parameter 1: queue name parameter 2: interactive machine name parameter 3: routing key
        channel.queueBind(RabbitConstant.QUEUE_ONE, RabbitConstant.EXCHANGE_CONTENT_ROUTING, "routing.one.a.20201127");
        channel.queueBind(RabbitConstant.QUEUE_ONE, RabbitConstant.EXCHANGE_CONTENT_ROUTING, "us.one.a.20201127");
        channel.basicQos(1);
        channel.basicConsume(RabbitConstant.QUEUE_ONE , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 1 receives a message:" + new String(body));
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });

    }

}

Consumer 2:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

/**
 * Consumer 2
 */
public class ConsumerTwo {

    public static void main(String[] args) throws IOException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_TWO, false, false, false, null);
        //queueBind is used to bind queues to switches
        //Parameter 1: queue name parameter 2: interactive machine name parameter 3: routing key
        channel.queueBind(RabbitConstant.QUEUE_TWO, RabbitConstant.EXCHANGE_CONTENT_ROUTING, "routing.one.d.20201128");
        channel.queueBind(RabbitConstant.QUEUE_TWO, RabbitConstant.EXCHANGE_CONTENT_ROUTING, "routing.two.e.20201128");
        channel.basicQos(1);
        channel.basicConsume(RabbitConstant.QUEUE_TWO , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 2 receives information:" + new String(body));
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });

    }

}

effect:

The routing mode requires the consumer to specify the routing key

Topics wildcard mode:

In order to distinguish and understand, I create a virtual machine for each mode. Here, I go to the rabbitMq control page to create a virtual machine

Modify virtual machine of tool class

Create an interactive machine with the type of topic

producer:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Publisher
 */
public class Producer {

    public static void main(String[] args) throws Exception {
        Map area = new LinkedHashMap<String, String>();
        area.put("routing.one.a.20201127", "Changsha, Hunan, China 20201127 private data");
        area.put("routing.one.d.20201128", "China Hebei Shijiazhuang 20201128 private data");
        area.put("routing.two.b.20201127", "Wuhan, Hubei, China 20201127 private data");
        area.put("routing.two.e.20201128", "Wuhan, Hubei, China 20201128 private data");
        area.put("routing.three.c.20201127", "China Hunan Zhuzhou 20201128 private data");
        area.put("routing.three.f.20201128", "Zhengzhou, Henan, China 20201128 private data");
        area.put("us.one.a.20201127", "Private data in Los Angeles, California 20201127");
        area.put("us.two.b.20201128", "Private data in Los Angeles, California 20201128");
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();
        Iterator<Map.Entry<String, String>> itr = area.entrySet().iterator();
        while (itr.hasNext()) {
            Map.Entry<String, String> me = itr.next();
            //The first parameter is the name of the switch, and the second parameter is the routing key of the message
            channel.basicPublish(RabbitConstant.EXCHANGE_CONTENT_TOPIC,me.getKey() , null , me.getValue().getBytes());
        }
        channel.close();
        connection.close();
    }
}

Consumer 1:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

/**
 * Consumer 1
 */
public class ConsumerOne {

    public static void main(String[] args) throws IOException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_ONE, false, false, false, null);
        //queueBind is used to bind queues to switches
        //Parameter 1: queue name parameter 2: interactive machine name parameter 3: routing key
        channel.queueBind(RabbitConstant.QUEUE_ONE, RabbitConstant.EXCHANGE_CONTENT_TOPIC, "*.*.*.20201127");
       // channel.queueBind(RabbitConstant.QUEUE_ONE, RabbitConstant.EXCHANGE_CONTENT_TOPIC, "us.two.b.20201128");
        channel.basicQos(1);
        channel.basicConsume(RabbitConstant.QUEUE_ONE , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 1 receives a message:" + new String(body));
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });

    }

}

Consumer 2:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

/**
 * Consumer 2
 */
public class ConsumerTwo {

    public static void main(String[] args) throws IOException {
        //Get TCP long connection
        Connection connection = RabbitUtils.getConnection();
        //Get virtual connection
        final Channel channel = connection.createChannel();
        //Declare queue information
        channel.queueDeclare(RabbitConstant.QUEUE_TWO, false, false, false, null);
        //Specify the relationship between the queue and the switch and the routing key
        channel.queueBind(RabbitConstant.QUEUE_TWO, RabbitConstant.EXCHANGE_CONTENT_TOPIC, "us.#");
        channel.basicQos(1);
        channel.basicConsume(RabbitConstant.QUEUE_TWO , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 2 receives information:" + new String(body));
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }

}

effect:

Note: if you want to switch modes for testing, you only need to modify the virtual machine in the tool class. The previous names are the same, which is to reflect that each virtual machine is isolated at this time, so it doesn't matter if the key is the same.

3, Message confirmation mechanism: confirm status and return status

1, Theory

confirm status: indicates the status generated when the producer delivers the message to the Broker. There are two situations:

  • ack: indicates that it has been signed by the Broker
  • nack: indicates that it has been rejected by the Broker. The reason may be that the queue is full, current limit, IO exception

return status: indicates that the producer delivers a message to the Broker and is signed in by the Broker, but there is no corresponding queue for delivery and returns the message to the producer.

Note: these two states are only related to producers and have nothing to do with consumers.

2, Code

Using the previous topic virtual machine, you won't create a new virtual machine. Create a switch:

producer:

package com.liao.rabbitmq.confirm;

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Publisher
 */
public class Producer {

    public static void main(String[] args) throws Exception {
        Map area = new LinkedHashMap<String, String>();
        area.put("routing.one.a.20211001", "China Changsha 20211001 private data");
        area.put("routing.two.b.20211001", "Wuhan, China 20211001 private data");
        area.put("routing.three.c.20211001", "China Zhuzhou 20211001 private data");
        area.put("routing.one.d.20211002", "China Shijiazhuang 20211002 private data");
        area.put("routing.two.e.20211002", "Wuhan, China 20211002 private data");
        area.put("routing.three.f.20211002", "China Zhengzhou 20211002 private data");
        area.put("routing.error.f.aaa", "Unsuccessful delivery of private data");
        area.put("us.one.a.20211001", "Los Angeles 20211001 private data");
        area.put("us.two.b.20211002", "Los Angeles 20211002 private data");
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();
        //Enable confirm listening mode
        channel.confirmSelect();
        channel.addConfirmListener(new ConfirmListener() {
            public void handleAck(long l, boolean b) throws IOException {
                //The second parameter represents whether the received data is received in batch, which is generally not used by us.
                System.out.println("The message has been Broker receive,Tag:" + l );
            }

            public void handleNack(long l, boolean b) throws IOException {
                System.out.println("The message has been Broker rejection,Tag:" + l);
            }
        });
        channel.addReturnListener(new ReturnCallback() {
            public void handle(Return r) {
                System.err.println("===========================");
                System.err.println("Return code:" + r.getReplyCode() + "-Return describe:" + r.getReplyText());
                System.err.println("Switch:" + r.getExchange() + "-route key:" + r.getRoutingKey() );
                System.err.println("Return Subject:" + new String(r.getBody()));
                System.err.println("===========================");
            }
        });
        Iterator<Map.Entry<String, String>> itr = area.entrySet().iterator();
        while (itr.hasNext()) {
            Map.Entry<String, String> me = itr.next();
            //The second parameter of Routing key is equivalent to the condition of data filtering
            //The third parameter is mandatory. True means to return to the producer if the message cannot be delivered normally. If false, the message will be discarded directly.
            channel.basicPublish(RabbitConstant.EXCHANGE_CONTENT_TOPIC_CONFIRM,me.getKey() ,true, null , me.getValue().getBytes());
        }
        //If it is turned off, monitoring cannot be performed, so it is not necessary to turn it off here
        /*channel.close();
        connection.close();*/
    }
}

Consumer 1:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

/**
 * Consumer 1
 */
public class ConsumerOne {

    public static void main(String[] args) throws IOException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_ONE, false, false, false, null);
        //queueBind is used to bind queues to switches
        //Parameter 1: queue name parameter 2: interactive machine name parameter 3: routing key
        channel.queueBind(RabbitConstant.QUEUE_ONE, RabbitConstant.EXCHANGE_CONTENT_TOPIC_CONFIRM, "*.*.*.20211001");
        channel.basicQos(1);
        channel.basicConsume(RabbitConstant.QUEUE_ONE , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 1 receives a message:" + new String(body));
                //channel. Three parameters of basicnack
                //The first parameter: long deliveryTag: unique ID.
                //The second parameter: boolean multiple: batch processing. When this parameter is true, the delivery can be confirmed at one time_ All messages with tag less than or equal to the incoming value.
                //The third parameter: Boolean request: if the request parameter is set to true, RabbitMQ will re store this message in the queue to send it to the consumer of the next subscription; If the request parameter is set to false, RabbitMQ immediately removes the message from the queue instead of sending it to a new consumer.
//                channel.basicNack(envelope.getDeliveryTag() , false,false);
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }
}

Consumer 2:

import com.liao.rabbitmq.utils.RabbitConstant;
import com.liao.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;

/**
 * Consumer 2
 */
public class ConsumerTwo {

    public static void main(String[] args) throws IOException {
        //Get TCP long connection
        Connection connection = RabbitUtils.getConnection();
        //Get virtual connection
        final Channel channel = connection.createChannel();
        //Declare queue information
        channel.queueDeclare(RabbitConstant.QUEUE_TWO, false, false, false, null);
        //Specify the relationship between the queue and the switch and the routing key
        channel.queueBind(RabbitConstant.QUEUE_TWO, RabbitConstant.EXCHANGE_CONTENT_TOPIC_CONFIRM, "us.#");
        channel.basicQos(1);
        channel.basicConsume(RabbitConstant.QUEUE_TWO , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("Consumer 2 receives information:" + new String(body));
//                channel.basicNack(envelope.getDeliveryTag() , false,false);
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }

}

Demonstration effect:

You can see that the printed return s are data without 20211001 suffix or us prefix in the key

2, Spring integrates RabbitMQ (simple mode, broadcast mode, routing mode, wildcard mode, message reliability delivery, message loss prevention, TTL, dead letter queue, delay queue, message backlog, message idempotence)

Through the actual code, Spring integrates RabbitMQ. The project is divided into two modules, consumer and produle.

1, Project code

1. Producers

1. Project architecture:

The code is as follows (example):

2.pom.xml dependency:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.sky</groupId>
    <artifactId>spring-rabbitmq-produle</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
3.spring-rabbitmq-producer.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">
    <!--Load profile-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- definition rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>
    <!--Define and manage switches and queues-->
    <rabbit:admin connection-factory="connectionFactory"/>

    <!--Define a persistent queue. If it does not exist, it will be created automatically; If it is not bound to the switch, it is bound to the default switch
    The default switch type is direct,The name is:"",The route name of the queue key is
    -->
    <!--
        id: bean Name of
        name: queue Name of
        auto-declare:Automatic creation
        auto-delete:Automatically delete. When the last consumer disconnects from the queue, the queue is automatically deleted
        durable: Persistent
    -->

    <rabbit:queue id="spring_queue" name="spring_queue"    auto-declare="true"/>

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~radio broadcast; All queues can receive messages~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--Define the persistent queue in the broadcast switch. If it does not exist, it will be created automatically-->
    <rabbit:queue id="spring_fanout_queue_1" name="spring_fanout_queue_1" auto-declare="true"/>

    <!--Define the persistent queue in the broadcast switch. If it does not exist, it will be created automatically-->
    <rabbit:queue id="spring_fanout_queue_2" name="spring_fanout_queue_2" auto-declare="true"/>

    <!--Define broadcast type switch; And bind the above two queues-->
    <rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange"  auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding queue="spring_fanout_queue_1"  />
            <rabbit:binding queue="spring_fanout_queue_2"/>
        </rabbit:bindings>
    </rabbit:fanout-exchange>


    <!-- Define queue-->
    <rabbit:queue id="spring_direct_queue" name="spring_direct_queue"  auto-declare="true"/>

    <!--
      definition Routing  Routing mode interactive machine
    -->
    <rabbit:direct-exchange name="spring_direct_exchange" >
        <rabbit:bindings>
            <!--direct Switch binding queue of type  key : route key  queue: Queue name-->
            <rabbit:binding queue="spring_direct_queue" key="direct"></rabbit:binding>
        </rabbit:bindings>

    </rabbit:direct-exchange>

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~Wildcards;*Match a word,#Match multiple words
    <!--Define the persistent queue in the broadcast switch. If it does not exist, it will be created automatically-->
    <rabbit:queue id="spring_topic_queue_one" name="spring_topic_queue_one"  auto-declare="true"/>
    <!--Define the persistent queue in the broadcast switch. If it does not exist, it will be created automatically-->
    <rabbit:queue id="spring_topic_queue_two" name="spring_topic_queue_two" auto-declare="true"/>
    <!--Define the persistent queue in the broadcast switch. If it does not exist, it will be created automatically-->
    <rabbit:queue id="spring_topic_queue_three" name="spring_topic_queue_three" auto-declare="true"/>

    <!--
      statement  topic Type of switch
    -->
    <rabbit:topic-exchange id="spring_topic_exchange"  name="spring_topic_exchange" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding pattern="one.*"  queue="spring_topic_queue_one"/>
            <rabbit:binding pattern="two.#" queue="spring_topic_queue_two"/>
            <rabbit:binding pattern="three.#" queue="spring_topic_queue_three"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!--definition rabbitTemplate Object operation can send messages easily in code-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>

</beans>
4.rabbitmq.properties:
rabbitmq.host=110.42.239.246
rabbitmq.port=5672
rabbitmq.username=guest
rabbitmq.password=guest
rabbitmq.virtual-host=spring

Note: rabbitmq connection mode is provided here for free for everyone to use and learn

5.ProducerTest:
package com.sky.springrabbitmqprodule;

import org.junit.Test;
import org.junit.runner.RunWith;
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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     * Simple mode messaging
     */
    @Test
    public void testHelloWorld(){
        rabbitTemplate.convertAndSend("spring_queue","hello world spring....");
    }


    /**
     * Message sending in broadcast mode
     */
    @Test
    public void testFanout(){
        rabbitTemplate.convertAndSend("spring_fanout_exchange","","spring fanout....");
    }

    /**
     * Send message in routing mode
     */
    @Test
    public void testDirect(){
        rabbitTemplate.convertAndSend("spring_direct_exchange","direct","spring Direct....");
    }

    /**
     * Send message in Wildcard mode
     */
    @Test
    public void testTopics(){
        rabbitTemplate.convertAndSend("spring_topic_exchange","one.onekey","spring topic one....");
        rabbitTemplate.convertAndSend("spring_topic_exchange","two.twokey.topic","spring topic two....");
        rabbitTemplate.convertAndSend("spring_topic_exchange","three.threekey.topic","spring topic three....");
    }
}

2. Consumers

1. Project architecture

The code is as follows (example):

2.pom.xml dependency:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.sky</groupId>
    <artifactId>spring-rabbitmq-consumer</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
3.spring-rabbitmq-consumer.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">
    <!--Load profile-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>
    
    <!-- definition rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>
        <!--Simple mode-->
<!--    <bean id="springQueueListener" class="com.sky.springrabbitmqconsumer.listener.SpringQueueListener"/>-->

        <!--Broadcast mode-->
<!--    <bean id="fanoutListener1" class="com.sky.springrabbitmqconsumer.listener.FanoutListener"/>-->
<!--    <bean id="fanoutListener2" class="com.sky.springrabbitmqconsumer.listener.FanoutListener2"/>-->

        <!--Routing mode-->
<!--    <bean id="springDirectQueue" class="com.sky.springrabbitmqconsumer.listener.SpringDirectQueue"/>-->

    <!--wildcard pattern -->
    <bean id="topicListenerOne" class="com.sky.springrabbitmqconsumer.listener.TopicListenerOne"/>
    <bean id="topicListenerTwo" class="com.sky.springrabbitmqconsumer.listener.TopicListenerTwo"/>
    <bean id="topicListenerThree" class="com.sky.springrabbitmqconsumer.listener.TopicListenerThree"/>

    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
            <!--Simple mode-->
<!--       <rabbit:listener ref="springQueueListener" queue-names="spring_queue"/>-->

            <!--Broadcast mode-->
<!--        <rabbit:listener ref="fanoutListener1" queue-names="spring_fanout_queue_1"/>-->
<!--        <rabbit:listener ref="fanoutListener2" queue-names="spring_fanout_queue_2"/>-->

            <!--Routing mode-->
<!--        <rabbit:listener ref="springDirectQueue" queue-names="spring_direct_queue"/>-->

        <!--wildcard pattern -->
        <rabbit:listener ref="topicListenerOne" queue-names="spring_topic_queue_one"/>
        <rabbit:listener ref="topicListenerTwo" queue-names="spring_topic_queue_two"/>
        <rabbit:listener ref="topicListenerThree" queue-names="spring_topic_queue_three"/>
    </rabbit:listener-container>
</beans>
4.rabbitmq.properties
rabbitmq.host=110.42.239.246
rabbitmq.port=5672
rabbitmq.username=guest
rabbitmq.password=guest
rabbitmq.virtual-host=spring

Note: the configuration is consistent with that of the producer

5.ConsumerTest
package com.sky.springrabbitmqconsumer.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ConsumerTest {

    public static void main(String[] args) {
        //Initialize IOC container
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring-rabbitmq-consumer.xml");


    }
}
6.FanoutListener
package com.sky.springrabbitmqconsumer.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class FanoutListener implements MessageListener {
    @Override
    public void onMessage(Message message) {
        //Print message
        System.out.println(new String(message.getBody()));
    }
}
7.FanoutListener2
package com.sky.springrabbitmqconsumer.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class FanoutListener2 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        //Print message
        System.out.println(new String(message.getBody()));
    }
}
8.SpringDirectQueue
package com.sky.springrabbitmqconsumer.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class SpringDirectQueue implements MessageListener {
    @Override
    public void onMessage(Message message) {
        //Print message
        System.out.println(new String(message.getBody()));
    }
}
9.SpringQueueListener
package com.sky.springrabbitmqconsumer.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class SpringQueueListener implements MessageListener {
    @Override
    public void onMessage(Message message) {
        //Print message
        System.out.println(new String(message.getBody()));
    }
}
10.TopicListenerOne
package com.sky.springrabbitmqconsumer.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class TopicListenerOne implements MessageListener {
    @Override
    public void onMessage(Message message) {
        //Print message
        System.out.println(new String(message.getBody()));
    }
}
11.TopicListenerTwo
package com.sky.springrabbitmqconsumer.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class TopicListenerTwo implements MessageListener {
    @Override
    public void onMessage(Message message) {
        //Print message
        System.out.println(new String(message.getBody()));
    }
}
12.TopicListenerThree
package com.sky.springrabbitmqconsumer.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class TopicListenerThree implements MessageListener {
    @Override
    public void onMessage(Message message) {
        //Print message
        System.out.println(new String(message.getBody()));
    }
}

The above is all the code of this project, and the following is the Demo content.

2, Project demonstration

Demonstrate simple mode:

Consumer uncomment:

Consumer initiated services:

Message sent by producer:

Consumer view message:

Demo broadcast mode:

Consumer uncomment:

Consumer initiated services:

Message sent by producer:

Consumer view message:

Demonstrate routing mode:

Consumer uncomment:

Consumer initiated services:

Message sent by producer:

Consumer view message:

Demonstrate wildcard mode:

Consumer uncomment:

Consumer initiated services:

Message sent by producer:

Consumer view message:

3, Message reliability delivery

The implementation of message reliability needs to ensure the following points:

  • Persistence exchange to be persistent queue to be persistent message to be persistent
  • Confirm confirmed by the manufacturer
  • Consumer confirms Ack
  • Broker high availability 1.rabbitmq the path of the whole message delivery producer--->rabbitmq broker--->exchange--->queue--->consumer
  • If the message goes from producer to exchange, a confirmCallback will be returned.
  • If the message fails to be delivered from exchange -- > queue, a returnCallback will be returned.

2. Steps to realize reliable message delivery

The producer sets publisher confirms = "true" of ConnectionFactory to enable confirmation mode.

The producer sets publisher returns = "true" of ConnectionFactory to enable the return mode.

Producers use rabbittemplate Setconfirmcallback sets the callback function. When the message is sent to exchange, the confirm method is called back. Judge the ack in the method. If it is true, the sending succeeds. If it is false, the sending fails and needs to be handled.

Producers use rabbittemplate Setreturncallback sets the return function. When the message fails to be routed from exchange to queue, if rabbittemplate is set Setmandatory (true) parameter, the message will be returned to the producer. And execute the callback function returnedMessage.

The consumer sets the acknowledge attribute in the rabbit: listener container tag, and sets the ack method none: automatic confirmation, manual: manual (none automatic confirmation mode is very dangerous. When the producer sends multiple messages and the consumer receives a message, it will automatically think that the currently sent message has been signed in. At this time, the consumer has an exception in business processing, and it will also think that the message has been signed in and processed normally, and the display in the queue has been consumed. Therefore, the real development will be changed to, (yes)

If there is no exception on the consumer side, the consumer calls channel basicAck(deliveryTag,false); Method to confirm the sign in message

If an exception occurs, the consumer calls basicNack or basicReject in catch, rejecting the message, and letting MQ resend the message. Note: Based on the above Spring integrated RabbitMQ code, the first change is to set the confirmation mode and return mode code:

The second change: declare the bean of queue and interaction machine

code:

    <!--Message reliability delivery (production side)-->
    <rabbit:queue id="test_queue_confirm" name="test_queue_confirm"></rabbit:queue>
    <rabbit:direct-exchange name="test_exchange_confirm">
        <rabbit:bindings>
            <rabbit:binding queue="test_queue_confirm" key="confirm"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

The third change: write the Confirm test method

    //Test Confirm mode
    @Test
    public void testConfirm() {
        //Define callback
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * @param correlationData Relevant configuration information
             * @param ack   exchange Whether the switch successfully received the message. true means success and false means failure
             * @param cause Failure reason
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("confirm Method was executed....");
                //ack is true, indicating that the message has reached the switch
                if (ack) {
                    //Received successfully
                    System.out.println("Receive success message" + cause);
                } else {
                    //If it is not delivered to the switch, it will fail to receive. For example, write the switch name incorrectly
                    System.out.println("Receive failure message" + cause);
                    //Do some processing to send the message again.
                }
            }
        });
        //Send message
        rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message Confirm...");
        //Perform sleep operation
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

The fourth change: write the Return test method

    //Test return mode
    @Test
    public void testReturn() {
        //When the switch processing failure message mode is set to true, when the message cannot reach the queue, the message will be returned to the producer again
        rabbitTemplate.setMandatory(true);
        //Define callback
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * @param message   Message object
             * @param replyCode Error code
             * @param replyText error message
             * @param exchange  Switch
             * @param routingKey Routing key
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("return Yes....");
                System.out.println("message:"+message);
                System.out.println("replyCode:"+replyCode);
                System.out.println("replyText:"+replyText);
                System.out.println("exchange:"+exchange);
                System.out.println("routingKey:"+routingKey);
                //Processing business
            }
        });
        //Send message
        rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message return...");
        //Perform sleep operation
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
consumer

First change: listener: AckListener

package com.sky.springrabbitmqconsumer.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;


@Component
public class AckListener implements ChannelAwareMessageListener {

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        //1. Get the id of the message
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //2. Get message
            System.out.println("message:"+new String(message.getBody()));
            //3. Conduct business processing
            System.out.println("=====Conduct business processing====");
            //The simulation is abnormal
            int  i = 5/0;
            //4. Sign in for messages
            channel.basicAck(deliveryTag, true);
        } catch (Exception e) {
            //Refuse to sign
             /*
              * The third parameter: request: return to the queue. If it is set to true, the message will return to the queue, and the broker will resend the message to the consumer
              */
            System.out.println("=====Business processing exception, message returned to queue,broker It will resend the message to the consumer====");
            channel.basicNack(deliveryTag, true, true);
        }
    }
}

Second change:

Instead of declaring bean objects one by one, you can scan the classes under a package

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

Third change:

In the rabbit: listener container tag, set the acknowledge attribute to manual confirmation (flow limit setting: prefetch attribute to grab 2 messages at a time and listen to the user-defined ackListener)

4. Demonstration of reliable message delivery

Demo demonstration of sending messages normally

Start producer Confirm mode:

Start consumer:

Start producer Return mode:

The consumer's console will keep printing:

Demo demonstration of sending message abnormally

Producer Confirm mode:

Producer Return mode:

4, Message flow restriction at the consumer end

1. Example diagram of current limiting

2. Implementation steps

  • stay rabbit:listener-container Configure the prefetch attribute in to set how many messages the consumer pulls at a time
  • The confirmation mode of the consumer must be manual confirmation: acknowledge="manual"

3. Specifically implement the current limiting code at the consumer end

consumer

The first modification: listener: QosListener

package com.sky.springrabbitmqconsumer.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;


@Component
public class QosListener implements ChannelAwareMessageListener {

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
          //Received message
        System.out.println(new String(message.getBody()));
        Thread.sleep(3000);
        //Processing business logic
        //Sign in the message. The second parameter: true indicates that all messages that have not been signed in before will be signed in
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
    }
}

Second amendment:

    <!--Define listener container
      acknowledge="manual":Manual sign in
        Automatic confirmation: acknowledge="none"
        Manual confirmation: acknowledge="manual"
        Confirm according to abnormal conditions: acknowledge="auto",(This method is troublesome and will not be explained)
      prefetch="1":How many messages are captured each time. The next message will be pulled only after the message is confirmed and signed in, otherwise the message will not be pulled
    -->
<rabbit:listener-container connection-factory="connectionFactory"
                               auto-declare="true"
                               acknowledge="manual"
                               prefetch="2">


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

Test method for sending messages in batches

    //Send messages in batches, allowing consumers to pull the specified quantity each time
    @Test
    public void  testQos(){
        for (int i = 0; i < 10; i++) {
            // send message
            rabbitTemplate.convertAndSend("test_exchange_confirm", "confirm", "message confirm....");
        }
    }

4. Demo demonstration of current limiting at the consumer end

Start consumer
Start producer
View consumer console log

Description: print a message every 3 seconds

Abnormal conditions, consumption is not signed

Restart the consumer and producer to send messages. At this time, you will see that only two of the ten messages originally sent are actually printed on the consumer's console. Because the prefetch attribute is configured with 2, two messages are pulled at one time.

<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

5, TTL

1. Business scenario

For example, when we place an order, if we fail to pay for more than 30 minutes, we will cancel the order and add back the inventory of the current goods.

2. Definitions

The full name of TTL is Time To Live (survival time / expiration time). When the message reaches the survival time and has not been consumed, it will be automatically cleared. RabbitMQ can set the expiration time for messages or for the entire Queue.

3. Implementation steps
  • Set the usage parameter of queue expiration time: x-message-ttl, unit: ms, which will uniformly expire the messages of the whole queue.
  • Set the expiration time of the message and use the parameter: expiration. Unit: ms (milliseconds). When the message is at the head of the queue (consumption), it will judge whether the message has expired.
  • If both are set, whichever is shorter. 4. Implement Demo through RabbitMQ management console page 1. Create message

2. Create a switch

3. Bind the switch to the message

4. Send message

If there is no consumer consumption for more than 5 seconds, it will automatically fail. 5. Realize TTL through code Add ttl queue

    <!--ttl-->
    <rabbit:queue name="test_queue_ttl" id="test_queue_ttl">
        <!--set up queue Parameters of-->
        <rabbit:queue-arguments>
            <!--x-message-ttl Refers to the expiration time of the queue-->
            <entry key="x-message-ttl" value="100000" value-type="java.lang.Integer"></entry>
        </rabbit:queue-arguments>
    </rabbit:queue>
     <rabbit:topic-exchange name="test_exchange_ttl" >
         <rabbit:bindings>
             <rabbit:binding pattern="ttl.#" queue="test_queue_ttl"></rabbit:binding>
         </rabbit:bindings>
     </rabbit:topic-exchange>

Sending message test method

    //ttl test
    @Test
    public void  testTtl(){
        for (int i = 0; i < 10; i++) {
            // send message
            rabbitTemplate.convertAndSend("test_exchange_confirm", "ttl.test", "message confirm....");
        }
    }

Start test method

Wait 10 seconds

6, Dead letter queue

1. Definitions

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

Note: there is no difference between dead letter switch and dead letter queue. When a message becomes dead letter, if the queue is bound to the dead letter switch, the message will be rerouted to the dead letter queue by the dead letter switch.

2. There are three situations in which a message becomes a dead letter

  • Message arrival queue length limit;
  • The consumer rejects the consumption message, basicNack/basicReject, and does not put the message back into the original target queue, request = false;
  • There is a message expiration setting in the original queue, and the message arrival timeout has not been consumed; 3. Queue binding dead letter switch Set parameters for the queue: x-dead-letter-exchange and x-dead-letter-routing-key

4. Code implementation

In spring rabbitmq producer Adding queues and switches to XML

    <!--
        Dead letter queue:
            1. Declare a normal queue(test_queue_dlx)And switches(test_exchange_dlx)
            2. Declare dead letter queue(queue_dlx)Dead letter switch(exchange_dlx)
            3. Normal queue binding dead letter switch
                Set two parameters:
                    * x-dead-letter-exchange: Name of dead letter switch
                    * x-dead-letter-routing-key: Sent to dead letter exchange routingkey
    -->
    <!--
        1. Declare a normal queue(test_queue_dlx)And switches(test_exchange_dlx)
    -->
     <rabbit:queue name="test_queue_dlx" id="test_queue_dlx">
         <!--3. Normal queue binding dead letter switch-->
         <rabbit:queue-arguments>
             <!--3.1 x-dead-letter-exchange: Name of dead letter switch-->
             <entry key="x-dead-letter-exchange" value="exchange_dlx" />
             <!--3.2 x-dead-letter-routing-key: Sent to dead letter exchange routingkey-->
             <entry key="x-dead-letter-routing-key" value="dlx.test" />
             <!--4.1 Set the expiration time of the queue ttl-->
             <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer" />
             <!--4.2 Set the length limit of the queue max-length -->
             <entry key="x-max-length" value="10" value-type="java.lang.Integer" />
         </rabbit:queue-arguments>
     </rabbit:queue>
     <!--    A normal switch is bound to a normal queue-->
     <rabbit:topic-exchange name="test_exchange_dlx">
         <rabbit:bindings>
             <rabbit:binding pattern="test.dlx.#" queue="test_queue_dlx"></rabbit:binding>
         </rabbit:bindings>
     </rabbit:topic-exchange>
    <!--
       2. Declare dead letter queue(queue_dlx)Dead letter switch(exchange_dlx)
   -->
     <rabbit:queue name="queue_dlx" id="queue_dlx"></rabbit:queue>
     <rabbit:topic-exchange name="exchange_dlx">
         <rabbit:bindings>
             <rabbit:binding pattern="dlx.#" queue="queue_dlx"></rabbit:binding>
         </rabbit:bindings>
     </rabbit:topic-exchange>

Start producer:

Viewing RabbitMQ control page

Message rejection is the same

7, Delay queue

1. Definitions

Delay queue means that messages will not be consumed immediately after entering the queue, but only after reaching the specified time. Rabbl + can provide delay effect in queue, but rabbl + does not provide delay effect in queue.

2. Scene

  1. If the order is not paid within 30 minutes after placing the order, the order shall be cancelled and the inventory shall be rolled back.
  2. Send SMS greetings after 7 days of successful registration of new users. 3. Concrete realization 1. Producers In spring rabbitmq producer XML add the following code
    <!--
        Delay queue:
            1. Define normal switch( order_exchange)And queue(order_queue)
            2. Define dead letter switch( order_exchange_dlx)And queue(order_queue_dlx)
            3. Bind and set the expiration time of the normal queue to 30 minutes
    -->
    <!-- 1. Define normal switch( order_exchange)And queue(order_queue)-->
    <rabbit:queue id="order_queue" name="order_queue">
        <!--3. Bind and set the expiration time of the normal queue to 30 minutes-->
        <rabbit:queue-arguments>
            <entry key="x-dead-letter-exchange" value="order_exchange_dlx" />
            <entry key="x-dead-letter-routing-key" value="dlx.order.cancel" />
            <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer" />
        </rabbit:queue-arguments>
    </rabbit:queue>
<!--    Switch and queue of order service-->
    <rabbit:topic-exchange name="order_exchange">
        <rabbit:bindings>
            <rabbit:binding pattern="order.#" queue="order_queue"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>
    <!--2. Define dead letter switch( order_exchange_dlx)And queue(order_queue_dlx)-->
    <rabbit:queue id="order_queue_dlx" name="order_queue_dlx"></rabbit:queue>
    <rabbit:topic-exchange name="order_exchange_dlx">
        <rabbit:bindings>
            <rabbit:binding pattern="dlx.order.#" queue="order_queue_dlx"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>

Send message test

    /*
     * Test delay message
     * */
    @Test
    public  void testDelay() throws InterruptedException {
        //1. Send order message. In the future, in the order system, after the order is placed successfully, a message will be sent
        rabbitTemplate.convertAndSend("order_exchange","order.msg","Order information: id=1,time=2021 October...");
        //2. Print countdown 10 seconds
        for (int i = 10; i > 0 ; i--) {
            System.out.println(i+"...");
            Thread.sleep(1000);
        }
    }
2. Consumers

spring-rabbitmq-consumer.xml configuration

<!--Delay queue effect: be sure to monitor the dead letter queue!!!-->
          <rabbit:listener ref="orderListener" queue-names="order_queue_dlx"></rabbit:listener>

Add listener OrderListener

package com.sky.springrabbitmqconsumer.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;


@Component
public class OrderListener implements ChannelAwareMessageListener {

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //1. Receive conversion message
            System.out.println(new String(message.getBody()));
            //2. Processing business logic
            System.out.println("Processing business logic...");
            System.out.println("According to the order id Query its status...");
            System.out.println("Judge whether the status is payment success");
            System.out.println("Cancel order and roll back inventory....");
            //3. Manual sign in
            channel.basicAck(deliveryTag,true);
        } catch (Exception e) {
            //e.printStackTrace();
            System.out.println("Exception occurred, rejected");
            //4. Refuse to sign in and do not return to the queue. Request = false
            channel.basicNack(deliveryTag,true,false);
        }
    }
}

4.Demo demo

Start producer

Start consumer

Note: the message is sent after ten seconds

8, Message backlog

1. Scene

  • Consumer downtime message backlog
  • Insufficient consumer spending power
  • Sending traffic is too large 2 Solution

More consumers go online for normal consumption and special queue consumption access. First take out the messages in batch, record them in the database, and then process them slowly.

9, Message idempotency

1. Definitions

Idempotency refers to one or more requests for a resource, which should have the same result for the resource itself. In other words, the impact of any multiple execution on the resource itself is the same as that of one execution. In MQ, it refers to consuming multiple identical messages and getting the same result as consuming the message once.

2. Solutions

Message idempotency Guarantee -- optimistic locking mechanism

In addition, I also provided the project address to clone. The address link is:[ https://gitee.com/java_wxid/liao ]( https://gitee.com/java_wxid/liao ), the spring rabbitmq produle in the project is changed to spring rabbitmq producer. The original meaning is that the product production information is given to consumers for consumption. I'm afraid that everyone will make a mistake and change the produle to a producer, which is more understandable.

3, Springboot integrates RabbitMQ (direct connection mode, work queue mode, publish subscribe mode, routing mode, wildcard mode

Tip: the Springboot integration Rabbitmq actual combat case is demonstrated through interface call.

1, Integration steps

1, Producer:

Create SpringBoot project

Introducing pom dependency

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

Write rabbitmq configuration message

Configuration classes that define switches, queues, and binding relationships

Inject RabbitTemplate and call method to complete message sending

2, Consumer:

Create SpringBoot project

Introducing pom dependency

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

Write rabbitmq configuration message

Define a listening class and use the @ RabbitListener annotation to complete queue listening.

2, Implementation steps

1. Project architecture

2. Create project

The code is as follows (example):

1.pom dependency
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.sky</groupId>
    <artifactId>springboot-rabbitmq-module</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-rabbitmq-module</name>
    <description>springboot-rabbitmq-module</description>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

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

        <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>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>springboot_rabbitmq</finalName>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

</project>
2.application.properties configuration
server.port=8080
#spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.addresses=110.42.239.246
spring.rabbitmq.virtual-host=springboot

#spring.rabbitmq.addresses=110.42.239.246:5672,110.42.239.247:5672,110.42.239.248:5672

Note: rabbitmq connection mode is provided here for free for everyone to use and learn

3.config configuration
HelloWorldConfig
package com.sky.springbootrabbitmqmodule.config;

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

/**
 * HelloWorld rabbitmq The first working mode explained in class
 * The direct connection mode only needs to declare the queue, and all messages are forwarded through the queue.
 * There is no need to set up a switch
 */
@Configuration
public class HelloWorldConfig {

    @Bean
    public Queue setQueue() {
        return new Queue("helloWorldqueue");
    }
}
FanoutConfig
package com.sky.springbootrabbitmqmodule.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Fanout The mode needs to declare exchange and bind the queue, and exchange is responsible for forwarding to the queue.
 * The broadcast mode switch type is set to fanout
 */
@Configuration
public class FanoutConfig {

    //Declaration queue
    @Bean
    public Queue fanoutQ1() {
        return new Queue("fanout.q1");
    }
    @Bean
    public Queue fanoutQ2() {
        return new Queue("fanout.q2");
    }


    //Declare exchange
    @Bean
    public FanoutExchange setFanoutExchange() {
        return new FanoutExchange("fanoutExchange");
    }


    //Declare the binding relationship between Binding,exchange and queue
    @Bean
    public Binding bindQ1() {
        return BindingBuilder.bind(fanoutQ1()).to(setFanoutExchange());
    }
    @Bean
    public Binding bindQ2() {
        return BindingBuilder.bind(fanoutQ2()).to(setFanoutExchange());
    }

}
WorkConfig
package com.sky.springbootrabbitmqmodule.config;



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

@Configuration
public class WorkConfig {


    //Declaration queue
    @Bean
    public Queue workQ1() {
        return new Queue("work_sb_mq_q");
    }

}
DirectConfig
package com.sky.springbootrabbitmqmodule.config;

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


/*
   Routing mode | routing mode switch type: direct
*/
@Configuration
public class DirectConfig {

    //Declaration queue
    @Bean
    public Queue directQ1() {
        return new Queue("direct_sb_mq_q1");
    }
    @Bean
    public Queue directQ2() {
        return new Queue("direct_sb_mq_q2");
    }


    //Declare exchange
    @Bean
    public DirectExchange setDirectExchange() {
        return new DirectExchange("directExchange");
    }

    //To declare binding, you need to declare a routingKey
    @Bean
    public Binding bindDirectBind1() {
        return BindingBuilder.bind(directQ1()).to(setDirectExchange()).with("directBind.one");
    }
    @Bean
    public Binding bindDirectBind2() {
            return BindingBuilder.bind(directQ2()).to(setDirectExchange()).with("directBind.two");
    }

}
TopicConfig
package com.sky.springbootrabbitmqmodule.config;

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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/*
Topics Mode switch type topic
* */
@Configuration
public class TopicConfig {

    //Declaration queue
    @Bean
    public Queue topicQ1() {
        return new Queue("topic_sb_mq_q1");
    }
    @Bean
    public Queue topicQ2() {
        return new Queue("topic_sb_mq_q2");
    }


    //Declare exchange
    @Bean
    public TopicExchange setTopicExchange() {
        return new TopicExchange("topicExchange");
    }

    //To declare binding, you need to declare a roytingKey
    @Bean
    public Binding bindTopicHebei1() {
        return BindingBuilder.bind(topicQ1()).to(setTopicExchange()).with("directBind.*");
    }
    @Bean
    public Binding bindTopicHebei2() {
        return BindingBuilder.bind(topicQ2()).to(setTopicExchange()).with("#.two");
    }

}
4. Consumer component
package com.sky.springbootrabbitmqmodule.component;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class ConcumerReceiver {

    //Multiple consumers in the direct connection mode will be assigned to one of them for consumption. Similar to task mode
    //Set some properties by injecting RabbitContainerFactory object, which is equivalent to channel in task basicQos
    @RabbitListener(queues="helloWorldqueue")
    public void helloWorldReceive(String message) {
         System.out.println("helloWorld pattern received message : " +message);
    }

    //Work queue mode
    @RabbitListener(queues="work_sb_mq_q")
    public void wordQueueReceiveq1(String message) {
        System.out.println("Work queue mode 1 received message : " +message);
    }

    @RabbitListener(queues="work_sb_mq_q")
    public void wordQueueReceiveq2(String message) {
        System.out.println("Work queue mode 2 received message : " +message);
    }


    //Message listening in pub/sub mode
    @RabbitListener(queues="fanout.q1")
    public void fanoutReceiveq1(String message) {
        System.out.println("Publish subscribe mode 1 received message : " +message);
    }
    @RabbitListener(queues="fanout.q2")
    public void fanoutReceiveq2(String message) {
        System.out.println("Publish subscribe mode 2 received message : " +message);
    }


    //Routing mode
    @RabbitListener(queues="direct_sb_mq_q1")
    public void routingReceiveq1(String message) {
        System.out.println("Routing Routing mode routingReceiveqOne received message : " +message);
    }

    @RabbitListener(queues="direct_sb_mq_q2")
    public void routingReceiveq2(String message) {
        System.out.println("Routing Routing mode routingReceiveqTwo received message : " +message);
    }


    //topic mode
    //Note that this pattern will have a priority matching principle. For example, send routingkey = Hunan It, that matches Hunan* (Hunan. It, Hunan. ECO), then you won't match * ITd
    @RabbitListener(queues="topic_sb_mq_q1")
    public void topicReceiveq1(String message) {
        System.out.println("Topic pattern topic_sb_mq_q1 received message : " +message);
    }

    @RabbitListener(queues="topic_sb_mq_q2")
    public void topicReceiveq2(String message) {
        System.out.println("Topic pattern topic_sb_mq_q2 received  message : " +message);
    }
    
}
5. Producer controller
package com.sky.springbootrabbitmqmodule.controller;

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.UnsupportedEncodingException;

@RestController
public class ProducerController {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    //helloWorld direct connection mode
    @GetMapping(value="/helloWorldSend")
    public Object helloWorldSend(String message) throws AmqpException, UnsupportedEncodingException {
        //Set some request parameters
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //Send a message
        rabbitTemplate.send("helloWorldqueue",new Message(message.getBytes("UTF-8"),messageProperties));
        return "message sended : "+message;
    }


    //Work queue mode
    @GetMapping(value="/workqueueSend")
    public Object workqueueSend(String message) throws AmqpException, UnsupportedEncodingException {
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //Create multiple messages for sending operation
        for (int i = 0; i <10 ; i++) {
            rabbitTemplate.send("work_sb_mq_q",  new Message(message.getBytes("UTF-8"),messageProperties));
        }
        return "message sended : "+message;
    }


    // pub/sub publish subscribe mode switch type fanout
    @GetMapping(value="/fanoutSend")
    public Object fanoutSend(String message) throws AmqpException, UnsupportedEncodingException {
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //fanout mode only sends messages to exchange. Distribute to all queue s under exchange
        rabbitTemplate.send("fanoutExchange", "", new Message(message.getBytes("UTF-8"),messageProperties));
        return "message sended : "+message;
    }


    //routing working mode switch type direct
    @GetMapping(value="/directSend")
    public Object routingSend(String routingKey,String message) throws AmqpException, UnsupportedEncodingException {
        if(null == routingKey) {
            routingKey="directBind.one";
        }
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //fanout mode only sends messages to exchange. Distribute to all queue s under exchange
        rabbitTemplate.send("directExchange", routingKey, new Message(message.getBytes("UTF-8"),messageProperties));
        return "message sended : routingKey >"+routingKey+";message > "+message;
    }


    //Topic working mode switch type topic
    @GetMapping(value="/topicSend")
    public Object topicSend(String routingKey,String message) throws AmqpException, UnsupportedEncodingException {
        if(null == routingKey) {
            routingKey="directBind.one";
        }
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //fanout mode only sends messages to exchange. Distribute to all queue s under exchange
        rabbitTemplate.send("topicExchange", routingKey, new Message(message.getBytes("UTF-8"),messageProperties));
        return "message sended : routingKey >"+routingKey+";message > "+message;
    }

}

The above is all the code of this project, and the following is the Demo content.

3, Demonstration steps

1. Start the project

2. Demonstration of calling interface

1. Direct connection mode
1. Interface call
2. Console printing
2. Work queue mode
1. Interface call
2. Console printing
3. Publish subscribe mode (switch type: fanout)
1. Interface call
2. Console printing
4. Routing mode (switch type: direct)
1. Interface call
2. Console printing
5. Wildcard mode (switch type: topic)
1. Interface call
2. Console printing

In addition, I also provided the project address to clone. The address link is: https://gitee.com/java_wxid/liao

4, RabbitMQ cluster construction

1, Preparatory work

First, build many independent RabbitMQ. Here you can use the pagoda graphical page to install or install by yourself. Assuming that the two servers have been built separately, it is necessary to exchange data between servers at this time.

2, Cluster construction

1. Cluster building steps

Set server alias

 - Server 1: hostnamectl set‐hostname m1 
 - Server 2: hostnamectl set‐hostname m2

Unify Erlang in m1 server The cookie value in the cookie file will be the value in m1 erlang. Sync cookies to m2

scp /var/lib/rabbitmq/.erlang.cookie m2:/var/lib/rabbitmq/.erlang.cookie

Note: in the above command, m2 can also use ip to add nodes to rabbitmq cluster: restart the service of rabbitmq in m2 machine and execute it in m2

#Stop user request
rabbitmqctl stop_app
#Merge m2 into cluster
rabbitmqctl join_cluster ‐‐ram rabbit@m2
#Open user request
rabbitmqctl start_app
#Open the management page
rabbitmq‐plugins enable rabbitmq_management
#Restart service
systemctl restart rabbitmq‐server.service

View cluster information

rabbitmqctl cluster_status

2. Cluster setup load balancing - HAProxy setup

1. Perform installation

#1. Install yum install haproxy #2 and configure haproxy Refer to the following configuration for the cfg file: haproxy VIM / etc / haproxy / haproxy cfg. Enter the file, find maxconn 3000, delete the following contents, add cluster monitoring, and start haproxy monitoring service. The code is as follows:

#Monitor the MQ cluster
listen rabbitmq_cluster
    bind 0.0.0.0:5672
    option tcplog
    mode tcp
    option clitcpka
    timeout connect 1s
    timeout client 10s
    timeout server 10s
    balance roundrobin
    server node1 Node 1 ip address:5672 check inter 5s rise 2 fall 3 
    server node2 Node 2 ip address:5672 check inter 5s rise 2 fall 3
#Start haproxy monitoring service
listen http_front
    bind 0.0.0.0:1080 
    stats refresh 30s 
    stats uri /haproxy_stats 
    stats auth admin:admin

#3. Start haproxy systemctl start haproxy #4. View the haproxy process status systemctl status haproxy The service # status is as follows: it has been started successfully Active: active (running) # access the following address to monitor the mq node http: / / server IP: 1080 / haproxy_ Accessing the mq cluster address in stats # code changes to accessing the haproxy address: 5672

2.haproxy.cfg configuration details
listen rabbitmg cluster 
    bind 0.0.0.0:5672#Map M1 and M2 through 5672 
    option tcplog #Record the status and time of tcp connection 
    mode tcp#Four layer protocol proxy, that is, forwarding TCP protocol 
    option clitcpka #Turn on Keep Alive.TCP (long connection mode)
    timeout connect 1s #Timeout of establishing connection between haproxy and mq 
    timeout client 10s#Maximum idle time between client and haproxy.
    timeout server 10s #Maximum idle time between server and haproxy 
    balance roundrobin #Use polling to forward messages 
    #Send a heartbeat packet every 5 seconds. If there is a response twice in a row, it means that it is in good condition. 
    #If there is no response for three consecutive times, it will be regarded as a service failure and the node will be eliminated.
    server node1 ip1:5672 check inter 5s rise 2 fall 3 
    server node2 ip2:5672 check inter 5s rise 2 fall 3 
listen http front 
    #Listening port 
    bind 0.0.0.0:1080 
    #Statistics page automatic refresh time stats refresh 30s 
    #Statistics page url 
    stats uri /haproxy?stats 
    #Specify the HAproxy access user name and password settings 
    stats auth admin:admin

At this time, you can connect through haproxy proxy. Of course, haproxy also has its own management page, that is, you can directly access the ip of the server and the configured 1080 port. Of course, HA can also be configured with multiple servers.

summary

The above is what I want to talk about today. The full text has a total of more than 88000 words. It introduces in detail the basic concepts of RabbitMQ, six working modes, message reliability delivery, dead letter queue, delay queue and so on. It is intuitively expressed through graphic code to demonstrate the effect to you. In addition, it also includes the integration of RabbitMQ with Spring framework and RabbitMQ with Spring boot framework. I also hope that readers can actively participate in the discussion in the comment area and put forward some valuable opinions or suggestions to the article. I will adopt the updated blog and share with you again.