Springboot集成RabbitMQ

RabbitMQ配置

引入依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

application.yml配置

yaml 复制代码
spring:
  rabbitmq:
    host: 42.194.132.44
    port: 5672
    username: guest
    password: guest

RabbitmqConfig配置

java 复制代码
package com.angel.ocean.rabbitmq.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class RabbitmqConfig implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * 单条消费
     */
    @Bean(name = "singleContainerFactory")
    public SimpleRabbitListenerContainerFactory singleContainerFactory() {
        SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
        containerFactory.setConnectionFactory(applicationContext.getBean(ConnectionFactory.class));
        containerFactory.setMaxConcurrentConsumers(1);
        containerFactory.setConcurrentConsumers(1);
        containerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        containerFactory.setConsumerBatchEnabled(false);
        containerFactory.setBatchListener(false);
        containerFactory.setAutoStartup(false);
        containerFactory.setPrefetchCount(100);
        return containerFactory;
    }

    /**
     * 批量消费
     */
    @Bean(name = "batchContainerFactory")
    public SimpleRabbitListenerContainerFactory batchContainerFactory() {
        SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
        containerFactory.setConnectionFactory(applicationContext.getBean(ConnectionFactory.class));
        containerFactory.setMaxConcurrentConsumers(1);
        containerFactory.setConcurrentConsumers(1);
        containerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        containerFactory.setBatchListener(true);
        containerFactory.setConsumerBatchEnabled(true);
        containerFactory.setBatchSize(50);
        containerFactory.setAutoStartup(false);
        containerFactory.setPrefetchCount(100);
        return containerFactory;
    }
}

RabbitMQ 队列声明、启动消费者设置

java 复制代码
package com.angel.ocean.rabbitmq.runner;

import com.angel.ocean.rabbitmq.util.RabbitmqUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;

@Slf4j
@Component
public class RabbitmqQueueRunner implements CommandLineRunner {

    @Resource
    private AmqpAdmin amqpAdmin;

    @Resource
    private RabbitListenerEndpointRegistry endpointRegistry;

    @Override
    public void run(String... args) throws Exception {
        log.info("动态创建MQ配置信息...");
        RabbitmqUtil.declareRabbitmqQueue(amqpAdmin, "ocean");
        // 启动消费者
        endpointRegistry.start();
    }
}

RabbitmqUtil 工具类

java 复制代码
package com.angel.ocean.rabbitmq.util;

import org.springframework.amqp.core.*;

public class RabbitmqUtil {

    /**
     * 交换机
     */
    public static Exchange getExchange(String key) {
        String name = key + "_exchange";
        return new DirectExchange(name, true, false);
    }

    /**
     * 队列
     */
    public static Queue getQueue(String key) {
        String name = key + "_queue";
        return new Queue(name, true, false, false);
    }

    /**
     * 绑定
     */
    public static Binding getBinding(String key) {
        String routingKey = key + "_routing_key";
        return new Binding(getQueue(key).getName(), Binding.DestinationType.QUEUE,
                getExchange(key).getName(), routingKey, null);
    }

    /**
     * 创建RabbitMQ队列
     */
    public static void declareRabbitmqQueue(AmqpAdmin amqpAdmin, String key) {

        // 获取队列
        Queue queue = RabbitmqUtil.getQueue(key);

        // 获取交换机
        Exchange exchange = RabbitmqUtil.getExchange(key);

        // 绑定关系
        Binding binding = RabbitmqUtil.getBinding(key);

        // 创建队列
        amqpAdmin.declareQueue(queue);
        amqpAdmin.declareExchange(exchange);
        amqpAdmin.declareBinding(binding);
    }
}

RabbitMQ 使用验证

生产者

java 复制代码
package com.angel.ocean.rabbitmq;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSON;
import com.angel.ocean.rabbitmq.domain.UserInfo;
import com.angel.ocean.rabbitmq.service.RabbitmqSenderService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.Date;

@Slf4j
@SpringBootTest
class ApplicationTest {

    @Resource
    private RabbitmqSenderService rabbitmqSenderService;

    @Test
    void test() {
        for (int i = 0; i < 500; i++) {
            UserInfo userInfo = new UserInfo(i, "Name" + i, RandomUtil.randomString(32), new Date());
            rabbitmqSenderService.send(JSON.toJSONString(userInfo));
        }
    }
}
java 复制代码
package com.angel.ocean.rabbitmq.service;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RabbitmqSenderService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void send(String message) {
        rabbitTemplate.convertAndSend("ocean_exchange", "ocean_routing_key", message);
    }
}

消费者

java 复制代码
package com.angel.ocean.rabbitmq.listener;

import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.List;

@Slf4j
@Component
public class RabbitmqListener {

    /**
     *  单条消费
     */
    @RabbitListener(queues = "ocean_queue", containerFactory = "singleContainerFactory")
    public void receive(Message message, Channel channel) throws IOException {

        try {
            log.info("Received data: {}", new String(message.getBody()));
            // 消费完成后,手动ACK
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            int sleepTime = RandomUtil.randomInt(2);
            Thread.sleep(sleepTime);
        } catch (IOException e) {
            // 处理消费失败的情况
            log.error("RabbitmqListener.receive() IOException, {}", e.getMessage(), e);
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        } catch (InterruptedException e) {
            log.error("RabbitmqListener.receive() InterruptedException, {}", e.getMessage(), e);
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }

    /**
     *  批量消费
     */
    @RabbitListener(queues = "ocean_queue", containerFactory = "batchContainerFactory")
    public void batchReceive(List<Message> messages, Channel channel) throws IOException {

        log.info("batchReceive, size:{}", messages.size());

        // 消费完成后,手动ACK
        for(Message message: messages) {
            try {
                // TODO 消费消息的逻辑
                log.info("batchReceive data: {}", new String(message.getBody()));
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            } catch (IOException e) {
                // 处理消费失败的情况
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            }
        }
    }
}

RabbitMQ 使用注意事项

  1. 为了保证数据一致性,设置RabbitMQ的消息确认机制ack为手动确认
java 复制代码
containerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);

或者

  1. 如果要保证消息顺序消费,不能设置并发消费
java 复制代码
containerFactory.setConcurrentConsumers(1);

或者

如果设置成并发消费,消费结果如下图,可以看出消息消费出现了乱序。

  1. 批量消费设置,可以根据实际业务设置批次大小

1)批量消费设置

java 复制代码
/**
 * 批量消费
  */
 @Bean(name = "batchContainerFactory")
 public SimpleRabbitListenerContainerFactory batchContainerFactory() {
     SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
     containerFactory.setConnectionFactory(applicationContext.getBean(ConnectionFactory.class));
     containerFactory.setMaxConcurrentConsumers(1);
     containerFactory.setConcurrentConsumers(1);
     containerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
     containerFactory.setBatchListener(true);
     containerFactory.setConsumerBatchEnabled(true);
     containerFactory.setBatchSize(50);
     containerFactory.setAutoStartup(false);
     containerFactory.setPrefetchCount(100);
     return containerFactory;
 }

2)@RabbitListener配置,使用containerFactory = "batchContainerFactory(见RabbitmqConfig)

java 复制代码
@RabbitListener(queues = "ocean_queue", containerFactory = "batchContainerFactory")
public void batchReceive(List<Message> messages, Channel channel) throws IOException {

    log.info("batchReceive, size:{}", messages.size());

    // 消费完成后,手动ACK
    for(Message message: messages) {
        try {
            // TODO 消费消息的逻辑
            log.info("batchReceive data: {}", new String(message.getBody()));
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            // 处理消费失败的情况
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }
}

验证结果如下图:
4. @RabbitListener先创建队列,再消费配置

如果使用默认配置,即设置containerFactory.setAutoStartup(true),而且队列又不存在,就会报如下错误:

java 复制代码
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'ocean_queue' in vhost '/', class-id=50, method-id=10)

1)设置消费者监听自动启动为false
2)服务启动后,启动消费者监听

相关推荐
用户908324602732 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840823 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解3 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解3 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记3 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者4 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840824 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解4 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
用户8307196840825 天前
RabbitMQ vs RocketMQ 事务大对决:一个在“裸奔”,一个在“开挂”?
后端·rabbitmq·rocketmq
初次攀爬者5 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq