从源码角度,分析RocketMQ之NameServer

35 阅读5分钟

前言

前面的文章中介绍了RocketMQ一些用法和特性(优点,集群等),接下来的文章咱们一起了解下RocketMQ的源码。NameServer 是RocketMQ的核心组件之一,咱们先拿NameServer“开刀吧”。这篇文章主要围绕 NamesrvStartupNamesrvController 这两个核心类(启动类)来展开,在这里先感谢各位小伙伴儿们的支持啦!!!

在这里插入图片描述

NameServer 概述

在前面的文章中,我们已经介绍过NameServer的作用了,为了让大家了解的更全面,在这里再给NameServer一个特写!!!NameServer 是 RocketMQ 的服务发现组件,它主要负责:

  1. Broker 管理:Broker 启动时会向 NameServer 注册(重点),NameServer 会维护 Broker 的数据信息。
  2. 路由信息管理:Producer 和 Consumer 通过 NameServer 获取 Broker 的路由信息,以此进行消息的发送和消费。

NameServer 启动流程

为什么在前言部分说主要介绍两个核心类呢 》》》因为,NameServer 的启动过程主要分为两个阶段:

  1. 启动类 NamesrvStartup:负责解析命令行参数、初始化配置、启动 NamesrvController
  2. 核心控制器 NamesrvController:负责管理 NameServer 的核心逻辑,包括路由信息的管理、Broker 的注册与注销等。

在这里插入图片描述 详细的源码我们下面聊!

NamesrvStartup 启动类

NamesrvStartup 是 NameServer 的启动入口类,它主要负责解析命令行参数、初始化配置、启动 NamesrvController

启动流程

从头开始看是调用了main0这个方法,接着又调用了createNamesrvController这个方法。

在这里插入图片描述

源码和每一块主要做了什么:

public static NamesrvController main0(String[] args) {
    try {
        // 解析命令行参数
        Options options = ServerUtil.buildCommandlineOptions(new Options());
        commandLine = ServerUtil.parseCmdLine("mqnamesrv", args, buildCommandlineOptions(options), new PosixParser());
        if (null == commandLine) {
            System.exit(-1);
            return null;
        }

        // 初始化日志配置
        final Logger log = LoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
        String namesrvConfigFile = commandLine.getOptionValue('c');
        String rocketmqHome = commandLine.getOptionValue('h');
        if (UtilAll.isBlank(namesrvConfigFile)) {
            namesrvConfigFile = System.getProperty("rocketmq.namesrv.config.file", "namesrv.properties");
        }
        if (UtilAll.isBlank(rocketmqHome)) {
            rocketmqHome = System.getenv("ROCKETMQ_HOME");
        }

        // 加载配置文件
        NamesrvConfig namesrvConfig = new NamesrvConfig();
        NettyServerConfig nettyServerConfig = new NettyServerConfig();
        nettyServerConfig.setListenPort(9876);

        if (namesrvConfigFile != null) {
            InputStream in = new BufferedInputStream(new FileInputStream(namesrvConfigFile));
            properties = new Properties();
            properties.load(in);
            MixAll.properties2Object(properties, namesrvConfig);
            MixAll.properties2Object(properties, nettyServerConfig);
            namesrvConfig.setConfigStorePath(namesrvConfigFile);
            in.close();
        }

        // 设置环境变量
        MixAll.printObjectProperties(log, namesrvConfig);
        MixAll.printObjectProperties(log, nettyServerConfig);

        final NamesrvController controller = createNamesrvController(namesrvConfig, nettyServerConfig);
        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是NameServer的配置参数,NettyServerConfig是接收网络请求的配置参数,底层是使用Nettey实现的,并且设置了监听端口为9876。

所以上面代码归根结底就是:

  1. 解析命令行参数:NamesrvStartup 解析命令行参数,获取配置文件路径这些必要信息。
  2. 初始化配置:根据命令行参数,加载,解析配置文件,初始化 NamesrvConfigNettyServerConfig
  3. 创建并启动 NamesrvController:通过 createNamesrvController 方法创建 NamesrvController 实例,调用 start 方法启动。

在这里插入图片描述

NamesrvController核心控制器

NamesrvController 是 NameServer 的核心控制器,负责管理 NameServer 的核心逻辑,包括路由信息的管理、Broker 的注册和注销等等。

初始化流程

public boolean initialize() {
     // 加载 KV 配置
    this.kvConfigManager.load();
     // 初始化 Netty 服务器
    this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);
     // 注册处理器
    this.registerProcessor();
     // 启动定时任务
    this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            NamesrvController.this.routeInfoManager.scanNotActiveBroker();
        }
    }, 5, 10, TimeUnit.SECONDS);

    return true;
}

所以,NamesrvController 的初始化流程主要包括以下几个步骤:

  1. 初始化 KV 配置管理器:KVConfigManager 负责管理 NameServer 的 KV 配置。
  2. 初始化路由信息管理器:RouteInfoManager 负责管理 Broker 的路由信息。
  3. 初始化 Netty 服务器:RemotingServer 负责处理与 Broker、Producer、Consumer 的网络通信。
  4. 启动定时任务:定时扫描不活跃的 Broker,清理路由信息。

在这里插入图片描述

/**
 * 定期扫描并移除不活跃的Broker连接。
 * 这个方法会遍历当前存储的所有Broker连接,并检查每个连接是否已经超时。
 * 便利,如果当前Broker连接的最后更新时间超过了设定的过期时间,关闭这个连接并从表中移除。
 */
public void scanNotActiveBroker() {

    // 获取还存活的Broker的迭代器
    Iterator<Entry<String, BrokerLiveInfo>> it = this.brokerLiveTable.entrySet().iterator();
    while (it.hasNext()) {
        Entry<String, BrokerLiveInfo> next = it.next();
        // 获取便利的这个Broker连接的最后更新时间戳
        long last = next.getValue().getLastUpdateTimestamp();
        // 检查这个Broker连接是否已经超过设定的过期时间
        if ((last + BROKER_CHANNEL_EXPIRED_TIME) < System.currentTimeMillis()) {
            // 如果超过过期时间,关闭这个Broker的网络通道
            RemotingUtil.closeChannel(next.getValue().getChannel());
            // 从brokerLiveTable中移除这个Broker的记录
            it.remove();
            log.warn("The broker channel expired, {} {}ms", next.getKey(), BROKER_CHANNEL_EXPIRED_TIME);
            this.onChannelDestroy(next.getKey(), next.getValue().getChannel());
        }
    }
}

RouteInfoManager路由信息管理器

在这里插入图片描述

RouteInfoManager 也是 NameServer 的核心组件之一,它负责管理 Broker 的路由信息。它维护了以下这几个关键数据结构:

  • HashMapString topic , ListQueueData topicQueueTable:存储每个 Topic 的队列信息。
  • HashMapString brokerName , BrokerData brokerAddrTable:存储每个 Broker 的地址信息。
  • HashMapString clusterName , SetString brokerName clusterAddrTable:存储每个集群的 Broker 信息。
  • HashMapString brokerAddr , BrokerLiveInfo brokerLiveTable:存储每个 Broker 的活跃状态信息,这个上面提到过。

RemotingServer网络通信

RemotingServer 是 NameServer 的网络通信组件,底层是基于 Netty 实现(看名字也能看得出来hhhh)。它负责处理与 Broker、Producer、Consumer 的网络通信。

public class NettyRemotingServer extends NettyRemotingAbstract implements RemotingServer {
    private final ServerBootstrap serverBootstrap;
    private final EventLoopGroup eventLoopGroupSelector;
    private final EventLoopGroup eventLoopGroupBoss;
}

定时任务

NameServer 会定期扫描不活跃的 Broker,清理它的路由信息。定时任务通过 ScheduledExecutorService 实现,默认每隔 10 秒执行一次。这个咱们上面详细介绍过了!!!

在这里插入图片描述

this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        NamesrvController.this.routeInfoManager.scanNotActiveBroker();
    }
}, 5, 10, TimeUnit.SECONDS);

在这里插入图片描述

NameServer 的底层启动过程

通过我们上面的介绍,这里再总结一下,RocketMQ的NameServer是如何启动并运行的:

  1. 命令行参数解析NamesrvStartup首先解析命令行参数,并加载配置文件。
  2. 创建NamesrvController实例:初始化NamesrvController,加载K-V配置。
  3. 注册处理器:在NamesrvController中注册各种处理器,以便处理不同类型的请求。
  4. 启动Netty服务器:启动Netty服务器,开始监听来自Broker和客户端的请求。
  5. 定时任务:定期扫描不活跃的Broker和打印K-V配置信息,确保系统的健康运行。

在这里插入图片描述

以上过程结束,除了加载配置文件辣么多配置很难理解外,整体流程还是比较清晰的,做了以上流程图,可以配合理解和复习使用。

文章小结

本篇文章到这里就结束了,后续会继续分享RocketMQ相关的知识,感谢各位小伙伴们的支持!

在这里插入图片描述