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
- 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.
- 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.