SpringBoot 使用 spring-boot-starter-amqp 起步依赖实现 RabbitMQ 代码示例(一)


RabbitMQ
本文是工作之余的随手记,记录在工作期间使用 RabbitMQ 的笔记。



1、实现注意细节

  • 1、使用 @Bean 定义 QueueExchangeBinding,Spring Boot 会在启动时自动声明这些组件。 若需动态配置,可通过 RabbitAdmin 管理组件生命周期。
  • 2、发送消息有多种解决方式,本文选用实现 SimpleMessageListenerContainer 类,也可以使用注入 RabbitTemplate,直接调用 convertAndSend 方法发送消息,支持自动消息转换(如 JSON 序列化)。
  • 3、消费消息有多种解决方式,本文选用实现 ChannelAwareMessageListener 接口,也可以使用在方法上使用 @RabbitListener 注解监听队列,支持消息体自动反序列化为对象。
  • 4、2条和3条所实现的 RabbitMQ 的代码示例如下:

SpringBoot 使用 spring-boot-starter-amqp 起步依赖实现 RabbitMQ 代码示例(二)

  • 5、在启动类添加 @EnableRabbit 注解,可以激活监听器。

  • 6、在实现过程中可以配置一些高级特性:

    • 消息确认模式(Ack/Nack)
    • 重试机制(Retry Template)
    • 死信队列(Dead Letter Exchange)
    • 多消费者并发配置

2、实现注意事项

  • 1、敏感信息加密:避免明文存储密码,最好有密码服务等。
  • 2、持久化设置:队列、交换机、消息均需设置为持久化(Durable),防止服务重启丢失数据。
  • 3、本文选用的手动确认模式,处理完成后需显式调用 channel.basicAck()basicNack()
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
    	container.setAcknowledgeMode(AcknowledgeMode.MANUAL); 
    
  • 4、性能优化连接池配置,调整 spring.rabbitmq.cache 相关参数,优化 Channel 和 Connection 复用。
    spring.rabbitmq.listener.simple.concurrency=5
    spring.rabbitmq.listener.simple.max-concurrency=10
    
  • 5、批量消费:使用 @RabbitListener 批量接收消息提升吞吐量。
  • 6、异常处理:常见于使用死信队列,捕获处理失败的消息,避免消息无限重试。
  • 7、如果需要使用重试机制,可以配置重试策略,配置 RetryTemplate 控制重试次数和退避策略。

通过遵循以上步骤与注意事项,可快速构建高可靠、易维护的 RabbitMQ 消息系统,充分发挥 Spring Boot 自动化配置的优势,同时规避常见风险。


3、配置文件:application.yml

使用 use 属性,方便随时打开和关闭使用 MQ ,并且可以做到细化控制。

spring:
   rabbitmq:
    use: true
    host: 10.100.10.100
    port: 5672
    username: wen
    password: 123456
    exchangeSubPush: 'exWen'
    queueSubPush: 'ha.queue.SubPush'
    routeSubPush: '1000'
    exchangeState: sync.ex.State
    queueState: ha.q.Server
    queueStateSync: ha.q.StateServer
    routeState: state
    exchangeOnlineMonitor: 'sync.ex.State'
    routeOnlineMonitor: 'state'
    queueOnlineMonitor: 'ha.q.Online'

4、依赖文件:pom.xml

pom.xml 文件中使用的是 SpringBoot 项目,使用 spring-boot-starter-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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wen</groupId>
    <artifactId>springboot-mybatis</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>

    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.5.3</version>
    </parent>
    
    <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>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.18</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>
    </dependencies>
</project>

5、配置类:RabbitMqConfig

配置类,将可配置的参数使用 @Value 做好配置,与 application.yml 相互对应。

package com.wen.mq;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;

@Slf4j
@Configuration
@Data
public class RabbitMqConfig {

    @Value("${spring.rabbitmq.use:true}")
    private boolean use;

    @Value("${spring.rabbitmq.host}")
    private String host;

    @Value("${spring.rabbitmq.port}")
    private int port;

    @Value("${spring.rabbitmq.username}")
    private String username;

    @Value("${spring.rabbitmq.password}")
    private String password;

    @Value("${spring.rabbitmq.virtual-host:}")
    private String virtualHost;

    @Value("${spring.rabbitmq.exchangeState}")
    private String exchangeState;
    
    @Value("${spring.rabbitmq.queueState}")
    private String queueState;

    @Value("${spring.rabbitmq.routeState}")
    private String routeState;

    @Value(("${spring.rabbitmq.queueStateSync}"))
    private String queueStateSync;

    @Value("${spring.rabbitmq.exchangeOnlineInfo}")
    private String exchangeOnlineInfo;

    @Value("${spring.rabbitmq.routeOnlineInfo}")
    private String routeOnlineInfo;

    @Value("${spring.rabbitmq.queueOnlineInfo}")
    private String queueOnlineInfo;

    @PostConstruct
    private void init() {

    }
}

6、消息类:MqMessage

package com.wen.mq;

import lombok.Data;

@Data
public class MqMessage<T> {

    private String msgType;

    private String msgOrigin;

    private long time;
    
    private T data;

}

7、消息类:MqMessageItem

package com.wen.mq;

import lombok.Data;

@Data
public class MqMessageItem {

    private long userId;

    private String userName;

    private int userAge;

    private String userSex;

    private String userPhone;

    private String op;

}

8、配置中心类:DirectMode

配置中心:使用 SimpleMessageListenerContainer 进行配置。新加一个消费者队列就要在这里进行配置。

package com.wen.mq;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class DirectMode {

    @Autowired
    RabbitMqConfig rabbitMqConfig;
    
    @Autowired
    private CachingConnectionFactory connectionFactory;

    @Autowired
    private StateConsumer stateConsumer;

    @Autowired
    private InfoConsumer infoConsumer;

    @Bean
    public SimpleMessageListenerContainer initMQ() {
        if (!rabbitMqConfig.isUse()) {
            return null;
        }
        log.info("begin!");
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setConcurrentConsumers(1);
        container.setMaxConcurrentConsumers(1);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认

        // 设置一个队列
        container.setQueueNames(rabbitMqConfig.getQueueStateSync());
        //如果同时设置多个队列如下: 前提是队列都是必须已经创建存在的
        //container.setQueueNames("TestDirectQueue","TestDirectQueue2","TestDirectQueue3”);
        //另一种设置队列的方法,如果使用这种情况,那么要设置多个,就使用addQueues
        //container.setQueues(new Queue("TestDirectQueue",true));
        //container.addQueues(new Queue("TestDirectQueue2",true));
        //container.addQueues(new Queue("TestDirectQueue3",true));
        container.setMessageListener(stateConsumer);
        log.info("end");
        return container;
    }

    @Bean
    public SimpleMessageListenerContainer contactSyncContainer() {
        if (!rabbitMqConfig.isUse()) {
            return null;
        }
        log.info("contact begin");
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setConcurrentConsumers(1);
        container.setMaxConcurrentConsumers(1);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息
        //设置一个队列
        container.setQueueNames(rabbitMqConfig.getQueueOnlineInfo());
        container.setMessageListener(infoConsumer);
        log.info("contact end");
        return container;
    }

    @Bean
    public Queue queueState() {
        if (!rabbitMqConfig.isUse()) {
            return null;
        }
        return new Queue(rabbitMqConfig.getQueueState());
    }

    @Bean
    public Queue queueStateSync() {
        if (!rabbitMqConfig.isUse()) {
            return null;
        }
        return new Queue(rabbitMqConfig.getQueueStateSync());
    }
    
    @Bean
    DirectExchange exchangeState() {
        if (!rabbitMqConfig.isUse()) {
            return null;
        }
        return new DirectExchange(rabbitMqConfig.getExchangeState());
    }

    @Bean
    Binding bindingState() {
        if (!rabbitMqConfig.isUse()) {
            return null;
        }
        return BindingBuilder.bind(queueState()).to(exchangeState()).with(rabbitMqConfig.getRouteState());
    }

    @Bean
    Binding bindingStateSync() {
        if (!rabbitMqConfig.isUse()) {
            return null;
        }
        return BindingBuilder.bind(queueStateSync()).to(exchangeState()).with(rabbitMqConfig.getRouteState());
    }

    // 新加一个消费者
    @Bean
    public Queue queueOnlineMonitor() {
        if (!rabbitMqConfig.isUse()) {
            return null;
        }
        return new Queue(rabbitMqConfig.getQueueOnlineInfo());
    }

    @Bean
    DirectExchange exchangeOnlineMonitor() {
        if (!rabbitMqConfig.isUse()) {
            return null;
        }
        return new DirectExchange(rabbitMqConfig.getExchangeOnlineInfo());
    }

    @Bean
    Binding bindingExchangeOnlineMonitor() {
        if (!rabbitMqConfig.isUse()) {
            return null;
        }
        return BindingBuilder.bind(queueOnlineMonitor()).to(exchangeOnlineMonitor()).with(rabbitMqConfig.getRouteOnlineInfo());
    }
}

9、消费者类1:StateConsumer

实现 ChannelAwareMessageListener 接口,可以在这里面做相应的操作,例如存缓存,存库等。

package com.wen.mq;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class StateConsumer implements ChannelAwareMessageListener {

    @Autowired
    RabbitMqConfig rabbitMqConfig;

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        String queueName = message.getMessageProperties().getConsumerQueue();
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        if (!rabbitMqConfig.getQueueStateSync().equals(queueName)) {
            String bodyStr = new String(message.getBody(), StandardCharsets.UTF_8);
            try {
                MqMessage<List<MqMessageItem>> mqMessage = 
                	JSON.parseObject(bodyStr, new TypeReference<MqMessage<List<MqMessageItem>>>() {});
                // 这里可以对消息做其他处理,例如存储到缓存中
                List<MqMessageItem> items = mqMessage.getData();
                if (CollectionUtil.isNotEmpty(items)) {
                    applyToRedis(mqMessage);
                }
                log.info("consume mq msg ok, queue:{}, deliveryTag:{}, msg:{}", queueName, deliveryTag, mqMessage);
                channel.basicAck(deliveryTag, false);
            } catch (JSONException e) {
                log.error("parse mq msg exception, queue:{}, deliveryTag:{}", queueName, deliveryTag, e);
                channel.basicReject(deliveryTag, false);
            } catch (Exception e) {
                log.error("consume mq msg exception, queue:{}, deliveryTag:{}", queueName, deliveryTag, e);
                channel.basicReject(deliveryTag, true); //为true会重新放回队列
            }
        }
    }

    public static final String MQ_STATE_OP_REMOVE_STATE = "REMOVE_STATE";

    public static final String MQ_STATE_OP_CHANGE_STATE = "CHANGE_STATE";

    private void applyToRedis(MqMessage<List<MqMessageItem>> mqMessage) {

        List<MqMessageItem> data = mqMessage.getData();

        Map<String, List<MqMessageItem>> itemGroupByOp = 
        	data.stream().collect(Collectors.groupingBy(item -> item.getOp()));

        List<MqMessageItem> stateToRemove = itemGroupByOp.get(MQ_STATE_OP_REMOVE_STATE);

        List<MqMessageItem> stateToChange = itemGroupByOp.get(MQ_STATE_OP_CHANGE_STATE);

        if (CollectionUtil.isNotEmpty(stateToRemove)) {
            Map<Long, Set<String>> map = new HashMap<>();
            for (MqMessageItem item : stateToRemove) {
                map.computeIfAbsent(item.getUserId(), u -> new HashSet<>())
                .add(String.valueOf(item.getUserAge()));
            }
            // cacheService.removeUserState(map);
        }

        if (CollectionUtil.isNotEmpty(stateToChange)) {
            List<MqMessageItem> list = stateToChange.stream().map(u -> {
                MqMessageItem dto = new MqMessageItem();
                dto.setUserId(u.getUserId());
                dto.setUserAge(u.getUserAge());
                dto.setUserName(u.getUserName());
                dto.setUserSex(u.getUserSex());
                dto.setUserPhone(u.getUserPhone());
                return dto;
            }).collect(Collectors.toList());
            // cacheService.saveUserState(list);
        }
    }
}

10、消费者类2:InfoConsumer

实现 ChannelAwareMessageListener 接口,可以在这里面做相应的操作,例如存缓存,存库等。

package com.wen.mq;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class InfoConsumer implements ChannelAwareMessageListener {

    @Autowired
    RabbitMqConfig rabbitMqConfig;

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        String queueName = message.getMessageProperties().getConsumerQueue();
        log.info("queueName: {}", queueName);
        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        try {
            byte[] body = message.getBody();
            String content = new String(body);
            MqMessage msg = JSONObject.parseObject(content, MqMessage.class);
            if (rabbitMqConfig.getQueueOnlineInfo().equals(queueName)) {
                // 订阅到的消息就是变更的消息
                // 这里可使用service对消息进行消费,返回一个boolean
                log.info("用户监控数据写入失败!数据:{}", msg);
            }
            log.info("consume mq msg ok, queue:{}, deliveryTag:{}, msg:{}", queueName, deliveryTag, msg);
            channel.basicAck(deliveryTag, false);
        } catch (JSONException e) {
            log.error("parse mq msg exception, queue:{}, deliveryTag:{}", queueName, deliveryTag, e);
            channel.basicReject(deliveryTag, false); //为true会重新放回队列
        } catch (Exception e) {
            log.error("consume mq msg exception, queue:{}, deliveryTag:{}", queueName, deliveryTag, e);
            channel.basicReject(deliveryTag, true); //为true会重新放回队列
        }
    }
}

注意:本文是使用 SpringBoot 项目为核心进行开发,所使用的依赖和类都如上展示,可以应用于基本业务逻辑。

### 如何在 IntelliJ IDEA 中添加 `spring-boot-starter-amqp` 依赖 要在 IntelliJ IDEA 中成功添加并使用 `spring-boot-starter-amqp` 依赖,可以通过以下方法实现: #### 修改项目的 `pom.xml` 文件 确保项目是个 Maven 项目,并编辑其 `pom.xml` 文件,在 `<dependencies>` 部分添加如下代码块: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` 此操作会引入 RabbitMQ 的客户端库以及 Spring AMQP 支持的相关组件[^1]。 #### 处理可能的依赖下载失败问题 如果遇到依赖无法正常下载的情况,可能是由于网络原因或者仓库配置不正确引起的。以下是解决方案: 1. **检查 Maven 设置中的镜像源** 如果当前使用的 Maven 配置指向了阿里云或其他国内镜像站点,则需要确认这些站点是否提供所需的依赖项。例如,阿里云镜像通常不会包含最新的 SNAPSHOT 版本资源。此时可以在 `pom.xml` 文件中显式声明额外的仓库地址: ```xml <!-- 添加 Spring 官方仓库 --> <repositories> <repository> <id>spring-snapshots</id> <url>https://repo.spring.io/libs-snapshot</url> </repository> </repositories> ``` 2. **清理本地缓存** 若之前尝试过加载该依赖但未成功,可能会残留些损坏的文件于本地 `.m2/repository` 目录下。建议删除对应版本目录(如 `.../spring-boot-starter-amqp/2.x.x.RELEASE`),之后重新执行 Maven 命令刷新依赖树[^4]。 3. **强制更新依赖** 在 IntelliJ IDEA 的右侧工具栏找到 Maven 插件窗口,点击绿色循环箭头按钮以触发 “Reimport All Maven Projects”。另外也可以手动运行命令行指令完成相同目的: ```bash mvn clean install -U ``` 参数 `-U` 表示强制从远程服务器拉取最新版本数据覆盖旧版记录[^5]。 #### 刷新 IDE 并验证环境配置 完成上述更改后记得保存修改过的 XML 文档,接着回到 IntelliJ IDEA 主界面右键单击工程根节点选择菜单选项【Reload Project】或按快捷键组合 Ctrl+F9 编译整个工作区。最后观察控制台输出日志判断是否存在异常错误提示;如果没有则表明集成过程顺利完成。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值