RocketMQ NameServer源码剖析

RocketMQ的NameServer是一个Topic路由注册中心,负责Broker管理和路由信息管理。它接受Broker注册,存储路由信息,并通过心跳检测检查Broker存活。Producer和Consumer通过NameServer获取Broker路由进行消息交互。NameServer集群中的每个节点独立维护路由信息,采用轮询方式选择NameServer,以减轻压力。NameServer在启动时加载配置,与Broker建立长连接并进行心跳交互,以实现路由注册和剔除。NameServer不追求强一致性,降低了系统的复杂性和依赖。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

NameServer是一个简单的 Topic 路由注册中心,支持 Topic、Broker 的动态注册与发现。

主要包括两个功能:

  • Broker管理 ,NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活;
  • 路由信息管理 ,每个NameServer将保存关于 Broker 集群的整个路由信息和用于客户端查询的队列信息。Producer和Consumer通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费。

Broker集群、Producer集群、Consumer集群都需要与NameServer集群进行通信。

1、Producer集群

Producer集群和NameServer集群中的随机一台建立⻓连接,得知当前要发送的 Topic 存在哪台BrokerMaster上,然后再与其建立⻓连接,支持多种负载平衡模式发送消息。

注意:消息的生产者,通过NameServer集群获得Topic的路由信息,包括Topic下面有哪些Queue,这些Queue分布在哪些Broker上等。Producer只会将消息发送到Master节点上,因此只需要与Master节点建立连接。

2、Consumer集群

它会先和 NameServer 集群中的随机一台建立⻓连接,得 知当前要消费的 Topic 存在哪台 BrokerMaster、Slave上,然后它们建立⻓连接,支持集群消费和广播消费消息。

消息的消费者,通过NameServer集群获得Topic的路由信息,连接到对应的Broker上消费消息。注意,由于Master和Slave都可以读取消息,因此Consumer会与Master和Slave都建立连接。

3、Broker集群

Broker作用:

  1. 接收生产者发送消息,或者消费者消费消息的请求。
  2. 主要负责消息的存储、转发、查询,支持主从部署,一个 Master 可以对应多个 Slave,Master 支持读写,Slave 只支持读。

一个Broker集群由多组Master/Slave组成,Master可写可读,Slave只可以读,Master将写入的数据同步给Slave。

Broker 会向集群中的每一台 NameServer 注册自己的路由信息。每个Broker节点,在启动时,都会遍历NameServer列表,与每个NameServer建立长连接,注册自己的信息,之后定时上报。

4、NameServer集群

Topic 路由注册中心,支持 Broker 的动态注册和发现,保存 Topic 和 Borker之间的关系。

通常是集群部署,但是各 NameServer 之间不会互相通信, 各 NameServer 都有完整的 路由信息

总结:NameServer 相当于一个NettyServer,处理客户端的请求。RocketMQ学习源码之前最好了解一下Netty和NIO通信的基本流程。

NameServer主要功能

1、代码模块

主要有3个模块, kvconfig 数据存储,processor 通信处理模块, routeinfo 路由模块

  • kvconfig 模块 核心类KVConfigManager作用是,加载namesrvController指定的kvConfig配置文件(常xxx/kvConfig.json)到内存,读取或增加,删除kvConfig记录,将内存记录的配置,持久化到文件打印所有kvConfig配置。

  • processor 模块 DefaultRequestProcessor 处理client发过来的各种请求,比如kv 的crud,注册元数据,读取元数据

  • routeinfo 路由模块 RouteInfoManager 存储一些核心元数据

  1. brokerAddrTable 保存 Topic 的队列信息,也是真正的路由信息。队列信息中包含了其所在的 Broker 名称和读写队列数量。
  2. topicQueueTable 保存 Broker 信息,包含其名称、集群名称、主备 Broker 地址。
  3. clusterAddrTable 保存 Cluster信息,包含每个集群中所有的 Broker 名称列表
  4. brokerLiveTable 活跃Broker映射表,Broker 状态信息,包含当前所有存活的 Broker,和它们最后一次上报心跳的时间。
  5. filterServerTable Broker 上的 FilterServer 列表,用于类模式消息过滤,该机制在 4.4 版本后被废弃。

上面的这些数据都是存储为HashMap保存的内存中,不会做持久化操作。

2、NameServer启动流程

NamesrvStartup 启动类, 逻辑并不复杂,主要是下面两个主要功能。

(1)解析配置文件

填充NameServerConfig、NettyServerConfig属性值。包括:端口号,目录路径,netty线程池个数,消息请求并发度等。

(2) 根据启动属性,创建NamesrvController实例,并初始化该实例。NameServerController为nameServer的核心控制器。

NameServerController里面有一个nettyServer和两个定时任务。

  • nettyServer负责与broker、生产者、消费者进行网络通信
  • 定时任务1:每隔10s扫描一次broker,移除死亡状态broker。
  • 定时任务2:每隔10分钟打印一次KV配置。

3、路由注册

RocketMQ采取的策略是,在Broker节点在启动的时候,轮询NameServer列表,

(1)Broker与每个NameServer节点建立长连接,发起注册请求。

(2)Broker节点为了证明自己是存活的,会将最新的信息上报给NameServer,然后每隔30秒向NameServer发送心跳包。

心跳包中包含 BrokerId、Broker名称、Broker地址、Broker所属集群名称等等。

NameServer独立管理自己的Broker元数据副本:

(1)每个NameServer内部会维护一个Broker表,用来动态存储Broker的信息。

(2)然后NameServer接收到心跳包后,会更新时间戳,记录这个Broker的最新存活时间

路由注册的两个场景:

  • 启动注册:Broker与每个NameServer节点建立长连接,发起注册请求
  • 定时心跳:每隔30秒向NameServer发送心跳包

设计的亮点:

NameServer的性能优化:

为了提升并发修改Broker表的性能,路由注册操作引入了 ReadWriteLock 读写锁,这个设计亮点允许多个消息生产者并发读,保证了消息发送时的高并发,同一时刻NameServer只能处理一个Broker心跳包,多个心跳包串行处理。

这也是读写锁的经典使用场景,即读多写少。

4、路由剔除

NameServer路由剔除的两个场景:

  • 主动剔除场景:Broker在正常关闭的情况下,会执行unregisterBroker指令
  • 被动剔除场景:NameServer定期扫描brokerLiveTable检测上次心跳包与当前系统的时间差,如果时间超过120s,则需要移除broker。

3.1 主动剔除的场景

nameServer与broker保持长连接。

正常情况下,如果Broker关闭,则会与NameServer断开长连接,Netty的通道关闭监听器会监听到连接断开事件,然后会将这个Broker信息剔除掉。

对于一些日常运维工作,例如:Broker升级,RocketMQ提供了一种优雅剔除路由信息的方式。

如在升级一个节Master点之前,可以先通过命令行工具,禁止这个Broker的写权限:

客户端producer 发送消息到这个Broker的请求,都会收到一个NO_PERMISSION响应,客户端会自动重试其他的Broker。

当观察到这个broker没有流量后,再将这个broker移除。

3.2 被动剔除的场景

rocketMq的路由注册是通过broker与NameServer的心跳功能实现的。

broker启动的时候,向集群中的所有nameServer发送心跳,每间隔30秒,向所有的NameServer发送心跳。

nameServer收到心跳包时会更新brokerLiveTable缓存中的lastUpdateTimestamp。然后nameServer每10秒扫描一次brokerLiveTable,如果连续120s没有收到心跳包,判死刑,nameServer将移除该Broker的路由信息,同时关闭nettysocket连接。

被动剔除下,NameServer中有一个定时任务, 每隔 10秒扫描一下Broker表,如果某个Broker的心跳包最新时间戳距离当前时间超多120秒,也会判定Broker失效并将其移除。

5、路由发现

路由发现是客户端的行为,这里的客户端主要说的是生产者和消费者。

主动刷新场景:

(1)对于生产者,在发送第一条消息时,才能明确topic,所以会根据Topic获取从NameServer获取路由信息。

(2)对于消费者,订阅的Topic一般是固定的,所在在启动时就会拉取。

被动刷新的场景:

定时刷新机制

6、客户端NameServer选择策略

RocketMQ会将用户设置的NameServer列表会设置到 NettyRemotingClient类的namesrvAddrList字段中,NettyRemotingClient是RocketMQ对Netty进行了封装,如下:

具体选择哪个NameServer,也是使用round-robin的策略。需要注意的是,尽管使用round-robin策略,但是在选择了一个NameServer节点之后,后面总是会优先选择这个NameServer,除非与这个NameServer节点通信出现异常的情况下,才会选择其他节点。

为什么客户端不与所有NameServer节点建立连接呢,而是只选择其中一个?

通常NameServer节点是配置好的,固定的那么几个,但是客户端的数量可能是成百上千,为了减少每个NameServer节点的压力,所以每个客户端节点开始的时候,随机找一个nameserver建立连接,后面round-robin与其中一个NameServer节点建立连接。

为了尽可能保证NameServer集群每个节点的负载能相对均衡,在round-robin策略选择时,每个客户端的初始随机位置都不同。

NettyRemotingClient#namesrvIndex

private final AtomicInteger namesrvIndex = new AtomicInteger(initValueIndex()); 
private static int initValueIndex() {
    Random r = new Random();
    return Math.abs(r.nextInt() % 999) % 999;
}
复制代码

之后每次选择NameServer时,namesrvIndex+1之后,对namesrvAddrList取模,计算在数据下标的位置,尝试创建连接,一旦创建成功,会将当前选择的NameServer地址记录到namesrvAddrChoosed字段中:

private final AtomicReference<String> namesrvAddrChoosed = new AtomicReference<String>();
复制代码

如果某个NameServer节点创建连接失败是,会自动重试其他节点。

NettyRemotingClient#getAndCreateNameserverChannel

7、NameServer的CAP特征:是AP不是CP?

那么为什么rocketmq选择自己开发一个NameServer,而不是使用这些开源组件呢?

事实上,在RocketMQ的早期版本,即MetaQ 1.x和MetaQ 2.x阶段,也是依赖Zookeeper的。但MetaQ 3.x(即RocketMQ)却去掉了ZooKeeper依赖,转而采用自己的NameServer。

而RocketMQ的架构设计决定了:

nameserver只需要一个轻量级的元数据服务器就足够了nameserver只需要保持最终一致,而不需要Zookeeper这样的强一致性解决方案,不需要再依赖另一个中间件,从而减少整体维护成本。

对于Zookeeper、Etcd这样强一致性组件,数据只要写到主节点,内部会通过状态机将数据复制到其他节点,

Zookeeper使用的是Zab协议,etcd使用的是raft协议。

但是:NameServer节点之间是互不通信的,无法进行数据复制。

RocketMQ NameServerRocketMQ 的一个核心组件,主要负责管理 RocketMQ 集群中的各个 Broker 节点的信息,包括 Broker 的名称、IP 地址、状态等信息。在 RocketMQ 集群中,所有的 Broker 都需要向 NameServer 注册,以便 NameServer 能够掌握整个集群的状态信息。 RocketMQ NameServer源码位于 `rocketmq-namesrv` 模块中,其主要实现了以下功能: 1. 启动时加载配置文件,包括监听端口、存储路径、集群名称等信息; 2. 处理 Broker 节点的注册、注销请求,维护 Broker 节点的状态信息; 3. 处理 Consumer 节点的心跳请求,维护 Consumer 节点的状态信息; 4. 处理 Topic 的创建、删除请求,维护 Topic 的状态信息; 5. 提供查询 Broker 节点、Topic 等信息的接口。 RocketMQ NameServer 的核心类是 `NamesrvController`,它继承了 Netty 的 `NettyRemotingServer` 类,并实现了 `RequestProcessor` 接口,用于处理来自 Broker 和 Consumer 节点的请求。在 `NamesrvController` 中,还包含了 `RouteInfoManager`、`BrokerHousekeepingService`、`KVConfigManager` 等组件,用于维护集群状态信息和管理配置文件。 RocketMQ NameServer 的启动入口是 `main` 方法,它会加载配置文件并启动 `NamesrvController`。启动后,NameServer 会监听指定端口,等待来自 Broker 和 Consumer 节点的请求,并根据请求类型调用相应的处理方法进行处理。 总之,RocketMQ NameServer 的主要作用是管理整个 RocketMQ 集群的状态信息,确保集群中各个节点的状态始终保持同步。其源码实现比较复杂,需要深入理解 RocketMQ 的设计思想和架构原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值