1, Introduction
Message queue functions: decoupling, asynchronous and peak clipping
2, RabbitMQ introduction
Several popular MQ models in the market:
ActiveMQ,RocketMQ,Kafka,RabbitMQ.
-
Language support: ActiveMQ, RocketMQ only support Java language, Kafka can support multiple languages, and RabbitMQ supports multiple languages.
-
In terms of efficiency: ActiveMQ, RocketMQ and Kafka are all at the millisecond level, and RabbitMQ is at the microsecond level.
-
Message loss and message duplication: RabbitMQ has mature solutions for message persistence and duplication.
-
Learning cost: RabbitMQ is very simple.
RabbitMQ is developed and maintained by Rabbit company, and finally in pivot.
RabbitMQ strictly follows AMQP protocol and advanced message queuing protocol to help us deliver asynchronous messages between processes.
3, RabbitMQ installation (for example, Alibaba cloud server deployment)
First, check whether there are running containers in docker and whether the port number is occupied
Next, create a folder in which you create the docker-compose.yaml file
The configuration in the ymal file is as follows
version: "3.1" services: rabbitmq: image: daocloud.io/library/rabbitmq:management restart: always container_name: rabbitmq ports: - 5672:5672 - 15672:15672 volumes: - ./data:/var/lib/rabbitmq
Start (it is recommended to start at the front desk for the first time)
docker-compose up
Log in (note that there is a compatibility problem with Google browser, use IE)
be careful! My alicloud server ip is 8.130.166.101. Please change it according to your own ip address
The following 15672 is the port number
http://8.130.166.101:15672/http://8.130.166.101:15672/
User name guest password guest
After entering, you can come in
4, RabbitMQ Architecture [key]
4.1 official simple structure diagram
-
Publisher - Producer: publishes messages to Exchange in RabbitMQ
-
Consumer - Consumer: listens for messages in the Queue in RabbitMQ
-
Exchange - switch: establishes a connection with the producer and receives messages from the producer
-
Queue - queue: Exchange will distribute messages to the specified queue, which interacts with consumers
-
Routes - routes: what policy does the switch use to publish messages to the Queue
Simple architecture diagram |
---|
4.2 complete architecture diagram of rabbitmq
Complete architecture diagram
Complete architecture diagram |
---|
5, Use of RabbitMQ [key]
5.1 RabbitMQ communication mode
Communication mode |
---|
5.2 Java connection RabbitMQ
5.2.1 create maven project
5.2.2 import dependency
<dependencies> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.6.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
5.2.3 create tool class connection RabbitMQ
import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import java.io.IOException; import java.util.concurrent.TimeoutException; public class RabbitMqUtils { public static Connection getConnection() throws IOException, TimeoutException { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("8.130.166.101"); connectionFactory.setPort(5672); //Set login user name and password connectionFactory.setUsername("guest"); connectionFactory.setPassword("guest"); //Where is it stored connectionFactory.setVirtualHost("/"); return connectionFactory.newConnection(); } }
5.3 Hello-World
A producer, a default switch, a queue, and a consumer
Structure diagram |
---|
Create a producer, create a channel, publish messages to exchange, and specify routing rules.
@Test public void Publisher() throws IOException, TimeoutException { Channel channel = connection.createChannel(); // Parameter 1: specify exchange and use ''. // Parameter 2: specify the routing rule and use the specific queue name. // Parameter 3: specify the properties carried by the delivered message and use null. // Parameter 4: Specifies the specific message to be published, byte [] type // Send the message "Hello queue" to the queue channel.basicPublish("","hello-queue",null,"hello-queue".getBytes()); channel.close(); } @After public void destroy() throws IOException { connection.close(); }
Create a consumer, create a channel, create a queue, and consume the current queue
/* * consumer*/ @Test public void consumerTest() throws IOException { //Create pipe Channel channel = connection.createChannel(); //Parameter 1: queue - specifies the name of the queue //Parameter 2: durable - whether the current queue needs persistence (true) //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer) //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically //Parameter 5: arguments - specifies additional information about the current queue //channel and queue binding channel.queueDeclare("hello-queue",true,true,false,null); //Hold one piece of data every time consumers consume channel.basicQos(1); Consumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { //Get information from queue System.out.println("Accept message:"+new String(body,"utf-8")); } }; // channel and consumer binding // Parameter 1 queue name // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again // Parameter 3 Consumer channel.basicConsume("hello-queue",true,consumer); //Get the program stuck System.in.read(); }
Run twice
Messages can be obtained from consumers
Manual ack mechanism
Manual ack mechanism: ensure that the service corresponding to the message has been actually processed, rather than just receiving the message
/* * consumer*/ @Test public void consumerTest() throws IOException { //Create pipe Channel channel = connection.createChannel(); //Parameter 1: queue - specifies the name of the queue //Parameter 2: durable - whether the current queue needs persistence (true) //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer) //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically //Parameter 5: arguments - specifies additional information about the current queue //channel and queue binding channel.queueDeclare("hello-queue",true,true,false,null); //Hold one piece of data every time consumers consume channel.basicQos(1); Consumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { //Get information from queue System.out.println("Accept message:"+new String(body,"utf-8")); // After all services are completed, you can manually ack // envelope.getDeliveryTag() / / message tag 0 1 2 // False do not delete after ACK channel.basicAck(envelope.getDeliveryTag(),false); } }; // channel and consumer binding // Parameter 1 queue name // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again // Parameter 3 Consumer channel.basicConsume("hello-queue",false,consumer); //Get the program stuck System.in.read(); }
5.4 Work
One producer, one default switch, one queue, two consumers
Structure diagram |
---|
On the consumer side, you only need to add Qos capabilities and change to manual ack to allow consumers to consume the specified messages according to their capabilities, instead of being evenly distributed by RabbitMQ by default, the producer remains unchanged, and normally publish messages to the default exchange and specify routing
Consumer specifies Qoa and manual ack
Two consumers consume the same message queue
public class WorkerQueueTest { private Connection connection; @Before public void init() throws IOException, TimeoutException { connection = RabbitMqUtils.getConnection(); } /* * consumer*/ @Test public void consumer1Test() throws IOException { //Create pipe Channel channel = connection.createChannel(); //Parameter 1: queue - specifies the name of the queue //Parameter 2: durable - whether the current queue needs persistence (true) //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer) //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically //Parameter 5: arguments - specifies additional information about the current queue //channel and queue binding channel.queueDeclare("work-queue",true,false,false,null); //Hold one piece of data every time consumers consume channel.basicQos(1); Consumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { //Get information from queue System.out.println("Accept message:"+new String(body,"utf-8")); } }; // channel and consumer binding // Parameter 1 queue name // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again // Parameter 3 Consumer channel.basicConsume("work-queue",true,consumer); //Get the program stuck System.in.read(); } @Test public void consumer2Test() throws IOException { //Create pipe Channel channel = connection.createChannel(); //Parameter 1: queue - specifies the name of the queue //Parameter 2: durable - whether the current queue needs persistence (true) //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer) //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically //Parameter 5: arguments - specifies additional information about the current queue //channel and queue binding channel.queueDeclare("work-queue",true,false,false,null); //Hold one piece of data every time consumers consume channel.basicQos(1); Consumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { //Get information from queue System.out.println("Accept message:"+new String(body,"utf-8")); } }; // channel and consumer binding // Parameter 1 queue name // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again // Parameter 3 Consumer channel.basicConsume("work-queue",true,consumer); //Get the program stuck System.in.read(); } @Test public void Publisher() throws IOException, TimeoutException { Channel channel = connection.createChannel(); //Parameter 1: if the switch name does not exist, it is the default '' // Parameter 2: match the queue name or message type information with the routing rules // Parameter 3: specify the properties carried by the delivered message and use null. // Parameter 4: message // Send ten messages "Hello queue" to the queue for (int i = 0; i < 10; i++) { channel.basicPublish("","work-queue",null,("work-queue"+i).getBytes()); } channel.close(); } @After public void destroy() throws IOException { connection.close(); } }
Run two consumers first, and then one producer
You can see the results as follows
The following must first start consumers before starting producers
5.5 Publish/Subscribe
One producer, one switch, two queues, two consumers
Structure diagram |
---|
Declare an exchange of Fanout type and bind exchange and queue together. The binding method is direct binding.
Let the producer create an exchange and specify the type to bind to one or more queues.
public class PublishSubTest { private Connection connection; @Before // Call initialization data before @Test public void init() throws IOException, TimeoutException { connection = RabbitMqUtils.getConnection(); } /** * When testing, be sure to start the consumer first and start the producer later */ @Test // Conduct unit tests public void consumer1Test() throws IOException { // channel pipes connect consumers and queues final Channel channel = connection.createChannel(); //Parameter 1: queue - specifies the name of the queue //Parameter 2: durable - whether the current queue needs persistence (true) //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer) //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically //Parameter 5: arguments - specifies additional information about the current queue // chanel and queue binding channel.queueDeclare("pubsub-queue1",true,false,false,null); // Hold one piece of data every time consumers consume channel.basicQos(1); Consumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { // Get the message from the queue and the processor System.out.println("Consumer 1 received message:"+new String(body,"utf-8") ); // After all services are completed, you can manually ack // envelope.getDeliveryTag() / / message tag 0 1 2 // False do not delete after ACK channel.basicAck(envelope.getDeliveryTag(),false); } }; // chanel and consumer binding // Parameter 1 queue name // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again // false requires manual ack // Parameter 3 Consumer channel.basicConsume("pubsub-queue1", false,consumer); // Let the program consistent card where consumers can consistently consume messages System.in.read();// Wait for input from the client command line } @Test // Conduct unit tests public void consumer2Test() throws IOException { // channel pipes connect consumers and queues final Channel channel = connection.createChannel(); //Parameter 1: queue - specifies the name of the queue //Parameter 2: durable - whether the current queue needs persistence (true) //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer) //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically //Parameter 5: arguments - specifies additional information about the current queue // chanel and queue binding channel.queueDeclare("pubsub-queue2",true,false,false,null); // Hold one piece of data every time consumers consume channel.basicQos(1); Consumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { // Get the message from the queue and the processor System.out.println("Consumer 2 received message:"+new String(body,"utf-8") ); // After all services are completed, you can manually ack // envelope.getDeliveryTag() / / message tag 0 1 2 // False do not delete after ACK channel.basicAck(envelope.getDeliveryTag(),false); } }; // chanel and consumer binding // Parameter 1 queue name // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again // false requires manual ack // Parameter 3 Consumer channel.basicConsume("pubsub-queue2", false,consumer); // Let the program consistent card where consumers can consistently consume messages System.in.read();// Wait for input from the client command line } /** * Producer model */ @Test public void publishTest() throws IOException, TimeoutException { Channel channel = connection.createChannel(); //Bind the change to the custom switch "PubSub exchange" //Parameter 1: name of exchange //Parameter 2: specify the type of exchange fanout - PubSub, direct - routing, topic - topics //FANOUT - pubsub exchange sends messages to all queues channel.exchangeDeclare("pubsub-exchange", BuiltinExchangeType.FANOUT); //Parameter 1 queue name //Parameter 2 switch name //Parameter 3 routing rules channel.queueBind("pubsub-queue1", "pubsub-exchange", ""); channel.queueBind("pubsub-queue2", "pubsub-exchange", ""); //Parameter 1: if the switch name does not exist, it is the default '' // Parameter 2: the queue name or message type information will really match the routing rules // Parameter 3: specify the properties carried by the delivered message and use null. // Parameter 4: message // Send message to queue // Send 10 pieces of data and each consumer gets 5 pieces of data for (int i = 0; i < 10; i++) { // The message is not sent to the default switch, but to the custom switch PubSub exchange // "PubSub exchange" switch name // '' routing rule channel.basicPublish("pubsub-exchange", "",null,("pubsub--i:" +i).getBytes()); } channel.close(); } @After// After @ Test, data is destroyed public void destroy() throws IOException { connection.close(); } }
Consumers can listen to a queue normally.
5.6 Routing
The following must first start consumers before starting producers
One producer, one switch, two queues, two consumers
Structure diagram |
---|
After creating an exchange of DIRECT type, the producer binds the corresponding queue according to the RoutingKey, and specifies the specific RoutingKey of the message when sending the message.
Consumers have not changed
public class RoutingTest { private Connection connection; @Before public void getConnection() throws IOException, TimeoutException { connection = RabbitMqUtils.getConnection(); } @Test public void consumer1Test() throws IOException { final Channel channel = connection.createChannel(); channel.queueDeclare("routing-info-queue",true,false,false,null); channel.basicQos(1); Consumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("routing-info-queue Message received"+new String(body,"utf-8")); channel.basicAck(envelope.getDeliveryTag(),false); } }; // chanel and consumer binding // Parameter 1 queue name // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again // false requires manual ack // Parameter 3 Consumer channel.basicConsume("routing-info-queue",false,consumer); System.in.read(); } @Test public void consumer2Test() throws IOException { final Channel channel = connection.createChannel(); channel.queueDeclare("routing-error-queue",true,false,false,null); channel.basicQos(1); Consumer consumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("routing-error-queue Accept message:"+new String(body,"utf-8")); channel.basicAck(envelope.getDeliveryTag(),false); } }; channel.basicConsume("routing-error-queue",false,consumer); System.in.read(); } @Test public void publisherTest() throws IOException, TimeoutException { Channel channel = connection.createChannel(); //Bind the channel to the custom switch "routing exchange" //Parameter 1: name of exchange //Parameter 2: specify the type of exchange fanout - PubSub, direct - routing, topic - topics // DIRECT - Routing exchange sends messages to all queues channel.exchangeDeclare("Routing-exchange",BuiltinExchangeType.DIRECT); //Parameter 1 queue name //Parameter 2 switch name //Parameter 3 routing rules // All messages with info will be sent to the routing info queue by routing exchange channel.queueBind("routing-info-queue","Routing-exchange","info"); channel.queueBind("routing-error-queue","Routing-exchange","error"); //Parameter 1: if the switch name does not exist, it is the default '' // Parameter 2: the queue name or message type information will really match the routing rules // Parameter 3: specify the properties carried by the delivered message and use null. // Parameter 4: message // Send message to queue // Send 10 pieces of data and each consumer gets 5 pieces of data for (int i = 0; i < 10; i++) { if (i%2==1){//Odd number channel.basicPublish("Routing-exchange","info",null,("Routing--i="+i).getBytes()); }else { channel.basicPublish("Routing-exchange","error",null,("Routing--i="+i).getBytes()); } } channel.close(); } @After public void destroy() throws IOException { connection.close(); } }
Operation results
5.7 Topic
The following must first start consumers before starting producers
One producer, one switch, two queues, two consumers
Structure diagram |
---|
The producer creates the exchange of the Topic and binds it to the queue. This binding can use the * and # keywords to specify the RoutingKey content. When writing, pay attention to the format xxx.xxx.xxx, * - > one XXX, and # - > represents multiple xxx.xxx. When sending messages, specify the specific RoutingKey.
Take a chestnut
//2. Create exchange and specify the binding method channel.exchangeDeclare("topic-exchange", BuiltinExchangeType.TOPIC); channel.queueBind("topic-queue-1","topic-exchange","*.red.*"); channel.queueBind("topic-queue-2","topic-exchange","fast.#"); channel.queueBind("topic-queue-2","topic-exchange","*.*.rabbit"); //3. Publish messages to exchange and specify routing rules channel.basicPublish("topic-exchange","fast.red.monkey",null,"Red quick monkey".getBytes()); channel.basicPublish("topic-exchange","slow.black.dog",null,"Black dog".getBytes()); channel.basicPublish("topic-exchange","fast.white.cat",null,"Quick white cat".getBytes());
Consumers just listen to the queue, no change.
public class TopicTest { private Connection connection; @Before // Call initialization data before @Test public void init() throws IOException, TimeoutException { connection = RabbitMqUtils.getConnection(); } /** * When testing, be sure to start the consumer first and start the producer later */ @Test // Conduct unit tests public void consumer1Test() throws IOException { // channel pipes connect consumers and queues final Channel channel = connection.createChannel(); //Parameter 1: queue - specifies the name of the queue //Parameter 2: durable - whether the current queue needs persistence (true) //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer) //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically //Parameter 5: arguments - specifies additional information about the current queue // chanel and queue binding channel.queueDeclare("topic-queue-1",true,false,false,null); // Hold one piece of data every time consumers consume channel.basicQos(1); Consumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { // Get the message from the queue and the processor System.out.println("Consumer 1 received message:"+new String(body,"utf-8") ); // After all services are completed, you can manually ack // envelope.getDeliveryTag() / / message tag 0 1 2 // False do not delete after ACK channel.basicAck(envelope.getDeliveryTag(),false); } }; // chanel and consumer binding // Parameter 1 queue name // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again // false requires manual ack // Parameter 3 Consumer channel.basicConsume("topic-queue-1", false,consumer); // Let the program consistent card where consumers can consistently consume messages System.in.read();// Wait for input from the client command line } @Test // Conduct unit tests public void consumer2Test() throws IOException { // channel pipes connect consumers and queues final Channel channel = connection.createChannel(); //Parameter 1: queue - specifies the name of the queue //Parameter 2: durable - whether the current queue needs persistence (true) //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer) //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically //Parameter 5: arguments - specifies additional information about the current queue // chanel and queue binding channel.queueDeclare("topic-queue-2",true,false,false,null); // Hold one piece of data every time consumers consume channel.basicQos(1); Consumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { // Get the message from the queue and the processor System.out.println("Consumer 2 received message:"+new String(body,"utf-8") ); // After all services are completed, you can manually ack // envelope.getDeliveryTag() / / message tag 0 1 2 // False do not delete after ACK channel.basicAck(envelope.getDeliveryTag(),false); } }; // chanel and consumer binding // Parameter 1 queue name // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again // false requires manual ack // Parameter 3 Consumer channel.basicConsume("topic-queue-2", false,consumer); // Let the program consistent card where consumers can consistently consume messages System.in.read();// Wait for input from the client command line } /** * Producer model */ @Test public void publishTest() throws IOException, TimeoutException { Channel channel = connection.createChannel(); //Bind the change to the custom switch "PubSub exchange" //Parameter 1: name of exchange //Parameter 2: specify the type of exchange fanout - PubSub, direct - routing, topic - topics // DIRECT - Routing exchange sends messages to all queues channel.exchangeDeclare("topic-exchange", BuiltinExchangeType.TOPIC); //Parameter 1 queue name //Parameter 2 switch name //Parameter 3 routing rules // All messages with info will be sent to the topic-queue-1 queue by routing exchange channel.queueBind("topic-queue-1", "topic-exchange", "*.orange.*"); channel.queueBind("topic-queue-2", "topic-exchange", "big.*.*"); //Parameter 1: if the switch name does not exist, it is the default '' // Parameter 2: the queue name or message type information will really match the routing rules // Parameter 3: specify the properties carried by the delivered message and use null. // Parameter 4: message // Send message to queue // Send 10 pieces of data and each consumer gets 5 pieces of data for (int i = 0; i < 10; i++) { // The message is not sent to the default switch, but to the custom switch PubSub exchange //Parameter 1: "PubSub exchange" switch name //Parameter 2: '' routing rule if (i%2==1){// Odd orange channel.basicPublish("topic-exchange", "xxxasdasd.orange.xfsdf",null,("topic--i:" +i).getBytes()); }else{ // Even error channel.basicPublish("topic-exchange", "big.xxxx.uii",null,("routing--i:" +i).getBytes()); } } channel.close(); } @After// After @ Test, data is destroyed public void destroy() throws IOException { connection.close(); } }
Operation results:
6, RabbitMQ integrates SpringBoot [key]
6.1 SpringBoot integration RabbitMQ
6.1.1 create SpringBoot project
6.1.2 import dependency
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <!--<scope>test</scope>--> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies>
6.1.3 writing configuration files in application.properties
spring.rabbitmq.host=8.130.166.101 spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest spring.rabbitmq.virtual-host=/
6.1.4 declare exchange and queue
@Configuration public class RabbitConfig { @Bean public TopicExchange topicExchange(){ TopicExchange topicExchange = new TopicExchange("springboot-topic-exchange", true, false); return topicExchange; } @Bean public Queue queue(){ Queue queue = new Queue("springboot-queue", true, false, false, null); return queue; } @Bean public Binding binding(TopicExchange topicExchange,Queue queue){ Binding binding = BindingBuilder.bind(queue).to(topicExchange).with("*.java.*"); return binding; } }
6.1.5 publish messages to RabbitMQ, create test classes and producers
@SpringBootTest @RunWith(SpringRunner.class) public class Mytest { @Autowired private RabbitTemplate rabbitTemplate; @Test public void publisherTest(){ rabbitTemplate.convertAndSend("springboot-topic-exchange","xxxx.java.12dssad","Is that so?"); System.out.println("send message"); } }
6.1.6 create consumer listening message
@Component public class Consumer { @RabbitListener(queues = "springboot-queue") public void consumer1(String msg, Channel channel, Message message){ System.out.println("Consumers get:"+msg); System.out.println("msg = "+message); } }
The results are as follows:
6.2 manual Ack
6.2.1 add profile
spring.rabbitmq.listener.simple.acknowledge-mode=manual
6.2.2 manual ack
At this time, if you run Test multiple times, you will receive multiple messages that are not consumed
@Component public class Consumer { @RabbitListener(queues = "springboot-queue") public void consumer1(String msg, Channel channel, Message message) throws IOException { System.out.println("Consumers get:"+msg); System.out.println("msg = "+message+" "+message.getMessageProperties().getDeliveryTag()); int i = 1/0; // Manual ack // Send the sequence number of the message ack message.getMessageProperties().getDeliveryTag() // Whether multiple messages ack false together channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); } }
This has been run for three times. On the third time, multiple non consumed objects will be directly generated
7, Other operations of RabbitMQ
7.1 message reliability
RabbitMQ transaction: the transaction can ensure 100% message delivery. The log can be recorded through transaction rollback, and the current message will be sent again regularly later. The efficiency of transaction operation is too low. After adding transaction operation, the operation efficiency is at least 100 times slower than usual.
In addition to transactions, RabbitMQ also provides a confirmation mechanism for Confirm, which is much more efficient than transactions.
7.2 SpringBoot implementation
7.2.1 preparation of configuration files
#Solve data security problems spring.rabbitmq.publisher-confirm-type=simple spring.rabbitmq.publisher-returns=true
7.2.2 enable Confirm and Return
@Component public class ConfirmReturnCallBack implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback { @Autowired private RabbitTemplate rabbitTemplate; // Adding this bean to the container will call this method (init method, @ PostConstruct tag method) @PostConstruct//Equivalent to init of bean life cycle public void init(){ rabbitTemplate.setConfirmCallback(this); rabbitTemplate.setReturnCallback(this); } @Override public void confirm(CorrelationData correlationData, boolean b, String s) { System.out.println("CorrelationData"+correlationData); System.out.println("s="+s); if (b){ System.out.println("Message arrival switch"); } } // return mechanism. Generally, there is no callback. It is called only when the message of the switch cannot be written to the queue @Override public void returnedMessage(Message message, int i, String s, String s1, String s2) { System.out.println("message"+message); } }
7.3 avoid repeated consumption of messages ()
Repeated consumption of messages will cause problems for non idempotent line operations
The reason for the repeated consumption message is that the consumer did not give RabbitMQ an ack
Repeated consumption |
---|
In order to solve the problem of repeated consumption of messages, Redis can be used. Before consumers consume messages, the message id is now put in Redis,
id-0 (executing business)
id-1 (successful execution of business)
If the ACK fails, when RabbitMQ sends the message to other consumers, it first executes setnx. If the key already exists, it obtains its value. If it is 0, the current consumer will do nothing. If it is 1, it will directly ack.
Extreme case: the first consumer has a deadlock when executing the business. On the basis of setnx, set a lifetime for the key.
Producer, specify messageId when sending a message
Consumers operate redis according to specific business logic when consuming messages
7.4 how does springboot realize message non repetition
7.4.1 import dependency
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
7.4.2 preparation of configuration files
#Configure redis spring.redis.host=8.130.166.101 spring.redis.port=6379
7.4.3 modify producer
@SpringBootTest @RunWith(SpringRunner.class) public class Mytest { @Autowired private RabbitTemplate rabbitTemplate; @Test public void publisherTest(){ CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString()); rabbitTemplate.convertAndSend("springboot-topic-exchange","xxxx.java.12dssad","Is that so?"+System.currentTimeMillis(),correlationData); System.out.println("send message"); } }
7.4.4 modifying consumers
@Component public class Consumer { @Autowired private RedisTemplate redisTemplate; @RabbitListener(queues = "springboot-queue") public void consumer1(String msg, Channel channel, Message message) throws IOException { System.out.println("Consumers get:"+msg); System.out.println("msg = "+message+" "+message.getMessageProperties().getDeliveryTag()); // Get the unique id of the processed message String id = (String) message.getMessageProperties().getHeaders().get("spring_returned_message_correlation"); System.out.println("id="+id); // If the setting is successful, the message has not been processed // The id value is 0 for processing and 1 for processing completion if (redisTemplate.opsForValue().setIfAbsent(id,"0",10, TimeUnit.SECONDS)){ System.out.println("Consumer, deal with the business:"+msg); //After processing, it becomes 1 redisTemplate.opsForValue().set(id,"1",10,TimeUnit.SECONDS); channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); }else { // If the key cannot be set, it indicates that a consumer is already processing if (redisTemplate.opsForValue().get(id).equals("1")){ // Manual ack // Send the sequence number of the message ack message.getMessageProperties().getDeliveryTag() // Whether multiple messages ack false together channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); } } } }
If the first run does not come out, the second run is allowed. If the second run does not come out, it is wrong