RocketMQ 消费消息的Push模式,是推还是拉?

RocketMQ 消费消息是推还是拉?

RocketMQ 是一个分布式消息中间件,广泛应用于高性能、高可靠性的消息传输场景。对于很多开发者来说,理解 RocketMQ 消费消息的机制是至关重要的。一个常见的问题是:RocketMQ 的消息消费是推(Push)还是拉(Pull)?

消费消息的基本模式

在消息中间件的设计中,消息消费通常有两种模式:

  1. 推模式(Push):消息中间件主动将消息推送给消费者。
  2. 拉模式(Pull):消费者主动从消息中间件拉取消息。

整理了一份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记的【点击此处即可】即可免费获取

这两种模式各有优缺点:

  • 推模式的优点是消息可以立即送达消费者,缺点是如果消费者处理能力不足,可能会导致消息堆积或丢失。
  • 拉模式的优点是消费者可以根据自身的处理能力主动控制消息的拉取速度,缺点是可能会增加延迟。

RocketMQ 的消费机制

RocketMQ 的消息消费机制是一种混合模式,表面上看是推模式,但实际上是基于拉模式实现的

 

diff

复制代码

+-------------------+ | 1. 消费者初始化 | +-------------------+ | v +-------------------+ | 创建消费者实例 | | 设置 NameServer | | 订阅主题 | | 注册监听器 | +-------------------+ | v +-----------------------+ | 2. 内部拉取线程 | +-----------------------+ | v +-----------------------+ | 启动多个拉取线程 | | 每个线程运行 | | PullMessageService | +-----------------------+ | v +-----------------------+ | 3. 消息拉取 | +-----------------------+ | v +--------------------------+ | 从队列中取出 PullRequest | +--------------------------+ | v +--------------------------+ | 调用 consumerImpl.pull | | Message 拉取消息 | +--------------------------+ | v +--------------------------+ | 如果找到消息,将消息 | | 放入 ConsumeMessageService | | 队列 | +--------------------------+ | v +-----------------------+ | 4. 消息推送给监听器 | +-----------------------+ | v +-------------------------+ | ConsumeMessageService | | 从队列中处理消息 | +-------------------------+ | v +-----------------------------+ | 调用注册的 MessageListener | | Concurrently | +-----------------------------+ | v +-----------------------+ | 监听器处理消息 | +-----------------------+

1. 消费者启动并注册监听器

当消费者启动时,用户会创建 DefaultMQPushConsumer 实例,并注册一个 MessageListenerConcurrently 监听器。代码示例如下:

 

java

复制代码

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("PushConsumerGroup"); consumer.setNamesrvAddr("127.0.0.1:9876"); consumer.subscribe("TopicTest", "*"); consumer.registerMessageListener(new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { for (MessageExt msg : msgs) { System.out.printf("Receive message: %s%n", new String(msg.getBody())); } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); consumer.start(); System.out.printf("Consumer started.%n");

2. 内部拉取线程

消费者内部启动多个拉取线程,这些线程不断从 Broker 拉取消息。这个过程由 PullMessageService 服务实现:

 

java

复制代码

public class PullMessageService extends ServiceThread { private final BlockingQueue<PullRequest> pullRequestQueue = new LinkedBlockingQueue<>(); private final DefaultMQPushConsumerImpl consumerImpl; @Override public void run() { while (!this.isStopped()) { try { PullRequest pullRequest = this.pullRequestQueue.take(); this.pullMessage(pullRequest); } catch (InterruptedException e) { log.error("PullMessageService interrupted", e); } } } private void pullMessage(PullRequest pullRequest) { // 调用拉取消息的方法 this.consumerImpl.pullMessage(pullRequest); } }

3. 消息推送给监听器

拉取到的消息会被放入 ConsumeMessageService 的阻塞队列中,然后由消费者的 ConsumeMessageService 服务处理。ConsumeMessageService 会从阻塞队列中取出消息,并调用用户注册的监听器进行处理:

 

java

复制代码

public void pullMessage(PullRequest pullRequest) { // 拉取消息 PullResult pullResult = this.pullKernelImpl.pull(pullRequest); switch (pullResult.getPullStatus()) { case FOUND: // 将拉取到的消息放入阻塞队列 this.consumeMessageService.submitConsumeRequest( pullResult.getMsgFoundList(), pullRequest.getProcessQueue(), pullRequest.getMessageQueue(), false ); break; // 其他状态处理 } }

ConsumeMessageService 会从阻塞队列中取出消息,并调用注册的监听器:

 

java

复制代码

public class ConsumeMessageConcurrentlyService extends ConsumeMessageService { @Override public void run() { while (!this.isStopped()) { try { // 从阻塞队列中取出消息 ConsumeRequest consumeRequest = this.consumeRequestQueue.take(); // 调用注册的监听器进行处理 this.consumeMessage(consumeRequest); } catch (InterruptedException e) { log.error("ConsumeMessageService interrupted", e); } } } private void consumeMessage(ConsumeRequest consumeRequest) { List<MessageExt> msgs = consumeRequest.getMsgs(); ConsumeConcurrentlyContext context = new ConsumeConcurrentlyContext(consumeRequest.getMessageQueue()); ConsumeConcurrentlyStatus status = this.messageListener.consumeMessage(msgs, context); // 根据返回的状态进行相应的处理 } }

总结

RocketMQ 的消费机制实际上是一种混合模式,内部采用拉取模式实现消息的获取,然后再将消息推送给用户注册的监听器处理。这种设计不仅保证了消息的及时处理,还简化了用户的使用体验。

所以,RocketMQ 消费消息表面上是推模式(Push),但实际上是基于拉模式(Pull)实现的。这种混合模式结合了两种模式的优点,使得 RocketMQ 在消息消费方面具有很高的灵活性和性能。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值