Namesrv 之于 RocketMQ,即 Zookeeper 之于 Kafka,即服务注册中心之于微服务。Namesrv在RocketMQ体系中是一个Topic路由注册和管理、Broker注册和发现的管理者。
本章主要介绍了:
● Topic路由数据结构。
● Broker注册、发现及剔除机制。
● Namesrv启动、停止及执行过程。
5.1 Namesrv概述
Namesrv在RocketMQ体系中主要用于保存元数据、提高Broker的可用性。
在 RPC 通信中,我们通常将服务提供者称为服务端,使用服务的端称为客户端。如果服务端有扩容或缩容,客户端如何感知呢?业内常用的做法是,服务注册与发现。通过注册,可以添加更多提供服务的服务端实例,当然有实例宕机,也可以通过摘除来保证服务的可靠性。
Broker作为RocketMQ服务的提供者,其工作原理也是一样的。
5.1.1 什么是Namesrv
在RocketMQ中,如果有生产者、消费者加入或者掉线,Broker扩容或者掉线等各种异常场景,RocketMQ集群如何保证高可用呢?一个管理者或者协调者的角色应运而生。
Namesrv是专门针对RocketMQ开发的轻量级协调者,多个Namesrv节点可以组成一个Namesrv集群,帮助RocketMQ集群达到高可用。
Namesrv的主要功能是临时保存、管理Topic路由信息,各个Namesrv节点是无状态的,即每两个Namesrv节点之间不通信,互相不知道彼此的存在。在Broker、生产者、消费者启动时,轮询全部配置的 Namesrv 节 点 , 拉 取 路 由 信 息 。 具 体 路 由 信 息 详 见
org.apache.rocketmq.namesrv.routeinfo下各个HashMap保存的数据。
5.1.2 Namesrv核心数据结构和API
Namesrv中保存的数据被称为Topic路由信息,Topic路由决定了Topic消息发送到哪些Broker,消费者从哪些Broker消费消息。 那么路由信息都包含哪些数据呢?路由数据结构的实现代码都在
org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager类中,该类包含的数据结构如下:
![](https://i-blog.csdnimg.cn/blog_migrate/785d0ec48abb8b328c6ed0b0934a1125.png)
BROKER_CHANNEL_EXPIRED_TIME:
Broker存活的时间周期,默认为120s。
topicQueueTable:
保存Topic和队列的信息,也叫真正的路由信息。一个Topic全部的Queue可能分布在不同的Broker中,也可能分布在同一个Broker中。
brokerAddrTable:
存储了Broker 名字和Broker信息的对应信息。
clusterAddrTable:
集群和Broker的对应关系。
brokerLiveTable:
当前在线的Broker地址和Broker信息的对应关系。filterServerTable:
过滤服务器信息。
Namesrv 支持的全部API 在 org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor 类中,部分代码如下:
以上代码中部分核心API说明如下:
RequestCode.REGISTER_BROKER:Broker注册自身信息到Namesrv。
RequestCode.UNREGISTER_BROKER:Broker取消注册自身信息到 Namesrv。
RequestCode.GET_ROUTEINTO_BY_TOPIC:获取Topic路由信息。
RequestCode.WIPE_WRITE_PERM_OF_BROKER : 删 除 Broker 的 写 权 限。
RequestCode.GET_ALL_TOPIC_LIST_FROM_NAMESERVER : 获 取 全 部Topic名字。
RequestCode.DELETE_TOPIC_IN_NAMESRV:删除Topic信息。
RequestCode.UPDATE_NAMESRV_CONFIG:更新Namesrv配置,当前配置是实时生效的。
RequestCode.GET_NAMESRV_CONFIG:获取Namesrv配置。
5.1.3 Namesrv和Zookeeper
曾几何时,RocketMQ 也采用 Zookeeper 作为协调者,但是繁杂的 运行机制和过多的依赖导致 RocketMQ 最终完全重新开发了一个零依赖、更简洁的 Namesrv 来替换Zookeeper。事实证明逻辑足够简单、使 用足够方便的 Namesrv 不负众望,也勇敢地承担起了这个责任。
下面将Namesrv和Zookeeper从功能和设计上做一个简单的比较,如表5-1所示。
5.3 RocketMQ的路由原理
如图5-1所示,生产者发送消息、消费者消费消息时都需要从 Namesrv拉取Topic路由信息,那么这些路由信息是如何注册到 Namesrv 的呢?如果 Broker 异常宕机,路由信息又是如何更新的呢?
下面,我们通过路由注册和路由剔除两个方面进行详细讲解。
5.3.1 路由注册
Namesrv获取的 Topic路由信息来自 Broker定时心跳,心跳时Brocker 将 Topic 信 息 和 其 他 信 息 发 送 到 Namesrv 。 Namesrv通过 RequestCode.REGISTER_BROKER接口将心跳中的Broker信息和Topic信息存储在Namesrv中。
Namesrv接收请求后,以3.0.11版本作为分水岭,按照版本分别做不同的处理,相关代码如下:
因 为 当 前 RocketMQ 的 版 本 为 4.2.0 版 本 , 所 以 会 执 行 registerBrokerWithFilterServer (ctx,request)方法。我们主要看 这 个 方 法 中 的
this.namesrvController.getRouteInfoManager.registerBroker ( )方法,该方法的主要功能是将request解码为路由对象,保存在Namesrv 内存中,具体保存的数据结构参考 5.1.2 节中的核心数据结构。由于 该方法比较简单,所以不对代码进行分析。
在路由信息注册完成后,Broker 会每隔 30s 发送一个注册请求给集群中全部的Namesrv,俗称心跳信,会把最新的 Topic 路由信息注册到 Namesrv 中。具体的心跳过程可以参考6.1.3节的Broker启动过程。
5.3.2 路由剔除
如果Broker长久没有心跳或者宕机,那么Namesrv会将这些不提供服务的Broker剔除。同时生产者和消费者在与 Namesrv 心跳后也会感知被踢掉的 Broker,如此 Broker 扩容或者宕机对生产、消费无感知的情况就处理完了。
Namesrv有两种剔除Broker的方式:
第一种:Broker 主动关闭时,会调用 Namesrv 的取消注册 Broker 的接口RequestCode=RequestCode.UNREGISTER_BROKER,将自身从集群中删除。这个过程和5.3.1节中路由注册的过程相反。
第二种:Namesrv 通过定时扫描已经下线的 Broker,将其主动剔除 , 实 现 过 程 在
org.apache.rocketmq.namesrv.NamesrvController.initialize()方法中,具体代码如下:
该方法会扫描全部已经注册的 Broker,依次将每一个 Broker 心跳的最后更新时间和当前时间做对比,如果 Broker 心跳的最后更新时间超过 BROKER_CHANNEL_EXPIRED_TIME (1000×60×2=120s),则将Broker剔除。从此没有心跳的Broker从路由中被剔除,而客户端无任何感知。