NameServer 的功能
NameServer是整个消息队列中的状态服务器,集群的各个组件通过它来了解全局的信息。同时,各个角色的机器都要定期向NameServer上报自己的状态,超时不上报的话,NameServer会认为某个机器出故障不可用了,其他的组件会把这个机器从可用列表里移除。
NamServer可以部署多个,相互之间独立,其他角色同时向多个NameServer机器上报状态信息,从而达到热备份的目的。NameServer本身是无状态的,也就是说NameServer中的Broker、Topic等状态信息不会持久存储,都是由各个角色定时上报并存储到内存中的(NameServer支持配置参数的持久化,一般用不到)。
NameServer 的存储结构
NameServer 保存了五个变量。
- topicQueueTable:topic 消息队列路由信息。每一个主题对应一个
QueueData
,其中的属性包括:brokerName
,readQueueNums
,writeQueueNums
,perm
,topicSynFlag
- brokerAddrTable:broker 基础信息,包含 brokerName、所属集群名称、主备 Broker 地址
- clusterAddrTable:broker 集群信息,存储集群中所有 broker 名称
- brokerLiveTable:broker 状态信息。NameServer 每次收到心跳包时会替换该信息
- filterServerTable:过滤服务器列表
RocketMQ 基于订阅发布机制, 一个 Topic 拥有多个消息队列 ,一个Broker 为每一主题默 认创建 4 个读队列 4 个写队列 。 多个 Broker 组成 一个集群 , BrokerName 由相同的多台 Broker 组 成 Master-Slave 架构 , brokerId 为 0 代表 Master, 大于 0 表示 Slave。 BrokerLivelnfo 中 的 lastUpdateTimestamp 存储上次收到 Broker 心跳包的时间 。
路由注册
RocketMQ路由注册是通过 Broker与 NameServer的心跳功能实现的。Broker启动时向集群中 所有的 NameServer 发送心跳语句,每隔 30s 向 集群中所有 NameServer 发送心跳包,NameServer收到Broker心跳包时会更新brokerLiveTable缓存中BrokerLivelnfo的 lastUpdateTimestamp,然后 NameServer每隔 10s 扫描 brokerLiveTable,如果连续120s没有收到心跳包, NameServer将移除该 Broker的路由信息同时关闭 Socket连接。
RocketMQ 的网络传输基于 Netty,每一个请求,RocketMQ 都会定义一个 RequestCode,然后在服务端会有对应的 Processor。
1.发送心跳包
broker 发送的心跳包有以下内容:
- brokerAddr:broker 地址
- brokerId:0 表示 master,大于 0 表示 slave
- brokerName:broker 名称
- clusterName:集群名称
- haServerAddr:master 地址,除此请求时该值为空,slave 向 NameServer 注册后返回。
- requestBody
- filterServerList。消息过滤服务器列表
- topicConfigWrapper。主题配置,
topicConfigWrapper
内部封装的是TopicConfigManager
中的topicConfigTable
,内部存储的是 Broker启动时默认的一些Topic,MixAll.SELF TEST_TOPIC
、MixAll.DEFAULT_TOPIC ( AutoCreateTopic- Enable=true )
,MixAll.BENCHMARK_TOPIC
、MixAll.OFFSET_MOVED_EVENT
、BrokerConfig#brokerClusterName
、BrokerConfig#brokerName
。
该配置文件默认存储在:${Rocket_Home}/store/confg/topic.json中。
2.接收心跳包
NameServer 会解析请求类型为RequestCode.REGISTER_BROKER
类型的请求,并交由RouteInfoManager
处理。
首先判断集群是否存在,然后将 broker 加入到集群集合中。然后更新维护BrokerData 信息,直接替换原来的。如果 Broker 为 master,并且 Broker Topic 配置信息发生变化或者是除此注册,则需要创建或更新 Topic 路由元数据,填充 topicQueueTable。在初次发送心跳包时,就是为默认主题注册路由信息,包含MixAll.DEFAULT_TOPIC
的路由信息。当消息生产者发送主题时,如果该主题未创建并且BrokerConfig
的autoCreateTopicEnable
为 true 时,将返回它的路由信息。
路由注册时需要加写锁,防止并发修改
RouteInfoManager
中的路由表。更新路由表使用了锁粒度较小的读写锁,允许多个消息发送者并发读,保证消息发送时的高并发。但同一时刻 NameServer 只处理一个 Broker 心跳包,多个心跳包请求串行执行。
路由删除
RocketMQ 有两个触发点来触发路由删除。
1.NameServer 定时扫描brokerLiveTable
检测上次心跳包与当前系统时间的时间差,如果时间大于 120s,则需要移除该 Broker 信息
2.Broker 在正常被关闭的情况下,执行 unregisterBroker 指令
路由发现
客户端定时拉取主题最新的路由。发现过程:
1.调用 RouterlnfoManager
的方法,从路由表 topicQueueTable
、brokerAddrTable
、 filterServerTable
中分别填充TopicRouteData中的List<QueueData>
、List<BrokerData>
和 filterServer
地址表 。
2.如果找到主题对应的路由信息并且该主题为顺序消息,则从 NameServer KVconfig 中获取关于顺序消息相关 的配置填充路由信息 。
Q & A
RocketMQ 的路由中心存储的是什么数据
见 NameServer 存储结构。
如何避免 NameServer 的单点故障,提供高可用性
启动多个 NameServer,让每个 broker 向每个 NameServer 发送心跳包。
为何不使用 ZooKeeper
ZooKeeper的功能很强大,包括自动Master选举等,RocketMQ的架构设计决定了它不需要进行Master选举,用不到这些复杂的功能,只需要一个轻量级的元数据服务器就足够了。
中间件对稳定性要求很高,RocketMQ的NameServer只有很少的代码,容易维护,所以不需要再依赖另一个中间件,从而减少整体维护成本。
u
为什么 NameServer 的路由变化不会立即通知生产者
为了降低 NameServer 实现的复杂性,在消息发送端提供容错机制来保证消息发送的高可用性。(故障延迟机制)