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

 前言

RocketMQ在启动Broker过程会创建BorkerContoller控制器,初始化大量配置、核心组件、启动大量后台线程。从部署架构中可以看出它主要负责与Namesrv建立netty长连接,上报Routing Info路由信息,也与Producer Cluster和Consumer Cluster建立netty长连接,发送、接收消息。

源码版本:4.9.3

rocketmq部署架构

源码分析

源码入口

broker模块下的 org.apache.rocketmq.broker.BrokerStartup#main

主流程

今天看主流程,它的源码特别清晰,先创建BorkerController控制器对象,在启动控制器,就可以了。那么我们下面,深入一层细看一下怎么创建和怎么启动的?

    public static void main(String[] args) {
        // 创建BrokerController控制器,且启动
        start(createBrokerController(args));
    }

    public static BrokerController start(BrokerController controller) {
        try {
            // 启动broker控制器
            controller.start();

            String tip = "The broker[" + controller.getBrokerConfig().getBrokerName() + ", "
                + controller.getBrokerAddr() + "] boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer();

            if (null != controller.getBrokerConfig().getNamesrvAddr()) {
                tip += " and name server is " + controller.getBrokerConfig().getNamesrvAddr();
            }

            log.info(tip);
            System.out.printf("%s%n", tip);
            return controller;
        } catch (Throwable e) {
            e.printStackTrace();
            System.exit(-1);
        }

        return null;
    }

创建Borker控制器流程

  1. 设置系统属性,rocketmq版本;
  2. 解析命令行参数到命令行对象;
  3. 初始化核心配置,borker配置、nettyServer配置、nettyClient配置;
  4. 初始消息存储配置,如果是slave从节点,则减少内存最大占用10%,默认主节点(异步复制)不做内存占用优化;
  5. 如果命令号参数中有-c参数,则从指定磁盘文件中加载配置文件;
  6. 对环境变量和namsrv进行解析校验;
  7. 设置brokerId,默认角色ASYNC_MASTER(主节点且异步复制)设置为0,从节点设置为必须大于0;
  8. 如果是Dleger 高可用模式,则将brokerId设置为 -1,因为该模式下,会自动进行选主操作,后边会安排单独的笔记分享;
  9. 设置监听的高可用端口;
  10. 初始化日志组件;
  11. 处理命令行参数,如果包含 -p,则在控制台打印配置信息,并退出程序;
  12. 如果包含命令行参数,则在控制台 仅仅打印重要的配置信息,并退出程序;
  13. 打印配置信息到日志文件;
  14. 【重要】创建BrokerController控制器实例;
  15. 初始化BrokerController控制器实例;
  16. 注册JVM钩子,在JVM关闭时执行钩子回调,进行BrokerController控制器实例关闭处理;
    public static BrokerController createBrokerController(String[] args) {
        // 设置系统属性,rocketmq版本
        System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));

        try {
            //PackageConflictDetect.detectFastjson();
            // 创建了一个命令行配置项
            Options options = ServerUtil.buildCommandlineOptions(new Options());
            // 解析启动参数到命令行
            commandLine = ServerUtil.parseCmdLine("mqbroker", args, buildCommandlineOptions(options),
                new PosixParser());
            if (null == commandLine) {
                System.exit(-1);
            }

            // 配置初始化,broker配置,nettyServer配置,nettyClient配置
            final BrokerConfig brokerConfig = new BrokerConfig();
            final NettyServerConfig nettyServerConfig = new NettyServerConfig();
            final NettyClientConfig nettyClientConfig = new NettyClientConfig();

            nettyClientConfig.setUseTLS(Boolean.parseBoolean(System.getProperty(TLS_ENABLE,
                String.valueOf(TlsSystemConfig.tlsMode == TlsMode.ENFORCING))));
            // broker作为server服务端的监听的端口号
            nettyServerConfig.setListenPort(10911);

            // 存储配置,
            final MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
            // 如果是slave,则减少内存占用 10%,默认主节点(异步复制)不做内存占用优化
            if (BrokerRole.SLAVE == messageStoreConfig.getBrokerRole()) {
                int ratio = messageStoreConfig.getAccessMessageInMemoryMaxRatio() - 10;
                messageStoreConfig.setAccessMessageInMemoryMaxRatio(ratio);
            }

            // 如果命令行参数中有-c参数,则加载配置文件
            if (commandLine.hasOption('c')) {
                String file = commandLine.getOptionValue('c');
                if (file != null) {
                    configFile = file;
                    InputStream in = new BufferedInputStream(new FileInputStream(file));
                    properties = new Properties();
                    properties.load(in);

                    properties2SystemEnv(properties);
                    MixAll.properties2Object(properties, brokerConfig);
                    MixAll.properties2Object(properties, nettyServerConfig);
                    MixAll.properties2Object(properties, nettyClientConfig);
                    MixAll.properties2Object(properties, messageStoreConfig);

                    BrokerPathConfigHelper.setBrokerConfigPath(file);
                    in.close();
                }
            }

            MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), brokerConfig);

            // 必须配置在环境中配置 ROCKETMQ_HOME 地址
            if (null == brokerConfig.getRocketmqHome()) {
                System.out.printf("Please set the %s variable in your environment to match the location of the RocketMQ installation", MixAll.ROCKETMQ_HOME_ENV);
                System.exit(-2);
            }

            // 对集群化部署的namesrv地址进行解析校验
            String namesrvAddr = brokerConfig.getNamesrvAddr();
            if (null != namesrvAddr) {
                try {
                    String[] addrArray = namesrvAddr.split(";");
                    for (String addr : addrArray) {
                        RemotingUtil.string2SocketAddress(addr);
                    }
                } catch (Exception e) {
                    System.out.printf(
                        "The Name Server Address[%s] illegal, please set it as follows, \"127.0.0.1:9876;192.168.0.1:9876\"%n",
                        namesrvAddr);
                    System.exit(-3);
                }
            }

            // 设置brokerId,默认角色为ASYNC_MASTER(主节点 异步复制)
            switch (messageStoreConfig.getBrokerRole()) {
                // 主节点,必须设置为 0
                case ASYNC_MASTER:
                case SYNC_MASTER:
                    brokerConfig.setBrokerId(MixAll.MASTER_ID);
                    break;

                // 从节点,brokerId必须大于0
                case SLAVE:
                    if (brokerConfig.getBrokerId() <= 0) {
                        System.out.printf("Slave's brokerId must be > 0");
                        System.exit(-3);
                    }

                    break;
                default:
                    break;
            }

            // 如果是分布式集群(HA高可用模式),则brokerId必须为-1
            if (messageStoreConfig.isEnableDLegerCommitLog()) {
                brokerConfig.setBrokerId(-1);
            }
            // 设置ha ListenPort 比系统端口号大1,避免与系统端口冲突
            messageStoreConfig.setHaListenPort(nettyServerConfig.getListenPort() + 1);

            // 初始化日志组件
            LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
            JoranConfigurator configurator = new JoranConfigurator();
            configurator.setContext(lc);
            lc.reset();
            System.setProperty("brokerLogDir", "");
            if (brokerConfig.isIsolateLogEnable()) {
                System.setProperty("brokerLogDir", brokerConfig.getBrokerName() + "_" + brokerConfig.getBrokerId());
            }
            if (brokerConfig.isIsolateLogEnable() && messageStoreConfig.isEnableDLegerCommitLog()) {
                System.setProperty("brokerLogDir", brokerConfig.getBrokerName() + "_" + messageStoreConfig.getdLegerSelfId());
            }
            configurator.doConfigure(brokerConfig.getRocketmqHome() + "/conf/logback_broker.xml");

            // 如果包含参数 -p,则在控制台打印配置信息,并退出
            if (commandLine.hasOption('p')) {
                InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);
                MixAll.printObjectProperties(console, brokerConfig);
                MixAll.printObjectProperties(console, nettyServerConfig);
                MixAll.printObjectProperties(console, nettyClientConfig);
                MixAll.printObjectProperties(console, messageStoreConfig);
                System.exit(0);
            } else if (commandLine.hasOption('m')) {
                // 在控制台仅仅打印重要配置信息
                InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);
                MixAll.printObjectProperties(console, brokerConfig, true);
                MixAll.printObjectProperties(console, nettyServerConfig, true);
                MixAll.printObjectProperties(console, nettyClientConfig, true);
                MixAll.printObjectProperties(console, messageStoreConfig, true);
                System.exit(0);
            }

            // 打印配置信息到日志文件
            log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
            MixAll.printObjectProperties(log, brokerConfig);
            MixAll.printObjectProperties(log, nettyServerConfig);
            MixAll.printObjectProperties(log, nettyClientConfig);
            MixAll.printObjectProperties(log, messageStoreConfig);

            // 创建BrokerController实例
            final BrokerController controller = new BrokerController(
                brokerConfig,
                nettyServerConfig,
                nettyClientConfig,
                messageStoreConfig);
            // remember all configs to prevent discard
            controller.getConfiguration().registerConfig(properties);

            // 初始化BrokerController控制器
            boolean initResult = controller.initialize();
            if (!initResult) {
                // 初始化失败,自动关闭,然后退出程序
                controller.shutdown();
                System.exit(-3);
            }

            // 注册JVM钩子,在JVM关闭时执行关闭操作
            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
                private volatile boolean hasShutdown = false;
                private AtomicInteger shutdownTimes = new AtomicInteger(0);

                @Override
                public void run() {
                    synchronized (this) {
                        log.info("Shutdown hook was invoked, {}", this.shutdownTimes.incrementAndGet());
                        if (!this.hasShutdown) {
                            this.hasShutdown = true;
                            long beginTime = System.currentTimeMillis();

                            // 关闭控制器
                            controller.shutdown();
                            long consumingTimeTotal = System.currentTimeMillis() - beginTime;
                            log.info("Shutdown hook over, consuming total time(ms): {}", consumingTimeTotal);
                        }
                    }
                }
            }, "ShutdownHook"));

            return controller;
        } catch (Throwable e) {
            e.printStackTrace();
            System.exit(-1);
        }

        return null;
    }

 启动Broker控制器流程

启动控制流程涉及的组件非常多,跟broker本身的复杂度有关系,详细的流程已经体现在下面的代码注释中。

    public void start() throws Exception {
        // 启动消息存储组件
        if (this.messageStore != null) {
            this.messageStore.start();
        }
        // 启动RemotingServer网络通信服务器
        if (this.remotingServer != null) {
            this.remotingServer.start();
        }

        if (this.fastRemotingServer != null) {
            this.fastRemotingServer.start();
        }
        // 启动文件监听服务(监控本地磁盘文件中配置TLS安全加密文件的变更)
        if (this.fileWatchService != null) {
            this.fileWatchService.start();
        }
        // 启动broker对外API服务
        if (this.brokerOuterAPI != null) {
            this.brokerOuterAPI.start();
        }
        // 启动Pull请求方式服务
        if (this.pullRequestHoldService != null) {
            this.pullRequestHoldService.start();
        }
        // 启动客户端HouseKeeping服务(长连接变更监听器)
        if (this.clientHousekeepingService != null) {
            this.clientHousekeepingService.start();
        }
        // 启动filter服务
        if (this.filterServerManager != null) {
            this.filterServerManager.start();
        }
        // 启动broker主从同步服务
        if (!messageStoreConfig.isEnableDLegerCommitLog()) {
            startProcessorByHa(messageStoreConfig.getBrokerRole());
            handleSlaveSynchronize(messageStoreConfig.getBrokerRole());
            this.registerBrokerAll(true, false, true);
        }
        
        // 注册一个定时任务,每隔10秒注册Broker信息到NameServer(心跳)
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                try {
                    BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
                } catch (Throwable e) {
                    log.error("registerBrokerAll Exception", e);
                }
            }
        }, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);

        // 启动Broker统计信息服务
        if (this.brokerStatsManager != null) {
            this.brokerStatsManager.start();
        }

        // 
        if (this.brokerFastFailure != null) {
            this.brokerFastFailure.start();
        }
    }

关闭Broker控制器流程

整体流程很清晰,就如关闭大量的服务组件和后台线程池,如下:

    public void shutdown() {
        // 关闭broker统计信息服务
        if (this.brokerStatsManager != null) {
            this.brokerStatsManager.shutdown();
        }
        // 关闭网络连接监听服务
        if (this.clientHousekeepingService != null) {
            this.clientHousekeepingService.shutdown();
        }
        // 关闭拉取请求服务
        if (this.pullRequestHoldService != null) {
            this.pullRequestHoldService.shutdown();
        }
        // 关闭网络通信服务器
        if (this.remotingServer != null) {
            this.remotingServer.shutdown();
        }
        // 关闭网络通信服务器
        if (this.fastRemotingServer != null) {
            this.fastRemotingServer.shutdown();
        }
        // 关闭文件监听服务
        if (this.fileWatchService != null) {
            this.fileWatchService.shutdown();
        }
        // 关闭消息存储服务
        if (this.messageStore != null) {
            this.messageStore.shutdown();
        }
        // 关闭定时任务服务
        this.scheduledExecutorService.shutdown();
        try {
            this.scheduledExecutorService.awaitTermination(5000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
        }
        // 删除namesrv注册的broker信息
        this.unregisterBrokerAll();
        // 关闭发送消息线程池
        if (this.sendMessageExecutor != null) {
            this.sendMessageExecutor.shutdown();
        }
        // 关闭接受消息Future线程池
        if (this.putMessageFutureExecutor != null) {
            this.putMessageFutureExecutor.shutdown();
        }
        // 关闭拉取消息线程池
        if (this.pullMessageExecutor != null) {
            this.pullMessageExecutor.shutdown();
        }
        // 关闭回查消息线程池
        if (this.replyMessageExecutor != null) {
            this.replyMessageExecutor.shutdown();
        }
        // 关闭后台管理服务线程池
        if (this.adminBrokerExecutor != null) {
            this.adminBrokerExecutor.shutdown();
        }
        // 关闭外部接口服务
        if (this.brokerOuterAPI != null) {
            this.brokerOuterAPI.shutdown();
        }
        // 持久化消费进度
        this.consumerOffsetManager.persist();
        // 关闭过滤器服务
        if (this.filterServerManager != null) {
            this.filterServerManager.shutdown();
        }
        // 关闭快速失败服务
        if (this.brokerFastFailure != null) {
            this.brokerFastFailure.shutdown();
        }
        // 持久化filter消费进度
        if (this.consumerFilterManager != null) {
            this.consumerFilterManager.persist();
        }
        // 关闭客户端管理服务
        if (this.clientManageExecutor != null) {
            this.clientManageExecutor.shutdown();
        }
        // 关闭查询消息线程池
        if (this.queryMessageExecutor != null) {
            this.queryMessageExecutor.shutdown();
        }
        // 关闭消费管理线程池
        if (this.consumerManageExecutor != null) {
            this.consumerManageExecutor.shutdown();
        }
        // 关闭文件监听服务
        if (this.fileWatchService != null) {
            this.fileWatchService.shutdown();
        }
        // 关闭事务消息检查服务
        if (this.transactionalMessageCheckService != null) {
            this.transactionalMessageCheckService.shutdown(false);
        }
        // 关闭事务消息结束服务
        if (this.endTransactionExecutor != null) {
            this.endTransactionExecutor.shutdown();
        }
    }

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值