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)服务启动后,启动消费者监听

相关推荐
en-route12 小时前
如何在 Spring Boot 中指定不同的配置文件?
java·spring boot·后端
栀椩13 小时前
springboot配置请求日志
java·spring boot·后端
胚芽鞘68116 小时前
我对rabbitmq的理解(第一次)
分布式·rabbitmq
KIDAKN16 小时前
RabbitMQ 幂等性, 顺序性 和 消息积压
分布式·rabbitmq
Gu Gu Study16 小时前
Spring运行程序RabbitMQ并没有自动创建队列的问题解决
java·spring cloud·rabbitmq
学Java的bb16 小时前
后端Web实战-Spring原理
java·spring boot·spring
码畜也有梦想17 小时前
springboot响应式编程笔记
java·spring boot·笔记
王同学 学出来18 小时前
跟做springboot尚品甄选项目(二)
java·spring boot·后端
计算机毕业设计指导19 小时前
基于Spring Boot + Vue 3的社区养老系统设计与实现
vue.js·spring boot·后端
从零开始学习人工智能20 小时前
SpringBoot + Apache Tika:一站式解决文件数据提取难题
spring boot·后端·apache