1. 使用@RocketMQMessageListener注解
(默认模式)
如果你使用的是org.apache.rocketmq.spring.annotation.RocketMQMessageListener注解来配置消息监听器,你可以在监听器内部实现多线程消费。这通常通过在监听器内部启动一个线程池来处理消息。
- 手动提交ack
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Service
@RocketMQMessageListener(topic = "your-topic", consumerGroup = "your-group", consumeMode = ConsumeMode.CONCURRENTLY)
public class YourMessageListener implements RocketMQListener<String> {
private final ExecutorService executor = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池
@Override
public void onMessage(String message) {
executor.submit(() -> {
// 处理消息的逻辑
System.out.println("Received: " + message);
// 确保资源释放,例如关闭数据库连接等
});
}
}
在异步并发消费模式下,当消息处理完成后,会自动提交 ACK。
提交时机说明
- 正常处理:如果
onMessage方法正常执行完毕,没有抛出异常,RocketMQ 客户端会自动认为消息消费成功,并向 Broker 提交 ACK,表示该消息已被成功消费。 - 异常处理:如果
onMessage方法抛出异常,RocketMQ 会根据重试策略对消息进行重试。默认情况下,消息会重试 16 次,每次重试的间隔时间逐渐增加。如果重试次数达到上限仍然失败,消息会进入死信队列。
手动 ACK 模式
MessageListenerConcurrently 是 Apache RocketMQ 提供的一个消息监听器接口,主要用于实现并发消费消息的功能。
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RocketMQMessageListener(topic = "your-topic", consumerGroup = "your-group", consumeMode = ConsumeMode.CONCURRENTLY)
public class ManualAckConsumer implements MessageListenerConcurrently {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
try {
// 处理消息的业务逻辑
System.out.println("Received message: " + new String(msg.getBody()));
} catch (Exception e) {
// 处理异常
e.printStackTrace();
// 消费失败,返回 RECONSUME_LATER 进行重试
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
// 消费成功,手动提交 ACK
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
}
提交时机说明
-
消费成功:当一个消息队列中的消息依次处理完成,且
consumeMessage方法返回ConsumeOrderlyStatus.SUCCESS时,会向 Broker 提交 ACK,表示该消息队列中的消息已全部成功消费。 -
消费失败:如果在消息处理过程中出现异常,返回
ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT,表示当前消息队列需要暂停消费一段时间,RocketMQ 会对消息进行重试。
综上所述,ACK 的提交时机取决于消费模式和消息处理结果,你可以根据具体的业务需求选择合适的消费模式和 ACK 提交方式。
在某些场景下,你可能需要手动控制 ACK 的提交,这时可以使用 RocketMQReplyListener 接口,并结合 MessageListenerConcurrently 实现手动 ACK。
2. 使用DefaultMQPushConsumer手动管理消费者
如果你需要更细粒度的控制,比如使用DefaultMQPushConsumer手动创建和配置消费者,你也可以在其中实现多线程逻辑。
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
import java.util.concurrent.*;
public class YourConsumer {
private final ExecutorService executor = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("your-group");
consumer.setNamesrvAddr("localhost:9876"); // 设置NameServer地址
consumer.subscribe("your-topic", "*"); // 订阅主题和标签
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
msgs.forEach(msg -> executor.submit(() -> {
// 处理消息的逻辑
System.out.println("Received: " + new String(msg.getBody()));
}));
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; // 消费成功
}
});
consumer.start();
System.out.printf("Consumer Started.%n");
}
}