RabbitMQ拉模式批量消费消息

本文深入探讨了RabbitMQ的两种消费模式:推模式和拉模式。推模式通过继承DefaultConsumer或使用SpringAMQP实现,适用于大多数场景;拉模式则通过Channel的basicGet方法实现,适合于需要在特定条件下消费消息或批量处理的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现RabbitMQ的消费者有两种模式,推模式(Push)和拉模式(Pull)。

实现推模式推荐的方式是继承 DefaultConsumer 基类,也可以使用Spring AMQP的 SimpleMessageListenerContainer 。

推模式是最常用的,但是有些情况下推模式并不适用的,比如说:

  • 由于某些限制,消费者在某个条件成立时才能消费消息

  • 需要批量拉取消息进行处理

 

实现拉模式

RabbitMQ的Channel提供了 basicGet 方法用于拉取消息。

/**
 * Retrieve a message from a queue using {@link com.rabbitmq.client.AMQP.Basic.Get}
 * @see com.rabbitmq.client.AMQP.Basic.Get
 * @see com.rabbitmq.client.AMQP.Basic.GetOk
 * @see com.rabbitmq.client.AMQP.Basic.GetEmpty
 * @param queue the name of the queue
 * @param autoAck true if the server should consider messages
 * acknowledged once delivered; false if the server should expect
 * explicit acknowledgements
 * @return a {@link GetResponse} containing the retrieved message data
 * @throws java.io.IOException if an error is encountered
 */
GetResponse basicGet(String queue, boolean autoAck) throws IOException;

basicGet 返回 GetResponse 类。

public class GetResponse {
    private final Envelope envelope;
    private final BasicProperties props;
    private final byte[] body;
    private final int messageCount;
    
    // ...
 

public class GetResponse { private final Envelope envelope; private final BasicProperties props; private final byte[] body; private final int messageCount; // ...

rabbitmq-client版本4.0.3

使用 basicGet 拉取消息需要注意:

basicGet
DefaultConsumer

示例代码:

private void consume(Channel channel) throws IOException, InterruptedException {
    while (true) {
        if (!isConditionSatisfied()) {
            TimeUnit.MILLISECONDS.sleep(1);
            continue;
        }
        GetResponse response = channel.basicGet(CAOSH_TEST_QUEUE, false);
        if (response == null) {
            TimeUnit.MILLISECONDS.sleep(1);
            continue;
        }
        String data = new String(response.getBody());
        logger.info("Get message <= {}", data);
        channel.basicAck(response.getEnvelope().getDeliveryTag(), false);
    }
}

批量拉取消息

RabbitMQ支持客户端批量拉取消息,客户端可以连续调用 basicGet 方法拉取多条消息,处理完成之后一次性ACK。需要注意:

basicGet
basicAck

示例代码:

String bridgeQueueName = extractorProperties.getBridgeQueueName();
int batchSize = extractorProperties.getBatchSize();
List<GetResponse> responseList = Lists.newArrayListWithCapacity(batchSize);
long tag = 0;
while (responseList.size() < batchSize) {
    GetResponse getResponse = channel.basicGet(bridgeQueueName, false);
    if (getResponse == null) {
        break;
    }
    responseList.add(getResponse);
    tag = getResponse.getEnvelope().getDeliveryTag();
}
if (responseList.isEmpty()) {
    TimeUnit.MILLISECONDS.sleep(1);
} else {
    logger.info("Get <{}> responses this batch", responseList.size());
    // handle messages
    channel.basicAck(tag, true);
}

关于QueueingConsumer

QueueingConsumer 在客户端本地使用 BlockingQueue 缓冲消息,其nextDelivery方法也可以用于实现拉模式(其本质上是 BlockingQueue.take ),但是 QueueingConsumer 现在已经标记为Deprecated。

欢迎工作一到五年的Java工程师朋友们加入Java架构开发: 855835163
群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

<think>我们讨论的是Spring AMQP与RabbitMQ集成时如何开启批量消费。在Spring AMQP中,批量消费是指消费一次接收多条消息进行处理,而不是逐条处理。这可以提高吞吐量,减少网络开销和事务提交次数。 在Spring AMQP中,批量消费的开启需要配置`SimpleRabbitListenerContainerFactory`,设置其`batchListener`属性为`true`,并配置相应的批处理大小(通过`setBatchSize`)或批处理接收超时(通过`setReceiveTimeout`)。 以下是具体步骤和代码示例: 1. **配置`SimpleRabbitListenerContainerFactory`**: 在配置类中,创建一个`SimpleRabbitListenerContainerFactory`实例,并设置相关属性。 2. **设置批量消费属性**: - `setBatchListener(true)`: 开启批量监听模式。 - `setConsumerBatchEnabled(true)`: 启用消费批量模式(实际上,在2.0版本之后,这个设置和`batchListener`是关联的,但为了明确,我们通常同时设置)。 - `setBatchSize(int batchSize)`: 设置批处理大小,即每次最多接收多少条消息。 - `setReceiveTimeout(long receiveTimeout)`: 设置接收超时时间,即等待达到批处理大小的最大时间(毫秒)。如果在此时间内没有达到批处理大小,也会将已收集到的消息发送给监听器。 3. **在监听方法中使用`List`接收消息**: 在`@RabbitListener`注解的方法中,参数类型为`List<Message>`或者`List<YourPayloadType>`。注意,如果使用`List<YourPayloadType>`,需要确保消息转换器能够正确转换。 4. **配置消息转换器**: 批量消费时,通常使用`BatchMessagingMessageConverter`,但也可以使用其他转换器。不过,默认情况下,使用`SimpleMessageConverter`可能无法直接转换,因此建议使用`Jackson2JsonMessageConverter`并配置信任包等。 示例配置类: ```java @Configuration public class RabbitMQConfig { @Bean public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) { SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); // 设置批量消费 factory.setBatchListener(true); factory.setConsumerBatchEnabled(true); factory.setBatchSize(10); // 每次最多处理10条 factory.setReceiveTimeout(5000L); // 等待5秒,如果5秒内没有达到10条,则发送已收集的消息 // 配置消息转换器(以JSON为例) Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(); messageConverter.setClassMapper(classMapper()); factory.setMessageConverter(messageConverter); return factory; } @Bean public DefaultClassMapper classMapper() { DefaultClassMapper classMapper = new DefaultClassMapper(); classMapper.setTrustedPackages("com.example.dto"); // 设置信任的包,避免反序列化问题 return classMapper; } } ``` 然后,在监听器中: ```java @Component public class BatchConsumer { @RabbitListener(queues = "queue.batch") public void handleBatch(List<Order> orders) { // 批量处理 for (Order order : orders) { // 处理每个订单 } // 或者使用批量操作 } } ``` **注意事项**: 1. **生产者发送**:生产者需要发送多条消息到队列,这些消息会被消费批量接收。注意,批量消费并不要求生产者一次性发送多条(使用`RabbitTemplate`的`convertAndSend`方法多次发送的单条消息也会被批量接收)。 2. **事务**:批量消费时,如果使用事务,整个批次会在一个事务中处理。如果处理过程中发生异常,整个批次的消息都会回滚。 3. **手动确认**:如果使用手动确认模式,需要确认整个批次的消息。在监听方法中,可以获到`Channel`和`List<Message>`,然后对每条消息进行确认。但注意,批量模式下,确认一条消息意味着确认该消息之前的所有消息(因为RabbitMQ的确认机制是基于delivery tag的,而同一个批次的消息的delivery tag是连续的)。因此,通常我们确认整个批次的最大delivery tag即可。 手动确认示例(在监听方法中): ```java @RabbitListener(queues = "queue.batch") public void handleBatch(List<Message> messages, Channel channel) throws IOException { for (Message message : messages) { // 处理消息 } // 获最后一条消息的delivery tag,确认整个批次 long deliveryTag = messages.get(messages.size()-1).getMessageProperties().getDeliveryTag(); channel.basicAck(deliveryTag, true); // 批量确认,第二个参数为true表示确认该tag之前的所有消息 } ``` 但是,在Spring AMQP的批量监听中,我们通常使用自动确认,或者使用容器管理的事务。如果使用手动确认,需要确保异常处理,避免消息丢失。 **为什么需要批量消费?** 在高并发场景下,批量消费可以显著提高处理效率,减少数据库IO次数(比如批量插入)或者减少网络交互次数。 **可能遇到的问题**: 1. **消息堆积**:如果批处理等待时间过长,可能导致消息处理延迟增加。 2. **内存溢出**:如果批处理大小设置过大,可能会占用大量内存。 3. **部分失败**:如果一批消息中部分处理失败,需要设计补偿机制。 因此,根据实际业务场景调整批处理大小和超时时间非常重要。 --- §§相关问题§§: 1. Spring AMQP中如何实现单个消息消费? 2. 在RabbitMQ批量消费中,如何处理消息处理失败的情况? 3. 如何配置RabbitTemplate以实现批量消息发送? 4. 在批量消费模式下,消息确认机制与单条消费模式有何不同?</think>### Spring与RabbitMQ开启批量消费的解决方案 #### 1. 配置批量监听容器 ```java @Configuration public class RabbitBatchConfig { @Bean public SimpleRabbitListenerContainerFactory batchContainerFactory( ConnectionFactory connectionFactory) { SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); // 开启批量消费模式 factory.setBatchListener(true); // 设置批量参数 factory.setBatchSize(50); // 每批最大消息数 factory.setReceiveTimeout(5000L); // 等待超时时间(毫秒) factory.setConsumerBatchEnabled(true); // 设置并发消费者数量 factory.setConcurrentConsumers(5); factory.setMaxConcurrentConsumers(10); return factory; } } ``` #### 2. 创建批量消息监听器 ```java @Component public class BatchMessageListener { // 使用批量监听工厂 @RabbitListener( queues = "your.queue.name", containerFactory = "batchContainerFactory" ) public void handleBatchMessages(List<Message> messages) { // 处理批量消息 for (Message message : messages) { String payload = new String(message.getBody()); System.out.println("Received message: " + payload); // 业务处理逻辑... } // 批量处理完成后的操作 System.out.println("Batch processed: " + messages.size() + " messages"); } } ``` #### 3. 生产者发送批量消息(可选) ```java @Autowired private RabbitTemplate rabbitTemplate; public void sendBatchMessages() { for (int i = 0; i < 100; i++) { rabbitTemplate.convertAndSend( "exchange.name", "routing.key", "Message " + i ); } } ``` --- ### 关键配置解释 #### 1. `setBatchListener(true)` - **作用**:启用批量消费模式 - **原理**:RabbitListener会将多条消息聚合成`List<Message>`传递给处理方法 - **注意事项**:处理方法参数必须声明为`List<Message>`或`List<YourObject>` #### 2. 批处理参数配置 | 参数 | 默认值 | 说明 | |------|--------|------| | `batchSize` | 无 | 每批最大消息数量 | | `receiveTimeout` | 无 | 等待消息达到批大小的超时时间(毫秒) | | `consumerBatchEnabled` | false | 启用消费者端批处理 | #### 3. 批处理工作流程 1. 容器启动时创建指定数量的消费者 2. 每个消费者从队列消息 3. 消息消费者端缓冲: - 当消息数量达到`batchSize` - 或等待时间超过`receiveTimeout` 4. 缓冲区的消息集合作为单一批次传递给监听方法 5. 监听方法处理完整批次 --- ### 性能优化建议 1. **批量确认模式** ```java factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); @RabbitListener(...) public void handleBatch(List<Message> messages, Channel channel) throws IOException { // 处理消息... // 批量确认最后一条消息 long lastTag = messages.get(messages.size()-1).getMessageProperties().getDeliveryTag(); channel.basicAck(lastTag, true); // 确认该tag之前的所有消息 } ``` 2. **并发控制** ```java // 根据系统负载动态调整 factory.setConcurrentConsumers(5); factory.setMaxConcurrentConsumers(20); factory.setStartConsumerMinInterval(10000L); // 增加消费者最小间隔 ``` 3. **异常处理** ```java @Bean public SimpleRabbitListenerContainerFactory batchContainerFactory(...) { factory.setAdviceChain( RetryInterceptorBuilder.stateless() .maxAttempts(3) .backOffOptions(1000, 2.0, 5000) .build() ); } ``` --- ### 与传统单条消费的对比 | 特性 | 批量消费 | 单条消费 | |------|----------|----------| | 吞吐量 | 高 (减少网络开销) | 低 | | 延迟 | 有缓冲延迟 | 实时性高 | | 资源消耗 | CPU/内存集中使用 | 均匀消耗 | | 错误处理 | 整批重试 | 单条重试 | | 适用场景 | 大数据量ETL | 实时交易处理 | --- ### 常见问题解决 **问题1:消息处理顺序混乱** **解决方案**: ```java factory.setConsumerTagStrategy(new ConsumerTagStrategy() { @Override public String createConsumerTag(String queue) { return queue + "-" + System.currentTimeMillis(); } }); ``` **问题2:内存溢出风险** **解决方案**: ```java // 限制批处理大小 factory.setDeBatchingEnabled(true); factory.setBatchSize(100); // 根据系统内存调整 factory.setPrefetchCount(200); // 预消息数 ``` **问题3:消费者阻塞** **解决方案**: ```java // 设置任务执行器 factory.setTaskExecutor(taskExecutor()); factory.setConcurrency("5-10"); // 动态调整并发数 ``` ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值