[RabbitMQ] Introduction to native API learning

Reference: "RabbitMQ Practical Guide"
Reference: RabbitMQ official website tutorial
PS: A sample code acquisition address (code cloud) will be attached at the end of the article.

For installation and other related blogs, please refer to the following:
[RabbitMQ] Basic related concepts and installation (including installation under windows)
[RabbitMQ] Installation under Linux [Express version]
[RabbitMQ] Installation under Linux [Detailed version]
[RabbitMQ] Basics A brief introduction to terms and consoles


1.General Overview

First of all, why do we need to learn native API here ? Because although RabbitMQ is a relatively mature technology, for example, it can be integrated and used in Spring and Spring Boot . Development can be completed using native unencapsulated code and integrated using Spring. Configuration files can simplify development code, and using annotation development in Spring Boot can further improve efficiency. However, no matter how encapsulated or upgraded, its essence remains unchanged (of course, the most commonly used one now is definitely integrated in Spring Boot). The purpose of this blog is When newbies get started, and lay a solid foundation, and master the native use, most of the encapsulated content is just to master the simplified usage, and still focus on the basics.
In Spring , the integration and use in Spring Boot will be introduced in other blogs later.

2. Create a project and introduce dependencies

This article uses IDEA to create a skeleton-free ordinary Maven project. When we use RabbitMQ, we need to introduce its jar package. Using Maven is undoubtedly a convenient choice (lazy). Of course, we can also download the jar and introduce it directly into the project. There is a download details page for the corresponding Java Client client on the official website: RabbitMQ Java Client Library
takes a simple screenshot to understand:
The 5.x version needs to support JDK8 or above, and the 4.x version needs JDK6 or above. This article uses the 5.x version, JDK1.8. It's open source.

Insert image description here
As of now, the latest version 5.10.0 provides dependency coordinates for Mavne and Gradle. You can copy it directly later.

Insert image description here
And finally, the download address of the Jar package is provided:

Insert image description here
And the github source code project address:

Insert image description here
Before writing the code, I’d better put this picture here to facilitate understanding of the process:

Insert image description here

3. Step one: Connect to RabbitMQ

Overview of the book:
Insert image description here
Here we are using ConnectionFactory: connection factory
using the latest client package:

Insert image description here
Next, we create a producer code package and write the code to connect to Rabbitmq:
here I use the self-created account cfl and virtual machine/test. It is no problem to use the default user guest and virtual machine/. Created The user mentioned at the end of this article: [RabbitMQ] A brief introduction to basic terms and consoles

public static void main(String[] args) throws
            IOException, TimeoutException, NoSuchAlgorithmException, KeyManagementException, URISyntaxException {
    
    

        // 第一种方式,方法设定参数

        // 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 主机地址: 默认为 localhost
        connectionFactory.setHost("localhost");
        // 连接端口: 默认为 5672
        connectionFactory.setPort(5672);
        // 虚拟主机名称: 默认为 /
        connectionFactory.setVirtualHost("/test");
        // 连接用户名: 默认为guest
        connectionFactory.setUsername("cfl");
        // 连接密码: 默认为guest
        connectionFactory.setPassword("cfl");
        // 从工厂获得连接:
        Connection connection = connectionFactory.newConnection();

        // 第二种方式,使用URL方式
        ConnectionFactory connectionFactoryForURL = new ConnectionFactory();
		connectionFactoryForURL.setUri("amqp://userName:password@ipAddress:portNumber/virtualHost");

        // 用连接创建Channel
        Channel channel = connection.createChannel();

    }

After creation, the Channel can be used to send or receive messages.

4. Official website tutorial introduction (Java example)

Address: RabbitMQ official website tutorial
As of the end of 2020, the official website tutorial has provided seven pieces of content. This time our introductory study will only learn the first five pieces of content. Why not learn the last two pieces? There are two reasons. First, RPC calls are not Too RabbitMQ, second: Publisher Confirms is not a typical mode, it can only be regarded as an extension. The sender confirms the service arrival of the message. Interested friends can come down and learn by themselves. The demo content is relatively simple.
We mainly understand the four parts 2, 3, 4, and 5 (also called RabbitMQ working mode ) plus the first getting started example.
RabbitMQ provides a variety of client operations and supports many languages. As follows:

Insert image description here
Insert image description here
Insert image description here
And finally, front row tipsIf you are a beginner, it is not recommended to skip or read these tutorials on the official website. The use of RabbitMQ components in these tutorials is a step-by-step process. You can also feel the advancement of knowledge in the examples and introductions below. Enter the relationship.

Learning steps from top to bottom:
hello world : producer+queue+consumer
work queue : producer+queue+MultipleConsumer
Publish/Subscribe : Producer +switch+queue+binding+Consumer
Routing : Producer+Switch+Queue+Binding+routing key+Bind key+ConsumerTopics
: producer+switch+queue+binding+routing keywildcard+Bind keywildcard+consumer

as well as:The more general and common patterns are the last two high-level content Routing and Topics .

5. Official website tutorial "Hello World!" and extension [Queue Statement] + [Two implementation methods of consuming messages]

Insert image description here


Above we have completed the creation of RabbitMQ connections and information channels. Next we need to declare (create) switches and queues . When we use them in the program, we need to ensure that both of them already exist, otherwise an exception will be thrown.
There is no explicit statement (creation) of the switch in the "Hello World!" tutorial because it uses the default switch. Here we also omit the creation process of the switch and mainly explain the creation of the queue. We will explain the switch in the following content. of creation.
For the creation of switches and queues, the method provided by the official website is called declaration: declare
The following code is the producer P:producer side code: declare the queue and send a message to it

// 声明(创建)一个队列
channel.queueDeclare("HELLO_WORLD_QUEUE", false, false, false, null);
String message = "Hello World!";
// 发送消息
channel.basicPublish("", "HELLO_WORLD_QUEUE", null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
// 关闭资源
channel.close();
connection.close();

declare queueThe parameters of the method are as follows:
Insert image description here
Here is directly attached the parameter introduction in the book, which is more detailed:
Insert image description here
There are not many overloaded methods for declaring queue methods, but there are two similar methods. Here is a brief introduction:

Insert image description here

1. The queueDeclare() method has no parameters and uses the default value. It is also called an anonymous queue. The queue name it creates is also an automatically generated garbled code, similar to amq.gen-LhQzlgv3GhDOv8PIDabOXA, which is exclusive and automatically deleted. , non-persistent queue.
2.queueDeclare(…) has been introduced above.
3.queueDeclareNoWait(...) method, the method is as its name, nowait, impatient sub-method. Among the four methods, this method has no return value. The others will return the result of creating the queue, DeclareOk, and this method does not need it. If the creation of the queue occurs, Exception, or there is a delay in creating the queue, problems may occur when using it. It is not recommended to use this method (why bother if you can’t eat hot tofu in a hurry ( ̄▽ ̄)"). 4.queueDeclarePassive(…) method, which is more commonly
used . This method is used to detect whether the corresponding queue exists. If it exists, it will return normally. If it does not exist, it will throw an exception: 404 channel exception. The parameter is the queue name.

Here we execute the code on the producer side, and then check the corresponding console:
It was found that the queue was created successfully and a message was received.

Insert image description here
Let’s open the message to see:

Insert image description here
Indeed the message we sent before, no problem.
Let’s take a brief look at the method of sending messages, and there will be chapters to explain in detail:

Insert image description here
There are four parameters. We have not specifically defined the use of a switch here, so the switch parameter is an empty string. The routingKey has not been introduced yet. Let’s use it directly here. We will introduce it in detail later. The value here is the name of the queue. The third parameter In order to extend the attribute, it is not used for the time being. The fourth parameter is the message body of the message we want to send, and a byte array needs to be passed in.

Next, complete the consumer C:consumer side code: declare the queue and listen to the queue to obtain messages.

		// 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 主机地址: 默认为 localhost
        connectionFactory.setHost("localhost");
        // 连接端口: 默认为 5672
        connectionFactory.setPort(5672);
        // 虚拟主机名称: 默认为 /
        connectionFactory.setVirtualHost("/test");
        // 连接用户名: 默认为guest
        connectionFactory.setUsername("cfl");
        // 连接密码: 默认为guest
        connectionFactory.setPassword("cfl");
        // 从工厂获得连接:
        Connection connection = connectionFactory.newConnection();

        // 用连接创建Channel
        Channel channel = connection.createChannel();

        // 声明一个队列
        channel.queueDeclare("HELLO_WORLD_QUEUE", false, false, false, null);
        // 1.官网示例:使用DeliverCallback接口接收和消费消息
        // 创建监听回调实例
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
    
    
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" + message + "'");
        };
        // 开始监听接收并消费
        channel.basicConsume("HELLO_WORLD_QUEUE", true, deliverCallback, consumerTag -> {
    
     });

The DeliverCallback interface is a functional interface, which is more convenient and simple to use.

Insert image description here
Let's execute the above code to see the results:
Insert image description here
Let's check the console again and find that the message has been consumed:
Insert image description here
We can see in the Consumers Tab that the queue has a consumer at this time:
Insert image description here


The above is the official sample code implementation, but this is not the only way to consume messages. We also have other implementation methods with more functions, such as implementing the Consumer consumer interface. Here we use the default implementation class DefaultConsumer of the Consumer interface. Above code:
		// 声明一个队列
        channel.queueDeclare("HELLO_WORLD_QUEUE", false, false, false, null);

		// 2.使用Consumer消费者接口的默认实现类DefaultConsumer完成消息的接受和消费
        // 创建消费者;并设置消息处理
        DefaultConsumer consumer = new DefaultConsumer(channel){
    
    
            @Override
            /**
             * consumerTag 消息者标签,在channel.basicConsume时候可以指定
             * envelope 消息包的内容,可从中获取消息id,消息路由key:routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)
             * properties 属性信息
             * body 消息
             */
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                //路由key
                System.out.println("路由key为:" + envelope.getRoutingKey());
                //交换机
                System.out.println("交换机为:" + envelope.getExchange());
                //消息id
                System.out.println("消息id为:" + envelope.getDeliveryTag());
                //收到的消息
                System.out.println("接收到的消息为:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        /**
         * 参数1:队列名称
         * 参数2:是否自动确认,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置为false则需要手动确认
         * 参数3:消息接收到后回调
         */
        channel.basicConsume("HELLO_WORLD_QUEUE", true, consumer);

The above code can also complete the reception of messages, and there are more other methods in the Consumer interface that we can implement and use later:

Insert image description here


Finally, I would like to add here that in the above producer and consumer codes, we have declared that the queue has been created. This is no problem. We only need to ensure that the queue is sending and listening to this queue to obtain this queue information.forwardAs long as it exists, if in the above code, we first execute the original unchanged producer code, it creates the queue, and the consumer side can get it without declaring the queue, no error will be reported, and there is no problem with repeated creation and declaration. , at the same time, if you create the queue in advance, it is okay not to declare the queue on the consumer and producer sides. When the queue is known, it is also a good choice to "warm up" the queue in advance.
Attached is a description from the book:
Insert image description here

6. Official website tutorial: Work queues

Insert image description here


Work queues translates to work queue mode .
The concept of a switch is still not mentioned here, and the default switch is still used. The only difference between this mode and the hello world above is that there are multiple consumers in a queue here. The description on the official website is that assuming that the queue delivers one job after another and we need to complete the work quickly, we can use the work queue mode, which is equivalent to multiple workers doing their own work on their own lines. The distribution of messages here It feels similar to load balancing. Let’s briefly describe the process here:

We copy the consumer code in the first example twice and listen to a queue at the same time. There is no message in the queue at this time. Both consumers are waiting for the arrival of the message. Then we start the
producer and send ten messages to the queue in sequence. , then the two consumers will receive five messages respectively, and there is an interval between the messages, similar to consumer C1 receiving the first message, consumer C2 receiving the second message, and consumer C1 receiving the third message. , the C2 consumer receives the fourth message, and so on. . .

Here we go directly to the code, first the two on the consumer side. The only difference is that the strings we print here are different:

public static void main(String[] args) throws
            IOException, TimeoutException, NoSuchAlgorithmException, KeyManagementException, URISyntaxException {
    
    

        // 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 主机地址: 默认为 localhost
        connectionFactory.setHost("localhost");
        // 连接端口: 默认为 5672
        connectionFactory.setPort(5672);
        // 虚拟主机名称: 默认为 /
        connectionFactory.setVirtualHost("/test");
        // 连接用户名: 默认为guest
        connectionFactory.setUsername("cfl");
        // 连接密码: 默认为guest
        connectionFactory.setPassword("cfl");
        // 从工厂获得连接:
        Connection connection = connectionFactory.newConnection();

        // 用连接创建Channel
        Channel channel = connection.createChannel();

        // 声明一个队列
        channel.queueDeclare("Work_Queue", false, false, false, null);

        // 2.使用Consumer消费者接口的默认实现类DefaultConsumer完成消息的接受和消费
        // 创建消费者;并设置消息处理
        DefaultConsumer consumer = new DefaultConsumer(channel){
    
    

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                //收到的消息
                System.out.println("WorkQueue1 接收到的消息为:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        channel.basicConsume("Work_Queue", true, consumer);

    }

Insert image description here
Start two consumers and wait for the arrival of messages.
Then we write the producer. In fact, the code is almost the same as the previous example. Here we just send a few more messages. We only modify part of the code:

		// 声明一个队列
        channel.queueDeclare("Work_Queue", false, false, false, null);
        String message = "work queue ";
        for (int i = 0; i < 10; i++) {
    
    
            channel.basicPublish("", "Work_Queue", null, (message + i).getBytes());
            System.out.println(" [x] Sent '" + (message + i) + "'");
        }

Then we start the producer, and the producer's console prints:

Insert image description here
Then we view the console printing of the two consumers respectively:
Consumer 1:

Insert image description here
Consumer 2:

Insert image description here
It is indeed a balanced way of distributing and receiving messages. Multiple consumers (similar to multi-threading) can speed up the processing of our messages.


7. Official website tutorial Publish/Subscribe and extension [switch declaration] + [queue binding]

Insert image description here


Publish/Subscribe translates to the publish/subscribe model . Simply put, it means that you follow a certain official account on WeChat, or you follow a certain blogger on Weibo. This is a subscription process. When this official account/blog The owner publishes a new article, this is the publishing process, and then we can see and read it, etc. Here the public account/blogger is the producer, and the people we subscribe to are consumers.
There is new content in this mode, which is the X in the picture, which is our switch. Starting from a small section, the content of these sections is centered around the type of switch, and there will be no major changes. The common function of the switch is to receive messages sent by consumers and then distribute them according to their types. are different, and the strategies for distributing messages are also inconsistent. There are three common types of switches :

Fanout : Broadcast, deliver the message to all queues bound to the switch
Direct : Direct, deliver the message to the queue that matches the specified routing key Topic
: Wildcard, deliver the message to the queue that matches the routing pattern (routing pattern)

The switch type we use in this section is Fanout, a broadcast model. It will distribute all received messages to all queues bound to itself.
Here we also give a brief description of the code sample content in this section:

First, one producer, two consumers, the number remains unchanged. This time we need to create (declare) a switch, and its type is Fanout, and then declare two queues. The order of declaration of queues and switches does not matter, in order to ensure After both the queue and the switch are declared, complete the binding of the queue and the switch. Both queues need to be bound to the switch respectively, and the binding is completed. Finally, we start two consumers to monitor and consume a queue respectively, and then start the producer to send a test message. The result is that both queues receive the test message, and then the two consumers consume the message respectively.

We start writing code, first of allProducer-side code, we omit the connection part of the code and start the business logic code directly. The complete code will have a link address at the end of the article:

		// 声明队列1
        channel.queueDeclare("Pub_Sub_Queue1", false, false, false, null);// 声明队列1
        // 声明队列2
        channel.queueDeclare("Pub_Sub_Queue2", false, false, false, null);

        /*
        AMQP.Exchange.DeclareOk exchangeDeclare(
            String exchange,
            BuiltinExchangeType type,
            boolean durable,
            boolean autoDelete,
            boolean internal,
            Map<String, Object> arguments) throws IOException;
        */
        // 声明一个类型为FANOUT的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("Pub_Sub_Exchange", BuiltinExchangeType.FANOUT, false, false, null);

        /*
        Queue.BindOk queueBind(
            String queue,
            String exchange,
            String routingKey,
            Map<String, Object> arguments
        ) throws IOException;
        */
        // 将两个队列分别和交换机绑定,绑定方法全部参数如上
        // 这里我们没有使用到routingKey,在下一章会说到,这里是传空字符串
        channel.queueBind("Pub_Sub_Queue1", "Pub_Sub_Exchange", "", null);
        channel.queueBind("Pub_Sub_Queue2", "Pub_Sub_Exchange", "", null);

        // 最后我们发送一条消息
        String message = "hello Publish and Subscribe !!!";
        /*
        * 参数1:指定交换机名称
        * 参数2:指定路由键routingKey为空字符串
        * 参数3:指定扩展参数暂无
        * 参数4:指定发送的消息
        */
        channel.basicPublish("Pub_Sub_Exchange", "", null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");

The BuiltExchangeType class is an enumeration of switch type definitions, as follows, which defines four switch types:
We will also have detailed usage and instructions for these four switch types later.

Insert image description here
Let's take a look at the declaration method of the switch:
It also has multiple overloaded methods, but of course there is one with the most parameters that will cover all situations. The method with the most parameters has been given in the code comments. Here we can take a look at all the overloaded methods: it also has NoWait and Passive. Method, usage is consistent with the similarly named method of the queue.

Insert image description here
Insert image description here
The detailed parameter analysis is as follows, directly attached to the instructions in the book:

Insert image description here


Next, let’s take a look at the queue binding method. The parameters are slightly less than the one above:

Insert image description here
Insert image description here
The detailed parameter analysis is as follows, and the bound queue can be unbound from the switch:

Insert image description here


Okay, the above method parameters have been introduced. Here we run the code and go to the RabbitMQ console to check the effect. It should create a switch and two queues, and the switch and the queue are bound. At this time, two There is a message just sent in the queue. First, we run the code and check the code console to confirm that the message was sent successfully:

Insert image description here
Next, we open the RabbitMQ console to view the status of the switch and queue respectively:
First the queue: Each queue has one message
Insert image description here
Let's open the queue and confirm the message: no problem
Insert image description here
Let's confirm the switch: He was arrested on the spot and found to be a fanout.
Insert image description here
Let’s click on the switch’s details page to view the binding status of its queues: Confirm that it is successfully bound to the above two queues

Insert image description here


The code on the producer side comes to an end here, and we return to the consumer side. In fact, the code on the consumer side is almost the same as the previous example. The logic on the consumer side is just to subscribe to the queue and then get and print the message. It is nothing more than publish and subscribe here. The queues subscribed to in the pattern code are different. There is just an additional declaration of the switch and the binding process of the queue. We will not describe it too much here. We will simply show the code of one of the consumers, and then attach the result output of the console. Yes (still omitting the connection code):

		// 声明一个队列
        channel.queueDeclare("Pub_Sub_Queue1", false, false, false, null);

        // 声明一个类型为FANOUT的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("Pub_Sub_Exchange", BuiltinExchangeType.FANOUT, false, false, null);

        // 将队列和交换机绑定
        channel.queueBind("Pub_Sub_Queue1", "Pub_Sub_Exchange", "", null);

        // 2.使用Consumer消费者接口的默认实现类DefaultConsumer完成消息的接受和消费
        // 创建消费者;并设置消息处理
        DefaultConsumer consumer = new DefaultConsumer(channel){
    
    

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                //收到的消息
                System.out.println("Pub_Sub_Queue1 接收到的消息为:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        channel.basicConsume("Pub_Sub_Queue1", true, consumer);

Console print results:

Insert image description here
Insert image description here

8. Official website tutorial Routing and extensions [Binding keys and routing keys]

Insert image description here


Routing means routing. As we said above, the following content is actually a model of different switch types. Yes, the type of switch in the previous section is fanout, and the type of switch in this section is direct . Those who know Java web will definitely We all know redirect, so the meaning of direct is naturally directed . This switch here is also called a directional switch. Combining the two words routing and directional , let’s briefly outline the functional characteristics of the direct type switch: Same as the previous one. The difference between fanout and fanout is that fanout is equivalent to an undifferentiated fan broadcast message. All queues bound to the switch can receive the message. The original intention of directing is to add conditions to the above "universal exposure", even if multiple queues are bound to the switch , a message will also be forwarded to a specific queue, not all queues will receive the message.
To specifically implement the above functions, we need to illustrate it with pictures:

First, when binding the queue to the switch, we can specify ourBind key, in the previous section we introduced the binding methods and parameters, but in the previous section we did not specify the binding key and specified an empty string when binding. In this section we will start to specify the binding key. Binding keys 1 and 2 are as shown in the picture below, as are orange, black, and green in the picture above.
Insert image description here
There is actually no concept of binding key in rabbitmq. The keys specified when binding and sending are collectively called routing keys, but for the convenience of understanding, we divide them into binding keys and routing keys, as follows
Insert image description here
Insert image description here
:routing keyIt is what needs to be specified when the producer sends a message.

The final working principle is:
After receiving the message, the direct switch compares the routing key in the message with the binding keys specified when binding all bound queues. If they match completely, the message will be forwarded to this queue. As shown in the figure above, there are three binding keys:orange,black,green.
If we specify the routing key as orange when sending a message, the message will eventually be sent to queue Q1, and if black or green is specified, it will be sent to queue Q2. The queue can be bound to the switch multiple times, each time Specify different binding keys. Finally, a queue and the switch can hold multiple different binding keys, and the relationship between them is or. Also, the same binding key can be bound between queues. At this time, it is similar to downgrading to the model in the previous section. The message will be sent to all queues that satisfy the binding key. For example, in the figure below, a message with the routing key "warning" is sent. , because both queues have specified the binding key as warning at this time, so both queues will receive this message.

Insert image description here


Without further ado, let’s see how the code is implemented, using the aboveorange,black,greenThe example is simple and easy to understand. The code here is very similar to the content in the previous section. Here we will give a brief description of the code implementation scenario:

1. There is still one producer, two consumers, one switch and two queues.
2. The switch type is redirect.
3. Queue 1 is bound to the switch once, and the binding key is specified as orange when binding.
4. Queue 2 is bound to the switch twice. When binding, specify the binding keys as black and green respectively.
5. Finally, we send three messages, specify their routing keys as orange, black, and green, and check the arrival and consumption of the messages.

Let's look at the code on the producer side (omitting the code that creates the connection):

		// 声明队列1
        channel.queueDeclare("Routing_Queue1", false, false, false, null);
        // 声明队列2
        channel.queueDeclare("Routing_Queue2", false, false, false, null);

        /*
        AMQP.Exchange.DeclareOk exchangeDeclare(
            String exchange,
            BuiltinExchangeType type,
            boolean durable,
            boolean autoDelete,
            boolean internal,
            Map<String, Object> arguments) throws IOException;
        */
        // 声明一个类型为Direct的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("Routing_Exchange", BuiltinExchangeType.DIRECT, false, false, null);

        /*
        Queue.BindOk queueBind(
            String queue,
            String exchange,
            String routingKey,
            Map<String, Object> arguments
        ) throws IOException;
        */
        // 将两个队列分别和交换机绑定,绑定方法全部参数如上
        // 队列1和交换机绑定,绑定key为orange。
        channel.queueBind("Routing_Queue1", "Routing_Exchange", "orange", null);

        // 队列2和交换机绑定,绑定key分别为black和green。
        channel.queueBind("Routing_Queue2", "Routing_Exchange", "black", null);
        channel.queueBind("Routing_Queue2", "Routing_Exchange", "green", null);

        // 最后我们发送三条消息,分别指定routingKey
        String message_orange = "hello Routing and orange !!!";
        String message_black = "hello Routing and black !!!";
        String message_green = "hello Routing and green !!!";
        /*
         * 参数1:指定交换机名称 Routing_Exchange
         * 参数2:指定路由键routingKey
         * 参数3:指定扩展参数暂无
         * 参数4:指定发送的消息
         */
        channel.basicPublish("Routing_Exchange", "orange", null, message_orange.getBytes());
        System.out.println(" [x] Sent '" + message_orange + "'");

        channel.basicPublish("Routing_Exchange", "black", null, message_black.getBytes());
        System.out.println(" [x] Sent '" + message_black + "'");

        channel.basicPublish("Routing_Exchange", "green", null, message_green.getBytes());
        System.out.println(" [x] Sent '" + message_green + "'");

Then we execute the code and check the console to confirm that the sending is successful:
Insert image description here
OK, no problem, let's go to the RabbitMQ background to check the status of the switch and queue:
First we look at the switch:
Insert image description here
View the queue details bound to the switch:No problem
Insert image description here
Next, check the queue and the messages that have been sent to the queue:
Insert image description here
View queue 1:
Insert image description here
View queue 2:
Insert image description here
The above are all in line with our expectations. As for the consumer code, there is still no change, and I don’t want to write it anymore. . . There is no logic on the consumer side here. As usual, some consumer codes are posted here. For details, you can download the code to view:
Consumer side code:

		// ************************************** 消费者1 **************************************
		// 声明一个队列
        channel.queueDeclare("Routing_Queue1", false, false, false, null);

        // 声明一个类型为Direct的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("Routing_Exchange", BuiltinExchangeType.DIRECT, false, false, null);

        // 将队列和交换机绑定
        channel.queueBind("Routing_Queue1", "Routing_Exchange", "orange", null);

        // 2.使用Consumer消费者接口的默认实现类DefaultConsumer完成消息的接受和消费
        // 创建消费者;并设置消息处理
        DefaultConsumer consumer = new DefaultConsumer(channel){
    
    

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                //收到的消息
                System.out.println("Consumer_RoutingQueue1 接收到的消息为:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        channel.basicConsume("Routing_Queue1", true, consumer);
        
		// ************************************** 消费者2 **************************************
		// 声明一个队列
        channel.queueDeclare("Routing_Queue2", false, false, false, null);

        // 声明一个类型为Direct的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("Routing_Exchange", BuiltinExchangeType.DIRECT, false, false, null);

        // 将队列和交换机绑定
        channel.queueBind("Routing_Queue2", "Routing_Exchange", "black", null);
        channel.queueBind("Routing_Queue2", "Routing_Exchange", "green", null);

        // 2.使用Consumer消费者接口的默认实现类DefaultConsumer完成消息的接受和消费
        // 创建消费者;并设置消息处理
        DefaultConsumer consumer = new DefaultConsumer(channel){
    
    

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                //收到的消息
                System.out.println("Consumer_RoutingQueue2 接收到的消息为:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        channel.basicConsume("Routing_Queue2", true, consumer);

We start two consumers respectively and check the consumption of messages:
Consumer 1:
Insert image description here
Consumer 2:Insert image description here
The above is the MQ implementation of Routing mode. It is a relatively typical implementation. It integrates almost all the basic components of MQ and is a complete producer-consumer MQ model. Our use in practical applications is basically based on this. mode or Topics mode. After studying the last chapter, you will find that there is actually no substantial difference between the Routing and Topics modes. The similarity between the two is as high as 90%.


9. Topics of the official website tutorial

Insert image description here


Topics, translated as topics,
are also mentioned above. This mode is actually developed based on different switch types. The switch type here is topic, which is very similar to the previous chapter. The direct switch in the previous picture provides The function is: to match the routing key and the binding key when sending. It is a complete matching mode. The two must be completely consistent, but everyone knows that as the saying goes: if there is equal, there must be approximately equal!
As you can guess from the picture above, what the topic switch provides us is the fuzzy matching mode of routing keys and binding keys . The fuzzy matching here does not refer to our traditional regular expressions, but is more advanced. Simpler.
The fuzzy matching mechanism of the topic switch uses wildcards , so we also call Topics mode wildcard mode .
Insert image description here
In fact, wildcards can also be understood as a placeholder, as follows:
Insert image description here
Official website:
Insert image description here
Here is another example from the book, this one is a little more complicated:
Insert image description here
Okay, basically the content of this section is all about wildcards. Let’s make a simple code example. Here we will introduce the example content first:

1. There is still one producer, two consumers, one switch and two queues.
2. The switch type is topic.
3. Queue 1 and the switch, when binding, specify the binding key as rabbit.*
4. Queue 2 and the switch, when binding, specify the binding key as rabbit.#
5. Finally, we send three messages, specifying their routing keys as rabbit, rabbit.mq, rabbit.mq.cfl, and check the arrival and consumption of messages.
6. Queue 1 should receive one message, and queue 2 will receive three messages.

OK, let’s look directly at the code. First of allproducer sideof:

		// 声明队列1
        channel.queueDeclare("Topics_Queue1", false, false, false, null);
        // 声明队列2
        channel.queueDeclare("Topics_Queue2", false, false, false, null);

        /*
        AMQP.Exchange.DeclareOk exchangeDeclare(
            String exchange,
            BuiltinExchangeType type,
            boolean durable,
            boolean autoDelete,
            boolean internal,
            Map<String, Object> arguments) throws IOException;
        */
        // 声明一个类型为TOPIC的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("Topics_Exchange", BuiltinExchangeType.TOPIC, false, false, null);

        /*
        Queue.BindOk queueBind(
            String queue,
            String exchange,
            String routingKey,
            Map<String, Object> arguments
        ) throws IOException;
        */
        // 将两个队列分别和交换机绑定,绑定方法全部参数如上
        // 队列1和交换机绑定,绑定key为rabbit.*
        channel.queueBind("Topics_Queue1", "Topics_Exchange", "rabbit.*", null);

        // 队列2和交换机绑定,绑定key为rabbit.#
        channel.queueBind("Topics_Queue2", "Topics_Exchange", "rabbit.#", null);

        // 最后我们发送三条消息
        String message1 = "message1 the routingKey is rabbit !!!";
        String message2 = "message2 the routingKey is rabbit.mq !!!";
        String message3 = "message3 the routingKey is rabbit.mq.cfl !!!";
        /*
         * 参数1:指定交换机名称 Topics_Exchange
         * 参数2:指定路由键routingKey
         * 参数3:指定扩展参数暂无
         * 参数4:指定发送的消息
         */
        // 分别指定routingKey
        channel.basicPublish("Topics_Exchange", "rabbit", null, message1.getBytes());
        System.out.println(" [x] Sent '" + message1 + "'");

        channel.basicPublish("Topics_Exchange", "rabbit.mq", null, message2.getBytes());
        System.out.println(" [x] Sent '" + message2 + "'");

        channel.basicPublish("Topics_Exchange", "rabbit.mq.cfl", null, message3.getBytes());
        System.out.println(" [x] Sent '" + message3 + "'");

Let's run the code and check the idea console to see the sending status:
Insert image description here

OK, no problem,Next let’s check the RabbitMQ console:
Arrest the virtual machine on the spot:
Insert image description here
The queue to force him to confess and bind:
Insert image description here
No problem, let’s check the queue and messages:
First is Queue 1:
Insert image description here
And the messages in the queue:
Insert image description here
Next we look at Queue 2:
Insert image description here
And the messages received inside it:
Insert image description here


OK, no problem at all. Next, let’s look at the consumer’s code. It’s still the same as the previous sections. Since there is no logic code on the consumer side for the time being, a part of the code is still attached. We can check the consumption results. The complete code There are links at the end of the article:
Consumer sidePart of the code:

		// ************************* 消费者1 *************************
		// 声明一个队列
        channel.queueDeclare("Topics_Queue1", false, false, false, null);

        // 声明一个类型为TOPIC的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("Topics_Exchange", BuiltinExchangeType.TOPIC, false, false, null);

        // 将队列和交换机绑定
        channel.queueBind("Topics_Queue1", "Topics_Exchange", "rabbit.*", null);

        // 2.使用Consumer消费者接口的默认实现类DefaultConsumer完成消息的接受和消费
        // 创建消费者;并设置消息处理
        DefaultConsumer consumer = new DefaultConsumer(channel){
    
    

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                //收到的消息
                System.out.println("Consumer_TopicsQueue1 接收到的消息为:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        channel.basicConsume("Topics_Queue1", true, consumer);
		
		// ************************* 消费者2 *************************
		// 声明一个队列
        channel.queueDeclare("Topics_Queue2", false, false, false, null);

        // 声明一个类型为TOPIC的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("Topics_Exchange", BuiltinExchangeType.TOPIC, false, false, null);

        // 将队列和交换机绑定
        channel.queueBind("Topics_Queue2", "Topics_Exchange", "rabbit.#", null);

        // 2.使用Consumer消费者接口的默认实现类DefaultConsumer完成消息的接受和消费
        // 创建消费者;并设置消息处理
        DefaultConsumer consumer = new DefaultConsumer(channel){
    
    

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                //收到的消息
                System.out.println("Consumer_TopicsQueue2 接收到的消息为:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        channel.basicConsume("Topics_Queue2", true, consumer);

Start two consumers respectively to check the message consumption:
Consumer 1:
Insert image description here
Consumer 2:
Insert image description here

10. Extension of [Headers switch] + [Summary of RabbitMQ working mode]

After understanding the above MQ modes, everyone must have an overall understanding of MQ, especially about switches. Based on the type of switch, we can do different things. Here we take a look again at which switches there are. Type:
Insert image description here
There are four major types. The only one we did not use in our study above is the headers switch. Why is this switch not introduced on the official website? I was also confused at the time, but later the book explained it this way: The headers attribute in the content of sending messages
Insert image description here
mentioned in the book refers to the expanded attributes that we specify when sending messages, as follows:In the advanced content, we will use and explain various expanded attribute contents.
Insert image description here
Because of its performance reasons and practicality, it is not widely used, just understand it.
System and custom type switches are also mentioned in the AMQP protocol. They are not reviewed in the book, so I won’t understand them yet and will give you a break.
Finally, here we make a summary of all modes:

Mode summary, RabbitMQ working mode:
1: Simple mode HelloWorld, one producer and one consumer, no need to set up a switch (use the default switch).
2: Work queue mode Work Queue has one producer and multiple consumers (competition relationship), and there is no need to set up a switch (use the default switch).
3: Publish/subscribe mode requires setting up a switch of type fanout, and binding the switch to the queue. When a message is sent to the switch, the switch will send the message to the bound queue.
4: Routing mode Routing needs to set up a switch of type 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 based on the routing key.
5: Wildcard mode Topic needs to set up a switch of topic type, bind the switch to the queue, and specify the routing key in wildcard mode. When a message is sent to the switch, the switch will send the message to the corresponding queue based on the routing key.


11. Extension [detailed explanation of sending messages]

In RabbitMQ, sending a message is the most basic operation. We have also briefly used it in the above code example. Here we give a detailed explanation of the method of sending a message. The first method of sending a message is Channel's basicPublish. There are multiple overloaded methods:
Insert image description here
In the example code above, we all use the first method with the fewest parameters. Let’s briefly review the parameters:
Insert image description here
Insert image description here
We will focus on the content of props in the advanced chapter later.
The code to send a message is as follows:

/*
 * 参数1:指定交换机名称 Topics_Exchange
 * 参数2:指定路由键routingKey
 * 参数3:指定扩展参数暂无
 * 参数4:指定发送的消息
 */
channel.basicPublish("Topics_Exchange", "rabbit", null, message1.getBytes());

Among the two methods with more parameters, we directly introduce the third method with the most parameters here, because it already contains the second parameter. Here we first copy the method comments to make a brief introduction: Here is
Insert image description here
actually Compared with the above, there are two more parameters: mandatory and immediate
are both Boolean values ​​and a flag identifier.
Insert image description here
First let’s look at the mandatory parameters:
Just go to the description in the book:
Insert image description here

Briefly describe the code scenario:
We use the DIRECT switch to bind a queue with the binding key being rabbit, and then we send two irrelevant keys whose routing keys cannot match the binding key. The two sending messages will separate the mandatory Set to true and false, and then use ReturnListener to obtain and print the rolled-back message to the console. There should be only one message that we obtain and print. The message with mandatory setting false will be discarded directly.

OK, go directly to the code:

    public static void main(String[] args) throws
            IOException, TimeoutException, NoSuchAlgorithmException, KeyManagementException, URISyntaxException {
    
    

        // 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 主机地址: 默认为 localhost
        connectionFactory.setHost("localhost");
        // 连接端口: 默认为 5672
        connectionFactory.setPort(5672);
        // 虚拟主机名称: 默认为 /
        connectionFactory.setVirtualHost("/test");
        // 连接用户名: 默认为guest
        connectionFactory.setUsername("cfl");
        // 连接密码: 默认为guest
        connectionFactory.setPassword("cfl");
        // 从工厂获得连接:
        Connection connection = connectionFactory.newConnection();

        // 用连接创建Channel
        Channel channel = connection.createChannel();
        channel.addReturnListener(new ReturnListener() {
    
    
            @Override
            public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                String message = new String(body);
                System.out.println("return 的消息是:" + message);
            }
        });

        // 声明队列
        channel.queueDeclare("Mandatory_Queue1", false, false, false, null);

        /*
        AMQP.Exchange.DeclareOk exchangeDeclare(
            String exchange,
            BuiltinExchangeType type,
            boolean durable,
            boolean autoDelete,
            boolean internal,
            Map<String, Object> arguments) throws IOException;
        */
        // 声明一个类型为DIRECT的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("Mandatory_Exchange", BuiltinExchangeType.DIRECT, false, false, null);

        /*
        Queue.BindOk queueBind(
            String queue,
            String exchange,
            String routingKey,
            Map<String, Object> arguments
        ) throws IOException;
        */
        // 队列和交换机绑定,绑定key为rabbit
        channel.queueBind("Mandatory_Queue1", "Mandatory_Exchange", "rabbit", null);

        String message1 = "test with mandatory true !!!";
        String message2 = "test with mandatory false !!!";
        /*
         * 参数1:指定交换机名称 Topics_Exchange
         * 参数2:指定路由键routingKey
         * 参数3:指定扩展参数暂无
         * 参数4:指定发送的消息
         */
        // 分别指定routingKey
        channel.basicPublish("Mandatory_Exchange", "nop", true, false, null, message1.getBytes());
        System.out.println(" [x] Sent '" + message1 + "'");

        channel.basicPublish("Mandatory_Exchange", "cfl", false, false, null, message2.getBytes());
        System.out.println(" [x] Sent '" + message2 + "'");

        // 关闭资源
        // channel.close();
        // connection.close();

    }

We run the code and view the results on the console:
Insert image description here
There was no problem. I tried it again and there was no problem using the topic switch:
Brothers, there is a pitfall in the above code, that is, what we are testing now is the fallback of failed matching after the message is sent. If you disconnect the channel connection before the message is returned, you will not receive the returned message. …We used to naturally close it after sending the message. It must not be closed during the test here. If there are only one or two messages, you may not see the difference. If you send multiple pieces of data that do not meet the conditions, you may The next few returned messages could not be received.


As for the immediate parameter
, RabbitMQ version 3.0 has removed support for the imrnediate parameter. The official explanation for RabbitMQ is: the immediate parameter will affect the performance of the mirror queue and increase the complexity of the code. It is recommended to use the TTL + DLX method instead. TTL + DLX is the expiration time and dead letter queue, which we will explain in detail in other blogs later.
Therefore, immediate will not be explained here.


12. Extension [Two modes of consuming messages]

Let’s start with the concepts directly:
Insert image description here


Let’s give a detailed explanation of these two consumption methods:

  1. Push mode: Basic.Consume

In fact, this Basic.Consume has been used in the code above. The classes used are as follows:
the Consumer interface and its default implementation class DefaultConsumer .
Insert image description here
In the above code, we created DefaultConsumer and overridden its consumption method handleDelivery:

DefaultConsumer consumer = new DefaultConsumer(channel){
    
    

    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
        //收到的消息
        System.out.println("Consumer_TopicsQueue1 接收到的消息为:" + new String(body, "utf-8"));
    }
};

channel.basicConsume("Topics_Queue1", true, consumer);

Let's take a look at the reconstruction methods and parameters of basicConsume:
Refactoring methods: so many are scary
Insert image description here

Parameter introduction:
Among them, autoAck will be explained later in the advancement. Here we set it to true first. The consumerTag
Insert image description here
here is because our channel allows multiple consumers to exist, and different consumers here need to be distinguished.
And, when we use Consumer or DefaultConsumer, it provides many methods, and we can implement the necessary methods as needed:
Insert image description here
And the last mentioned thread safety issue on the consumer side:
Insert image description here


  1. Pull mode: Basic.Get

Insert image description here
The push mode is a consumption model with continuous subscription. Multiple messages can be obtained at one time and can be obtained continuously. The characteristic of the pull mode here is that it can only obtain one message at a time. Obviously, the implementation model of the pull mode has a great impact on the business. There are big requirements and there are limitations in efficiency. There is only one method: (here is autoAck, which we will explain in the advanced content later). There are only two
Insert image description here
main parameters. The first is the name of the queue, and the second is whether it is automatic. confirm.
Since the use of pull mode has relatively high limitations, I will briefly introduce it here without giving code examples. Finally, I will attach the summary points in the book:
Insert image description here
Note: The content of Basic.Qos is also explained in the advanced content like autoAck.


13. Extension [Close connection] + [Delete queue and switch]

There is not much to add about closing the connection, just go to the instructions in the book:
Insert image description here
Insert image description here


In each working mode above, we have declared (created) various switches and queues. As the saying goes, if they are created, they will be deleted!
Here we take a brief look at the method RabbitMQ provides us with to delete queues and switches:
First is the removal of the switch:
Insert image description here
Insert image description here

The main parameters are two:
1. exchange : the name of the switch
2. ifUnused : If it is true, it will only be deleted when the switch is not in use. If it is false, it will be forcibly deleted, regardless of its status.

Next is the deletion of the queue:
Insert image description here
Insert image description here

The parameter of queue deletion here has one more parameter than the deletion of the switch above, which is ifEmpty . You can know its function from the name here. It is used to determine whether there are still messages in the queue. If it is true, it will only be used when there is no message in the queue. It will be deleted only if it is false. If it is false, it will be deleted forcibly, regardless of whether there is any message.
As for the two deleted Nowait methods, they have been explained in the declaration of queues and switches above. The principles are the same. You can refer back to them for details.

14. Extension [Switch binding and unbinding to each other] + [Backup switch]

Above we have demonstrated the binding of switches and queues. This is the most basic binding method and the most commonly used. However, in fact, switches can also be bound. It is equivalent to adding the binding between switches and queues. Like a filter, the specific usage is also the same. This scenario of adding a switch in the middle is suitable for more complex business scenarios. Let’s take a look at its binding method: Here is a brief introduction to the parameters:
Insert image description here
Insert image description here
1.
destination switch ( destination)
2. source source switch
3. binding key between routingKey
switches 4. arguments extended parameter, which is a map collection

The pictures in the book are pretty good, let’s take a look:
Insert image description here


Let's take a look at the backup switch . First of all, it's conceptual. Let's read the instructions in the book: We
Insert image description here
also explained the mandatory parameter when sending messages above. Its function is that when the switch does not find a queue that meets the routing conditions, it will To return the message to the producer, the channel on the producer side needs to add a listener to obtain the message and perform custom processing on it . This step may be a little cumbersome. The backup switch can actually complete such a simple task, but the backup The implementation of the switch is the default. It accepts the rollback message by default and then forwards it to other queues for storage or etc. It can be understood as a default implementation of the mandatory parameter mode. If we have our own unique method for rollback messages If you are dealing with business, it is still recommended to use a listener to obtain messages and handle the rollback messages yourself.

Let's take a look at how to implement a backup switch . Here we use the method of adding the alternate-exchange parameter to implement it:
As usual, let's first explain the logic of our test code:

First we declare two queues, a normal queue normalQueue , and a queue unroutedQueue bound to the backup exchange.
Declare two switches, one is the normal switch normal_Exchange , type is DIRECT, and the other is the backup switch myAe
. Then bind the queue and switch. Note Here when declaring normal_Exchange, we pass in the extended parameter alternate-exchange to bind it to the backup switch.
Finally, we send two messages. One message conforms to the routing key and will be routed to the normalQueue queue by normal_Exchange.
The other message does not conform to the routing key. Will be sent back to the backup switch and eventually routed to the unroutedQueue queue

Insert image description here


Cuihua, the code:

	public static void main(String[] args) throws
            IOException, TimeoutException, NoSuchAlgorithmException, KeyManagementException, URISyntaxException {
    
    

        // 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 主机地址: 默认为 localhost
        connectionFactory.setHost("localhost");
        // 连接端口: 默认为 5672
        connectionFactory.setPort(5672);
        // 虚拟主机名称: 默认为 /
        connectionFactory.setVirtualHost("/test");
        // 连接用户名: 默认为guest
        connectionFactory.setUsername("cfl");
        // 连接密码: 默认为guest
        connectionFactory.setPassword("cfl");
        // 从工厂获得连接:
        Connection connection = connectionFactory.newConnection();

        // 用连接创建Channel
        Channel channel = connection.createChannel();

        // 声明队列 普通交换机绑定的队列
        channel.queueDeclare("normalQueue", false, false, false, null);
        // 声明队列 alternate-exchange交换机绑定的队列
        channel.queueDeclare("unroutedQueue", false, false, false, null);

        /*
        AMQP.Exchange.DeclareOk exchangeDeclare(
            String exchange,
            BuiltinExchangeType type,
            boolean durable,
            boolean autoDelete,
            boolean internal,
            Map<String, Object> arguments) throws IOException;
        */
        // 定义参数
        Map<String, Object> arges = new HashMap<>();
        arges.put("alternate-exchange", "myAe");

        // 声明普通交换机 为DIRECT的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("normal_Exchange", BuiltinExchangeType.DIRECT, false, false, arges);
        // 声明alternate-exchange交换机 为FANOUT的交换机,交换机构造方法全部参数如上
        channel.exchangeDeclare("myAe", BuiltinExchangeType.FANOUT, false, false, null);

        /*
        Queue.BindOk queueBind(
            String queue,
            String exchange,
            String routingKey,
            Map<String, Object> arguments
        ) throws IOException;
        */
        // 队列和普通交换机绑定,绑定key为rabbit
        channel.queueBind("normalQueue", "normal_Exchange", "rabbit", null);
        // 队列和备份交换机绑定 因为交换机类型是Fanout 绑定时不需要指定绑定key
        channel.queueBind("unroutedQueue", "myAe", "", null);

        String message1 = "test with rabbit true !!!";
        String message2 = "test with rabbit false !!!";
        /*
         * 参数1:指定交换机名称
         * 参数2:指定路由键routingKey
         * 参数3:指定扩展参数暂无
         * 参数4:指定发送的消息
         */
        // 分别指定routingKey
        channel.basicPublish("normal_Exchange", "rabbit", null, message1.getBytes());
        System.out.println(" [x] Sent '" + message1 + "'");

        channel.basicPublish("normal_Exchange", "nop", null, message2.getBytes());
        System.out.println(" [x] Sent '" + message2 + "'");

        // 关闭资源
        channel.close();
        connection.close();

    }

Once the code is running, let’s take a look at the console: two messages were sent successfully.Insert image description here
Let’s take a look at the switch:There is no problem and the switch that has the backup switch is marked by AE
Insert image description here
And the arrival of the news we care about most:There is no problem
Insert image description here
Insert image description here
here. We can also find that although the messages in the unroutedQueue queue are forwarded by the backup switch, the message is still marked as coming from normal_Exchange, which is the first switch from which it originated. The backup switch is an invisible existence. !
Finally, a summary is attached:
Insert image description here


15. Sample code address

https://gitee.com/chenfeilin/RabbitMQ_Practice.git

Guess you like

Origin blog.csdn.net/cjl836735455/article/details/109901823
Recommended