[MQ Series] RabbitListener consumer use basic gestures introduction
Before the introduction of the rabbitmq messaging posture, since there are sending, of course you have to have consumers in SpringBoot environment, consumption can be said is relatively simple, with @RabbitListener
annotations, you can basically meet more than 90% of the business development needs
Here we look at @RabbitListener
the most common use of gestures
I. placement
SpringBoot first create a project for subsequent presentation
- springboot version
2.2.1.RELEASE
- rabbitmq version
3.7.5
(refer to installation tutorial: [MQ Series] springboot + rabbitmq first experience )
Dependent configuration file pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- 注意,下面这个不是必要的哦-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/libs-release-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
In the application.yml
configuration file, add the relevant attributes of rabbitmq
spring:
rabbitmq:
virtual-host: /
username: admin
password: admin
port: 5672
host: 127.0.0.1
II. Consumer posture
This article will target on practicality, combining specific scenarios to demonstrate @RabbitListener
the use of gestures, so this comment which some attribute or do not understand, please do not worry when you find reading this article, the next will come one by one
0. mock data
Consumer spending, there is no data, how to spend? So we first step, create a message producer, you can write data to exchange, for subsequent use consumer test
Benpian consumption mainly in the topic model to be explained (several other models use little difference, if there is demand, then follow padded)
@RestController
public class PublishRest {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping(path = "publish")
public boolean publish(String exchange, String routing, String data) {
rabbitTemplate.convertAndSend(exchange, routing, data);
return true;
}
}
Provides a simple interface to rest, you can specify which exchange to push data and develop key routes
1. case1: exchange, queue already exists
For consumers, they really need to manage the creation of exchange / destruction, which is defined by the sender; In general, consumers are more concerned with their own queue, including the definition and exchange queue and binding, and this a process that can be directly operated by rabbitmq console oh
Therefore, the actual development process, exchange and queue corresponding binding relationship and the possibility that already exists is very high, then the code does not need additional processing;
In this scenario, consumption data, we can say very, very simple, as follows:
/**
* 当队列已经存在时,直接指定队列名的方式消费
*
* @param data
*/
@RabbitListener(queues = "topic.a")
public void consumerExistsQueue(String data) {
System.out.println("consumerExistsQueue: " + data);
}
Annotations directly specified in the queues
parameters can, parameter values for the column name (queueName)
2. case2: queue does not exist
When the queue is autoDelete property is false, using the above scenario is more appropriate; however, when this property is true, there is no consumer queue will be automatically deleted, and this time then the above position, may get the following abnormal
Typically this scenario, we need to take the initiative to create Queue, and establish a binding relationship with the Exchange is given below @RabbitListener
recommended posture
/**
* 队列不存在时,需要创建一个队列,并且与exchange绑定
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "topic.n1", durable = "false", autoDelete = "true"),
exchange = @Exchange(value = "topic.e", type = ExchangeTypes.TOPIC),
key = "r"))
public void consumerNoQueue(String data) {
System.out.println("consumerNoQueue: " + data);
}
A comment, declared inside the queue, and establish the binding relationship is so magical! ! !
Note @QueueBinding
annotation three properties:
- value: @Queue annotations for declaring the queue, value is queueName, durable indicates whether the queue is persistent, autoDelete indicates whether the queue is not automatically deleted after consumers
- exchange: @Exchange annotations for declaring exchange, type specified message delivery strategy, we are here with the way the topic
- key: In the topic the way, this is what we know routingKey
Above, is the use of gestures in the queue does not exist, it seems not complicated
3. Case3: oh
In the core knowledge of the learning process in front rabbitmq we will know that in order to ensure data consistency, there is a message acknowledgment mechanism;
ack here mainly for the consumer end, when we want to change the default ack mode (noack, auto, manual), can be processed as follows
/**
* 需要手动ack,但是不ack时
*
* @param data
*/
@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "topic.n2", durable = "false", autoDelete = "true"),
exchange = @Exchange(value = "topic.e", type = ExchangeTypes.TOPIC), key = "r"), ackMode = "MANUAL")
public void consumerNoAck(String data) {
// 要求手动ack,这里不ack,会怎样?
System.out.println("consumerNoAck: " + data);
}
The above implementation is relatively simple, provided ackMode=MANUAL
, manual ack
However, please note that our implementation, there is no place reflects the manual ack, which is equivalent to the same are not ack, in later tests, it can be seen when this is not ack, will find that the data has been unacked
this column when the number of Unacked exceeds the limit, it will not consume the new data
4. case4: manual ack
Although the above selection ack way, but still missing a step ack logic, let's look at how filled
/**
* 手动ack
*
* @param data
* @param deliveryTag
* @param channel
* @throws IOException
*/
@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "topic.n3", durable = "false", autoDelete = "true"),
exchange = @Exchange(value = "topic.e", type = ExchangeTypes.TOPIC), key = "r"), ackMode = "MANUAL")
public void consumerDoAck(String data, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel)
throws IOException {
System.out.println("consumerDoAck: " + data);
if (data.contains("success")) {
// RabbitMQ的ack机制中,第二个参数返回true,表示需要将这条消息投递给其他的消费者重新消费
channel.basicAck(deliveryTag, false);
} else {
// 第三个参数true,表示这个消息会重新进入队列
channel.basicNack(deliveryTag, false, true);
}
}
Note that the method adds two more arguments
deliveryTag
: Equivalent to uniquely identify the message, for which the message is to distinguish mq ack / nak thechannel
: Mq and conduit between the consumer, through which ack / nak
When we correct consumption, by calling the basicAck
method can be
// RabbitMQ的ack机制中,第二个参数返回true,表示需要将这条消息投递给其他的消费者重新消费
channel.basicAck(deliveryTag, false);
When we consume fails, the message needs to be re-inserted into the queue, waiting for re-consumption, can be used basicNack
// 第三个参数true,表示这个消息会重新进入队列
channel.basicNack(deliveryTag, false, true);
5. case5: concurrent consumption
When a lot of news, a Hangchihangchi consumer spending is too slow, but my machine performance and leverage, and this time I hope parallel consumption, equivalent to the same time there are more consumers to process data
To support concurrent consumption, the following settings can be
@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "topic.n4", durable = "false", autoDelete = "true"),
exchange = @Exchange(value = "topic.e", type = ExchangeTypes.TOPIC), key = "r"), concurrency = "4")
public void multiConsumer(String data) {
System.out.println("multiConsumer: " + data);
}
Please note annotation of the concurrency = "4"
property, which is a fixed four consumers;
In addition to the above assignment this way, there is a m-n
format represents m parallel consumers, there may be up to n
(Additional Description: This parameter explained it SimpleMessageListenerContainer
, the next article will introduce it at the scene of DirectMessageListenerContainer
a difference)
6. Test
Messaging interface through front reservation, we request the browser: http://localhost:8080/publish?exchange=topic.e&routing=r&data=wahaha
Then look at the output, five consumers have received, especially nak initiative that consumers have been receiving the message;
(As has been print the log, so restart the application, starts the next test)
Then send a success message, ACK true manual verification, whether there will be the case above, the request command: http://localhost:8080/publish?exchange=topic.e&routing=r&data=successMsg
Then look no ack of the queue, there have been news of a unack
II. Other
Bowen series
- [MQ Series] springboot + rabbitmq first experience
- [MQ Series] RabbitMq core knowledge Summary
- [MQ] SprigBoot + RabbitMq series of basic message sent using a gesture
- [] RabbitMq MQ Series message confirmation / transaction mechanism using gestures
Source Project
- Project: https://github.com/liuyueyi/spring-boot-demo
- Source: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/302-rabbitmq-consumer
1. a gray Blog : https://liuyueyi.github.io/hexblog
A gray personal blog, recording all study and work in the blog, welcome to go around
2. Statement
Believe everything the book is not as good, above, is purely one of the words, due to limited personal capacity, it is inevitable omissions and mistakes, such as find a bug or have better suggestions are welcome criticism and generous gratitude
- Microblogging Address: little gray Blog
- QQ: a gray / 3302797840
3. Scan concern
A gray blog