Daily Records - SpringBoot Integration RabbitMQ Section 1 (Getting Started)

Refer to the following blogger's article

Getting Started with RabbitMQ Install SpringAMQP Simple Queue, Work Queue, Publish and Subscribe (Fan-Out Mode, Broadcast Mode), Direct Mode (Roting Mode), Topic Mode

RabbitMQ (2), MQ issues: message reliability, delayed messages (delayed queue (plug-in)), message accumulation (lazy queue), high availability of MQ. ConfirmCallback mechanism, ReturnCallback mechanism, dead letter switch

I will only record here how to integrate SpringBoot. For details of installation and deployment, please refer to the article written by the blogger above.

1. Environmental preparation

1、pom.xml

SpringBoot integrates RabbitMQ core only this

<!--rabbitmq-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2、application.yml

2.1. Basic configuration

The easiest thing is to link to the RabbitMQ service

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    # rabbitmq虚拟机: 默认就是/ 你可以当他是一个命名空间,这个空间下有很多的交换机、队列。
    # 一般默认都不会自己创建新的虚拟机,因为业务不会多到需要分这么多rabbitmq虚拟机。
    virtual-host: /

insert image description here

2.2. Advanced configuration

This is the deluxe version, I will talk about it slowly later, start with the basic configuration first.

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    #virtual-host: /
    # 开启发布确认 消息从 producer 到 exchange 有一个 confirmCallback 确认模式 ->用于给生产者确认消息是否到达交换机
    publisher-confirm-type: correlated
    # 开启回退模式 消息从 exchange 到 queue 投递失败有一个 returnCallback 退回模式。
    publisher-returns: true
    template:
      # 如果消息没有送达queue,就强制回退消息。true : 消息路由失败时会调用returnCallback回退消息 | false : 消息路由失败时会丢弃消息
      mandatory: true
    listener:
      simple:
        #消费者一次抓取几条消息
        prefetch: 1
      # none(无应答模式) auto(自动应答模式) manual(手动应答模式)
        acknowledge-mode: auto
        retry:
          #开启重试 消费者本地的失败重试
          enabled: true
          #初始的失败等待时长,单位是ms,默认1000
          initial-interval: 1000
          #与上次重试间隔时长的倍数(1表示每次重试的时间间隔相同)。默认1
          multiplier: 1
          #最大重试的间隔,默认值10000
          max-interval: 10000
          #最多重试几次。默认3
          max-attempts: 3
          #是否无状态。默认true。如果涉及事务,要改成false
          stateless: true

2. Introduction to RabbitMQ

RabbitMQ provides a variety of working modes: click here to jump to view

1. Simple mode

The producer sends the message directly to the consumer (regardless of whether the message is successfully delivered to the consumer)
insert image description here

define queue

import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SimpleQueueConfig {
    
    

    /**
     * 声明 一个队列,队列名称为simple.queue
     * 注意:Queue类是org.springframework.amqp.core.Queue,不要导错了
     */
    @Bean
    public Queue simpleQueue(){
    
    
        return QueueBuilder.durable("simple.queue").build();
    }

}

mock producer

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class SimpleQueueTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //模拟生产者
    @Test
    public void test(){
    
    

        //参数1:队列名称
        //参数2:消息内容
        rabbitTemplate.convertAndSend("simple.queue","生产者,制造中...");

    }


}

Because there is no switch defined, the queue defaults to the default switch of RabbitMQ
insert image description here
insert image description here

mock consumer

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;


/**
 * 消费者
 */
@Component
public class SimpleQueueListener {
    
    

    /**
     * 监听名字为simple.queue的队列消息
     * @param msg 生产者发送的消息
     */
    @RabbitListener(queues = "simple.queue")
    public void handleSimpleQueueMsg(String msg){
    
    

        System.out.println("接收到simple.queue队列的消息,正在消费【simple.queue】队列的消息:"+msg);

    }

}

Restart the SpringBoot service, and you will see that the messages in the simple.queue queue are consumed immediately

insert image description here
insert image description here

2. Working mode

For a producer to multiple consumers, messages will not be consumed repeatedly, and will be shared among consumers.
insert image description here

application.yml add configuration

spring:
  rabbitmq:
    listener:
      simple:
        #消费者一次抓取几条消息,如果不设置这个,会发生分配不均的情况
        prefetch: 1 

define queue

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 *                                               ↗C(消费者1)
 *  P(生产者) -> 交换机(默认) -> 队列(work.queue) ->
 *                                               ↘C(消费者2)
 */
@Configuration
public class WorkQueueConfig {
    
    


    //定义持久化队列
    @Bean
    public Queue workQueue(){
    
    

        //参数1       name : 队列名称
        //参数2    durable : 是否持久化,默认是true,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
        //参数3  exclusive : 默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
        //参数4 autoDelete : 默认false是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。

        return new Queue("work.queue",true,false,false);
    }



}

mock producer

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class WorkQueueTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //模拟生产者
    @Test
    public void test() {
    
    
        for (int i = 0; i < 20; i++) {
    
    
            rabbitTemplate.convertAndSend("work.queue", "这是work队列消息" + i);
        }
    }

}

Simulate the consumer after calling the producer
insert image description here

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者
 */
@Component
public class WorkQueueListener {
    
    

    @RabbitListener(queues = "work.queue")
    public void workQueueMsg1(String msg){
    
    

        System.out.println("workQueueMsg1:接收到work.queue队列的消息,正在消费【work.queue】队列的消息:"+msg);

    }

    @RabbitListener(queues = "work.queue")
    public void workQueueMsg2(String msg){
    
    

        System.out.println("workQueueMsg2:接收到work.queue队列的消息,正在消费【work.queue】队列的消息:"+msg);

    }

}

After restarting SpringBoot
insert image description here
insert image description here

3. Publish/subscribe model

Delivering the same message to consumers of multiple queues is the publish/subscribe model. Similar merchants are about to engage in activities, and send event text messages to designated members or all members.

Here we will talk about the switch, which is the X on the picture .

Exchange type:

Fanout: Broadcast, which queues are bound to the switch, and deliver the message to these queues

Direct: Orientation, deliver the message to the queue that matches the specified routing key

Topic: wildcard, send the message to the queue that matches the routing pattern

3.1. Fanout switch

Fanout: broadcast, which queues are bound to the Fanout switch, and deliver the message to these queues
insert image description here

Define the Fanout type switch and define the queue

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.queue1 -> C(消费者1)
 * P(生产者) -> fanout.exchange
 *                           ↘ fanout.queue2 -> C(消费者2)
 *
 * 操作:
 *      1. 声明Fanout类型的交换机:fanout.exchange
 *
 *      2. 声明队列1:fanout.queue1
 *         声明队列2:fanout.queue2
 *
 *      3. 把交换机与队列1绑定
 *         把交换机与队列2绑定
 */
@Configuration
public class FanoutQueueConfig {
    
    


    //定义持久化交换机
    @Bean
    public FanoutExchange fanoutExchange() {
    
    
        return new FanoutExchange("fanout.exchange",true,false);
    }

    //定义持久化队列
    @Bean
    public Queue fanoutQueue1(){
    
    

        //参数1       name : 队列名称
        //参数2    durable : 是否持久化,默认是true,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
        //参数3  exclusive : 默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
        //参数4 autoDelete : 默认false是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。

        return new Queue("fanout.queue1",true,false,false);
    }

    //定义持久化队列
    @Bean
    public Queue fanoutQueue2(){
    
    

        //参数1       name : 队列名称
        //参数2    durable : 是否持久化,默认是true,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
        //参数3  exclusive : 默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
        //参数4 autoDelete : 默认false是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。

        return new Queue("fanout.queue2",true,false,false);
    }

    //fanoutExchange交换机与队列fanoutQueue1绑定
    @Bean
    public Binding fanoutQueue1Biding(Queue fanoutQueue1, FanoutExchange fanoutExchange){
    
    
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }
    //fanoutExchange交换机与队列fanoutQueue2绑定
    @Bean
    public Binding fanoutQueue2Binding(Queue fanoutQueue2,FanoutExchange fanoutExchange){
    
    
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }

}

mock producer

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class FanoutQueueTest {

@Autowired
private RabbitTemplate rabbitTemplate;

//模拟生产者
@Test
public void test() {
    //参数1:  交换机名称
    //参数2:  路由key  :  fanout类型的交换机不需要使用路由key,把这个值写成空的
    //参数3:  消息内容
    rabbitTemplate.convertAndSend("fanout.exchange", "", "这是一条fanout广播消息");
}

}


调用生产者后,你会看到绑定了该交换机的队列都收到消息了
![在这里插入图片描述](https://img-blog.csdnimg.cn/d08a2a3c31cb4e0a960b88f68608202c.png)

模拟消费者

```java
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

//消费者
@Component
public class FanoutQueueListener {

    @RabbitListener(queues = "fanout.queue1")
    public void fanoutQueue1Msg(String msg) {

        System.out.println("fanout.queue1队列收到fanout.exchange交换机发送的消息:"+msg);

    }
    @RabbitListener(queues = "fanout.queue2")
    public void fanoutQueue2Msg(String msg) {
        System.out.println("fanout.queue2队列收到fanout.exchange交换机发送的消息:"+msg);
    }


}

After adding the code for listening to the queue above, after restarting the service, you can see that all the queues listening to the bound fanout.exchange switch have received this message
insert image description here
insert image description here

3.2. Direct switch

Direct: Directed delivery of messages, delivering messages to queues that match the specified routing key
insert image description here
Description:

When a queue is bound to a Direct switch, a Routing Key (routing key) needs to be assigned to the queue

When a producer sends a message, it must specify the Routing Key of the message

The switch judges according to the RoutingKey of the message: only when the RoutingKey of the queue is exactly the same as the RoutingKey of the message, the message will be received

Define Direct type switches, define queues and set RoutingKey

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 需要:
 *      1. 声明Direct类型的交换机:direct.exchange
 *      2. 声明队列1:direct.queue1
 *         声明队列2:direct.queue2
 *      3. 把交换机与队列1绑定:把 RoutingKey为orange的消息 -> 投递到队列1
 *         把交换机与队列2绑定:把 RoutingKey为black的消息  -> 投递到队列2
 *         把交换机与队列2绑定:把 RoutingKey为green的消息  -> 投递到队列2
 *
 *                             ↗ RoutingKey = orange   ->  direct.queue1 -> C(消费者1)
 * P(生产者) -> direct.exchange
 *                             ↘ RoutingKey = black
 *                                                    ->  direct.queue2   -> C(消费者2)
 *                             ↘ RoutingKey = green
 *
 */
@Configuration
public class DirectQueueConfig {
    
    


    //定义direct类型交换机
    @Bean
    public DirectExchange directExchange() {
    
    
        return ExchangeBuilder.directExchange("direct.exchange").build();
    }


    //定义持久化队列
    @Bean
    public Queue directQueue1() {
    
    
        return new Queue("direct.queue1",true,false,false);
    }

    //定义持久化队列
    @Bean
    public Queue directQueue2() {
    
    
        return QueueBuilder.durable("direct.queue2").build();
    }

    //把交换机与队列direct.queue1绑定:把 RoutingKey为orange的消息,投递到队列direct.queue1
    @Bean
    public Binding directQueue1Binding(Queue directQueue1, DirectExchange directExchange) {
    
    
        return BindingBuilder.bind(directQueue1).to(directExchange).with("orange");
    }

    //把交换机与队列direct.queue2绑定:把 RoutingKey为black的消息,投递到队列direct.queue2
    @Bean
    public Binding directQueue2BindingInfo(Queue directQueue2, DirectExchange directExchange) {
    
    
        return BindingBuilder.bind(directQueue2).to(directExchange).with("black");
    }
    //把交换机与队列direct.queue2绑定:把 RoutingKey为green的消息,投递到队列direct.queue2
    @Bean
    public Binding direcQueue2BindingError(Queue directQueue2, DirectExchange directExchange) {
    
    
        return BindingBuilder.bind(directQueue2).to(directExchange).with("green");
    }



}

mock producer

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class DirectQueueTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //模拟生产者
    @Test
    public void test(){
    
    
        //rabbitTemplate.convertAndSend("direct.exchange","orange","这是orange");
        //rabbitTemplate.convertAndSend("direct.exchange","black","这是black");
        rabbitTemplate.convertAndSend("direct.exchange","green","这是green");
    }

}

mock consumer

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class DirectQueueListener {
    
    

    @RabbitListener(queues = "direct.queue1")
    public void directQueue1(String msg) {
    
    
        System.out.println("我是direct.queue1队列,接收到消息:"+msg);

    }

    @RabbitListener(queues = "direct.queue2")
    public void directQueue2(String msg) {
    
    
        System.out.println("我是direct.queue2队列,接收到消息:"+msg);
    }


}

I won’t show it here, just test it yourself

3.3. Topic switch

Topic: wildcard, send the message to the queue that matches the routing pattern
insert image description here
RoutingKey: The message sent to the Topic type switch cannot have any routing key.
It must be a list of words separated by dots
. There can be any number of words, up to 255 bytes
can be used * Asterisk, match one word
# hash sign, match 0 or more words

Define the Topic type switch, define the queue and bind it

/**
 * 需要:
 *      1. 声明Topic类型的交换机:topic.exchange
 *      2. 声明队列1:topic.queue1
 *         声明队列2:topic.queue2
 *      3. 把交换机与队列1绑定:将RoutingKey为*.orange.*的消息,投递到topic.queue1
 *         把交换机与队列2绑定:将RoutingKey为 *.*.rabbit 和 lazy.# 的消息,投递到topic.queue2
 *
 *                            ↗ *.orange.* -> topic.queue1 -> C(消费者1)
 * P(生产者) -> topic.exchange
 *                            ↘ *.*.rabbit -> topic.queue2 -> C(消费者2)
 *                            ↘ lazy.#     -> topic.queue2 -> C(消费者2)
 */

@Configuration
public class TopicQueueConfig {
    
    


    //定义topic类型交换机
    @Bean
    public TopicExchange topicExchange(){
    
    

        return ExchangeBuilder.topicExchange("topic.exchange").build();
    }


    //定义持久化队列
    @Bean
    public Queue topicQueue1(){
    
    
        return QueueBuilder.durable("topic.queue1").build();
    }

    //定义持久化队列
    @Bean
    public Queue topicQueue2(){
    
    
        return QueueBuilder.durable("topic.queue2").build();
    }


    //队列topic.queue1绑定topicExchange类型交换机,routingKey = *.orange.*
    @Bean
    public Binding queue1Binding(TopicExchange topicExchange, Queue topicQueue1){
    
    
        return BindingBuilder.bind(topicQueue1).to(topicExchange).with("*.orange.*");
    }

    //队列topic.queue2绑定topicExchange类型交换机,routingKey = *.*.rabbit
    @Bean
    public Binding queue2Binding(TopicExchange topicExchange, Queue topicQueue2) {
    
    
        return BindingBuilder.bind(topicQueue2).to(topicExchange).with("*.*.rabbit");
    }

    //队列topic.queue2绑定topicExchange类型交换机,routingKey = lazy.#
    @Bean
    public Binding queue22Binding(TopicExchange topicExchange, Queue topicQueue2) {
    
    
        return BindingBuilder.bind(topicQueue2).to(topicExchange).with("lazy.#");
    }




}

mock producer

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class TopicQueueTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void test(){
    
    

        //路由为*.orange.*的消息,投递到topic.queue1,将由消费者1接收
        //rabbitTemplate.convertAndSend("topic.exchange", "xxxx.orange.ttttt", "routingKey=xxxx.orange.ttttt");

        //路由为*.orange.*的消息,投递到topic.queue1  -> 这个topic.queue1队列收不到,因为左边的*没有(可使用* 星号,匹配一个单词)
        //rabbitTemplate.convertAndSend("topic.exchange", "orange.ttttt", "routingKey=orange.ttttt");


        //路由为*.*.rabbit的消息,投递到topic.queue2,将由消费者2接收
        //rabbitTemplate.convertAndSend("topic.exchange", "a.b.rabbit", "routingKey=a.b.rabbit");

        //路由为*.*.rabbit的消息,投递到topic.queue2,-> 这个topic.queue2队列收不到,因为*至少要匹配一个单词
        //rabbitTemplate.convertAndSend("topic.exchange", "b.rabbit", "routingKey=b.rabbit");

        //路由为lazy.#的消息,投递到topic.queue2,将由消费者2接收
        //rabbitTemplate.convertAndSend("topic.exchange", "lazy", "routingKey=lazy");

        //路由为lazy.#的消息,投递到topic.queue2,将由消费者2接收
        //rabbitTemplate.convertAndSend("topic.exchange", "lazy.a", "routingKey=lazy.a");

        //路由为lazy.#的消息,投递到topic.queue2,将由消费者2接收
        //rabbitTemplate.convertAndSend("topic.exchange", "lazy.a.b", "routingKey=lazy.a.b");
    }



}

mock consumer

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class TopicQueueListener {
    
    

    @RabbitListener(queues = "topic.queue1")
    public void topicQueue1Msg(String msg) {
    
    

        System.out.println("topic.queue1队列收到消息:"+msg);
    }

    @RabbitListener(queues = "topic.queue2")
    public void topicQueue2Msg(String msg) {
    
    
        System.out.println("topic.queue2队列收到消息:"+msg);
    }

}

I won’t show it here, just test it yourself

Guess you like

Origin blog.csdn.net/qq407995680/article/details/132066301
Recommended