Spring Boot 整合 RabbitMQ 实现

1. 开发环境搭建

搭建RabbitMQ消费者和生产者模块。
在这里插入图片描述

1.1 pom配置

<?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">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.atang.rabbitmq</groupId>
    <artifactId>springboot-rabbitmq-consumer</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

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

1.2 application.yml配置

server:
  port: 8088

logging:
  config: classpath:logging-config.xml
  level:
    org.springframework: info
    org.springframework.amqp.rabbit: debug

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    publisher-confirm-type: correlated
    publisher-returns: true
    dynamic: true

2. 消费者模块(springboot-rabbitmq-consumer)

2.1 动态创建exchange、queue、binding

Rabbit自动配置会自动检测SpringContext中exchange、queue、binding相关Bean,动态的在RabbitMQ Server创建相关元素。

该功能主要通过 org.springframework.amqp.rabbit.core.RabbitAdmin#initialize 方法实现。而RabbitAdmin Bean是在RabbitAutoConfiguration自动配置类中注册的,通过设置 spring.rabbitmq.dynamic 为false可以禁用该功能。
在这里插入图片描述
下列代码创建类型为topic、名称为test_topic_exchange的exchange,当routingKey满足test.#时消息会被路由到 test_queue 队列。

@Configuration
public class RabbitMqConfig {
    
    
    public static final String TEST_TOPIC_EXCHANGE = "test_topic_exchange";
    public static final String TEST_QUEUE = "test_queue";

    @Bean("testExchange")
    public Exchange testExchange() {
    
    
        return ExchangeBuilder.topicExchange(TEST_TOPIC_EXCHANGE).durable(true).build();
    }

    @Bean("testQueue")
    public Queue testQueue() {
    
    
        return QueueBuilder.durable(TEST_QUEUE).build();
    }

    @Bean
    public Binding getBinding(@Qualifier("testQueue") Queue queue, @Qualifier("testExchange") Exchange exchange) {
    
    
        return BindingBuilder.bind(queue).to(exchange).with("test.#").noargs();
    }
}

2.3.1 动态创建时机

这时候我们启动应用发现这些元素并没有注册,而真正注册的时机是在RabbitMQ Connection创建的时候。可参考
org.springframework.amqp.rabbit.core.RabbitAdmin#afterPropertiesSet。

final AtomicBoolean initializing = new AtomicBoolean(false);
this.connectionFactory.addConnectionListener(connection -> {
    
    
	if (!initializing.compareAndSet(false, true)) {
    
    
		// If we are already initializing, we don't need to do it again...
		return;
	}
	try {
    
    
		/*
		 * ...but it is possible for this to happen twice in the same ConnectionFactory (if more than
		 * one concurrent Connection is allowed). It's idempotent, so no big deal (a bit of network
		 * chatter). In fact it might even be a good thing: exclusive queues only make sense if they are
		 * declared for every connection. If anyone has a problem with it: use auto-startup="false".
		 */
		if (this.retryTemplate != null) {
    
    
			this.retryTemplate.execute(c -> {
    
    
				initialize();
				return null;
			});
		}
		else {
    
    
			initialize();
		}
	}
	finally {
    
    
		initializing.compareAndSet(true, false);
	}
});

2.4 创建Listener

@RabbitListener(queues = "test_queue")
@Component
public class TestListener {
    
    
    private final Logger logger = LoggerFactory.getLogger(TestListener.class);

    @RabbitHandler
    public void logMessage(Message message) {
    
    
        String body = new String(message.getBody(), StandardCharsets.UTF_8);
        logger.info("receiving body {}.", body);
    }
}

这时再启动应用,可以从启动日志中看到RabttiMQ的自动配置过程。
在这里插入图片描述

2.5 从RabbitMQ管理端看配置详情

2.5.1 Connections

在这里插入图片描述

2.5.2 Channels

在这里插入图片描述

2.5.3 Exchanges

在这里插入图片描述

2.5.4 Queues

在这里插入图片描述

3. 生产者模块(springboot-rabbitmq-producer)

3.1 创建生产者Controller

@RestController
public class SendMessageController {
    
    
    public static final Logger logger = LoggerFactory.getLogger(SendMessageController.class);

    @Resource
    private RabbitTemplate rabbitTemplate;

    @RequestMapping("/sendMessage")
    public void sendMessage() {
    
    
        String context = "Current Date: " + new Date();
        logger.info("Sender : " + context);
        this.rabbitTemplate.convertAndSend("test_topic_exchange", "test.date", context);
    }
}

4. 验证

4.1 调用Controller发送消息

通过RabbitMQ管理端查看,显示收到一个消息,但未回告。
在这里插入图片描述

4.2 查看问题原因

查看Consumer端日志,未回告原因是因为Consumer端没有处理test_queue队列中String类型消息的Handler。
在这里插入图片描述

4.3 TestListener增加处理String类型消息的方法

@RabbitHandler
public void logMessage(String message) {
    
    
    logger.info("receiving body {}.", message);
}

4.4 重新发送消息

重新发送消息,Consumer成功收到消息并打印日志
在这里插入图片描述

4.5 查看RabbitMQ管理端

4.5.1 Connections

一个是生产者连接、一个是消费者连接
在这里插入图片描述

4.5.2 Channels

同理一个生产者Channel、一个消费者Channel
在这里插入图片描述

4.5.3 Queues

由于所有消息都都成功消费,所以消息没有积压。
在这里插入图片描述

参考

RabbitMQ的四种ExChange
Spring Boot 整合RabbitMQ
SpringBoot整合RabbitMQ

猜你喜欢

转载自blog.csdn.net/ttyy1112/article/details/126665509