从源码角度窥探RocketMQ之NameServer原理

NameServer架构设计

NameServer可以说是RocketMQ的“大脑”。她负责路由管理、服务注册及服务发现。客户端想要往服务端发送消息或者从服务端订阅消息时,必须要知道服务端的路由信息。在服务端集群部署下,更加尤为重要。为了解决这个问题,必须引入类似zookeeper这类分布式协调中心。那么业界已有ZK这个神器,为何RocketMQ要架设NameServer,不用ZK呢?
原因很简单,ZK具有强一致性,在ZK、消息服务端都在大规模集群环境下,网络通信将变得异常庞大复杂,尤其是ZK。所以RocketMQ架设了NameServer。NameServer在设计就定位简单,确实简单得不得了,总共才8个类。但是,简单关简单,她却很强大!
接下来,我们先来看看RocketMQ的逻辑部署图,这张图将贯穿整个RocketMQ专栏。
RocketMQ部署图
Broker消息服务器在启动时向所有NameServer注册,消息生产者在发送消息之前先从NameServer获取Broker服务器地址列表,然后根据负载算法从列表中选择一台消息服务器进行消息发送。NameServer与每台Broker服务器保持长链接,并间隔30s检测Broker是否存活,如果检测到Broker宕机,则从路由注册列表中将其移除。但是路由变化不会马上通知消息生产者。
NameServer本身的高可用可通过集群部署来实现,但是彼此之间互不通信,也就是说,存在某一个时刻,NameServer服务器之间的数据并不会完全相同。但这对消息发送不会造成任何影响。
不知道读者是否也有这样的疑问?路由变化不是实时通知,NameServer服务器之间数据不同步,那么生产者拿到宕机的路由信息是极有可能的,消息发送的高可用何以体现?
原因我们不着急分析,留待下文【消息发送】逐一揭开。在这里我们要转换思考点,尝试去理解为什么要这样设计?因为要降低NameServer实现的复杂度,同时这也是NameServer的一个设计亮点。
总结:

  • NameServer通过心跳检测到Broker不可用时,并不会马上通知客户端
  • NameServer集群中各节点互不通信

NameServer大道至简到哪怕所有NameServer所有节点宕机,依旧能在一定条件下保证RocketMQ稳定运行。这里的一定条件可以是NameServer所有节点宕机期间内,Broker集群所有节点没有全宕机。不得不钦佩设计者们!

NameServer启动流程

从这一节开始,开始进入源码分析。启动NameServer,需要关注NameServer相关启动参数。
我们看一下启动类org.apache.rocketmq.namesrv.NamesrvStartup,会创建一个NameServer核心控制器–NamesrvController 。

public static void main(String[] args) {
   
        main0(args);
    }

    public static NamesrvController main0(String[] args) {
   

        try {
   
            NamesrvController controller = createNamesrvController(args);
            start(controller);
            String tip = "The Name Server boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer();
            log.info(tip);
            System.out.printf("%s%n", tip);
            return controller;
        } catch (Throwable e) {
   
            e.printStackTrace();
            System.exit(-1);
        }

        return null;
    }

创建核心控制器时,需要两个配置对象NamesrvConfig 、NettyServerConfig 。可以发现底层网络通信是基于Netty实现的。两个配置对象是在NameServer启动时把指定的配置文件或启动命令中的选项值解析出来,并填充到各自对象内。

public static NamesrvController createNamesrvController(String[] args) throws IOException, JoranException {
   
   
        final NamesrvConfig namesrvConfig = new NamesrvConfig();
        final NettyServerConfig nettyServerConfig = new NettyServerConfig();
        nettyServerConfig.setListenPort(9876);
        if (commandLine.hasOption('c')) {
   
            // 指定的配置文件
            String file = commandLine.getOptionValue('c');
            if (file != null) {
   
                InputStream in = new BufferedInputStream(new FileInputStream(file));
                properties = new Properties();
                properties.load(in);
                MixAll.properties2Object(properties, namesrvConfig);
                MixAll.properties2Object(properties, nettyServerConfig);

                namesrvConfig.setConfigStorePath(file);

                System.out.printf("load config properties file OK, %s%n", file);
                in.close();
            }
        }
		// 启动命令的参数选项
        if (commandLine.hasOption('p')) {
   
            InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_CONSOLE_NAME);
            MixAll.printObjectProperties(console, namesrvConfig);
            MixAll.printObjectProperties(console, nettyServerConfig);
            System.exit(0);
        }

        MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), namesrvConfig);
    }

参数来源具体是怎样的呢?

  • -c configFile 通过-c命令指定配置文件的路径。
  • 使用 “–属性名 属性值” 例如 --listenPort 9876

我们来具体看看这两个对象的参数属性,了解一下即可。
NamesrvConfig:

   /**
     * rocketmq 主目录 可以通过-Drocketmq.home.dir=path或通过设置环境变量ROCKETMQ_HOME来配置ROCKETMQ的主目录
     */
    private String rocketmqHome = System.getProperty(MixAll.ROCKETMQ_HOME_PROPERTY, System.getenv(MixAll.ROCKETMQ_HOME_ENV));
    /**
     * NameServer 存储KV配置属性的持久化路径
     */
    private String kvConfigPath = System.getProperty("user.home") + File.separator + "namesrv" + File.separator + "kvConfig.json";
    /**
     * NameServer 默认配置文件路径,不生效。NameServer启动时如果需要通过配置文件配置NameServer启动属性的话,请使用-c选项
     */
    private String configStorePath = System.getProperty("user.home") + File.separator + "namesrv" + File.separator + "namesrv.properties";
    private String productEnvName = "center";
    private boolean clusterTest = false;
    /**
     * 是否支持顺序消息,默认是不支持
     */
    private boolean orderMessageEnable = false;

NettyServerConfig:

    /**
     * NameServer监听端口,该值默认会被初始化为9876
     */
    private int listenPort = 8888;
    /**
     * Netty业务线程池线程个数
     */
    private int serverWorkerThreads = 8;
    /**
     * Netty public 任务线程池线程个数,netty网络设计,
     * 根据业务类型会创建不同的线程池,比如处理消息发送、消息消费、心跳检测等
     * 如果该业务类型(RequestCode)未注册线程池,则由public线程池执行
     */
    private int serverCallbackExecu
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
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 的设计思想和架构原理
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啊杰eboy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值