RocketMQ源码 NameServer启动流程源码分析

 前言


RocketMQ在启动 Namesrv 的过程中会创建NamesrvController控制器,并初始化一些核心组件RouteInfoManager路由信息管理组件、NettyRemotingServer网络通信服务器组件、BrokerHousekeepingService网络通信监听器组件,使用了大量JUC并发知识和Netty网络通信的应用。

它启动后,主要负责与Broker通信维护消息队列路由信息,且与Producer/Consumer通信分发路由信息。

源码版本:4.9.3

RocketMQ入门请查阅我的另一篇笔记 RocketMQ入门与最佳实践-CSDN博客

Namesrv整体源码架构图

核心流程与源码解析

源码入口 org.apache.rocketmq.namesrv.NamesrvStartup#main

主流程

主流程如下,很简洁,先创建核心的NamesrvController控制器,再启动控制器。

    public static NamesrvController main0(String[] args) {

        try {
            // 创建NameServer控制器
            NamesrvController controller = createNamesrvController(args);
            // 启动NameServer控制器
            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;
    }

下面深入看下每一步实现:

创建Namesrv控制器阶段

创建Namesrv控制器阶段:先解析并处理的命令行参数,有处理的设计的日志配置,并且将配置参数加载到Namesrv配置,最后创建了非常关键的

    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;
        }

        // 创建NameServer配置、NettyServer配置,并初始化nettyServerConfig监听端口为9876
        final NamesrvConfig namesrvConfig = new NamesrvConfig();
        final NettyServerConfig nettyServerConfig = new NettyServerConfig();
        nettyServerConfig.setListenPort(9876);
        if (commandLine.hasOption('c')) {
            // 包含命令行参数-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')) {
            // 包含命令行参数-p,打印命令行参数,并退出程序
            InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_CONSOLE_NAME);
            MixAll.printObjectProperties(console, namesrvConfig);
            MixAll.printObjectProperties(console, nettyServerConfig);
            System.exit(0);
        }
        // 将命令行配置参数设置到NameServer配置中
        MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), namesrvConfig);

        // 必须设置rocketmqHome
        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);

        // 创建NameServer控制器
        final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);

        // remember all configs to prevent discard
        // 将命令行参数设置到NameServer配置中
        controller.getConfiguration().registerConfig(properties);

        return controller;
    }

启动Namesrv控制器阶段

先对控制器进行初始化操作(具体怎么初始化和启动,后续一步一步点进去分析),又对当前应用所在的JVM注册了一个钩子函数,在JVM关闭时,进行回调,用于清理控制器所占用的系统资源。最后,启动了控制器。

    public static NamesrvController start(final NamesrvController controller) throws Exception {

        if (null == controller) {
            throw new IllegalArgumentException("NamesrvController is null");
        }
        // 初始化控制器
        boolean initResult = controller.initialize();
        if (!initResult) {
            controller.shutdown();
            System.exit(-3);
        }

        // 注册JVM关闭钩子
        Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                // JVM关闭是,关闭控制器
                controller.shutdown();
                return null;
            }
        }));

        // 启动控制器
        controller.start();

        return controller;
    }

跟进去看下控制器初始化都干了什么事情:

  1. 从磁盘加载持久化后的KV配置到内存中;
  2. 创建网络通信服务器:
    1. 两种RPC请求方式,对应的信号量阈值,用于发送RPC请求时,进行并发控制,保护系统内存;
    2. 创建netty提供的启动netty网络服务器的启动脚本对象;
    3. 创建netty服务器配置对象;
    4. 创建监听netty服务器事件的监听器;
    5. 公共线程池初始化,默认4个线程,用于执行服务器回调逻辑;
    6. 非epoll模式:初始化建立物理网络连接的线程池,默认1个线程;
    7. 初始化处理请求的线程池(多路复用,一个线程管理多个物理连接),默认3个线程;
    8. 加载SSL安全相关上下文;
  3. 初始化网络通信请求处理线程池,默认8个从左线程;
  4. 注册默认的请求处理器到netty网络通信服务器;
  5. 添加路由信息扫定时扫描任务,每隔10秒钟检查一次broker的保活信息,如果超过2分钟未发送心跳,就清理掉当前broker对应的主题队列路由信息;
  6. 添加路由信息定时打印线程,每隔10分钟打印namesrv管理的KV配置;
  7. 如果,启用TLS安全通信服务,就创建FileWatchService本地磁盘文件监听服务,定时扫描本地磁盘密钥文件,如果文件的消息摘要发生变化,就回调监听器,将密钥文件重新加载到内存中。
    public boolean initialize() {
        // 从磁盘文件加载历史KV配置
        this.kvConfigManager.load();
        // 启动网络通信服务器
        this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);

        // 启动异步执行的工作线程池,8个线程
        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);
        // 注册KV配置打印线程,定时打印所有KV配置
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                NamesrvController.this.kvConfigManager.printAllPeriodically();
            }
        }, 1, 10, TimeUnit.MINUTES);

        // 启动TLS服务
        if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {
            // Register a listener to reload SslContext
            try {
                // 初始化文件监听服务,监听本地指定磁盘目录的TLS证书文件变化
                fileWatchService = new FileWatchService(
                        // 监听的TLS证书文件目录
                    new String[] {
                        TlsSystemConfig.tlsServerCertPath,
                        TlsSystemConfig.tlsServerKeyPath,
                        TlsSystemConfig.tlsServerTrustCertPath
                    },
                        // 监听回调
                    new FileWatchService.Listener() {
                        boolean certChanged, keyChanged = false;
                        @Override
                        public void onChanged(String path) {
                            // 监听到TLS证书文件变化,重新加载SSL上下文
                            if (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) {
                                log.info("The trust certificate changed, reload the ssl context");
                                reloadServerSslContext();
                            }
                            if (path.equals(TlsSystemConfig.tlsServerCertPath)) {
                                certChanged = true;
                            }
                            if (path.equals(TlsSystemConfig.tlsServerKeyPath)) {
                                keyChanged = true;
                            }
                            if (certChanged && keyChanged) {
                                log.info("The certificate and private key changed, reload the ssl context");
                                certChanged = keyChanged = false;
                                reloadServerSslContext();
                            }
                        }
                        private void reloadServerSslContext() {
                            ((NettyRemotingServer) remotingServer).loadSslContext();
                        }
                    });
            } catch (Exception e) {
                log.warn("FileWatchService created error, can't load the certificate dynamically");
            }
        }

        return true;
    }
    public NettyRemotingServer(final NettyServerConfig nettyServerConfig,
        final ChannelEventListener channelEventListener) {
        // 两种RPC请求方式,对应的信号量阈值,用于发送RPC请求时,进行并发控制,保护系统内存
        super(nettyServerConfig.getServerOnewaySemaphoreValue(), nettyServerConfig.getServerAsyncSemaphoreValue());
        // netty提供的启动netty网络服务器的启动脚本
        this.serverBootstrap = new ServerBootstrap();
        // netty服务器配置
        this.nettyServerConfig = nettyServerConfig;
        // 监听netty服务器事件的监听器
        this.channelEventListener = channelEventListener;

        int publicThreadNums = nettyServerConfig.getServerCallbackExecutorThreads();
        if (publicThreadNums <= 0) {
            publicThreadNums = 4;
        }

        // 公共线程池初始化,默认4个线程,用于执行服务器回调逻辑
        this.publicExecutor = Executors.newFixedThreadPool(publicThreadNums, new ThreadFactory() {
            private AtomicInteger threadIndex = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "NettyServerPublicExecutor_" + this.threadIndex.incrementAndGet());
            }
        });


        if (useEpoll()) {
            this.eventLoopGroupBoss = new EpollEventLoopGroup(1, new ThreadFactory() {
                private AtomicInteger threadIndex = new AtomicInteger(0);

                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, String.format("NettyEPOLLBoss_%d", this.threadIndex.incrementAndGet()));
                }
            });

            this.eventLoopGroupSelector = new EpollEventLoopGroup(nettyServerConfig.getServerSelectorThreads(), new ThreadFactory() {
                private AtomicInteger threadIndex = new AtomicInteger(0);
                private int threadTotal = nettyServerConfig.getServerSelectorThreads();

                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, String.format("NettyServerEPOLLSelector_%d_%d", threadTotal, this.threadIndex.incrementAndGet()));
                }
            });
        } else {
            // 非epoll模式
            // 初始化建立连接的线程池,默认1个线程
            this.eventLoopGroupBoss = new NioEventLoopGroup(1, new ThreadFactory() {
                private AtomicInteger threadIndex = new AtomicInteger(0);

                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, String.format("NettyNIOBoss_%d", this.threadIndex.incrementAndGet()));
                }
            });
            // 初始化处理请求的线程池,默认3个线程
            this.eventLoopGroupSelector = new NioEventLoopGroup(nettyServerConfig.getServerSelectorThreads(), new ThreadFactory() {
                private AtomicInteger threadIndex = new AtomicInteger(0);
                private int threadTotal = nettyServerConfig.getServerSelectorThreads();

                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, String.format("NettyServerNIOSelector_%d_%d", threadTotal, this.threadIndex.incrementAndGet()));
                }
            });
        }
        // 加载ssl上下文,安全相关
        loadSslContext();
    }

接下来看控制器start()启动都干了什么事:先启动网络通信服务器,又启动了监听TLS密钥文件的文件监听服务。

    public void start() throws Exception {
        // 启动网络通信服务器
        this.remotingServer.start();

        if (this.fileWatchService != null) {
            // 启动文件监听服务
            this.fileWatchService.start();
        }
    }

 先启动了网络通信服务器:

  1. 默认网络事件执行池初始化,默认8个线程;用于处理多路复用组件接收到的网络事件。
  2. 预处理握手用的handler:安全握手handler、编码handler、连接管理handler、服务器消息处理handler;
  3. 真正的创建一个netty server:
    1. 先设置了一些配置项;
    2. 有添加了一个pipeline请求处理通道。有handshakeHandler握手处理器、encoder编码器、NettyDecoder解码器、IdleStateHandler空闲检测处理器、connectionManageHandler网络连接管理器、serverHandler服务器消息处理器;
  4. 设置网络服务器收发消息缓存大小;
  5. 设置写缓冲区水位大小;
  6. 开启内存池缓存分配机制;
  7. 绑定端口,同步建立netty长连接通道,并且获取本地,设置监听端口;
  8. 启动网络异常事件监听器;
  9. 注册扫描超时请求定时任务,每秒扫描一次;
    public void start() {
        // 默认网络事件执行池初始化,默认8个线程
        this.defaultEventExecutorGroup = new DefaultEventExecutorGroup(
            nettyServerConfig.getServerWorkerThreads(),
            new ThreadFactory() {

                private AtomicInteger threadIndex = new AtomicInteger(0);

                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, "NettyServerCodecThread_" + this.threadIndex.incrementAndGet());
                }
            });

        // 预处理握手用的handler
        prepareSharableHandlers();
        // 真正的创建一个netty server
        ServerBootstrap childHandler =
            this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector)
                .channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, nettyServerConfig.getServerSocketBacklog()) // tcp3次握手队列长度
                .option(ChannelOption.SO_REUSEADDR, true) //
                .option(ChannelOption.SO_KEEPALIVE, false) // 是否自动发送探测包,探测网络连接是否存活
                .childOption(ChannelOption.TCP_NODELAY, true) // 禁止打包传输,避免网络延迟
                .localAddress(new InetSocketAddress(this.nettyServerConfig.getListenPort()))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline()
                                // 3次握手处理handler
                            .addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME, handshakeHandler)
                            .addLast(defaultEventExecutorGroup,
                                encoder, // 编码器
                                new NettyDecoder(), // 解码器
                                    // 空闲检测handler
                                new IdleStateHandler(0, 0, nettyServerConfig.getServerChannelMaxIdleTimeSeconds()),
                                connectionManageHandler, // 连接管理handler
                                serverHandler // 服务器handler
                            );
                    }
                });

        // 网络服务器收发消息缓存大小
        if (nettyServerConfig.getServerSocketSndBufSize() > 0) {
            log.info("server set SO_SNDBUF to {}", nettyServerConfig.getServerSocketSndBufSize());
            childHandler.childOption(ChannelOption.SO_SNDBUF, nettyServerConfig.getServerSocketSndBufSize());
        }
        if (nettyServerConfig.getServerSocketRcvBufSize() > 0) {
            log.info("server set SO_RCVBUF to {}", nettyServerConfig.getServerSocketRcvBufSize());
            childHandler.childOption(ChannelOption.SO_RCVBUF, nettyServerConfig.getServerSocketRcvBufSize());
        }
        // 设置写缓冲区水位
        if (nettyServerConfig.getWriteBufferLowWaterMark() > 0 && nettyServerConfig.getWriteBufferHighWaterMark() > 0) {
            log.info("server set netty WRITE_BUFFER_WATER_MARK to {},{}",
                    nettyServerConfig.getWriteBufferLowWaterMark(), nettyServerConfig.getWriteBufferHighWaterMark());
            childHandler.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(
                    nettyServerConfig.getWriteBufferLowWaterMark(), nettyServerConfig.getWriteBufferHighWaterMark()));
        }
        // 是否开启内存池分配机制
        if (nettyServerConfig.isServerPooledByteBufAllocatorEnable()) {
            childHandler.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        }

        try {
            // 绑定端口,同步获取长连接通道
            ChannelFuture sync = this.serverBootstrap.bind().sync();
            InetSocketAddress addr = (InetSocketAddress) sync.channel().localAddress();
            this.port = addr.getPort();
        } catch (InterruptedException e1) {
            throw new RuntimeException("this.serverBootstrap.bind().sync() InterruptedException", e1);
        }

        if (this.channelEventListener != null) {
            // 启动网络异常事件监听器
            this.nettyEventExecutor.start();
        }
        // 启动扫描超时请求任务
        this.timer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
                try {
                    NettyRemotingServer.this.scanResponseTable();
                } catch (Throwable e) {
                    log.error("scanResponseTable exception", e);
                }
            }
        }, 1000 * 3, 1000);
    }

然后启动了文件监听服务:

  1. 使用CAS并发控制方式,控制仅启动一次;
  2. 初始化停止标志为false,代表正在运行;
  3. 启动后台线程;
    public void start() {
        log.info("Try to start service thread:{} started:{} lastThread:{}", getServiceName(), started.get(), thread);
        // CAS方式进行并发控制,避免重复启动
        if (!started.compareAndSet(false, true)) {
            return;
        }
        // 初始化停止标志
        stopped = false;
        // 启动线程,run函数实现在子类中
        this.thread = new Thread(this, getServiceName());
        this.thread.setDaemon(isDaemon);
        this.thread.start();
    }

最后看看如何关闭NamesrvController:

    public void shutdown() {
        // 关闭网络服务器
        this.remotingServer.shutdown();
        // 关闭网络服务器执行线程池
        this.remotingExecutor.shutdown();
        // 关闭定时调度任务线程池
        this.scheduledExecutorService.shutdown();
        // 关闭文件监听服务
        if (this.fileWatchService != null) {
            this.fileWatchService.shutdown();
        }
    }

 this.remotingServer.shutdown()的具体关闭逻辑:

    public void shutdown() {
        try {
            // 关闭定时器
            if (this.timer != null) {
                this.timer.cancel();
            }
            // 关闭物理网络连接管理器
            this.eventLoopGroupBoss.shutdownGracefully();
            // 关闭数据传输网络连接管理器
            this.eventLoopGroupSelector.shutdownGracefully();
            // 关闭网络异常事件监听器
            if (this.nettyEventExecutor != null) {
                this.nettyEventExecutor.shutdown();
            }
            // 关闭默认线程池
            if (this.defaultEventExecutorGroup != null) {
                this.defaultEventExecutorGroup.shutdownGracefully();
            }
        } catch (Exception e) {
            log.error("NettyRemotingServer shutdown exception, ", e);
        }

        if (this.publicExecutor != null) {
            try {
                this.publicExecutor.shutdown();
            } catch (Exception e) {
                log.error("NettyRemotingServer shutdown exception, ", e);
            }
        }
    }

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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值