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

相关推荐
武子康2 小时前
Java-72 深入浅出 RPC Dubbo 上手 生产者模块详解
java·spring boot·分布式·后端·rpc·dubbo·nio
G_whang6 小时前
jenkins使用Jenkinsfile部署springboot+docker项目
spring boot·docker·jenkins
hac132210 小时前
Spring Boot 双数据源配置
java·spring boot·后端
凤山老林11 小时前
Spring Boot中的中介者模式:终结对象交互的“蜘蛛网”困境
java·spring boot·后端·设计模式·中介者模式
沃夫上校12 小时前
Spring Boot 中使用 Redis
spring boot·redis
java_强哥13 小时前
Spring Boot启动原理:从main方法到内嵌Tomcat的全过程
spring boot·后端·tomcat
李剑一13 小时前
上传三个参数,两个接收正常,一个死活都是null?
spring boot·后端
泉城老铁14 小时前
Spring Boot深度整合RabbitMQ:从入门到企业级实战
java·后端·rabbitmq
想要成为祖国的花朵15 小时前
Java_Springboot技术框架讲解部分(二)
java·开发语言·spring boot·spring
Q_Q51100828515 小时前
python的小学课外综合管理系统
开发语言·spring boot·python·django·flask·node.js