SpringBoot整合RabbitMQ五种模式详解

1. 环境配置

pom.xml

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
    </parent>

    <dependencies>
        <!-- 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>
            <scope>test</scope>
        </dependency>
    </dependencies>

application.yml

server:
  port: 9001
spring:
  rabbitmq:
    host: 192.168.153.90 #默认端口不用写

Application 启动类

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

2. 直接模式(Direct)

我们需要将消息发给唯一一个节点时使用这种模式,这是最简单的一种形式,交换器不会做任何处理,直接将消息存入队列中

在这里插入图片描述

  1. 一般情况可以使用rabbitMQ自带的Exchange:”"(该Exchange的名字为空字符串,下文称其为default Exchange)
  2. 这种模式下不需要将Exchange进行任何绑定(binding)操作
  3. 消息传递时需要一个“RouteKey”,可以简单的理解为要发送到的队列名字
  4. 如果vhost中不存在RouteKey中指定的队列名,则该消息会被抛弃

任何发送到Direct Exchange的消息都会被转发到RouteKey中指定的Queue

2.1 消息生产者

@RunWith(SpringRunner.class) //和 junit4 一样
@SpringBootTest(classes = Application.class) 
public class MqTest {

    // 注入 rabbitmq 
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    // 直接模式(Direct)
    public void testSend(){
        // convertAndSend()重载方法,两个参数代表 (队列名称,生产的消息)
        rabbitTemplate.convertAndSend("sanpang","rabbitmq直接模式");
    }
}

运行生产者,可以看到有一条消息在队列中产生

在这里插入图片描述

2.2 消息消费者

在Application启动类同级目录下创建 Customer1 测试消费者

在这里插入图片描述
编写代码

@Component  // 把消费者类加载到spring中 spring容器启动,那么就启动监听队列
//直接使用queues=这种方式 需手动先创建队列 在指定监听队列名称
@RabbitListener(queues = "sanpang")
public class Customer1 {

    @RabbitHandler // rabbitmq处理器 监听到的消息由此注解处理
    public void receiveMessage(String message) {
        System.out.println("sanpang 接收到的消息是 : " + message);
    }

	// queuesToDeclare 不需要提前创建队列自动声明队列
    @RabbitListener(queuesToDeclare = @Queue("sanpang"))     
    public void helloWordListener(String message) {
        System.out.println("sanpang 接收到的消息是 : " + message);
    }
}

启动Application启动类,查看控制台输出,可以看到消息消费完毕

在这里插入图片描述

3. 分列模式(Fanout)

当我们需要将消息一次发给多个队列时,需要使用这种模式

在这里插入图片描述

任何发送到Fanout Exchange的消息都会被转发到与该Exchange绑(Binding)的所有Queue上

  1. 可以理解为路由表的模式
  2. 这种模式不需要RouteKey
  3. 这种模式需要提前将Exchange与Queue进行绑定,一个Exchange可以绑定多个Queue,一个Queue可以同多个Exchange进行绑定
  4. 如果接受到消息的Exchange没有与任何Queue绑定,则消息会被抛弃

3.1 消息生产者

 @Test
    // 分列模式(Fanout)无路由秘钥测试
    public void testSendFanout(){
        // convertAndSend()重载方法,三个参数代表 (交换器名称,路由秘钥,生产的消息)
        // 路由秘钥为空 那么交换器所有的队列都能收到消息,如指定路由秘钥,那么只有绑定时添加过相同路由秘钥的队列能收到消息
        rabbitTemplate.convertAndSend("sunmone","","rabbitmq分列模式..");
        System.out.println("消息已产生");
    }

启动测试,可以看到两个队列中都收到了一条消息

在这里插入图片描述

3.2 消息消费者

@Component
public class FanoutExchangeListener {

    // 指定队列 sanpang01 并自动声明  指定Exchange为fanoutTest,模式为FANOUT
    @RabbitListener(bindings = {@QueueBinding(value = @Queue(value = "sanpang01",durable = "true"),
            exchange = @Exchange(value = "sunmone", type = ExchangeTypes.FANOUT))})
    public void reveivel(String message) {
        System.out.println("sanpang01 = " + message);
    }

    // 指定队列 sanpang02 并自动声明  指定Exchange为fanoutTest,模式为FANOUT
    @RabbitListener(bindings = {@QueueBinding(value = @Queue(value = "sanpang02",durable = "true"),
            exchange = @Exchange(value = "sunmone", type = ExchangeTypes.FANOUT))})
    public void reveivel2(String message) {
        System.out.println("sanpang02 = " + message);
    }
}

  1. 启动Application消费信息,可以看到两个队列消息都被接受并处理了

在这里插入图片描述

4. Routing路由模型

routing模型也是将消息发送到交换机

使用的是Direct类型的交换机,会将接收到的消息根据规则路由到指定的Queue(队列),因此称为路由模式

在这里插入图片描述

4.1 消息生产者

	 // 路由模型 Routing 模式
    @Test
    public void direstExchangeTest(){
        rabbitTemplate.convertAndSend("direstTest","info","发送info的key的路由消息");
        rabbitTemplate.convertAndSend("direstTest","error","发送error的key的路由消息");
    }

4.2 消息消费者

// 消费者直接绑定交换机,指定类型为direct,并指定key表示能消费的key
@Component
public class RoutingExchangeListener {
    
    // 指定队列,可以接收缓存到队列里的消息
    // key = {"info","error"} 表示我能接收到routingKey为 info和error的消息
    @RabbitListener(bindings = {@QueueBinding(value = @Queue(value ="test1",durable = "true" ),
            exchange = @Exchange(value = "direstTest",type = ExchangeTypes.DIRECT),key = {"info","error"})})
    public void receivel(String message){
        System.out.println("message = " + message);
    }
    
    // key = {"error"} 表示我只能接收到routingKey为 error的消息
    @RabbitListener(bindings = {@QueueBinding(value = @Queue,exchange = @Exchange(value = "direstTest",type = ExchangeTypes.DIRECT),key = {"error"})})
    public void receivel1(String message){
        System.out.println("message1 = " + message);
    }
}

5. 主题模式(Topic)

任何发送到Topic Exchange的消息都会被转发到所有关心RouteKey中指定话题的Queue上

在这里插入图片描述

如上图所示:
此类交换器使得来自不同的源头的消息可以到达一个对列,其实说的更明白一点就是模糊匹配的意思

例如:上图中红色对列的 key为 usa.#,#代表匹配 usa. 后的任意字符,图中usa.news usa.weather 都能匹配到红色队列,#.news 代表匹配 .news 前面任意字符

符号 # 匹配一个或多个词,符号 * 只能匹配一个词
因此 usa.# 能够匹配到 usa.news.XXX尽管后面再多几个.也能匹配到 ,但是 usa.* 只会匹配到 usa.XXX ,只能匹配一个词

注:
交换器说到底是一个名称与队列绑定的列表。当消息发布到交换器时,实际上是由你所连接的信道,将消息路由键同交换器上绑定的列表进行比较,任何发送到Topic Exchange的消息都会被转发到所有关心RouteKey中指定话题的Queue上

  1. 这种模式较为复杂,简单来说,就是每个队列都有其关心的主题,所有的消息都带有一个“标题”(RouteKey),Exchange会将消息转发到所有关注主题能与RouteKey模糊匹配的队列
  2. 这种模式需要RouteKey,也需要提前绑定 Exchange与Queue
  3. 在进行绑定时,要提供一个该队列关心的主题,如 “#.log.#” 表示该队列关心所有涉及log的消息(一个RouteKey为 ”MQ.log.error” 的消息会被转发到该队列)
  4. “ # ”表示0个或若干个关键字,“ * ”表示一个关键字。如 “log.*” 能与“log.warn”匹配,无法与 “log.warn.timeout” 匹配;但是 “log.#” 能与上述两者匹配
  5. 同样,如果Exchange没有发现能够与RouteKey匹配的Queue,则会抛弃此消息

4.1 创建处理器

这里手动创建处理器是为了更清晰理解,也可以用注解自动创建,队列有三个,分别是sanpang sanpang01 sanpang02

在这里插入图片描述

4.2 处理器绑定队列

在这里插入图片描述

4.3 消息生产者

 @Test
    // 主题模式(Topic)
    public void testSendTopic() {
        // convertAndSend()重载方法,三个参数代表 (交换器名称,路由秘钥,生产的消息)
        // 路由秘钥为 aaa.log  #.log 与之匹配
        rabbitTemplate.convertAndSend("sunmoneTopic", "aaa.log", "routingKey 为 aaa.log");
        // 路由秘钥为 goods.aaa  goods.# 与之匹配
        rabbitTemplate.convertAndSend("sunmoneTopic", "goods.aaa", "routingKey 为 goods.aaa");
        // 路由秘钥为 goods.log goods.log 与之匹配
        rabbitTemplate.convertAndSend("sunmoneTopic", "goods.log", "routingKey 为 goods.log");
        System.out.println("消息已产生");
    }

4.4 消息消费者

@Component
public class TopicsExchangeListener {

    // 指定队列,可以接收缓存到队列里的消息
    //   key = {"goods.log"})})  表示能消费 routingkey 为 goods.log 的消息
    @RabbitListener(bindings = {@QueueBinding(value = @Queue(value = "sanpang", durable = "true"),
            exchange = @Exchange(name = "sunmoneTopic", type = ExchangeTypes.TOPIC),
            key = {"goods.log"})})
    public void recevicel(String message) {
        System.out.println("sanpang收到的消息是 : " + message);
    }

    //  key = {"#.log"})})  表示能消费 routingkey 为 aaa.log goods.log 的消息
    @RabbitListener(bindings = {@QueueBinding(value = @Queue(value = "sanpang01", durable = "true"),
            exchange = @Exchange(name = "sunmoneTopic", type = ExchangeTypes.TOPIC),
            key = {"#.log"})})
    public void recevice2(String message) {
        System.out.println("sanpang01收到的消息是 : " + message);
    }

    //  key = {"goods.#"})}) 表示能消费 routingkey 为 goods.aaa goods.log 的消息
    @RabbitListener(bindings = {@QueueBinding(value = @Queue(value = "sanpang02", durable = "true"),
            exchange = @Exchange(name = "sunmoneTopic", type = ExchangeTypes.TOPIC),
            key = {"goods.#"})})
    public void recevice3(String message) {
        System.out.println("sanpang02收到的消息是 : " + message);
    }
}

控制台结果为 :

在这里插入图片描述

6. WorkQueues工作队列

多个消费者,你一个我一个分配消费消息,有预取机制,默认公平消费,可配置能者多劳模式,谁完成的快,谁多做一点

在这里插入图片描述

6.1 消息生产者

 @Test
    public void testWorkQueue(){
        /*
        * 能者多劳模式
        * */
        String queueName = "workQueue";
        String message = "hello,work.queue__";
        for (int i = 0; i < 10; i++) {
            rabbitTemplate.convertAndSend(queueName,message+i);
            System.out.println("i = " + i);
        }
    }

6.2 消息消费者

@Component
public class WorkQueues {

    @RabbitListener(queuesToDeclare = @Queue("workQueue"))// 自动声明队列
    public void workListener1(String message) throws InterruptedException {
        Thread.sleep(200);
        System.out.println("workQueue 接收到的消息是 : " + message);
    }

    @RabbitListener(queuesToDeclare = @Queue("workQueue"))
    public void workListener2(String message) throws InterruptedException {
        Thread.sleep(400);
        System.out.println("workQueue2 接收到的消息是 : " + message);
    }
}

6.3 Application配置

server:
  port: 9001
spring:
  rabbitmq:
    host: 192.168.153.90
    virtual-host: /
    listener:
      simple:
        prefetch: 1 # 每次只能获取一条消息 处理完成才能获取下一条 能者多劳模式 默认为预取机制

https://blog.csdn.net/qq_48721706/article/details/125194646

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叫我三胖哥哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值