在SpringBoot中使用RabbitMQ

本文在预先部署过了RabbitMQ及其管理界面的情境下

一、RabbitMQ的工作模式

先介绍一下RabbitMQ的工作模式,RabbitMQ共五种工作模式:

  • 简单模式:就是一个生产者一个消费者,中间通过一个队列直接连接

  • work工作模式:就是有多个消费者消费队列里的消息,队列里的消息只能被一个消费者消费。这样多部署几个消费者,就可以缓解压力,比如过年的时候抢票,抢票成功后会给你发短信,这个时候就可以把发短信的任务放入队列里,然后有多个发短信的服务来处理队列里的任务。(其他场景:抢红包,抢票等)

  • pub/sub发布订阅模式:上面两种模式生产者的消息只能被一个消费者消费,不符合某些实际场景。需要用到一个Exchange交换机角色来帮助我们把消息发给所有订阅我们的服务商。每个消费者都能消费一整套完整的消息

  • Routing路由模式:路由模式就是交换机并不是给所有订阅他的队列发送消息了,而是根据路由键来确定应该给哪个队列发送消息,队列和交换机绑定的时候需要通过路由键,而生产者发送消息的时候也需要指定路由键,这样就可以确定给哪个队列发送消息了。

  • Topics主题模式:主题模式就相当于模糊匹配,假如我想给姓张的发送消息,那么我就可以通过主题模式给所有姓张的发送消息。

二、代码示例
1. 简单模式代码示例
1.1 首先到RabbitMQ的管理界面新建一个队列

image-20220507091715908

1.2 测试项目的基础结构如下

image-20220507092446270

1.3 父项目的pom.xml文件如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.microsoft</groupId>
    <artifactId>rabbitmq-demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>rabbit-producer</module>
        <module>rabbit-consumer</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
<!--            <scope>test</scope>-->
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.amqp/spring-rabbit-test -->
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
<!--            <scope>test</scope>-->
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
1.4 两个子项目的resource下建立springboot的配置文件:application.yml文件,内容如下:
spring:
  rabbitmq:
    host: (IP)
    port: 5672
    virtual-host: 
    username: 
    password: 
1.5 在两个子项目下建立两个启动类
package com.microsoft;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RabbitProducerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RabbitProducerApplication.class, args);
    }
}
package com.microsoft;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RabbitConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RabbitConsumerApplication.class, args);
    }
}
1.6 监听器
package com.microsoft.consumer;

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

@Component
@RabbitListener(queues = "test.rabbit.queue") //指定监听的队列名
public class RabbitConsumerOne {

    @RabbitHandler //消息接收处理
    public void showMsg(String message) { //得到我们producer中发送的Object数据,此处可根据传过来的类型来选择接收,否则抛出异常
        System.out.println("test.rabbit.queue收到的消息内容为:" + message);
    }

}
1.7 生产者
package com.microsoft.producer;

import com.microsoft.RabbitProducerApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RabbitProducerApplication.class)
public class RabbitProducerOne {

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Test
    public void sendQueue(){
        System.out.println("开始向队列中发送一条消息!");
        // 参数1:管理中的队列名 参数2:发送的消息
        rabbitTemplate.convertAndSend("test.rabbit.queue","message:这是一条消息!");
        System.out.println("消息发送完毕!");
    }

}
1.8 启动监听器的启动类,启动生产者的测试方法
2022-05-07 10:08:31.800  INFO 6508 --- [           main] com.microsoft.RabbitConsumerApplication  : Started RabbitConsumerApplication in 1.556 seconds (JVM running for 2.828)
test.rabbit.queue收到的消息内容为:message:这是一条消息!
2. 发布订阅模式代码示例
2.1 到RabbitMQ的管理界面新增一个路由交换机(Exchange)

image-20220507102501245

2.2 新建完路由之后,需要再新建几个队列

image-20220507103526220

2.3 把新建的路由和新建的队列绑定

image-20220507104117344

出现如图界面进行绑定

image-20220507104808312

绑定完成,可进行代码测试。

2.4 监听器
package com.microsoft.consumer;

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

@Component // 需要注入到Spring容器中
@RabbitListener(queues = "pubsubqueue.one") // 指定监听的队列名
public class SubConsumerOne {

    @RabbitHandler// 消息接收处理
    public void showMsg(String message) { // 得到我们producer中发送的Object数据,此处可根据传过来的类型来选择接收,否则抛出异常
        System.out.println("pubsubqueue.one收到的消息内容为:" + message);
    }

}
package com.microsoft.consumer;

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

@Component // 需要注入到Spring容器中
@RabbitListener(queues = "pubsubqueue.two") // 指定监听的队列名
public class SubConsumerTwo {

    @RabbitHandler// 消息接收处理
    public void showMsg(String message) {
        System.out.println("pubsubqueue.two收到的消息内容为:" + message);
    }

}
package com.microsoft.consumer;

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

@Component // 需要注入到Spring容器中
@RabbitListener(queues = "pubsubqueue.three") // 指定监听的队列名
public class SubConsumerThree {

    @RabbitHandler// 消息接收处理
    public void showMsg(String message) {
        System.out.println("pubsubqueue.three收到的消息内容为:" + message);
    }

}
2.5 生产者
package com.microsoft.producer;

import com.microsoft.RabbitProducerApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RabbitProducerApplication.class)
public class RabbitProducerTwo {

    @Resource
    private RabbitTemplate rabbitTemplate;

    // 广播的形式发送,同一个路由下的所有队列都能接收到消息
    @Test
    public void sendFanout() {
        System.out.println("开始向路由发送消息(路由下的所有Queue都能收到该消息)");
        // 参数1:路由名 参数2:可有可无 参数3:发送的消息内容
        rabbitTemplate.convertAndSend("test-myroute", "", "这是一条所有消费者都能收到的消息!");
        System.out.println("消息发送成功!");
    }

}
2.6 启动监听器的启动类,启动生产者的测试方法
2022-05-07 11:26:07.844  INFO 14428 --- [           main] com.microsoft.RabbitConsumerApplication  : Started RabbitConsumerApplication in 1.058 seconds (JVM running for 1.755)
pubsubqueue.two收到的消息内容为:这是一条所有消费者都能收到的消息!
pubsubqueue.three收到的消息内容为:这是一条所有消费者都能收到的消息!
pubsubqueue.one收到的消息内容为:这是一条所有消费者都能收到的消息!
3. 路由模式和主题模式代码示例

路由模式是精确匹配,主题模式是模糊匹配

2020111610031547

3.1 新建一个用来发送主题的路由

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AxfXDVb9-1651907937956)(https://gitee.com/zihanstudy/blog-pic/raw/master/img/image-20220507114107716.png)]

3.2 路由新建完之后,新建3个队列,用来接收发布的topic

image-20220507140618826

3.3 把新建的队列和路由进行绑定

image-20220507141337151

RabbitMQ主题模式(Topic Exchange)通配符的匹配规则

  • 每条消息会被发送到所有符合路由规则的key对应的队列
  • 可以使用"*“和”#"两种通配符
  • *代表匹配任意一个关键词,#代表匹配一个或多个关键词
  • 通配符和普通字符之间需要使用"."隔开
3.4 监听器
@Component
@RabbitListener(queues = "topicqueue.one")
public class TopicConsumerOne {

    @RabbitHandler
    public void showMsg(String message) {
        System.out.println("topicqueue.one收到的消息内容为:" + message);
    }

}
@Component
@RabbitListener(queues = "topicqueue.two")
public class TopicConsumerTwo {

    @RabbitHandler
    public void showMsg(String message) {
        System.out.println("topicqueue.two收到的消息内容为:" + message);
    }

}
@Component
@RabbitListener(queues = "topicqueue.three")
public class TopicConsumerThree {

    @RabbitHandler
    public void showMsg(String message) {
        System.out.println("topicqueue.three收到的消息内容为:" + message);
    }

    /**
     * 可以进行重载,会找到指定参数的queue上
     * @param map
     */
    @RabbitHandler
    public void showMsg(Map map){
        System.out.println("topicqueue.three收到的(map)消息如下:" + map);
    }
    @RabbitHandler
    public void showMsg(List list){
        System.out.println("topicqueue.three收到的(list)消息如下:" + list);
    }
}
3.5 测试1
    @Test
    public void sendTopic1(){
        System.out.println("开始向路由中发送消息!");
        // 参数1:路由器名 参数2:routingKey,类似于发送的规则名
        rabbitTemplate.convertAndSend("test-mytopic","java.py","这是一条java.py消息");
    }

测试结果:

2022-05-07 14:45:24.758  INFO 15548 --- [           main] com.microsoft.RabbitConsumerApplication  : Started RabbitConsumerApplication in 1.107 seconds (JVM running for 2.316)
topicqueue.two收到的消息内容为:这是一条java.py消息
topicqueue.one收到的消息内容为:这是一条java.py消息
topicqueue.three收到的消息内容为:这是一条java.py消息

image-20220507150944689

因为3个队列都符合了规则,所以都能消费到消息

3.6 测试2
    @Test
    public void sendTopic2(){
        System.out.println("开始向路由中发送消息!");
        rabbitTemplate.convertAndSend("test-mytopic","nlp.py","这是一条nlp.py消息");
        rabbitTemplate.convertAndSend("test-mytopic","ocr.py","这是一条ocr.py消息");
    }

测试结果:

topicqueue.two收到的消息内容为:这是一条nlp.py消息
topicqueue.two收到的消息内容为:这是一条ocr.py消息

此处只有topicqueue.two能接收到消息,因为topicqueue.two符合以 .py 结尾

3.7 测试3
    @Test
    public void sendTopic3(){
        // 1.准备发送的数据
        Map map = new HashMap();
        map.put(1,"a");
        map.put(2,"b");
        List list = new ArrayList();
        list.add("qq");
        list.add("ww");
        list.add("SS");
        System.out.println("开始向路由中发送消息!");
        // 2.开始发送消息(发送了2条)
        // 2.1 发送的数据为map类型
        rabbitTemplate.convertAndSend("test-mytopic", "java.cpp", map);
        // 2.2 发送的数据为list类型
        rabbitTemplate.convertAndSend("test-mytopic", "java.cpp", list);
    }

测试结果:

topicqueue.three收到的(map)消息如下:{1=a, 2=b}
topicqueue.three收到的(list)消息如下:[qq, ww, SS]

此处只有topicqueue.three能够收到消息,且被指定参数类型的监听器所消费。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值