RocketMQ源码学习-NameServer

本文深入剖析RocketMQ的NameServer组件,介绍其架构设计、核心功能及启动流程,包括服务发现、服务治理、Broker信息维护、Topic管理等关键机制。

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

系列文章总目录

前言

NameServer,用来提供服务发现和服务治理功能。RocketMQ中的NameServer是一个无状态的服务,可以有多个节点,各个节点之间不进行通信,Broker、Client跟所有的NameServer通信。NameServer的功能如下:

  • 维护Broker信息。每个Broker启动后向NameServer注册,NameServer保存活跃的Broker列表,包括Master和Slave。
  • 保存所有的topic信息和topic对应的queue信息。
  • 保存所有的Filter服务。
  • 接受并处理Client的请求。

源码核心类

源码namesrv包下的类结构相对简单,如图,比较核心的类有:NamesrvStartup、NamesrvController、RouteInfoManager、KVConfigManager、DefaultRequestProcessor等。结合NameServer工作流程,进行逐步分析。

NameServer启动

NamesrvStartup类为NameServer的启动入口,来看下main方法的代码:

public static void main(String[] args) {
	// 委托给main0()处理
        main0(args);
    }

    public static NamesrvController main0(String[] args) {
        try {
        // 创建NamesrvController
            NamesrvController controller = createNamesrvController(args);
            // 启动NamesrvController
            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;
    }

整个过程非常简单,创建并启动NamesrvController,将所有的功能实现交给NamesrvController。下面看createNamesrvController()

    public static NamesrvController createNamesrvController(String[] args) throws IOException, JoranException {
        System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));
        //PackageConflictDetect.detectFastjson();
// 读取启动参数
        Options options = ServerUtil.buildCommandlineOptions(new Options());
        commandLine = ServerUtil.parseCmdLine("mqnamesrv", args, buildCommandlineOptions(options), new PosixParser());
        if (null == commandLine) {
            System.exit(-1);
            return null;
        }
// NamesrvConfig为NameServer配置,NettyServerConfig为Netty配置,用来构造NamesrvController
        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);

        if (null == namesrvConfig.getRocketmqHome()) {
            System.out.printf("Please set the %s variable in your environment to match the location of the RocketMQ installation%n", MixAll.ROCKETMQ_HOME_ENV);
            System.exit(-2);
        }

        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
        JoranConfigurator configurator = new JoranConfigurator();
        configurator.setContext(lc);
        lc.reset();
        configurator.doConfigure(namesrvConfig.getRocketmqHome() + "/conf/logback_namesrv.xml");

        log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);

        MixAll.printObjectProperties(log, namesrvConfig);
        MixAll.printObjectProperties(log, nettyServerConfig);

        final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);

        // remember all configs to prevent discard
        controller.getConfiguration().registerConfig(properties);

        return controller;
    }

NamesrvStartup启动NamesrvController的代码很简单,这里就不贴了,下面重点分析NamesrvController。

NamesrvController构造与启动

看NamesrvController几个重要的属性:

// 两个配置,构造方法传入
	private final NamesrvConfig namesrvConfig;
	private final NettyServerConfig nettyServerConfig;
// 单线程池,处理定时任务
    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
        "NSScheduledThread"));
    //key-value配置管理
    private final KVConfigManager kvConfigManager;
    //路由信息管理
    private final RouteInfoManager routeInfoManager;
	//NettyRemotingServer,注册处理请求的Processor,处理请求
    private RemotingServer remotingServer;
	// netty事件处理
    private BrokerHousekeepingService brokerHousekeepingService;
	// 处理请求的线程池
    private ExecutorService remotingExecutor;
	// NamesrvConfig和NettyServerConfig两个的配置持久化以及获取工具,
    private Configuration configuration;

构造函数就是初始化上面的一些对象。NamesrvStartup的start()方法中先调用了initialize(),如下:

    public boolean initialize() {
		// kv持久化数据加载到内存
        this.kvConfigManager.load();
		// nettyserver初始化
        this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);
		// 线程池初始化
        this.remotingExecutor = Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_"));
		// 处理请求的线程注册,注册了默认的请求处理类DefaultRequestProcessor
        this.registerProcessor();
		// 定时任务,扫描并清除无心跳的broker
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
  NamesrvController.this.routeInfoManager.scanNotActiveBroker();
            }
        }, 5, 10, TimeUnit.SECONDS);
		// 打印kv配置信息
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
            	NamesrvController.this.kvConfigManager.printAllPeriodically();
            }
        }, 1, 10, TimeUnit.MINUTES);

        return true;
    }

NamesrvController的start()方法就是启动NettyRemotingServer,接受请求并处理。NameServer的核心逻辑是在DefaultRequestProcessor的processRequest中完成的,具体为根据RemotingCommand中不同的RequestCode定义值,执行不同的逻辑,最终交KVConfigManager和RouteInfoManager处理完成。下面看这两个类的源码。

KVConfigManager源码

部分源码如下,省略了具体实现部分,逻辑比较简单

public class KVConfigManager {
	// 读写锁
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    // key-value存储map
    private final HashMap<String/* Namespace */, HashMap<String/* Key */, String/* Value */>> configTable =
        new HashMap<String, HashMap<String, String>>();

	// 从文件加载
    public void load() {
		...
    }

	// 写kv
    public void putKVConfig(final String namespace, final String key, final String value) {
        ...
    }

	// 持久化到文件
    public void persist() {
		 ...
    }

	// 删除kv
    public void deleteKVConfig(final String namespace, final String key) {
        ...
    }

	// 使用namespace获取
    public byte[] getKVListByNamespace(final String namespace) {
        ...
    }

	// 读取value
    public String getKVConfig(final String namespace, final String key) {
		...
    }

    }

RouteInfoManager 源码

RouteInfoManager负责维护所有的cluster、broker、topic、queue、filter等信息。核心属性如下:

	private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;
    private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
    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;

具体的方法为配合读写锁,实现上面信息的维护,这里不再单独分析。

BrokerHousekeepingService

简单说下BrokerHousekeepingService,RocketMQ使用BrokerHousekeepingService来处理broker失效的情况。NamesrvController初始化netty的时候,会启动线程监听管道的失效、异常、关闭等事件,如果事件触发,则通过RouteInfoManager删除broker信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值