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

相关推荐
镜水灵动18 分钟前
docker运行springboot项目
spring boot·docker·容器
李南想做条咸鱼1 小时前
vscode spring boot项目编辑yaml不自动提示补全如何解决
ide·spring boot·vscode
程序员大金2 小时前
基于SpringBoot+Vue+MySQL的垃圾分类回收管理系统
java·vue.js·spring boot·后端·mysql·mybatis
coding侠客2 小时前
Spring Boot 注解探秘:常用配置值读取注解的魔力
java·spring boot·后端·spring·spring cloud
吃面不喝汤662 小时前
如何优雅地处理 RabbitMQ 连接中断问题
分布式·rabbitmq
程序员大金3 小时前
基于SpringBoot+Vue+MySQL的影院购票系统
java·vue.js·spring boot·后端·mysql·mybatis
ok你也是个coder4 小时前
RabbitMQ(高阶使用)死信队列
rabbitmq
王彬泽4 小时前
【RabbitMQ】概述
rabbitmq
工业甲酰苯胺5 小时前
Springboot中自定义监听器
java·spring boot·spring
一粟1026 小时前
Spring Boot:现代化Java应用开发的艺术
java·spring boot·后端