1.RocketMQ的简介

  1. 什么是RocketMQ?

    1.是一个队列模型的消息中间件。

    2.高性能、高可靠、高实时、分布式的特点。

  2.rocketMq的优点:

    1.亿级消息堆积能力.

    2.高效的订阅者水平扩展能力

    3.能够保证严格的消息顺序

    4.提供丰富的消息拉叏模式

    5.实时的消息订阅机制

  3.rocketMq的物理结构:

    

 

 

     1.Name Server是一个几乎无状态特点,可集群部署,节点之间无任何信息同步的(相对于zookeeper是较为轻量级的)。

    2.Broker

      1.broker分为Master和Slave,一个Master可以对应多个Slave,但是多个Slave只能对应一个Master;

      2.Master与Slave的对应关系通过指定相同的BrokerName;

      3.不同的BrokerID来定义,BrokerId为o表示Master,非o表示Slave,Master也可以部署多个;

      4.每个Broker与Name Server集群中的所有节点建立长连接,定时注册Topice信息到所有的Name Server.

    3.Producer:

      1.Producer与Name Server集群中的其中一个节点(随机选择)建立长连接,定期从Name Server取Topic路由信息;

      2.向提供Topic服务的Master建立长连接,且定时的向Master发送心跳。

      3.Producer完全无状态,可以集群部署。

    4.Consuemr

      1.Consuemr与Name Server集群中的其中一个节点(随机选择)建立长连接,定期从Name Server取Topic路由信息;

      2.向提供Topic服务的Master,Slave建立长连接,且定时向Master,Slave发送心跳;

      3.Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。

    5.rocketMq消息模型:

      1.顺序消息:

      https://blog.csdn.net/luanlouis/article/details/91368332

      

 

 

       2.事务消息:

      https://baijiahao.baidu.com/s?id=1638994745278332160&wfr=spider&for=pc

      https://zhuanlan.zhihu.com/p/249233648

        1.RocketMQ 事务使用案例

        createTransactionListener() 在init()方法中调用,构造实现RocketMQ的TransactionListener接口的匿名类,该接口需要实现如下两个方法:

          1.executeLocalTransaction:执行本地事务,在这里我们直接把订单数据插入到数据库中,并返回本地事务的执行结果。

          2.checkLocalTransaction:反查本地事务,上述流程中是在db中查询订单号是否存在,若存在则提交事务,若不存在,可能本地事务失败了,也可能本地事务还在执行,所以返回UNKNOW

        2.rocketMq事务消息实现原理:

          1.producer端如何发事务消息?

            1.DefaultMQProducerImpl.sendMessageInTransaction(final Message msg, final LocalTransactionExecuter localTransactionExecuter, final Object arg)

            2.DefaultMQProducerImpl.endTransaction(final SendResult sendResult, final LocalTransactionState localTransactionState, final Throwable localException)

            有事务反查机制作兜底,该RPC请求即使失败或丢失,也不会影响事务最终的结果。最后构建事务消息的发送结果,并返回。

            3.主要做了如下事情:

              1.给消息打上事务消息相关的标记,用于MQ服务端区分普通消息和事务消息

              2.发送半消息(half message)

              3.发送成功则由transactionListener执行本地事务
              4.执行endTransaction方法,如果半消息发送失败或本地事务执行失败告诉服务端是删除半消息,半消息发送成功且本地事务执行成功则告诉服务端生效半消息。

          2.Broker端如何处理事务消息?

            1.SendMessageProcessor#asyncSendMessage

            2.TransactionalMessageBridge

              1.putHalfMessage

              2.parseHalfMessageInner

              3.设计思想:

                1.RocketMQ并非将事务消息保存至消息中 client 指定的 queue,而是记录了原始的 topic 和 queue 后,把这个事务消息保存在 - 特殊的内部 topic:RMQ_SYS_TRANS_HALF_TOPIC - 序号为 0 的 queue

                2.这套 topic 和 queue 对消费者不可见,因此里面的消息也永远不会被消费。这就保证在事务提交成功之前,这个事务消息对 Consumer 是消费不到的。

                

 

 

            3.broker反查事务

              1.在Broker的TransactionalMessageCheckService服务中启动了一个定时器,定时从事务消息queue中读出所有待反查的事务消息。

              AbstractTransactionalMessageCheckListener#resolveHalfMsg - 针对每个需要反查的半消息,Broker会给对应的Producer发一个要求执行事务状态反查的RPC请求

              

 

               2.- AbstractTransactionalMessageCheckListener#sendCheckMessage

              

 

               3.Broker2Client#checkProducerTransactionState

              根据RPC返回响应中的反查结果,来决定这个半消息是需要提交还是回滚,或者后续继续来反查。

              

 

               4.最后,提交或者回滚事务。首先把半消息标记为已处理

                 1. 如果是提交事务,就把半消息从半消息队列中复制到该消息真正的topic和queue中

                2. 如果是回滚事务,什么都不做 - EndTransactionProcessor#processRequest

               

 

 

            3.总结:

              1.整体流程:

              

 

 

 

              RocketMQ是基于两阶段提交来实现的事务,把这些事务消息暂存在一个特殊的queue中,待事务提交后再移动到业务队列中。最后,RocketMQ的事务适用于解决本地事务和发消息的数据一致性问题。

 

  4. RockertMQ的逻辑部署结构

    

 

    1.ProducerGroup:用来表示一个发送消息应用,一个Producer Group下包含多个Producer实例,一个Producer Group可以发送多个Topic消息

    2.Consuermer Group:来表示一个消费消息的应用,一个Consumer Group下包含多个Consumer实例

  

2.RocketMQ搭建双Master集群

 双主结构搭建地址:https://www.cnblogs.com/Eternally-dream/p/9942849.html

3.RocketMQ监控平台rocketmq-console-ng的搭建

 https://www.cnblogs.com/Eternally-dream/p/9948084.html

  官网地址:https://github.com/apache/rocketmq-externals

4.RocketMQ的Client的使用 Producer/Consumer

  1.添加依赖:

  2.Producer 的开发步骤

    1.实例化ProducerGroup:DefaultMQProducer producer = new DefaultMQProducer("my-producer-group");

    2.设置namesrvAddr: producer.setNamesrvAddr("47.105.145.123:9876;47.105.149.61:9876");

    3.调用start()方法启动:producer.start();

    4.发送消息:producer.send(message);

    5.关闭生产者:producer.shutdown();

  3。Consumer开发步骤:

    1. 实例化Consumer Group;  DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("my-consumer-group");    

    2. 设置namesrvAddr,集群环境多个nameserver用;分割;  producer.setNamesrvAddr("47.105.145.123:9876;47.105.149.61:9876");

    3. 设置从什么位置开始; consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);

    4. 订阅topic; consumer.subscribe("MyQuickStartTopic", "*");

    5. 注册消息监听器; consumer.registerMessageListener();

    6. 重写MessageListenerConcurrently接口的consumeMessage()方法。

5。RocketMQ的整体架构

  1. RocketMQ主要的9个模块

  

 

 

   

  2. 模块介绍

      1. rocketmq-common:通用的常量枚举,基类方法或者数据结构,按描述的目标来分包,通俗易懂。包名有admin,consumer,filter,hook,message等。

      2. rocketmq-remoting:用Netty写的客户端和服务端,fastjson做的序列化,自定义二进制协议

      3. rocketmq-sevutil: 只用一个ServerUtil类,类注解是,只提供Server程序依赖,目的是为了拆解客户端依赖,尽可能减少客户端的依赖

      4. rocketmq-store: 存储服务,消息存储,索引存储,commitLog存储

      5. rocketmq-client: 客户端,包含producer端和consumer端,消息的生产发送和接收消费的过程。

      6. rocketmq-filtersrv: 消息过滤器server

      7. rocketmq-broker: 对consumer和producer来说是服务端,接收producer发来的消息并存储,同时consumer来这里拉取消息。

      8. rocketmq-tools:命令行工具。

      9. rocketmq-namesrv:NameServer,类似zookeeper注册中心,这里保存着消息的TopicName,队列等运行时的meta信息。一般系统分dataNode和nameNode,这里是nameNode。

 

   

  3. 底层通信

      1. ServerHouseKeepingService:守护线程,本质是ChannelEventListener,监听broker的channel变化来更新本地的RouteInfo。

      2. NSScheduledThread1: 定时任务线程,定时跑2个任务,第一个是,每隔十分钟扫描出不活动的broker,然后冲routeInfo中删除,第二个是,每隔十分钟打印configTable的消息。

      3. NettyBossSelector: Netty的boss线程(Accept线程),这里只有一个线程

      4.NettyEventExecuter:一个单独的线程,监听NettyChannel状态变化和通知ChannelEventListener要响应的动作。

      5. DestroyJavaVM:Java虚拟机析构钩子,一般是当虚拟机关闭时用来清理或者释放资源的。

      6. NettyServerSelector_x_x: Netty的Work线程(io)线程,这里可能有多个线程

      7. NettyServerWorkerThread_x:执行ChannelHandler方法的线程,ChannelHandler运行在线程上,这里可能有多个线程。

      8. RemotingExecutorThread_x: 服务端逻辑线程,这里可能有多个线程。rocketmq-namesrv扮演者nameNode角色,记录运行时meta信息已经broker和filtersrv运行时信息,可以部署集群。

  

  4. 数据存储

     1.rocketmq-broker模块介绍:

       1.这个是数据存储的核心,也就是真正的MQ服务器,我们所谓的消息存储,接收,拉去,推送这些操作都是在broker上进行的。

     2.rocketmq-filtersrv:

       1.在rocketmq中,使用独立的一个模块去对数据进行过滤,实现了真正意义上的高内聚,低耦合的设计思想。

       2.我们在使用rocketmq-filtersrv模块的时候,也需要启动filter服务。

6.RocketMQ的Producer API简介

  原文链接:https://www.cnblogs.com/Eternally-dream/p/9954704.html

  1.在RocketMQ中提供了三种发送消息的模式:

    1.NormalProducer(普通)

    2.OrderProducer(顺序)

      1.顺序消息原理图:

      

      2.即一类消息为满足顺序性,必须Producer单线程顺序发送,并且发送给到同一队列,这样Consumer就可以按照Producer发送的顺序去消费消息。

        1.普通顺序消息:

          1.正常情况下可以保证完全的顺序消息,但是一旦发生通信异常,Broker重启,由于队列总数发生变化,哈希取模后定位的队列会变化,产生短暂的消息顺序不一致。

        2.严格顺序消息:

          1.无论正常异常都保证顺序,但是牺牲了分布式Failover特性,即Broker集群中只要有一台机器不可用,则整个集群都不可用,服务可用性大大降低。

          2.如果服务器部署方式为同步双写模式,此缺陷可通过备机自动切换为主避免,不过仍然会存在短暂时间服务不可用。

          3.目前已知的应用只有数据库binlog同步强依赖严格顺序消息。其他应用绝大部分都可以容忍短暂乱序,推荐使用普通顺序消息。

        3.顺序消息缺陷:

          1. 发送顺序消息无法利用集群FailOver 特性
          2.  消费顺序消息的幵行度依赖亍队列数量
          3.  队列热点问题,个别队列由亍哈希丌均导致消息过多,消费速度跟丌上,产生消息堆积问题
          4.  遇到消息失败的消息,无法跳过,当前队列消费暂停

        4.顺序消息的使用:

          1.顺序消息的使用需要在producer的send()方法中添加MessageQueueSelector接口的实现类,并重写select选择使用的队列;

          2.因为顺序消息局部顺序,需要将所有消息指定发送到同一队列中。

    3.TransactionProducer(事务消息)

      1.https://www.jianshu.com/p/cc5c10221aa1

  2.producer中的各个API的使用:

    1. producerGroup:Producer组名, 默认值为DEFAULT_PRODUCER,多个Producer如果属于一个应用,发送同样的消息,则应该将它们归为同一组。

    2. createTopicKey: 默认值为TBW102,在发送消息时,自动创建服务器不存在的topic,需要指定Key。

    3.  defaultTopicQueueNums: 默认值为4, 在发送消息时,自动创建服务器不存在的topic,默认创建的队列数。

    4. sendMsgTimeout: 默认值10000,发送消息超时时间,单位毫秒

    5. compressMsgBodyOverHowmuch: 默认值4096,消息Body超过多大开始压缩(Consumer收到消息会自动解压缩),单位字节

    6. retryAnotherBrokerWhenNotStoreOK: 默认值false, 如果发送消息返回sendResult,但是sendStatus!=SEND_OK,是否重试发送

    7. maxMessageSize: 默认值131072,客户端限制的消息大小,超过报错,同时服务端也会限制

    8. transactionCheckListener: 事务消息回查监听器,如果发送事务消息,必须设置,在DefaultMQProducer的子类TransactionMQProducer中。

    9. checkThreadPoolMinSize:  默认值为1,Broker回查Producer事务状态时,线程池大小,在DefaultMQProducer的子类TransactionMQProducer中。

    10. checkThreadPoolMaxSize: 默认值为1,Broker回查Producer事务状态时,线程池大小。

    11. checkRequestHoldMax: 默认值为2000, Broker回查Producer事务状态时,Producer本地缓冲请求队列大小

  3.RocketMQ的PushConsumer和PullConsumer

    1.PushConsumer:推

      1.Broker主动向Consumer推消息,Consumer应用通常向对象注册一个Listener接口,一旦接收到消息,Consumer立刻回调Linstener接口方法。

      2.Push方式里,consumer把轮询过程封装了,并注册MessageListener监听器,取到消息后,唤醒MessageListener的consumeMessage()来消费,对用户而言,感觉消息是被推送过来的。

      3.缺点:

        1.消费者的速度比发送者的速度慢很多,势必造成消息在broker的堆积.

    2.PullConsumer:拉,

      1.Consumer主动的从Broker拉取消息,主动权由应用控制,可以实现批量的消费消息。

      2.通过打算消费的Topic拿到MessageQueue的集合,遍历MessageQueue集合,然后针对每个MessageQueue批量取消息,一次取完后,记录该队列下一次要取的开始offset,直到取完了,再换另一个MessageQueue。

      3.优点:  

        1.consumer可以按需消费,不用担心自己处理不了的消息;

        2.broker堆积消息也会相对简单,无需记录每一个要发送消息的状态,只需要维护所有消息的队列和偏移量就可以了.

  4.RocketMQ的Consumer API简介

    1. PushConsumer的配置

      1. consumerGroup: 默认值为DEFAULT_CONSUMER,Consumer组名,多个Consumer如果属于一个应用,订阅同样的消息,且消费逻辑一致,则应该将它们归为同一组

      2. messageModel: 消息模型,默认值为CLUSTERING,支持集群消费,广播消费两种模型

      3. consumeFromWhere: 默认值为CONSUME_FROM_LAST_OFFSET,Consumer启动后,默认从什么位置开始消费

      4. allocateMessageQueueStrategy: 默认值AllocateMessageQueueAveragely,Rebalance(轮询)算法实现策略

      5. subscription: 默认值{},订阅关系

      6. messageListener: 消息监听器

      7. offsetStore: 消息进度存储

      8. consumeThreadMin: 默认10,消费线程池数量

      9. consumeThreadMax: 默认20, 消费线程数量

      10. consumeConcurrentlyMaxSpan: 默认值2000, 单队列并行消费允许的最大跨度

      11. pullThresholdForQueue: 默认1000,拉消息本地队列缓存消息最大数

      12. pullInterval: 默认0,拉消息间隔,由于是长轮询,所以为0,但是如果应用为了流控,也可以设置大于0的值,单位毫秒

      13. consumeMessageBatchMaxSize: 默认1,批量消费,一次消费多少条消息

      14. pullBatchSize: 默认32, 批量拉消息,一次最多拉多少条

    2. PullConsumer的配置

      1. consumerGroup: 默认DEFAULT_CONSUMER,Consumer组名,多个Consumer如果属于一个应用,订阅同样的消息,且消费逻辑一致,则应该将它们归为同一组。

      2. brokerSuspendMaxTimeMillis:默认20000,长轮询,Consumer拉消息请求在Broker挂起最长时间,单位毫秒

      3. consumerTimeoutMillisWhenSuspend: 默认30000,长轮询,Consumer拉消息请求在Broker挂起超过指定时间,客户端认为超时,单位毫秒

      4. consumerPullTimeoutMillis: 默认10000,非长轮询,拉消息超时时间,单位毫秒

      5. messageModel:默认值BROADCASTING,消息模型,支持以下集群消费 、广播消费两种模型

      6. messageQueueListener: 监听队列变化

      7. offsetStore: 消费进度存储

      8. registerTopics:默认为[],注册的topic集合

      9. allocateMessageQueueStrategy: 默认AllocateMessageQueueAveragely,Rebalance(轮询)算法实现策略

  5.RocketMQ的Consumer消息重试:

    1.Producer端重试:

      生产者端的消息失败,也就是Producer往MQ上发消息没有发送成功,比如网络抖动导致生产者发送消息到MQ失败。 这种消息失败重试我们可以手动设置发送失败重试的次数。

    2.Consumer:

      Consumer消费消息失败后,要提供一种重试机制,令消息再消费一次,Consumer消费消息失败通常可以认为有以下几种情况:

      1. 由于消息本身的原因,例如反序列化失败,消息数据本身无法处理(例如话费充值,当前消息的手机被注销,无法充值)等。

       这种错误通常需要跳过这条消息,再消费其他消息,而且这条失败消息即使立刻重试消费,99%也不成功,所以最后提供一种定时的重试机制,即过10s再重试。

      2. 由于依赖下游应用服务不可用,例如db连接不可用,外系统网络不可达等。

      遇到这种错误,即使跳过当前失败的消息,消费其他消息也会报错,这种情况下建议应用sleep 30s,再消费下一条消息,这样可以减轻Broker重试消息的压力。

    3.Broker消息重试策略:

      

 7.RocketMQ的去重策略:

  1.Exactly Only Once条件:

    (1). 发送消息阶段,不允许发送重复的消息

    (2). 消费消息阶段,不允许消费重复的消息。

  2. 重复消费的原因

    1.在于回馈机制。正常情况下,消费者在消费消息时候,消费完毕后,会发送一个ACK确认信息给消息队列(broker),消息队列(broker)就知道该消息被消费了,就会将该消息从消息队列中删除。

    2.不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念。

    3.网络原因闪断,ACK返回失败等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。

  3.去重策略:

    1.去重策略原则:

      1.幂等性

      2.业务去重

    2.去重策略:

      1.业务流水

      2.redis去重

8.RocketMQ搭建双主双从(异步复制)集群

  原文文档:https://www.cnblogs.com/Eternally-dream/p/9961218.html

9.RocketMq的底层实现逻辑:

  1.rocketMq路由中心:

    原文链接:https://blog.csdn.net/wee616/article/details/89926852

    1.服务管理:

      1.服务注册:

        1.Broker在启动时向所有的NameServer心跳语句,每隔30S向所有NameServer发起心跳包。

        2.NameServer收到心跳包后更新缓存。

        3.NameServer每隔10S扫描brokerLiveTable,如果连续120S没有收到心跳包,则NameServer移除Broker的路由信息同时关闭Socket连接。

      2.服务发现:

        1.RocketMQ的路由发现是非实时的。当topic对应的路由信息发生变化,NameServer并不会通知给客户端。

        2.而是由客户端定时拉取Topic对应的最新路由。

        3.不实时的路由发现引起的问题由客户端进行解决,保证了NameServer逻辑的简洁。

    2.路由管理:

      1.路由注册:

        1.路由注册在broker启动时触发,broker启动时会和所有NameServer创建心跳连接,向NameServer发送Broker的相关信息。

        2.NameServer在RouteInfoManager类中维护了Broker相关信息的缓存,进行更新动作。更新时用了读写锁,既保证了极高并发场景下的读效率,又避免了并发修改缓存。

        

 

 

 

      2.路由发现:

        1.客户端定时向NameServer发起请求GET_ROUTEINFO_BY_TOPIC,获取对应的信息。流程较为简单

      3路由删除:

        路由删除的触发点有两个:

        1)NameServer启动时开启的定时任务,每隔10s扫描一次brokerLiveTable,检测上次心跳包与当前系统时间差,如果时间差大于120s,则移除Broker的相关信息。

        2)Broker正常关闭,会向NameServer发送UNREGISTER_BROKER消息。

  2.rocketMq消息存储

    1.MQ消息队列的一般存储方式:

      1.分布式KV:一般使用redis

        1.优点:高性能

        2.缺点:数据一致性问题

      2.文件系统:RocketMQ/Kafka/RabbitMQ

        1.优点:均采用的是消息刷盘至所部署虚拟机/物理机的文件系统来做持久化(刷盘一般可以分为异步刷盘和同步刷盘两种模式)。小编认为,消息刷盘为消息存储提供了一种高效率、高可靠性和高性能的数据持久化方式。

        2.缺点:

      3.关系数据库DB:activeMq

        1.普通关系型数据库(如Mysql)在单表数据量达到千万级别的情况下,其IO读写性能往往会出现瓶颈

     2.RocketMq存储整体架构:

        

 

        1.RocketMQ的混合型存储结构针对Producer和Consumer分别采用了数据和索引部分相分离的存储结构:

          1.Producer发送消息至Broker端,然后Broker端使用同步或者异步的方式对消息刷盘持久化,保存至CommitLog中;

          2.Broker端的后台服务线程—ReputMessageService不停地分发请求并异步构建ConsumeQueue(逻辑消费队列)和IndexFile(索引文件)数据。

            1.ConsumeQueue(逻辑消费队列)作为消费消息的索引,保存了指定Topic下的队列消息在CommitLog中的起始物理偏移量offset,消息大小size和消息Tag的HashCode值。

            2.IndexFile(索引文件)则只是为了消息查询提供了一种通过key或时间区间来查询消息的方法.

        2.PageCache与Mmap内存映射

          1.pageCache:系统的所有文件I/O请求,操作系统都是通过page cache机制实现的。对于操作系统来说,磁盘文件都是由一系列的数据块顺序组成,数据块的大小由操作系统本身而决定,x86的linux中一个标准页面大小是4KB。

          2.首先到page cache中查找(page cache中的每一个数据块都设置了文件以及偏移量地址信息),如果未命中,则启动磁盘I/O,将磁盘文件中的数据块加载到page cache中的一个空闲块,然后再copy到用户缓冲区中。

          3.page cache本身也会对数据文件进行预读取,对于每个文件的第一个读请求操作,系统在读入所请求页面的同时会读入紧随其后的少数几个页面。
            1.想要提高page cache的命中率(尽量让访问的页在物理内存中),从硬件的角度来说肯定是物理内存越大越好。
            2.从操作系统层面来说,访问page cache时,即使只访问1k的消息,系统也会提前预读取更多的数据,在下次读取消息时, 就很可能可以命中内存。
          4.在RocketMQ中,ConsumeQueue逻辑消费队列存储的数据较少,并且是顺序读取,在page cache机制的预读取作用下,Consume Queue的读性能会比较高近乎内存,即使在有消息堆积情况下也不会影响性能。
          5.而对于CommitLog消息存储的日志数据文件来说,读取消息内容时候会产生较多的随机访问读取,严重影响性能。
            1.如果选择合适的系统IO调度算法,比如设置调度算法为“Noop”(此时块存储采用SSD的话),随机读的性能也会有所提升。
          6.RocketMQ主要通过MappedByteBuffer对文件进行读写操作。
            1.其中,利用了NIO中的FileChannel模型直接将磁盘上的物理文件直接映射到用户态的内存地址中.
              1.这种Mmap的方式减少了传统IO将磁盘文件数据在操作系统内核地址空间的缓冲区和用户应用程序地址空间的缓冲区之间来回进行拷贝的性能开销),
            2.将对文件的操作转化为直接对内存地址进行操作,从而极大地提高了文件的读写效率
              1.(这里需要注意的是,采用MappedByteBuffer这种内存映射的方式有几个限制,其中之一是一次只能映射1.5~2G 的文件至用户态的虚拟内存,这也是为何RocketMQ默认设置单个CommitLog日志数据文件为1G的原因了)。
        3.RocketMQ文件存储模型层次结构
    
          (1)RocketMQ业务处理器层
             1.Broker端对消息进行读取和写入的业务逻辑入口.
             2.根据解析RemotingCommand中的RequestCode来区分具体的业务操作类型,进而执行不同的业务处理流程;比如前置的检查和校验步骤、构造MessageExtBrokerInner对象、decode反序列化、构造Response返回对象等;
          (2)RocketMQ数据存储组件层:
              1.该层主要是RocketMQ的存储核心类—DefaultMessageStore,其为RocketMQ消息数据文件的访问入口,通过该类的“putMessage()”和“getMessage()”方法完成对CommitLog消息存储的日志数据文件进行读写操作;
          (3)RocketMQ存储逻辑对象层:该层主要包含了RocketMQ数据文件存储直接相关的三个模型类IndexFile、ConsumerQueue和CommitLog。
              1.IndexFile为索引数据文件提供访问服务;
              2.ConsumerQueue为逻辑消息队列提供访问服务;
              3.CommitLog则为消息存储的日志数据文件提供访问服务。
          (4)封装的文件内存映射层
              1.RocketMQ主要采用JDK NIO中的MappedByteBuffer和FileChannel两种方式完成数据文件的读写。
              2.其中,采用MappedByteBuffer这种内存映射磁盘文件的方式完成对大文件的读写,在RocketMQ中将该类封装成MappedFile类。
                1.对于每类大文件(IndexFile/ConsumerQueue/CommitLog),在存储时分隔成多个固定大小的文件(单个IndexFile文件大小约为400M、单个ConsumerQueue文件大小约5.72M、单个CommitLog文件大小为1G);
                2.其中每个分隔文件的文件名为前面所有文件的字节大小数+1,即为文件的起始偏移量,从而实现了整个大文件的串联。      
                  1.MappedFile类提供了顺序写/随机读、内存数据刷盘、内存清理等和文件相关的服务;
          (5)磁盘存储层
              1.主要指的是部署RocketMQ服务器所用的磁盘。
              2.这里,需要考虑不同磁盘类型(如SSD或者普通的HDD)特性以及磁盘的性能参数(如IOPS、吞吐量和访问时延等指标)对顺序写/随机读操作带来的影响

   

      4.文件刷盘机制:同步和异步刷盘

        1.源码解读:https://www.cnblogs.com/lomoye/p/13297512.htm

      5.过期文件删除机制:

        1.源码解析:https://www.cnblogs.com/zuoyang/p/14465764.html

      6.实时更新消息队列与索引;

       7.消息队列与索引文件恢复

 

  3.消息过滤filterServer

    1. ClassFilter 运行机制:

      原文链接:https://blog.csdn.net/chenbin1991smile/article/details/113803081

      基于类模式过滤是指在 Broker 端运行 1 个或多个消息过滤服务器( FilterServer ),RocketMQ 允许消息消费者自定义消息过滤实现类并将其代码上传到 FilterServer 上,消息消 费者 向 FilterServer 拉取消息, FilterServer 将消息消费者的拉取命令转发到Broker,然后对返回的消息执行消息过滤逻辑,最终将消息返回给消费端。
      1.Broker 进程所在的服务器会启动多个 Fi lterServer 进程 。

      2.消费者在订阅消息主题时会上传一个自定义的消息过滤实现类, FilterServer 加载并实例化。

       3.消息消费者( Consume )

        1.Consume 向 FilterServer 发送消息拉取请求;

        2. FilterServer 接收到消息消费者消息拉取请求后, Fi lterServer 将消息拉取请求转发给 Broker,;

        3.Broker 返回消息后在FilterServer 端执行消息过滤逻辑;

        4.然后返回符合订阅信息的消息给消息消费者进行消费 。

        

 

 

    2.FilterServer 注册剖析

      1.FilterServer 在启动时会创建一个定时调度任务,每隔 10s 向 Broker 注册自己;

        1.FilterServer 从配置文件 中 获取 Broker 地址,然后将 Fi lterServer 所在机器的IP 与监昕端口发送到 Broker 服务器,请求命令类型为 RequestCode.REGISTER_FILTER_SERVER ;

        2.在Broker 端处理 REGISTER_FILTER_SERVER命令的核心实现FilterServerManager , 其实现过程是先从 filterServerTable 中以网络通道为 key 获取FilterServerlnfo ,如果不等于空 ,则更新一下上次更新时间为当前时间,否则创建一个新的 FilterServerlnfo对象并加入到 filterServerTable路由表中 。

      2.Filterserverlnfo 类:

        1。String filterServerAddr : filterServer 服 务器地址 。

 

    3.FilterClass 订阅信息注册  

      1.RocketMQ 通过 DefaultMQPushConsumerimpl.subscribe (String topic, String fullClassName,String filterClassSource)方法来实现基于类模式的消息过滤,其参数分别代表消费组订阅的消息主题、类过滤全路径名、类过滤源代码字符串 。

        1.构建订阅信息, 然后将该订阅信息添加到 Rebalancelmpl 中, 其主要目标是Rebalancelmpl 会对订阅 信息表 中的 主题进行消息 队列的负载,创建消息拉取任务,以便PullMessageService 线程拉取消息 。

        2.定时将消息端订阅信息中的类过滤模式的过滤类源码上传到 FilterServer.

          Step1:构建订阅信息, 然后将该订阅信息添加到 Rebalancelmpl 中, 其主要目标是Rebalancelmpl 会对订阅 信息表 中的 主题进行消息 队列的负载,创建消息拉取任务,以便PullMessageService 线程拉取消息 。

          Step2 :定时将消息端订阅信息中的类过滤模式的过滤类源码上传到 FilterServer
          Step3 :根据订阅的主题获取该主题的路由信息,如果该主题路由信息中的FilterServer缓存表不为空, 则需要将过滤类发送到 FilterServer 上 。 TopicRouteData 中filterServerTable缓存表 的存储 格式为 HashMap<String/* brokerAddr*/,List<String>/* Filter Server */> ,FilterServer 是依附于 Broker 消息服务器的 ,多个 FilterServer 共同从 Broker 上拉取消息 。

          Step4 :遍历主题路 由表中的 fiIterServerTable ,向 缓存中所有的 FilterServer 上传消息过滤代码 。
          Step5 : FilterServer 端处理 FilterClass上传并将其源码编译的实现为FilterClassManager ,该方法的参数含义分别是消息消费组名 、 消息主题、消息过滤类全路径名、源码的 CRC 验证码 、过滤类源码 。
          Step6 :根据消息消费组与主题名称构建 filterClasTable 缓存 key ,从缓存表中 尝试获取过滤类型信息 FilterClasslnfo 。 如果缓存表中不包含 FilterClasslnfo 则表示第一次注册,设置 registerNew 为 true ;如果 FlterClasslnfo 不为空, 说明该消息消费组不是第一次注册 。 如果服务端开启允许消息消费者上传 FilterClass ,比较两个的 classCRC ,如果不相同,说明FilterClass 的源码发生了变化,设置 registerNew 为 true 。
          Step7 :如果是第一次注册,则创建 FilterClasslnfo ,如果 FilterServer 允许消息消费者上传过滤类源码,则使用 JDK 提供的方法将源代码编译并加装,然后创建其实例,并强制类型转换为 MessageFilter ,也就是自定义的消息过滤类必须实现 MessageFilter 接口 。
          上述整个过程就完成了消息消费端向 FilterServer 上传过滤类的过程,但如果FilterServer 不允许消息消费者上传 FilterC!ass ,则 filterServerTable 中存在的过滤类信息只包含 className, classCRC 、 消息过滤类 MessageFilter 属性都为空,也就是说会忽略消息消费者上传的过滤类源代码,那过滤类的源码从哪获取呢?原来 FilterServer 会开启一个定时任务从配置好的远程服务器去获取过滤类的源码,再将其编译与实例化。