Table of contents
RabbitMQ
Install rabbitmq
First make sure you have installed docker
It is docker that pulls the image file:docker pull rabbitmq:3-management
After pulling, open the container
docker run \
-e RABBITMQ_DEFAULT_USER=itcast \
-e RABBITMQ_DEFAULT_PASS=123321 \
--name mq \
--hostname mq1 \
-p 15672:15672 \
-p 5672:5672 \
-d \
rabbitmq:3-management
Open the browser to port 15672 of the virtual machine and you will see the rabbitmq management interface.
We can add new users in the admin tab
wherecan access virtual hosts
represents the virtual host corresponding to the current user
It is recommended that different users correspond to different virtual hosts to achieve isolation effect
Virtual host can be created by clicking the virtual hosts
button on the right side of the picture above
SpringAMQP basic queue
Since the official native method of operating rabbitmq is too crude and has a lot of code, it is not suitable for daily development. It is recommended to use SpringAMQP to simplify the operation.
Import coordinates
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
Configure rabbitmq link
logging:
pattern:
dateformat: MM-dd HH:mm:ss:SSS
spring:
rabbitmq:
host: 192.168.113.146 # rabbitMQ的ip地址
port: 5672 # 端口
username: itcast
password: 123321
virtual-host: /
publisher writes test classes to test AMQP
Since our rabbitmq does not create a queue by defaultsimple.queue
, you will not receive any information if you send it directly. You must make a judgment first. If the queue does not exist, create the corresponding queue first. Only after sending it can you receive it!
@Test
public void testSendMessage2SimpleQueue() {
RabbitAdmin admin = new RabbitAdmin(rabbitTemplate);
String queueName = "simple.queue";
String message = "hello, spring amqp!";
// 队列是否存在的判断
if (Objects.isNull(admin.getQueueProperties(queueName))) {
Queue queue = new Queue(queueName);
admin.declareQueue(queue);
}
// 发送消息到消息队列
rabbitTemplate.convertAndSend(queueName, message);
}
If nothing else, you can see the newly created simple.queue queue in the queue option of the rabbitmq console, which contains the first message we sent.
consumer consumes messages in the corresponding queue
Before listening, you must also configure the same application.yaml as the publisher so that you can connect to rabbitmq.
Create a new monitoring consumer class SpringRabbitListener
and pass in the following code to execute monitoring
@Component
public class SpringRabbitListener {
// 设置消费者需要监听的队列
@RabbitListener(queues = "simple.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {
// 获取队列中信息
System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
Thread.sleep(20);
}
}
WorkQueue
The above figure shows the flow chart of the work queue. In fact, a consumer is added to consume the messages in the queue.
Since we have already created simple.queue in the previous section, there is no need to judge here. We can directly insert a piece of information every 20ms into it.
@Test
public void testSendMessage2WorkQueue() throws InterruptedException {
String queueName = "simple.queue";
String message = "hello, message__";
for (int i = 1; i <= 50; i++) {
rabbitTemplate.convertAndSend(queueName, message + i);
Thread.sleep(20);
}
}
Similarly, follow the instructions in the flow chart to set up two consumer listeners
Attention! The first consumer receives messages every 20ms, while the second consumer receives messages every 200ms
@RabbitListener(queues = "simple.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {
System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
Thread.sleep(20);
}
@RabbitListener(queues = "simple.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {
System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());
Thread.sleep(200);
}
Run the consumer first, then use publisher to insert 50 messages
From the log output, we found that consumers 1 and 2 processed the same amount of data (25 items each), but it is obvious that the second consumer is much slower because it only processes a message every 200ms.
The reason for this situation is消息预取
, which means that all consumers get the same number of messages, regardless of how often they process the messages
Solve the problem of uneven message distribution
The prefetch configuration item is added to the configuration file. It means that one message must be processed before the next message can be taken out for processing, which effectively avoids the situation of prefetching all messages and piling them up on one consumer in an instant.
spring:
rabbitmq:
host: 192.168.113.146 # rabbitMQ的ip地址
port: 5672 # 端口
username: itcast
password: 123321
virtual-host: /
listener:
simple:
prefetch: 1
At this time, repeat the above operation and find that the effect of "the capable is more laborous" is achieved. Consumer 1 consumes the vast majority of messages due to its fast processing speed, while consumer 2 processes very few messages.
This will compress the overall processing time to 1s or less.
Route publish subscribe FanoutExchange
Setting up routing publication and subscription requires three steps: setting up routing Exchange, setting up queue Queue, and binding the queue to routing.
Create the fanout configuration file FanoutConfig. We simply create 1 route and 2 queues according to the following code, and perform the binding operation.
@Configuration
public class FanoutConfig {
// 配置路由:itcast.fanout
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("itcast.fanout");
}
// 配置队列:fanout.queue1
@Bean
public Queue fanoutQueue1(){
return new Queue("fanout.queue1");
}
// 绑定队列1到交换机
@Bean
public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange){
return BindingBuilder
.bind(fanoutQueue1)
.to(fanoutExchange);
}
// fanout.queue2
@Bean
public Queue fanoutQueue2(){
return new Queue("fanout.queue2");
}
// 绑定队列2到交换机
@Bean
public Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange){
return BindingBuilder
.bind(fanoutQueue2)
.to(fanoutExchange);
}
}
Since they have all added bean annotations, run the consumer project directly and they will be automatically assembled.
At this time, open the rabbitmq control panel and enter the exchange option. You can find our newly created route and the corresponding queue binding relationship.
As for the subsequent code for publisher information and consumer consumption information, you can refer to the previous section to complete it yourself, so I won’t go into details here.
DirectExchange
It can be understood as a routing and forwarding mechanism with rules, and the forwarding operation is implemented through the consistent pairing of bindingkey and routingkey.
Configure consumer monitoring
// value 监听队列
// exchange 监听路由
// key bindingkey
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"),
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
key = {
"red", "blue"}
))
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue2"),
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
key = {
"red", "yellow"}
))
Then the publisher sends the message with routingkey to the corresponding route.
@Test
public void testSendDirectExchange() {
// 交换机名称
String exchangeName = "itcast.direct";
// 消息
String message = "hello, red!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName, "red", message);
}
TopicExchange
Similar to directexchange, but routingkey is a list of multiple words. Please refer to the figure above for the specific format.
As for the writing method of publisher and consumer, it is basically the same as directexchange. What needs to be noted is the writing method of routingkey and bindingkey.