SpringBoot 整合 RabbitMQ:和这只“兔子”交朋友

大家好,我是小悟。

第一章:认识 RabbitMQ ------ 不是普通的兔子!

RabbitMQ 就像是一个超级邮差兔,不过它不送胡萝卜,专门传递消息!想象一下:

  • 🏢 交换机(Exchange):邮局的分拣中心,负责把信件分到正确的路线
  • 📮 队列(Queue):你的专属邮箱,消息就在这里等你来取
  • 🏷️ 路由键(Routing Key):信封上的地址标签
  • 📦 消息(Message):你要传递的包裹(可以是任何数据)

这只"兔子"有四种工作模式:

  1. Hello World 模式:简单直接,像扔飞盘一样

  2. Work Queues 模式:多个工人抢活干,卷起来了!

  3. Publish/Subscribe 模式:广播模式,一人说话全村听见

  4. Routing & Topics 模式:智能分发,精准投喂


第二章:搭建"胡萝卜农场" ------ 环境准备

2.1 安装 RabbitMQ

ini 复制代码
# 用 Docker 快速召唤兔子
docker run -d \
  --name rabbitmq \
  -p 5672:5672 \
  -p 15672:15672 \
  -e RABBITMQ_DEFAULT_USER=admin \
  -e RABBITMQ_DEFAULT_PASS=admin123 \
  rabbitmq:management

# 访问管理界面:http://localhost:15672
# 账号:admin / 密码:admin123

2.2 创建 SpringBoot 项目

xml 复制代码
<!-- pom.xml 添加"兔粮" -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

第三章:配置"兔子窝" ------ 连接设置

yaml 复制代码
# application.yml
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: admin
    password: admin123
    # 虚拟主机,相当于兔子的公寓号
    virtual-host: /
    # 开启消息确认(确保兔子没偷吃消息)
    publisher-confirm-type: correlated
    publisher-returns: true
    listener:
      simple:
        # 手动确认消息(收到要说谢谢哦)
        acknowledge-mode: manual
        # 并发消费者数量(几只兔子一起干活)
        concurrency: 3
        max-concurrency: 10

第四章:定义"胡萝卜传输协议" ------ 配置类

typescript 复制代码
@Configuration
public class RabbitConfig {
    
    // 定义一个队列 - 就像给兔子一个收件箱
    @Bean
    public Queue carrotQueue() {
        // durable: true 表示兔子重启后队列还在(持久化)
        return new Queue("carrot.queue", true);
    }
    
    // 再定义一个队列,用来放烂掉的胡萝卜
    @Bean
    public Queue deadCarrotQueue() {
        return new Queue("dead.carrot.queue", true);
    }
    
    // 定义一个直连交换机 - 最简单的邮局
    @Bean
    public DirectExchange carrotExchange() {
        return new DirectExchange("carrot.exchange", true, false);
    }
    
    // 绑定队列和交换机(告诉兔子哪个邮箱放哪条路)
    @Bean
    public Binding bindCarrot() {
        return BindingBuilder.bind(carrotQueue())
                .to(carrotExchange())
                .with("carrot.routing.key"); // 路由键
    }
    
    // JSON 消息转换器(把胡萝卜包装成标准快递盒)
    @Bean
    public MessageConverter jsonMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }
    
    // 死信队列配置(处理那些没人要的胡萝卜)
    @Bean
    public Queue carrotQueueWithDLX() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", "dead.letter.exchange");
        args.put("x-dead-letter-routing-key", "dead.carrot");
        return new Queue("carrot.with.dlx", true, false, false, args);
    }
}

第五章:"种胡萝卜" ------ 生产者代码

typescript 复制代码
@Component
@Slf4j
public class CarrotProducer {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    /**
     * 发送一根普通胡萝卜
     */
    public void sendFreshCarrot(String message) {
        log.info("发射一根新鲜胡萝卜: {}", message);
        
        // 确保消息持久化(放冰箱保存)
        MessageProperties props = MessagePropertiesBuilder.newInstance()
                .setDeliveryMode(MessageDeliveryMode.PERSISTENT)
                .build();
        
        Message msg = new Message(message.getBytes(), props);
        
        rabbitTemplate.convertAndSend(
            "carrot.exchange",
            "carrot.routing.key",
            msg,
            new CorrelationData(UUID.randomUUID().toString())
        );
        
        log.info("胡萝卜已交给兔子快递员");
    }
    
    /**
     * 发送一根 JSON 格式的豪华胡萝卜
     */
    public void sendLuxuryCarrot(CarrotDTO carrot) {
        log.info("准备发送豪华胡萝卜: {}", carrot);
        
        // 设置回调(确认兔子收到没)
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.info("兔子确认收到豪华胡萝卜,ID: {}", correlationData.getId());
            } else {
                log.error("兔子拒收豪华胡萝卜!原因: {}", cause);
            }
        });
        
        rabbitTemplate.convertAndSend(
            "carrot.exchange",
            "carrot.routing.key",
            carrot
        );
    }
    
    /**
     * 发送延迟胡萝卜(10秒后才送达)
     */
    public void sendDelayedCarrot(String message, int delaySeconds) {
        log.info("设置延迟胡萝卜,{}秒后送达: {}", delaySeconds, message);
        
        rabbitTemplate.convertAndSend(
            "delay.exchange",
            "delay.key",
            message,
            msg -> {
                msg.getMessageProperties().setDelay(delaySeconds * 1000);
                return msg;
            }
        );
    }
}

// 胡萝卜数据传输对象
@Data
@AllArgsConstructor
@NoArgsConstructor
class CarrotDTO {
    private String color;
    private Integer weight;
    private Date harvestTime;
    private List<String> vitamins;
}

第六章:"吃胡萝卜" ------ 消费者代码

typescript 复制代码
@Component
@Slf4j
public class CarrotConsumer {
    
    /**
     * 处理新鲜胡萝卜 - 自动确认版
     */
    @RabbitListener(queues = "carrot.queue")
    public void eatFreshCarrot(String message) {
        log.info("兔子正在啃胡萝卜: {}", message);
        // 模拟吃胡萝卜的过程
        try {
            Thread.sleep(1000);
            log.info("真香!胡萝卜吃完了");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    /**
     * 处理豪华胡萝卜 - 手动确认版
     */
    @RabbitListener(queues = "luxury.carrot.queue")
    public void eatLuxuryCarrot(Message message, Channel channel, 
                               @Payload CarrotDTO carrot) {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        
        try {
            log.info("收到豪华胡萝卜!颜色: {}, 重量: {}g", 
                    carrot.getColor(), carrot.getWeight());
            
            // 模拟复杂的处理逻辑
            processLuxuryCarrot(carrot);
            
            // 手动确认(告诉兔子:我吃完了,你可以送下一个了)
            channel.basicAck(deliveryTag, false);
            log.info("豪华胡萝卜处理完成,已确认");
            
        } catch (Exception e) {
            log.error("吃豪华胡萝卜时噎住了: {}", e.getMessage());
            
            try {
                // 处理失败,拒绝消息(可以设置重新入队或丢弃)
                channel.basicNack(deliveryTag, false, true); // 重新入队
                // channel.basicReject(deliveryTag, false); // 直接丢弃
            } catch (IOException ioException) {
                log.error("无法拒绝消息: {}", ioException.getMessage());
            }
        }
    }
    
    /**
     * 批量吃胡萝卜(提高效率)
     */
    @RabbitListener(queues = "batch.carrot.queue", 
                   containerFactory = "batchFactory")
    public void eatBatchCarrot(List<Message> messages, Channel channel) {
        log.info("收到一批胡萝卜,共{}根", messages.size());
        
        for (Message message : messages) {
            try {
                String carrot = new String(message.getBody());
                log.info("正在处理胡萝卜: {}", carrot);
                // 批量确认
                channel.basicAck(message.getMessageProperties()
                    .getDeliveryTag(), false);
            } catch (Exception e) {
                log.error("处理失败: {}", e.getMessage());
            }
        }
    }
    
    private void processLuxuryCarrot(CarrotDTO carrot) {
        // 复杂的业务逻辑
        log.info("分析胡萝卜营养成分...");
        log.info("维生素含量: {}", carrot.getVitamins());
        // 这里可以添加数据库操作、调用其他服务等
    }
}

第七章:特殊场景处理

7.1 死信队列配置

typescript 复制代码
@Configuration
public class DeadLetterConfig {
    
    @Bean
    public DirectExchange deadLetterExchange() {
        return new DirectExchange("dead.letter.exchange");
    }
    
    @Bean
    public Queue deadLetterQueue() {
        return new Queue("dead.letter.queue");
    }
    
    @Bean
    public Binding deadLetterBinding() {
        return BindingBuilder.bind(deadLetterQueue())
                .to(deadLetterExchange())
                .with("dead.carrot");
    }
    
    /**
     * 监听死信队列
     */
    @Component
    @Slf4j
    public class DeadLetterConsumer {
        @RabbitListener(queues = "dead.letter.queue")
        public void handleDeadLetter(String message) {
            log.warn("收到死信胡萝卜,需要人工处理: {}", message);
            // 可以发送告警邮件、记录日志等
        }
    }
}

7.2 延迟队列(定时任务)

typescript 复制代码
@Configuration
public class DelayConfig {
    
    @Bean
    public CustomExchange delayExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange("delay.exchange", 
                "x-delayed-message", true, false, args);
    }
    
    @Bean
    public Queue delayQueue() {
        return new Queue("delay.queue");
    }
    
    @Bean
    public Binding delayBinding() {
        return BindingBuilder.bind(delayQueue())
                .to(delayExchange())
                .with("delay.key")
                .noargs();
    }
}

第八章:测试我们的"兔子农场"

less 复制代码
@SpringBootTest
@Slf4j
class RabbitmqTest {
    
    @Autowired
    private CarrotProducer carrotProducer;
    
    @Test
    void testSendCarrot() {
        // 测试发送普通胡萝卜
        carrotProducer.sendFreshCarrot("我是新鲜的胡萝卜!");
        
        // 测试发送豪华胡萝卜
        CarrotDTO luxuryCarrot = new CarrotDTO(
            "橙色",
            200,
            new Date(),
            Arrays.asList("维生素A", "维生素C", "胡萝卜素")
        );
        carrotProducer.sendLuxuryCarrot(luxuryCarrot);
        
        // 测试延迟胡萝卜
        carrotProducer.sendDelayedCarrot("我是10秒后的胡萝卜", 10);
        
        log.info("所有胡萝卜已发送,请观察兔子们的表现!");
    }
    
    @Test
    void testMassProduction() {
        // 批量生产胡萝卜,测试性能
        for (int i = 1; i <= 100; i++) {
            carrotProducer.sendFreshCarrot("批量胡萝卜#" + i);
        }
        log.info("100根胡萝卜已投入生产线!");
    }
}

第九章:监控和运维

9.1 健康检查

yaml 复制代码
management:
  endpoints:
    web:
      exposure:
        include: health,metrics,rabbit
  endpoint:
    health:
      show-details: always

9.2 自定义监控

java 复制代码
@Component
public class RabbitMonitor {
    
    @Autowired
    private RabbitAdmin rabbitAdmin;
    
    public void checkRabbitHealth() {
        Properties queueProperties = rabbitAdmin.getQueueProperties("carrot.queue");
        if (queueProperties != null) {
            int messageCount = Integer.parseInt(
                queueProperties.get("QUEUE_MESSAGE_COUNT").toString()
            );
            log.info("胡萝卜队列当前有 {} 根胡萝卜等待处理", messageCount);
        }
    }
    
    /**
     * 定期清理积压消息
     */
    @Scheduled(fixedDelay = 60000) // 每分钟检查一次
    public void clearBacklog() {
        // 实现清理逻辑
        log.info("正在打扫兔子窝...");
    }
}

第十章:总结 ------ 和兔子相处的秘诀

学到了什么:

  1. RabbitMQ 是个好邮差:可靠、灵活、功能强大的消息中间件
  2. SpringBoot 是兔子的好朋友:通过简单的配置就能轻松集成
  3. 消息确认很重要:别让胡萝卜在半路丢了
  4. 死信队列是保险箱:处理异常情况的最佳实践
  5. 延迟消息很实用:实现定时任务的好方法

实践建议:

  1. 给消息上保险:总是开启消息持久化和确认机制
  2. 合理设置TTL:别让胡萝卜放太久烂掉了
  3. 监控不能少:时刻关注兔子的健康状况
  4. 错误处理要优雅:准备好处理各种异常情况
  5. 性能要平衡:消费者数量不是越多越好

学习方向:

  1. 集群部署:养一群兔子,避免单点故障
  2. 镜像队列:给胡萝卜做备份
  3. 优先级队列:VIP 胡萝卜优先处理
  4. 流控机制:别把兔子撑坏了
  5. 与其他组件集成:让兔子和别的动物(其他中间件)合作

结尾:

记住,RabbitMQ 就像一只训练有素的邮差兔:

  • 发送 消息,它接收
  • 发布 消息,它路由
  • 消费 消息,它确认
  • 搞砸 了,它死信

只要遵循 RabbitMQ 的"兔子法则",你的消息系统就会像兔子繁殖一样------快速、可靠、生生不息!

最后:虽然 RabbitMQ 很可爱,但请不要真的喂它胡萝卜,它只吃 0 和 1(二进制数据)!


代码仓库建议结构:

bash 复制代码
rabbitmq-demo/
├── src/main/java/com/example/rabbitmq/
│   ├── config/          # 配置类
│   ├── producer/        # 生产者
│   ├── consumer/        # 消费者
│   ├── dto/            # 数据传输对象
│   └── RabbitmqDemoApplication.java
├── src/main/resources/
│   └── application.yml
└── pom.xml

现在就去和你的 RabbitMQ 兔子做好朋友吧!记得每天喂它足够的消息,但别把它撑着了哦~

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。

您的一键三连,是我更新的最大动力,谢谢

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

相关推荐
雨中飘荡的记忆2 小时前
Java并发工具深度剖析与实战
java
小股虫2 小时前
从零开始:ActiveMQ安装、Java应用实战与Web控制台体验
java·activemq·java-activemq
BingoGo2 小时前
万物皆字符串 PHP 中的原始类型偏执
后端·php
Carve_the_Code2 小时前
订单ID容量升级:从40位到64位的架构演进
后端
一粒麦仔2 小时前
物联网的低功耗守望者:全面解析Sigfox技术
后端·网络协议
java_logo2 小时前
RUSTFS Docker 容器化部署指南
java·运维·docker·容器·jenkins·运维开发
Frank_zhou2 小时前
192_如何基于复杂的指针移动完成单向链表的入队?
后端
Frank_zhou2 小时前
03_ArrayList核心方法的原理
后端
uup2 小时前
秒杀系统中的超卖与重复下单问题
java