【Apache Rocket】源码解析:消息消费源码解析(监听模式)

2024-01-21 00:36

本文主要是介绍【Apache Rocket】源码解析:消息消费源码解析(监听模式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

消费消息源码解析

1. Push模式消费源码解析

相比于生产者,消费者的代码要更为复杂一点,不过也不会太复杂,我们来逐一进行分解。RocketMQ的消费模式一般分为push模式和pull模式;push模式,又可以称为监听模式。

注意:push模式并不是Broker主动去push消息给客户端,本质上还是客户端去pull消息,只不过这个过程客户端是帮你做了而已。实际上还是在内部启了一个PullMessageServieScheduledThread线程专门去向Broker拉取消息。

2. Simple Demo

public static void main(String[] args) throws InterruptedException, MQClientException {DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_JODIE_1");consumer.subscribe("TopicTest", "*");consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);//wrong time format 2017_0422_221800consumer.setConsumeTimestamp("20181109221800");consumer.registerMessageListener(new MessageListenerConcurrently() {@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}});consumer.start();System.out.printf("Consumer Started.%n");
}

3. Consumer初始化

consumer init.jpg

  • 首先我们先来看看Consumer是如何初始化的,首先我们要创建一个DefaultMQPushConsumer,再设置对应的参数,然后再注册对应的MessageListener,最后在启动consumer。
  • 启动consumer的过程中,首先看检查相应的配置信息,如consumerGroup、messageModel是否存在,拉取、消费线程的配置情况,拉取队列的大小等等;然后将对应的订阅信息put到rebalance的subscriptionInner中。
  • 根据MessageModel的类型创建并加载对应的offsetStore
    • CLUSTERING:创建并加载RemoteBrokerOffsetStore,CLUSTERING模式的offset存放到Broker端
    • BROADCASTING:创建并加载LocalFileOffsetStore,BROADCASTING模式的offset存在到Client端。
  • 根据MessageListener的类型创建并启动MessageListener,两者的区别可以从字面意思就可以知晓,一个是并发的执行消费逻辑,另一个是顺序的执行消费逻辑。
    • orderly:创建并启动ConsumerMessageOrderlyService和ConsumeMessagePopOrderlyService
    • concurrently:创建并启动ConsumeMessageConcurrentlyService和ConsumeMessagePopConcurrentlyService
  • 将自己注册到MQClientFactory中。
  • 最后在启动MQClientFactory
    • 先检查是否已经获取到了NameServer的地址,没有的话去获取NameServer的地址
    • 再开启request-response的channel
    • 启动schedule任务线程。
      • 定时去获取NameServer的地址
      • 更新对应Topic的路由信息
      • 持久化当前服务的所有的Consumer的offset信息,只持久化LocalFileOffsetStore,即广播消费。
      • 调整线程池线程数量大小,目前concurrent模式和order模式都没有对应的实现。
    • 然后在启动pullService和rebalanceService
    • 最后再启动DefaultMQProducer,为什么要启动Producer?因为在消费监听返回RECONSUMER_LATER后,消息会发送到Broker,这个时候就需要发送者发送对应的RETRY消息。

4. 拉取消息(客户端处理)

当Consumer成功启动后,就开始拉取消息并消费。但是上述过程其实已经启动了拉取、消费线程,具体的执行如下:
pull message.jpg

  • 当start PullMessageService的时候,会启动一个PullMessageServiceScheduledThread线程,这个线程专门负责执行拉取消息的动作,先判断请求模式,POP消息先不做讲解,后面专门讲解POP消息,MessageRequestMode不为POP消息,执行pullMessage方法。
  • 首先是一些前置检查:检查状态是否OK,检查本地缓存的消息的size、byte是否大于阈值,检查无误后,读取offset信息,再到subscribeData中find一个Broker地址,远程请求Broker。
  • 请求Broker之后,等broker处理之后,根据返回的结果执行对应的回调
    • PullCallback.onException:调用executePullRequestLater,延迟相应的时间,执行executePullRequestImmediately,将当前的PullRequest对象put到到messageRequestQueue中。
      • PullMessageServiceScheduledThread线程,会不断地从messageRequestQueue take PullRequest对象,拿到PullRequest对象后,执行PullMessage方法,与上述的pullMessage执行过程一致。
    • PullCallback.onSuccess:先调用processPullResult方法处理broker返回的result,然后再判断状态:
      • FOUND
        • 先将对应的指标加到对应的metrics,判断是否返回了消息
          • 为null或者empty:调用executePullRequestLater
          • 不为null或者empty:先put到msgTreeMap中,然后根据pullInterval判断调用executePullRequestLater或者executePullRequestImmediately。
      • NO_NEW_MSG/NO_MATCHED_MSG:执行correctTagsOffset,更正offset,调用executePullRequestLater
      • OFFSET_ILLEGAL:延迟10s,更新offset信息并持久化到磁盘上(仅针对BROADCASTING模式,CLUSTERING模式的offset保存在broker,所以不需要持久化,这时候客户端对requestBroker进行更新。);然后remove对应的ProcessQueue。
  • 当put完到msgTreeMap中后,执行submitConsumeRequet方法
    • 先判断当前消息条数是否满足消费的阈值,
      • 不满足:执行submitConsumeRequestLater
      • 满足:先把对应的msg、processQueue、messageQueue封装成一个ConsumeRequest对象,丢给消费线程去调用,如果此时抛出rejectedExecutionException,执行submitConsumeRequet
      • ConsumeRequest实现了Runnable接口,所以被消费线程执行时,会执行内部的run方法
        • run:先执行用户自己的消费逻辑并返回结果并处理consumeResult并更新对应的ackIndex
          • 如果ackIndex不满足对应的要求,说明这批消息消费失败,根据messageModel来执行一下逻辑
            • CLUSTERING:执行sendBack,将消息发送给Broker,这就是为什么consumer也需要创建Producer对象的原因;等待sendback时候执行完成,如果执行失败,执行submitConsumeRequestLater,反之则结束。
            • BROADCASTING:输入warn日志即完成。

拉取消息(Broker端处理)

当拉取请求到达Broker端时,执行流程如下图:
pull message for broker.png

  • 拉取请求到达Broker后,通过Broker内部封装的netty,使请求流转到PullMessageProcessor,先进行一些前置检查,如:是否用户当前Broker的拉取权限;当前Broker的主动拉取是否开启;当前ConsumerGroup对应的SubscribeGroup是否存在;当前topic对应的TopicConfig是否存在;校验当前请求参数是否正确等等
  • 然后再查询resetOffsetTable中是否有对应重置之后的offset并且重置操作在Broker进行。
    • 是:把对应的resetSequence set到GetMessageResult中,最后PullMessageResultHandler处理,最后在response给客户端。
    • 否:先getMessageStore(我们以DefaultMessageStore为例,DLedgerMessageStore,后续再详细讲解),在调用messageStore.getMessageAsync()方法,getMessageAsync方法本质上就是用CompletableFuture包了一下getMessage方法。先判断当前topic是否开启了压缩,开启压缩:调用compactionStore.getMessage()(其实本质上还是查CommitLog,所以在流程图中并未体现),拿到ConsumeQueue后,先检查offset是否正确
      • 正确:遍历Mmap直至拿到正确的offsetPy,SizePy,再跟你offsetPy定位到具体的commitlog文件,定位到commitlog文件后,先确定开始拉取的postion(offset%mappedFileSize),把消息写到对应的byteBuffer中。
      • 不正确:修正offset,再把修正之后的offset set到getResult中
  • 上述执行完成后,在PullMessageResultHandler中进行处理
    • 先执行执行之后的回调
    • 如果是广播消费,更新对应广播消费的拉取进度,如果不是广播消费,这步可以忽略
    • 然后把对应的消费进去commit到offsetTable中。
    • 最后在把结果response给客户端。

创作不易:请关注微信公众号:【An前码后】获取更多源码解析,您的关注,就是博主创作的一份动力。

这篇关于【Apache Rocket】源码解析:消息消费源码解析(监听模式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/627846

相关文章

Linux中shell解析脚本的通配符、元字符、转义符说明

《Linux中shell解析脚本的通配符、元字符、转义符说明》:本文主要介绍shell通配符、元字符、转义符以及shell解析脚本的过程,通配符用于路径扩展,元字符用于多命令分割,转义符用于将特殊... 目录一、linux shell通配符(wildcard)二、shell元字符(特殊字符 Meta)三、s

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

SpringBoot使用Apache Tika检测敏感信息

《SpringBoot使用ApacheTika检测敏感信息》ApacheTika是一个功能强大的内容分析工具,它能够从多种文件格式中提取文本、元数据以及其他结构化信息,下面我们来看看如何使用Ap... 目录Tika 主要特性1. 多格式支持2. 自动文件类型检测3. 文本和元数据提取4. 支持 OCR(光学

使用Python实现批量访问URL并解析XML响应功能

《使用Python实现批量访问URL并解析XML响应功能》在现代Web开发和数据抓取中,批量访问URL并解析响应内容是一个常见的需求,本文将详细介绍如何使用Python实现批量访问URL并解析XML响... 目录引言1. 背景与需求2. 工具方法实现2.1 单URL访问与解析代码实现代码说明2.2 示例调用

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

Apache Tomcat服务器版本号隐藏的几种方法

《ApacheTomcat服务器版本号隐藏的几种方法》本文主要介绍了ApacheTomcat服务器版本号隐藏的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需... 目录1. 隐藏HTTP响应头中的Server信息编辑 server.XML 文件2. 修China编程改错误

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

在C#中合并和解析相对路径方式

《在C#中合并和解析相对路径方式》Path类提供了几个用于操作文件路径的静态方法,其中包括Combine方法和GetFullPath方法,Combine方法将两个路径合并在一起,但不会解析包含相对元素... 目录C#合并和解析相对路径System.IO.Path类幸运的是总结C#合并和解析相对路径对于 C