Spring Boot 集成 RabbitMQ 批量发送、消费消息

1. Spring Boot 集成 RabbitMQ 批量发送、消费消息

1.1. 版本说明

构件 版本
spring-boot 2.7.18
spring-boot-starter-amqp 2.7.18

1.2. Spring 配置

spring:
  application:
    name: spring-rabbit-batch-demo
  rabbitmq:
    addresses: 127.0.0.1:5672
    username: admin
    password: admin
    virtual-host: /

1.3. 定义常量

public class RabbitBatchConstants {
    public static final String QUEUE_1 = "spring-rabbit-batch-demo-queue-1";
    public static final String EXCHANGE_1 = "spring-rabbit-batch-demo-exchange-1";
    public static final String QUEUE_2 = "spring-rabbit-batch-demo-queue-2";
    public static final String EXCHANGE_2 = "spring-rabbit-batch-demo-exchange-2";
}

1.4. 配置交换机和队列

@Configuration
@Slf4j
public class RabbitBatchConfiguration {

    @Bean
    public Queue queue1() {
        return QueueBuilder.durable(RabbitBatchConstants.QUEUE_1).build();
    }

    @Bean
    public FanoutExchange exchange1() {
        return ExchangeBuilder.fanoutExchange(RabbitBatchConstants.EXCHANGE_1).durable(true).build();
    }

    @Bean
    public Queue queue2() {
        return QueueBuilder.durable(RabbitBatchConstants.QUEUE_2).build();
    }

    @Bean
    public FanoutExchange exchange2() {
        return ExchangeBuilder.fanoutExchange(RabbitBatchConstants.EXCHANGE_2).durable(true).build();
    }

    @Bean
    public Binding binding1() {
        return BindingBuilder.bind(queue1()).to(exchange1());
    }

    @Bean
    public Binding binding2() {
        return BindingBuilder.bind(queue2()).to(exchange2());
    }
}

1.5. 定义 BatchingRabbitTemplate 工厂

org.springframework.amqp.rabbit.core.BatchingRabbitTemplate 提供了批量发送消息的 API,但是一个 BatchingRabbitTemplate 实例只能发送同一交换机或 Routing Key 的消息,因此需要一个工厂类,针对同一交换机生成一个 BatchingRabbitTemplate 实例。

定义 TaskScheduler:

@Bean(name = "rabbitBatchTaskScheduler")
TaskScheduler taskScheduler() {
    ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
    threadPoolTaskScheduler.setPoolSize(Runtime.getRuntime().availableProcessors() * 2);
    threadPoolTaskScheduler.setThreadNamePrefix("rabbit-batch-");
    threadPoolTaskScheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    threadPoolTaskScheduler.initialize();
    return threadPoolTaskScheduler;
}

定义 BatchingRabbitTemplate 工厂:

@Component
public class BatchingRabbitTemplateFactory implements Lifecycle {

    @Resource
    private ConnectionFactory connectionFactory;

    @Resource(name = "rabbitBatchTaskScheduler")
    private TaskScheduler taskScheduler;

    private Map<String, BatchingRabbitTemplate> batchingRabbitTemplateMap = new ConcurrentHashMap<>();

    public BatchingRabbitTemplate getBatchingRabbitTemplate(String exchange) {
        return batchingRabbitTemplateMap.computeIfAbsent(exchange, exchangeName -> {
            //批量发送 100 条消息,假设 1 条消息大小 1Kb,缓存 1000 条消息,1 秒超时
            return new BatchingRabbitTemplate(connectionFactory, new SimpleBatchingStrategy(100, 1024 * 1000, 1000L), taskScheduler);
        });
    }

    @Override
    public void start() {

    }

    @Override
    public void stop() {
        batchingRabbitTemplateMap.forEach((exchange, batchingRabbitTemplate) -> batchingRabbitTemplate.stop());
    }

    @Override
    public boolean isRunning() {
        return true;
    }
}

1.6. 配置批量消费消息的 RabbitListenerContainerFactory

@Bean(name = "batchRabbitListenerContainerFactory")
SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
    SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
    factory.setConnectionFactory(connectionFactory);
    //启用批量消费消费
    factory.setBatchListener(true);
    //批量消费 100 条消息
    factory.setBatchSize(100);
    return factory;
}

1.7. 测试

@Component
@Slf4j
public class SpringRabbitBatchDemo implements ApplicationRunner {

    @Resource
    private BatchingRabbitTemplateFactory batchingRabbitTemplateFactory;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        String payload1 = "hello, queue 1";
        for (int i = 0; i < 100; i++) {
            batchingRabbitTemplateFactory.getBatchingRabbitTemplate(EXCHANGE_1).convertAndSend(EXCHANGE_1, null, payload1);
        }
        log.info("send 100 messages, exchange: {}, payload: {}", EXCHANGE_1, payload1);

        String payload2 = "hello, queue 2";
        for (int i = 0; i < 2; i++) {
            batchingRabbitTemplateFactory.getBatchingRabbitTemplate(EXCHANGE_2).convertAndSend(EXCHANGE_2, null, payload2);
        }
        log.info("send 2 messages, exchange: {}, payload: {}", EXCHANGE_2, payload2);
    }

    /**
     * 消息批量消费
     * @param messages
     */
    @RabbitListener(queues = {RabbitBatchConstants.QUEUE_1}, containerFactory = "batchRabbitListenerContainerFactory")
    public void listen1(List<Message<String>> messages) {
        log.info(
                "queue 1 received {} messages",
                messages.size()
        );
    }

    /**
     * 消息单条消费
     * @param message
     */
    @RabbitListener(queues = {RabbitBatchConstants.QUEUE_2})
    public void listen2(Message<String> message) {
        log.info("queue 2 received a message");
    }
}

启动程序,控制台将输出:

send 100 messages, exchange: spring-rabbit-batch-demo-exchange-1, payload: hello, queue 1
send 2 messages, exchange: spring-rabbit-batch-demo-exchange-2, payload: hello, queue 2
queue 1 received 100 messages
queue 2 received a message
queue 2 received a message
posted @ 2024-09-26 10:07  Jason207010  阅读(1243)  评论(0)    收藏  举报