NameServer源码解析

news2025/1/10 10:47:08

1 模块入口代码的功能

        本节介绍入口代码的功能,阅读源码的时候,很多人喜欢根据执行逻辑,先从入口代码看起。NameServer部分入口代码主要完成命令行参数解析,初始化Controller的功能。

1.1 入口函数

首先看一下NameServer的源码目录(见图10-1)。

NamesrvStartup是模块的启动入口,NamesrvController是用来协块各个调模功能的代码。

我们从启动代码开始分析,找到NamesrvStartup.java里的main函数public static void main(String[]args){main0(args);},发现它又把逻辑转到main0这个函数里。

图10-1 NameServer源码目录

1.2 解析命令行参数

main0函数主要完成两个功能,第一个功能是解析命令行参数,我们通过源码来看一看,重点是解析-c和-p参数,如代码清单10-1所示。

代码清单10-1 解析NameServer命令行参数

Options options = ServerUtil.buildCommandlineOptions(new Options());
commandLine = ServerUtil.parseCmdLine("mqnamesrv", args,
    buildCommandlineOptions(options), new PosixParser());
if (null == commandLine) {
    System.exit(-1);
    return null;
}
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, " +
            file + "%n");
        in.close();
    }
}
if (commandLine.hasOption('p')) {
    MixAll.printObjectProperties(null, namesrvConfig);
    MixAll.printObjectProperties(null, nettyServerConfig);
    System.exit(0);
}

 

-c命令行参数用来指定配置文件的位置;-p命令行参数用来打印所有配置项的值。注意,用-p参数打印配置项的值之后程序就退出了,这是一个帮助调试的选项。

1.3 初始化NameServer的Controller

main0函数的另外一个功能是初始化Controller,如代码清单10-2所示。

代码清单10-2 初始化并启动Controller

// remember all configs to prevent discard
controller.getConfiguration().registerConfig(properties);
boolean initResult = controller.initialize();
if (!initResult) {
    controller.shutdown();
    System.exit(-3);
}
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log,
    new Callable<Void>() {
    @Override
    public Void call() throws Exception {
        controller.shutdown();
        return null;
    }
}));
controller.start();

 

根据解析出的配置参数,调用controller.initialize()来初始化,然后调用controller.start()让NameServer开始服务。

还有一个逻辑是注册ShutdownHookThread,当程序退出的时候会调用controller.shutdown来做退出前的清理工作。

2 NameServer的总控逻辑

NameServer的总控逻辑在NamesrvController.java代码中。NameServer是集群的协调者,它只是简单地接收其他角色报上来的状态,然后根据请求返回相应的状态。首先,NameserverController把执行线程池初始化好,如代码清单10-3所示。

代码清单10-3 线程池初始化

this.remotingExecutor =
    Executors.newFixedThreadPool(nettyServerConfig
        .getServerWorkerThreads(), new ThreadFactoryImpl
        ("RemotingExecutorThread_"));
this.registerProcessor();

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

 

启动了一个默认是8个线程的线程池(private int serverWorkerThreads=8),还有两个定时执行的线程,一个用来扫描失效的Broker(scanNotActiveBroker),另一个用来打印配置信息(printAllPeriodically)。

然后启动负责通信的服务remotingServer,remotingServer监听一些端口,收到Broker、Client等发过来的请求后,根据请求的命令,调用不同的Processor来处理。这些不同的处理逻辑被放到上面初始化的线程池中执行,如代码清单10-4所示。

代码清单10-4 启动通信服务,关联初始化的线程池

this.remotingServer = new NettyRemotingServer(this.nettyServerConfig,
    this.brokerHousekeepingService);
……
if (namesrvConfig.isClusterTest()) {
    this.remotingServer.registerDefaultProcessor(new
            ClusterTestRequestProcessor(this, namesrvConfig
            .getProductEnvName()),
        this.remotingExecutor);
} else {
    this.remotingServer.registerDefaultProcessor(new
        DefaultRequestProcessor(this), this.remotingExecutor);
}

 

remotingServer是基于Netty封装的一个网络通信服务,要了解remoting-Server需要先对Netty有个基本的认知,后面会单独介绍。

3 核心业务逻辑处理

NameServer的核心业务逻辑,在DefaultRequestProcessor.java中可以一目了然地看出。网络通信服务模块收到请求后,就调用这个Processor来处理,如代码清单10-5所示。

代码清单10-5 根据请求码调用相应的处理逻辑

switch (request.getCode()) {
    case RequestCode.PUT_KV_CONFIG:
        return this.putKVConfig(ctx, request);
    case RequestCode.GET_KV_CONFIG:
        return this.getKVConfig(ctx, request);
    case RequestCode.DELETE_KV_CONFIG:
        return this.deleteKVConfig(ctx, request);
    case RequestCode.REGISTER_BROKER:
        Version brokerVersion = MQVersion.value2Version(request
            .getVersion());
        if (brokerVersion.ordinal() >= MQVersion.Version
            .V3_0_11.ordinal()) {
            return this.registerBrokerWithFilterServer(ctx, request);
        } else {
            return this.registerBroker(ctx, request);
        }
    case RequestCode.UNREGISTER_BROKER:
        return this.unregisterBroker(ctx, request);
    case RequestCode.GET_ROUTEINTO_BY_TOPIC:
        return this.getRouteInfoByTopic(ctx, request);
    case RequestCode.GET_BROKER_CLUSTER_INFO:
        return this.getBrokerClusterInfo(ctx, request);
    case RequestCode.WIPE_WRITE_PERM_OF_BROKER:
        return this.wipeWritePermOfBroker(ctx, request);
    case RequestCode.GET_ALL_TOPIC_LIST_FROM_NAMESERVER:
        return getAllTopicListFromNameserver(ctx, request);
    case RequestCode.DELETE_TOPIC_IN_NAMESRV:
        return deleteTopicInNamesrv(ctx, request);
    case RequestCode.GET_KVLIST_BY_NAMESPACE:
        return this.getKVListByNamespace(ctx, request);
    case RequestCode.GET_TOPICS_BY_CLUSTER:
        return this.getTopicsByCluster(ctx, request);
    case RequestCode.GET_SYSTEM_TOPIC_LIST_FROM_NS:
        return this.getSystemTopicListFromNs(ctx, request);
    case RequestCode.GET_UNIT_TOPIC_LIST:
        return this.getUnitTopicList(ctx, request);
    case RequestCode.GET_HAS_UNIT_SUB_TOPIC_LIST:
        return this.getHasUnitSubTopicList(ctx, request);
    case RequestCode.GET_HAS_UNIT_SUB_UNUNIT_TOPIC_LIST:
        return this.getHasUnitSubUnUnitTopicList(ctx, request);
    case RequestCode.UPDATE_NAMESRV_CONFIG:
        return this.updateConfig(ctx, request);
    case RequestCode.GET_NAMESRV_CONFIG:
        return this.getConfig(ctx, request);
    default:
        break;
}

 

逻辑主体是个switch语句,根据RequestCode调用不同的函数来处理,从RequestCode可以了解到NameServer的主要功能,比如:REGISTER_BROKER是在集群中新加入一个Broker机器;GET_ROUTEINTO_BY_TOPIC是请求获取一个Topic的路由信息;WIPE_WRITE_PERM_OF_BROKER是删除一个Broker的写权限。

4 集群状态存储

NameServer作为集群的协调者,需要保存和维护集群的各种元数据,这是通过RouteInfoManager类来实现的,如代码清单10-6所示。

代码清单10-6 RouteInfoManager的存储结构

private final HashMap<String/* topic */, List<QueueData>> topicQueue-Table;
private final HashMap<String/* brokerName */, BrokerData> brokerAddr-Table;
private final HashMap<String/* clusterName */, Set<String/* brokerName
*/>> clusterAddrTable;
private final HashMap<String/* brokerAddr */, BrokerLiveInfo>
    brokerLiveTable;
private final HashMap<String/* brokerAddr */, List<String>/* Filter
Server */> filterServerTable;
public RouteInfoManager() {
    this.topicQueueTable = new HashMap<String, List<QueueData>>(1024);
    this.brokerAddrTable = new HashMap<String, BrokerData>(128);
    this.clusterAddrTable = new HashMap<String, Set<String>>(32);
    this.brokerLiveTable = new HashMap<String, BrokerLiveInfo>(256);
    this.filterServerTable = new HashMap<String, List<String>>(256);
}

 

每个结构存储着一类集群信息,具体含义在第5章有介绍。了解RocketMQ各个角色的功能后,对每个结构的处理逻辑就好理解了。下面重点看一下控制访问这些结构的锁机制。

锁分为互斥锁、读写锁;也可分为可重入锁、不可重入锁。在NameServer的场景中,读取操作多,更改操作少,所以选择读写锁能大大提高效率。对于如何选择可重入和不可重入锁,重点看函数间的调用关系,比如多次获取锁的示例代码,如果这个lock是不可重入的,代码无法正常执行,如代码清单10-7所示。

代码清单10-7 多次获取锁示例

Lock lock = new Lock();
public void outer() {
    lock.lock();
    inner();
    lock.unlock();
}
public void inner() {
    lock.lock();
    //do something lock.unlock(); }
}

 

RouteInfoManager中使用的是可重入的读写锁(private final ReadWriteLock lock=new ReentrantReadWriteLock()),我们以deleteTopic函数为例,看一下锁的使用方式,如代码清单10-8所示。

代码清单10-8 锁的使用方式

public void deleteTopic(final String topic) {
    try {
        try {
            this.lock.writeLock().lockInterruptibly();
            this.topicQueueTable.remove(topic);
        } finally {
            this.lock.writeLock().unlock();
        }
    } catch (Exception e) {
        log.error("deleteTopic Exception", e);
    }
}

 

 

首先锁的获取和执行逻辑要放到一个try{}里,然后在finally{}中释放。这是一种典型的使用方式,我们可以参考这种方式实现自己的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1232552.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SOLIDWORKS2024钣金及结构系统功能增强

SOLIDWORKS钣金和结构系统是大家比较熟悉的模块了&#xff0c;在新版本中钣金和结构系统功能也做了相应的优化。接下来让我们看看在SOLIDWORKS 2024中钣金和结构系统有哪些功能增强。 首先是钣金方面&#xff0c;我们先来看看新增的槽口延伸功能&#xff0c;在装配体零部件中创…

树莓派的外设开发---树莓派中的wiringPi库

在树莓派中安装wiringPi库 wiringPi库其实已经很熟悉了&#xff0c;在香橙派中大量使用过&#xff0c;这个库中集成了很多使用的功能性函数。 现在在树莓派上也安装wiringPi库&#xff1a; 1. wget https://project-downloads.drogon.net/wiringpi-latest.deb 2. sudo dpkg …

VLAN综合实验

目录 一、实验拓扑 二、实验要求 三、实验步骤 1、交换机配置vlan 1&#xff09;SW1配置 2&#xff09;SW2配置 3&#xff09;SW3配置 2、路由器配置子接口、DHCP 配置结果&#xff1a; PC1-6IP地址 测试 一、实验拓扑 二、实验要求 1、pc1和pc3所在接口为access&a…

STM32 Flash

FLASH简介 Flash是常用的用于存储数据的半导体器件&#xff0c;它具有容量大&#xff0c;可重复擦写&#xff0c;按“扇区/块”擦除、掉电后数据可继续保存的特性。 常见的FLASH主要有NOR FLASH和NAND FLASH两种类型。NOR和NAND是两种数字门电路&#xff0c;可以简单地认为FL…

10个好用的Mac数据恢复软件推荐—恢复率高达99%

如果您正在寻找最好的 Mac 数据恢复软件来检索意外删除或丢失的文件&#xff0c;那么这里就是您的最佳选择。 我们理解&#xff0c;当您找不到 Mac 计算机或外部驱动器上保存的一些重要文件时&#xff0c;会感到多么沮丧和绝望。这些文件非常珍贵&#xff0c;无论出于何种原因…

基于springboot实现医院信管系统项目【项目源码+论文说明】

基于springboot实现医院信管系统演示 摘要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#x…

验证码常见安全问题与测试方法汇总

系统使用验证码主要是意图一般有两个个目的&#xff0c;即辅助身份验证&#xff08;短信或邮箱验证码&#xff09;和防止攻击者利用自动化脚本恶意攻击网站&#xff08;数字&#xff0c;图片&#xff0c;视频&#xff0c;行为式等验证码&#xff09;。 验证码的生命周期 验证码…

会议剪影 | 思腾合力受邀出席第四届长三角文博会并作主题演讲

以“担当新使命:长三角文化产业的力量”为主题的「第四届长三角国际文化产业博览会」于2023年11月16日-19日在国家会展中心&#xff08;上海&#xff09;成功举办。思腾合力作为行业领先的人工智能基础架构解决方案商出席本次盛会。 此次展会的面积首次超过10万平米&#xff0c…

Flask Web开发:数据库

目录 在虚拟环境中安装Flask-SQLAlchemy&#xff1a; 一、配置 数据库配置示例&#xff1a; 二、定义模型 Role 和 User 模型代码&#xff1a; &#xff08;1&#xff09;常用的 SQLAlchemy 列类型&#xff1a;​编辑 &#xff08;2&#xff09;常用的 SQLAlchemy 列选项…

AD9361寄存器功能笔记之本振频率设定

LO的产生过程如图&#xff1a; 各个模块都有高灵活性。 1、参考时钟即是AD9361全局参考时钟&#xff0c;可以是外接晶振的片上DCXO&#xff0c;或是外部输入的有驱动能力的时钟信号。根据FM-COMMS5的设计&#xff0c;参考时钟可以使用时钟Buffer 40MHz晶振构成的参考频率源。 …

基于springboot实现智能热度分析和自媒体推送平台系统项目【项目源码】计算机毕业设计

基于springboot实现智能热度分析和自媒体推送平台演示 系统开发平台 在该自媒体分享网站中&#xff0c;Eclipse能给用户提供更多的方便&#xff0c;其特点一是方便学习&#xff0c;方便快捷&#xff1b;二是有非常大的信息储存量&#xff0c;主要功能是用在对数据库中查询和编…

【Java基础】Java导Excel攻略

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

BUUCTF [BJDCTF2020]鸡你太美 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 得到的 flag 请包上 flag{} 提交。来源&#xff1a; https://github.com/BjdsecCA/BJDCTF2020 密文&#xff1a; 下载附件&#xff0c;解压得到两个.gif图片。 第一个gif图片&#xff1a; 第二个gif图片无法打开。…

Linux常用命令——builtin命令

在线Linux命令查询工具 builtin 执行shell内部命令 补充说明 builtin命令用于执行指定的shell内部命令&#xff0c;并返回内部命令的返回值。builtin命令在使用时&#xff0c;将不能够再使用Linux中的外部命令。当系统中定义了与shell内部命令相同的函数时&#xff0c;使用…

禁止linux shell 终端显示完整工作路径,如何让linux bash终端不显示当前工作路径

在操作linux时&#xff0c;默认安装的linux终端会显示当前完整的工作目录&#xff0c;如果目录比较短还是可以接收&#xff0c;如果目录比较长&#xff0c;就显得比较别扭&#xff0c;操作起来不方便&#xff0c;因此需要关闭这种功能。 要关闭这个功能&#xff0c;请按如下步骤…

Conditional GAN

Text-to-Image 对于根据文字生成图像的问题&#xff0c;传统的做法就是训练一个NN&#xff0c;然后输入一段文字&#xff0c;输出对应一个图片&#xff0c;输出图片与目标图片越接近越好。存在的问题就是&#xff0c;比如火车对应的图片有很多张&#xff0c;如果用传统的NN来训…

NX二次开发UF_CAM_ask_lower_limit_plane_data 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;里海NX二次开发3000例专栏 UF_CAM_ask_lower_limit_plane_data Defined in: uf_cam_planes.h int UF_CAM_ask_lower_limit_plane_data(tag_t object_tag, double origin [ 3 ] , double normal [ 3 ] ) overview 概述 Query …

人工智能的广泛应用与影响

目录 前言1 智能手机与个人助手2 医疗保健3 自动驾驶技术4 金融领域5 教育与学习6 智能家居与物联网7 娱乐与媒体8 环境保护结语 前言 人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;是当今科技领域的璀璨明星&#xff0c;它不仅在技术创新方面掀起了…

适合小白的超详细yolov8环境配置+实例运行教程,从零开始教你如何使用yolov8训练自己的数据集(Windows+conda+pycharm)

目录 一、前期准备所需环境配置 1.1. 虚拟环境创建 1.2 下载yolov8源码&#xff0c;在pycharm中进行配置 1.2.1 下载源码 1.2.2 在pycharm终端中配置conda 1.3 在pycharm的terminal中激活虚拟环境 1.4 安装requirements.txt中的相关包 1.5 pip安装其他包 1.6 预训练…

如果在手机没有root的情况下完成安卓手机数据恢复

您是否不小心从安卓设备中删除了重要数据&#xff1f; 担心如何取回您的照片、视频和文档&#xff1f; 有时您可能会不小心删除重要数据并使用安卓 root方法取回文件。 许多用户不喜欢根植他们的安卓设备&#xff0c;因为这是一种复杂的方法。 在本指南中&#xff0c;我们将向您…