RocketMQ 路由中心 NameServer

本章主要介绍 RocketMQ 路由管理 、 服务注册及服务发现的机制, NameServer 是整个RocketMQ 的“大脑” 。
本章重点内容如下 。
• NameServer 整体架构设计
• NameServer 动态路由发现与剔除机制
2.1 NameServer 架构设计
消息中间件的设计思路一般基于主题的订阅发布机制 消息生产者( Producer)发送某一 主题的消息到消息服务器,消息服务器负责该消息的持久化存储,消息消费者(Consumer)订阅感兴趣的主题,消息服务器根据订阅信息(路由信息)将消息推送到消费者( PUSH 模式)或者消息消费者主动向消息服务器拉取消息( PULL 模式),从而实现消息生产者与消息消费者解藕 。 为了避免消息服务器的单点故障导致的整个系统瘫痪,通常会部署多台消息服务器共同承担消息的存储 。
RocketMQ 的逻辑部署图如图 2 -1 所示 。

Broker 消息服务器在启动时向所有 NameServer 注册,消息生产者(Producer)在发送消息之前先从 NameServer 获取 Broker 服务器地址列表,然后根据负载算法从列表中选择一台消息服务器进行消息发送 。 NameServer 与每台 Broker 服务器保持长连接,并间隔 30s 检测 Broker 是否存活,如果检测到 Broker 宕机 , 则从路由注册表中将其移除 。 但是路由变化不会马上通知消息生产者,为什么要这样设计呢?这是为了降低 NameServer 实现的复杂性 ,在消息发送端提供容错机制来保证消息发送的高可用性。
NameServer 本身的高可用可通过部署多台 Nameserver 服务器来实现,但彼此之间互不通信,也就是 NameServer 服务器之间在某一时刻的数据并不会完全相同,但这对消息发送不会造成任何影响,这也是 RocketMQ NameServer 设计的 一个亮点, RocketMQ NameServer 设计追求简单高效 。
 

2 NameServer 启动流程

NameServer 启动类 : org .apache.rocketrmq.namesrv.NamesrvStartup 。

Step1: 首先来解析配置文件 , 需要填充 NameServerConfig 、 NettyServerConfig 属性值 。

Step2 :根据启动属性创建 NamesrvController 实例,并初始化该实例 ,NameServerController实例为 NameServer核心控制器。
加载 KV 配置,创建 NettyServer 网络处理对象,然后开启两个定时任务,在RocketMQ中此类定时任务统称为心跳检测 。
定时任务 1: NameServer 每隔 10s 扫描一次 Broker , 移除处于不激活状态的Broker
定时任务 2: nameserver 每隔 10 分钟打印一次 KV 配置 。
Step3 :注册 JVM 钩子函数并启动服务器, 以便监昕 Broker 、消息生产者 的网络请求 。

代码清单 2-5 注册 JVM 钩子函数代码
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new
Callable<Void>() {
public Void call() throws Exception {
controller. shutdown ( ) ;
return null;
}));
controller.start();

这里主要是向读者展示一种常用的编程技巧,如果代码中使用了线程池,一种优雅停机的方式就是注册一个 JVM 钩子函数, 在 JVM 进程关闭之前,先将线程池关闭 ,及时释放资源 。
 

3 NameServer 路由注册、故障剔除
NameServer 主要作用是为消息生产者 和 消息消费者提供关于主题 Topic 的路由信息,
那么 NameServer 需要存储路由 的基础信息,还要能够管理 Broker 节点,包括路由 注册 、
路由删除等功能 。
3.1 路由元信息
NameServer 路 由实 现类 : org.apache.rocketmq.namesrv.routeinfo.RoutelnfoManager , 在了解路由注册之前,我们首先看一下 NameServer 到底存储哪些信息 。
topicQueueTable: Topic 消息队列路由信息,消息发送时根据路由表进行负载均衡 。
brokerAddrTable : Broker 基础信息, 包含 brokerName 、 所属集群名称 、 主备 Broker地址 。
clusterAddrTable: Broker 集群信息,存储集群中所有 Broker 名称 。
brokerLiveTable: Broker 状态信息 。 NameServer 每次收到心跳包时会替换该信息 。
filterServerTable : Broker 上的 FilterServer 列表,用于类模式消息过滤,
RocketMQ 基于订阅发布机制 , 一个 Topic 拥有 多个消息队 列 ,一个 Broker 为每一主题默认创建 4 个读队列 4 个写队列 。 多个 Broker 组成 一个集群 , BrokerName 由相同的多台Broker组 成 Master-Slave 架构 , brokerId 为 0 代表 Master , 大于 0 表示 Slave 。 BrokerLivelnfo中 的lastUpdateTimestamp 存储上次收到 Broker 心跳包的时间 。

3.2 路由注册
RocketMQ 路由注册是通过 Broker 与 NameServer 的心跳功能 实现的 。 Broker 启动时向集群中 所有的 NameServer 发送 心跳语句,每隔 30s 向 集群 中所有 NameServer 发送心跳包, NameServer 收到 Broker 心 跳包时会更新 brokerLiveTable 缓存中 BrokerLivelnfo 的lastUpdateTimestamp ,然后 NameServer 每隔 10s 扫描 brokerLiveTable ,如果连续120s 没有收到心跳包, NameServer 将移除该 Broker 的路由 信息 同时关闭 Socket 连接 。

1. Broker 发送心跳包

this.scheduledExecutorService . scheduleAtFixedRate(new Runnable() {
public void run ( ) {
try {
BrokerController.this.registerBrokerAll(true, false);
} catch (Throwable e) {
log.error ( ” registerBrokerAll Exception”, e);
}
}, 1000 * 1 0, 1000 * 30, TimeUnit . MILLISECONDS) ;

2. NameServer 处理心跳包
org.apache . rocketmq.namesrv . processor.DefaultRequestProcessor 网络处理器解析请求类型, 如果请求类型为 RequestCode . REGISTER_BROKER ,则请求最终转发到RoutelnfoManager#registerBroker 。
Step1:路由注册需要加写锁 ,防止并发修改 RoutelnfoManager 中的路由 表 。 首先判断
Broker 所属 集群是否存在, 如果不存在,则创建,然后将 broker 名加入到集群 Broker 集
合中 。
Step2 :维护 BrokerData 信 息 ,首 先从 brokerAddrTable 根据 BrokerName 尝试获取
Broker 信息 ,如 果 不 存在, 则 新建 BrokerData 并放入 到 brokerAddrTable , registerFirst 设置为 true ;如果存在 , 直接替换原先的, registerFirst 设置为 false,表示非第一次注册 。

Step3 :如果 Broker 为 Master ,并且 Broker Topic 配置信息发生变化或者是初 次注册 ,
则需要创建或更新 Topic 路 由元数据,填充 topicQueueTable , 其实就是为默认主题自动注
册路 由 信息,其 中包含 MixAII.DEFAULT TOPIC 的路由信息 。 当消息生产者发送主题时 ,
如 果该主题未创 建并且 BrokerConfig 的 autoCreateTopicEnable 为 true 时, 将返回 MixAII.
DEFAULT TOPIC 的路由信息 。

Step4 : 更新 BrokerLivelnfo ,存活 Broker 信息表, BrokeLivelnfo 是执行路由 删除的重要依据 。
Step5 : 注册 Broker 的过滤器 Server 地址列表 ,一个 Broker 上会关联多个 FilterServer
消息过滤服务器;如果此 Broker 为从节点,则需要查找该 Broker 的 Master 的节点信息,并更新对应的 masterAddr 属性 。
设计亮点: Name Serve 与 Broker 保持长连接, Broker 状态存储在 brokerLiveTable 中,
NameS erver 每收到一个心跳包,将更新 brokerL iveTa ble 中关于 Broker 的状态信息以及路
由表( topicQueueTable 、 brokerAddrTable 、 brokerLiveTable 、 filterServerTable ) 。 更新上述路由表(HashTable )使用了锁粒度较少的读写锁,允许多个消息发送者(Producer)并发读,保证消息发送时的高并发 。 但同一时刻 NameServer 只处理一个 Broker心跳包,多个心跳包请求串行执行 。
3.3 路由删除
根据上面章节的介绍, Broker 每隔 3 0s 向 NameServer 发送一个心跳包,心跳包中包含BrokerId 、 Broker 地址 、 Broker 名称、 Broker 所属集群名称 、 Broker 关联 的 FilterServer 列表 。但是如果 Broker 宕机 , NameServer 无法收到心跳包,此时 NameServer 如何来剔除这些失效的 Broker 呢 ? NameServer 会每隔 10s 扫描 brokerLiveTable 状态表,如果 BrokerLive 的lastUpdateTimestamp 的时间戳距当前时间超过 120s ,则认为 Broker 失效,移除该 Broker,关 闭 与 Broker 连 接,并同 时 更新 topicQueueTable 、 brokerAddrTable 、brokerLiveTable 、filterServerTable 。
RocktMQ 有两个触发点来触发路由删除 。
1 ) NameServer 定 时扫描brokerLiveTable 检测上次心跳包与 当前系统时间的时间差,如果时间戳大于 120s ,则需要移除该 Broker 信息 。
2 ) Broker 在正常被关闭的情况下,会执行 unregisterBroker 指令。
由于不管是何种方式触发的路由删除,路由删除的方法都是一样的,就是从 topicQueueTable 、 brokerAddrTable 、 brokerLiveTable 、 filterServerTable 删除与 该 Broker 相 关的信息,但 RocketMQ 这两种方式维护路由信息时会抽取公共代码
Step1:申 请写锁,根据 brokerAddress 从 brokerLiveTable 、 filterserverTable 移除

Step2 :维护 brokerAddrTable 。 

Step3 : 根据 BrokerName ,从 clusterAddrTable 中找到 Broker 并从集群中移除 。如果移除后,集群中不包含任何 Broker ,则将该集群从 clusterAddrTable 中移除 。
Step4 : 根据 brokerName ,遍历所有主题的队列,如果队列中包含了当前 Broker 的队列, 则移除,如果 topic 只包含待移除 Broker 的队列的话,从路由表中删除该 topic ,
Step5 :释放锁,完成路由删除 。
 

3.4 路由发现
RocketMQ 路由发现是非实时的,当 Topic 路由出现变化后, NameServer 不主动推送给客户端 , 而是由客户端定时拉取主题最新的路由 。
Step1:调用 RouterlnfoManager 的方法,从路由 表 topicQueueTable 、 brokerAddrTable 、filterServerTable 中分别填充 TopicRouteData 中的 List<Queu 巳Data>、 List<BrokerData>和filterServer 地址表 。
Step2 : 如果找到主题对应的路由信息并且该主题为顺序消息,则从 NameServer KVconfig 中获取关于顺序消息相关的配置填充路由信息 。如果找不到路由信息 CODE 则使用 TOPIC NOT_EXISTS ,表示没有找到对应的路由 。
 

4 本章小结
本章主要介绍了 NameServer 路由功能,包含路由元数据、路由注册与发现机制 。

文章参考:《RocketMQ技术内幕》,作者:丁威,周继锋。

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值