Spring Boot整合消息中间件RabbitMQ入门

1. RabbitMQ组件架构图

核心组件:生产者、消费者、交换器、队列、绑定键、路由键、Broker

详细请参考:RabbitMQ 基本概念介绍
在这里插入图片描述

2. 项目快速搭建

(1) pom文件添加amqp依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.2</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>basic-rabbitmq-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>basic-rabbitmq-demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    </dependencies>
</project>

(2) application.properties 增加rabbitmq配置,对应 RabbitProperties 配置类

server.port=1050
spring.application.name=basic-rabbitmq-demo

spring.rabbitmq.host=192.168.174.129
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
# 设置虚拟主机host
spring.rabbitmq.virtual-host=/

(3) BasicRabbitmqDemoApplication 启动类

@SpringBootApplication
public class BasicRabbitmqDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(BasicRabbitmqDemoApplication.class, args);
    }
}
3. Direct交换器

direct类型的交换器会把消息路由到 binding key 与 routing key 完全匹配的队列中。

(1) RabbitConfigDemo01 创建交换器、队列、绑定三个bean,后续由RabbitAdmin#initialize 进行初始化。

@Configuration
public class RabbitConfigDemo01 {
    /**
     * 交换器名称
     */
    public static final String EXCHANGE = "exchange_demo01";

    /**
     * 队列名称
     */
    public static final String QUEUE = "queue_demo01";

    /**
     * 路由键名称
     */
    public static final String ROUTING_KEY = "routing_key_demo01";

    /**
     * 交换器
     */
    @Bean
    public DirectExchange demo01Exchange() {
        return new DirectExchange(EXCHANGE, true, false);
    }

    /**
     * 队列
     */
    @Bean
    public Queue demo01Queue() {
        return new Queue(QUEUE, true, false, false);
    }

    /**
     * 交换器与队列绑定
     */
    @Bean
    public Binding demo01Binding() {
        return BindingBuilder.bind(demo01Queue()).to(demo01Exchange()).with(ROUTING_KEY);
    }
}

(2) DemoMessage 消息实体类

public class DemoMessage implements Serializable {
    /**
     * 主键
     */
    private Integer id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;
    
   // 省略getter、setter、toString方法
}

(3) ProducerController 控制器

@RestController
public class ProducerController {

    @Autowired
    private ProducerService producerService;

    @PostMapping("/demo01SyncSend")
    public void demo01SyncSend(@RequestBody DemoMessage demoMessage) {
        try {
            producerService.demo01SyncSend(demoMessage);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

(4) ProducerServiceImpl 使用RabbitTemplate发送消息

@Service
public class ProducerServiceImpl implements ProducerService {

    Logger logger = LoggerFactory.getLogger(ProducerServiceImpl.class);

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void demo01SyncSend(DemoMessage demoMessage) {
        logger.info("发送消息内容:{}", demoMessage);
        rabbitTemplate.convertAndSend(RabbitConfigDemo01.EXCHANGE, RabbitConfigDemo01.ROUTING_KEY, demoMessage);
    }
}

(5) Demo01Consumer 监听指定的队列,消费推送过来的消息。

@Component
@RabbitListener(queues = RabbitConfigDemo01.QUEUE)
public class Demo01Consumer {

    private Logger logger = LoggerFactory.getLogger(Demo01Consumer.class);

    @RabbitHandler
    public void onMessage(DemoMessage demoMessage) {
        logger.info("消费消息[onMessage][线程编号:{},消息内容:{}]",
                Thread.currentThread().getId(), demoMessage);
    }
}

启动项目,打印如下所示的日志表示项目启动成功!

2021-02-03 10:40:03.813  INFO 16852 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 1050 (http) with context path ''
# 与rabbitmq服务端建立连接
2021-02-03 10:40:03.815  INFO 16852 --- [           main] o.s.a.r.c.CachingConnectionFactory       : Attempting to connect to: [192.168.174.129:5672]
# 本地对端端口是65455
2021-02-03 10:40:03.900  INFO 16852 --- [           main] o.s.a.r.c.CachingConnectionFactory       : Created new connection: rabbitConnectionFactory#119aa36:0/SimpleConnection@72a2312e [delegate=amqp://admin@192.168.174.129:5672/, localPort= 65455]
2021-02-03 10:40:04.028  INFO 16852 --- [           main] c.e.b.BasicRabbitmqDemoApplication       : Started BasicRabbitmqDemoApplication in 4.614 seconds (JVM running for 6.51)

rabbitmq 管理界面可以看到建立的连接,如下图所示:
在这里插入图片描述
使用postman请求接口发送消息
在这里插入图片描述
IDEA Console打印相关日志

# 生产者发送消息
2021-02-03 10:45:11.683  INFO 16852 --- [nio-1050-exec-1] c.e.b.service.impl.ProducerServiceImpl   : 发送消息内容:DemoMessage{id=1001, name='李四', age=25}

# 消费者消费消息
2021-02-03 10:45:11.706  INFO 16852 --- [ntContainer#0-1] c.e.b.consumer.Demo01Consumer            : 消费消息[onMessage][线程编号:47,消息内容:DemoMessage{id=1001, name='李四', age=25}]
4. Fanout交换器

fanout类型的交换器会把消息路由到与该交换器绑定的所有队列上,与路由键无关
在这里插入图片描述
(1) RabbitConfigDemo02 新建1个交换器和2个队列的bean

@Configuration
public class RabbitConfigDemo02 {

    public static final String EXCHANGE = "exchange_demo02";

    public static final String QUEUE_A = "queue_demo02_A";

    public static final String QUEUE_B = "queue_demo02_B";

    /**
     * 交换器
     */
    @Bean
    public FanoutExchange demo02Exchange() {
        return new FanoutExchange(EXCHANGE, true, false);
    }

    /**
     * 队列A
     */
    @Bean
    public Queue demo02QueueA() {
        return new Queue(QUEUE_A, true, false, false);
    }

    /**
     * 队列B
     */
    @Bean
    public Queue demo02QueueB() {
        return new Queue(QUEUE_B, true, false, false);
    }

    /**
     * 交换器绑定队列A
     */
    @Bean
    public Binding demo02BindingQueueA() {
        return BindingBuilder.bind(demo02QueueA()).to(demo02Exchange());
    }

    /**
     * 交换器绑定队列B
     */
    @Bean
    public Binding demo02BindingQueueB() {
        return BindingBuilder.bind(demo02QueueB()).to(demo02Exchange());
    }
}

(2) ProducerController 控制器增加接口方法

@PostMapping("/demo02SyncSend")
public void demo02SyncSend(@RequestBody DemoMessage demoMessage) {
    try {
        producerService.demo02SyncSend(demoMessage);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

(3) ProducerServiceImpl 增加方法

@Override
public void demo02SyncSend(DemoMessage demoMessage) {
    logger.info("fanout交换器,发送消息内容:{}", demoMessage);
    rabbitTemplate.convertAndSend(RabbitConfigDemo02.EXCHANGE, null, demoMessage);
}

(4) 新增两个消费者

@Component
@RabbitListener(queues = RabbitConfigDemo02.QUEUE_A)
public class Demo02ConsumerA {

    private Logger logger = LoggerFactory.getLogger(Demo02ConsumerA.class);

    @RabbitHandler
    public void onMessage(DemoMessage demoMessage) {
        logger.info("fanout交换器,消费者A [onMessage][线程编号:{},消息内容:{}]",
                Thread.currentThread().getId(), demoMessage);
    }
}

@Component
@RabbitListener(queues = RabbitConfigDemo02.QUEUE_B)
public class Demo02ConsumerB {

    private Logger logger = LoggerFactory.getLogger(Demo02ConsumerB.class);

    @RabbitHandler
    public void onMessage(DemoMessage demoMessage) {
        logger.info("fanout交换器,消费者B [onMessage][线程编号:{},消息内容:{}]",
                Thread.currentThread().getId(), demoMessage);
    }
}

重新启动项目,使用postman访问接口方法。
在这里插入图片描述
IDEA Console打印日志信息符合fanout交换器的特点,消息被两个消费者成功消费

# 生产者发送消息
2021-02-03 13:50:24.720  INFO 17324 --- [nio-1050-exec-2] c.e.b.service.impl.ProducerServiceImpl   : fanout交换器,发送消息内容:DemoMessage{id=1003, name='fanout交换器消息...', age=25}

# 消费者A消费消息
2021-02-03 13:50:24.835  INFO 17324 --- [ntContainer#1-1] c.e.b.consumer.Demo02ConsumerA           : fanout交换器,消费者A [onMessage][线程编号:52,消息内容:DemoMessage{id=1003, name='fanout交换器消息...', age=25}]

# 消费者B消费消息
2021-02-03 13:50:24.835  INFO 17324 --- [ntContainer#2-1] c.e.b.consumer.Demo02ConsumerB           : fanout交换器,消费者B [onMessage][线程编号:54,消息内容:DemoMessage{id=1003, name='fanout交换器消息...', age=25}]
5. Topic 交换器

与direct交换器类似,binding key允许使用通配符与routing key进行匹配,"*" 用于匹配一个单词,"#" 用于匹配多个单词(可以是零个)。binding key 与 routing key 都是根据句点号 “.” 来分隔字符串的,每一段独立的字符串看作一个单词,例如www.baidu.com表示3个单词。

(1) RabbitConfigDemo03 初始化交换器、队列、绑定三个bean

@Configuration
public class RabbitConfigDemo03 {
    /**
     * 交换器名称
     */
    public static final String EXCHANGE = "exchange_demo03";

    /**
     * 队列名称
     */
    public static final String QUEUE = "queue_demo03";

    /**
     * 模糊匹配路由键
     */
    public static final String ROUTING_KEY = "#.key.demo03";

    /**
     * 交换器
     */
    @Bean
    public TopicExchange demo03Exchange() {
        return new TopicExchange(EXCHANGE, true, false);
    }

    /**
     * 队列
     */
    @Bean
    public Queue demo03Queue() {
        return new Queue(QUEUE, true, false, false);
    }

    /**
     * 交换器与队列绑定
     */
    @Bean
    public Binding demo03Binding() {
        return BindingBuilder.bind(demo03Queue()).to(demo03Exchange()).with(ROUTING_KEY);
    }
}

(2) ProducerController 控制器添加接口方法

/**
 * topic交换器
 */
@PostMapping("/demo03SyncSend")
public void demo03SyncSend(@RequestBody DemoMessage demoMessage) {
    try {
        producerService.demo03SyncSend(demoMessage);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

(3) ProducerServiceImpl 新增方法

@Override
public void demo03SyncSend(DemoMessage demoMessage) {
    logger.info("topic交换器,发送消息内容:{}", demoMessage);
    rabbitTemplate.convertAndSend(RabbitConfigDemo03.EXCHANGE, "test.summer.key.demo03", demoMessage);
}

(4) Demo03Consumer 新增消费者,监听指定队列。

@Component
@RabbitListener(queues = RabbitConfigDemo03.QUEUE)
public class Demo03Consumer {

    private Logger logger = LoggerFactory.getLogger(Demo03Consumer.class);

    @RabbitHandler
    public void onMessage(DemoMessage demoMessage) {
        logger.info("topic类型交换器 [onMessage][线程编号:{},消息内容:{}]",
                Thread.currentThread().getId(), demoMessage);
    }
}

重新启动项目,使用postman请求接口方法发送消息
在这里插入图片描述
IDEA控制台打印消费成功的日志,说明路由键 “test.summer.key.demo03”可以正常匹配绑定键 “#.key.demo03”。

# 生产者发送消息
2021-02-03 15:38:17.516  INFO 14148 --- [nio-1050-exec-2] c.e.b.service.impl.ProducerServiceImpl   : topic交换器,发送消息内容:DemoMessage{id=1006, name='topic交换器消息...', age=25}

# 消费者消费消息 
2021-02-03 15:38:17.643  INFO 14148 --- [ntContainer#3-1] c.e.b.consumer.Demo03Consumer            : topic类型交换器 [onMessage][线程编号:52,消息内容:DemoMessage{id=1006, name='topic交换器消息...', age=25}]
6. 交换器参数说明

交换器的抽象类是AbstractExchange,下图的三个子类分别对应三种交换器类型。
在这里插入图片描述
交换器的源码及参数说明:

public abstract class AbstractExchange extends AbstractDeclarable implements Exchange {

	private final String name;

	private final boolean durable;

	private final boolean autoDelete;

	private boolean delayed;

	private boolean internal;
   
   // 省略n行代码
}
参数作用
name交换器的名称
durable是否持久化
autoDelete是否自动删除
internal是否内置交换器

(1) 自动删除

自动删除的前提是至少有一个队列或者交换器与这个交换器绑定,之后所有与这个交换器绑定的队列或者交换器都与它解绑,才会自动删除交换器。必须有解除绑定这个动作,并且是在全部解绑之后。

(2) 内置交换器

客户端无法直接发送消息到内置交换器,只能通过交换器路由到内置交换器。

7. 队列参数说明

队列对应Queue类,源码及参数说明如下:

public class Queue extends AbstractDeclarable implements Cloneable {

	/**
	 * Argument key for the master locator.
	 * @since 2.1
	 */
	public static final String X_QUEUE_MASTER_LOCATOR = "x-queue-master-locator";

	private final String name;

	private final boolean durable;

	private final boolean exclusive;

	private final boolean autoDelete;

	public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete,
			@Nullable Map<String, Object> arguments) {
		super(arguments);
		Assert.notNull(name, "'name' cannot be null");
		this.name = name;
		this.actualName = StringUtils.hasText(name) ? name
				: (Base64UrlNamingStrategy.DEFAULT.generateName() + "_awaiting_declaration");
		this.durable = durable;
		this.exclusive = exclusive;
		this.autoDelete = autoDelete;
	}
	// 省略n行代码
}
参数作用
name队列的名字
durable是否持久化
exclusive是否排他
autoDelete是否自动删除
arguments一组其他参数

(1) 排他队列

排他队列值仅对首次声明它的连接可见,并在连接断开时自动删除。

(2) 自动删除

当连接该队列的消费者都断开之后,队列删除,不管队列中是否有数据。

(3) arguments 参数说明

参数作用
x-message-ttl消息过期时间,单位毫秒
x-expires设置多长时间队列未使用将被删除,单位毫秒
x-max-length队列消息的最大长度值
x-max-length-bytes队列占用的最大空间大小,单位字节
x-dead-letter-exchange设置死信交换器
x-dead-letter-routing-key设置死信交换器的路由键
x-queue-modelazy 懒加载,先将消息保存到磁盘上,消费的时候加载到内存

项目代码下载地址

至此,Spring Boot整合消息中间件RabbitMQ入门分享完毕

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值