RocketMQ4.0源码分析之-路由管理

RocketMQ4.0源码分析之-路由管理

一 前言

  路由管理功能是RocketMQ的核心功能之一,涵盖了订阅管理,连接管理,负载均衡管理等一系列功能,代码布在NameServer,Broker,Producer和Consumer等各组件中。

  RocketMQ有一个特色功能:支持灵活的集群工作方式。Broker,Producer和Consumer都能够在运行时动态扩容或缩容,这都要依赖于强大的路由管理模块。

  路由管理的核心任务是实现分布式路由信息的同步和缓存。路由信息是Broker,Producer和Consumer等组件配置信息以及由这些配置信息生成的决策信息的总和,用于指导消息生产和消费的传输路径。NameServer,Broker,Producer和Consumer等组件各有多个实例分布在不同的机器上;各组件实例通过消息机制获得自己关心的一部分路由信息并缓存在本地内存。各组件实例的数量可能动态增加或减少,实例的配置信息可能动态变更,组件可能发生异常退出,网络通信可能发生异常。变更或异常发生时,各组件实例能够及时得知,并更新本地缓存。

  本文将从分布式路由信息的同步和缓存角度,介绍路由管理功能是如何实现的。


二 路由信息缓存

  各组件的实例中都缓存了自己感兴趣的一部分路由信息。当路由信息发生变更时,缓存能够得到及时更新。我们先来看看各组件分别缓存了哪些路由信息。

1 NameServer


NameServer的路由信息存放于RouteInfoManager类中,包含5张数据表:

1) Topic-Queue对应关系表(topicQueueTable)

  用于描述:各Topic分别分在哪些Broker上存在分片;每个分片包含多少个Queue;分片的读写权限及属性。

2) Broker地址表(brokerAddrTable)

  用于描述各Broker(包括Master和Slave)的ID和地址。

3) Cluster地址表(clusterAddrTable)

  用于描述每个Cluster分别包含哪些Broker。

4) Broker连接状态表(brokerLiveTable)

  用于描述:每个Broker最后一次发送来心跳包的时间,以及连接到当前NameServer使用的Chennel。

其中,“最后一次发来心跳包的时间”信息用于定期判断Broker是否保持存活状态。

5) FilterServer表(filterServerTable)

  用于描述每个Broker分别使用了哪些消息过滤服务器。


2 Broker


Broker的路由信息主要分布在ProducerManager等4个类的4张数据表中:

1)Producer表(groupChannelTable)

  用于描述:各ProducerGroup中分别有哪些存活的Producer连接;每个连接的Producer最后一次发来心跳的时间。

2) Consumer表(consumerTable)

  用于描述:每个ConsumerGroup中分别有哪些存活的Consumer连接,分别订阅了哪些Topic,订阅的每个Topic使用什么过滤条件(TAG)。

3) ConsumerGroup表(subscriptionGroupTable)

  用于描述:各ConsumerGroup的消费行为特点,例如:消费失败后的最大重试次数;重试队列个数;如果从MasterBroker消费缓慢,切换到哪个Slave Broker进行消费。

4) Topic表(topicConfigTable)

  用于描述:分布在当前Broker上的各Topic分片的配置信息,如:包含的读/写Queue的数量;是否有读/写权限。


3 Producer

Producer的路由信息主要分布在MQClientInstance和DefaultMQProducerImpl类的4张数据表中:


1) Producer表(producerTable)

  用于描述:当前的客户端实例中包含哪些Producer。缺省情况下,一个客户端实例对应于一个JVM进程。一个JVM进程内允许创建多个Producer,前提条件是:每个Producer从属于不同的ProducerGroup。

2) ClientInstance路由信息表(topicRouteTable)

  用于描述:和当前的客户端实例关联(生产或消费)的各Topic的路由信息,包括:每个Topic分布在哪些Broker上,包含多少个Queue;各Broker的ID和地址。

3) Broker地址表(brokerAddrTable)

  用于描述:当前客户端实例连接的各Broker的ID和地址。

4) Producer路由信息表(topicPublishInfoTable)

  用于描述:当前Producer生产的消息发送到哪些Queue(BrokerName + Topic + QueueId 的组合对应一个Queue);为当前Producer服务的各Broker的信息:每个Broker的BrokerName,ID和地址,分别包含多少个Queue。


4 Consumer


Consumer的路由信息主要分布在MQClientInstance和RebalanceImpl类的6张数据表中:

1) Consumer表(consumerTable)

  用于描述:当前的客户端实例中包含哪些Consumer。一个JVM进程内允许创建多个Consumer,前提条件是:每个Consumer从属于不同的ConsumerGroup。

2) ClientInstance路由信息表(topicRouteTable)

  在Producer中已经介绍。

3) Broker地址表(brokerAddrTable)

  在Producer中已经介绍。

4) Topic分片信息表(topicSubscribeInfoTable)

  用于描述:当前Consumer订阅的各个Topic分布在哪些Broker上,每个Broker上有多少个Queue。进行负载均衡处理时,这些Queue会按照负载均衡策略分配给ConsumerGroup内的各Consumer。

5) Consumer订阅信息表(subscriptionInner)

  用于描述:当前Consumer订阅的各Topic的消息过滤条件(TAG)。

6) 负载均衡结果表(processQueueTable)

  用于描述:经过负载均衡处理后,分配给当前Consumer的Queue,以及Queue对应的本地消息缓存。(Consumer从Broker批量获取消息后,会先缓存在本地的ProcessQueue对象中,消费线程从ProcessQueue中消费消息。)

三 各组件之间的网络连接关系

对照RocketMQ官方文档《RocketMQ 原理简介》中的网络部署图,各组件之间的连接关系为:


1)   每个Broker与NameServer集群中的所有节点建立长连接

  Broker每隔30秒会分别发送一个心跳消息(REGISTER_BROKER)给每个NameServer,上报Broker信息。

2)   每个SlaveBroker与对应的MasterBroker建立长连接

  SlaveBroker每隔60秒会分别发送GET_ALL_TOPIC_CONFIG和GET_ALL_SUBSCRIPTIONGROUP_CONFIG消息给对应的MasterBroker,将MasterBroker上的路由信息缓存同步到本地缓存。

3)   每个Producer与NameServer集群中的一个节点(随机选择)建立长连接

  Broker的路由信息是通过NameServer作为中间人“转告“给Producer的。

  Producer每隔30秒,会对Producer负责生产的每一个Topic发送一个GET_ROUTEINTO_BY_TOPIC消息给NameServer,拉取最新的路由信息并更新到本地缓存。

4)   每个Producer与提供Topic服务的所有MasterBroker建立长连接

  每隔30秒,Producer会发送心跳(HEART_BEAT)消息给每个MasterBroker,上报Producer的信息。

5)   每个Consumer与NameServer集群中的一个节点(随机选择)建立长连接

  Consumer定时从NameServer拉取路由信息的处理方法和Producer定时从NameServer拉取消息类似。

6)   每个Consumer与提供Topic服务的Master、Slave Broker建立长连接

  Consumer与Broker之间发送心跳包的工作流程和Producer与Broker之间发送心跳包流程类似。


四 典型场景下的路由管理流程

  下面,我们结合RocketMQ工作过程中的一些典型场景介绍路由管理工作流程。

  假定这样一个场景:NameServer,Broker,Producer和Consumer分别有一个实例已经启动并进行正常的消息生产和消费。使用的Topic名为“Routing_Topic”,消费模式为集群消费。接下来按顺序进行操作:

  启动另一个新的Broker实例->在新Broker上创建Topic->启动一个新的Consumer。

1 启动一个Broker


(注:数据表右边的箭头表示读数据表,左边的箭头表示写数据表)

Step1: Broker注册到NameServer上

  Broker启动时发送REGISTER_BROKER消息给每个NameServer。

 

2 创建一个Topic


  新启动的Broker内由于没有包含Producer和Consumer感兴趣的Topic,启动后会一直闲着。只需要在这个Broker上创建Topic,就可以让它开动起来。

  通过mqadmin管理命令或者Producer、Consumer的API,都能够在指定的一台Broker上创建一个Topic。假设我们通过mqadmin管理命令在新启动的Broker上创建“Routing_Topic”。后续发生的事件为:

Step1: Broker刷新路由信息到NameServer上

  创建Topic过程中,Broker会发送REGISTER_BROKER消息给每个NameServer,

  新增的Topic信息会更新到NameServer的topicQueueTable中。

Step2:Producer定时从NameServer获取最新的路由信息

  每隔30秒,Producer发送GET_ROUTEINTO_BY_TOPIC消息给NameServer,获取Broker信息的变更并刷新本地路由信息。

  新启动Broker的地址信息及新创建的Topic信息从NameServer传递到Produer。

Step3:Consumer定时从NameServer获取最新的路由信息

  每隔30秒,Consumer发送GET_ROUTEINTO_BY_TOPIC消息给NameServer,获取最新的Broker信息并刷新本地路由信息。

  新启动Broker的地址信息及新创建的Topic信息从NameServer传递到Consumer。

  本例中,Step2和Step3运行在不同的进程,没有严格的先后执行顺序。

  至此,新启动的Broker就已经被Producer和Consumer感知,并准备好分担一部分工作任务了。

Step4: Producer定时发送心跳给Broker,上报路由信息

  Producer每隔30秒会发送HEART_BEAT消息给Broker,上报路由信息。

Step5: Consumer定时发送心跳给Broker,上报路由信息

  Consumer每隔30秒会发送HEART_BEAT消息给Broker,上报路由信息。

  在HEART_BEAT消息处理过程中,Broker端除了更新consumerTable表以外,还会创建Consumer所属的ConsumerGroup,并创建对应的消息重发队列。

Step6,Step7: Consumer定时进行负载均衡操作

  Consumer每隔20秒会发发起一次负载均衡操作,对自己消费的每个Topic进行负载均衡。

  Consumer发送GET_CONSUMER_LIST_BY_GROUP消息给Broker,了解所在的ConsumerGroup下除了自己还有哪些其它的Consumer。获得的ConsumerID列表信息(cidAll)不会被缓存,作为局部变量使用。。

  Consumer对一个Topic进行负载均衡的处理过程:从本地缓存的topicSubscribeInfoTable表中取出该Topic分布在所有Broker上的Queue列表,按照指定的负载均衡策略,对照当前ConsumerGoup内的ConsumerID列表(cidAll),确定自己分配得到哪些Queue。负载均衡的结果被更新到processQueueTable。

3启动一个Consumer


Step1: Consumer从NameServer获取最新的路由信息

  Consumer启动过程中,会发送GET_ROUTEINTO_BY_TOPIC消息给NameServer,获取Broker信息并刷新本地路由信息。

Step2: Consumer注册到Broker上

  Consumer启动过程中, Step1获取到路由信息后立即发送HEART_BEAT消息给Broker,上报自己的订阅信息。

Step3: Broker通知Consumer进行负载均衡操作

  Broker发送NOTIFY_CONSUMER_IDS_CHANGED消息给新注册的Consumer所属ConsumerGroup的各Consumer,通知各Consumer进行一次Rebalance操作。

Step4,Step5: Consumer进行负载均衡操作

  工作流程和前面介绍的“创建一个Topic”负载均衡工作流程相同。

猜你喜欢

转载自blog.csdn.net/binzhaomobile/article/details/73743361