RocketMQ源码解析之NameServer启动和停止

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/heroqiang/article/details/85988438

阅读须知

  • 文章中使用/* */注释的方法会做深入分析

正文

关于RocketMQ源码的下载和构建,这里不多重复,很简单,相信大家都搞的定。

在前面的文章中,我们分析了RocketMQ的整体结构,其中也说明了NameServer的RocketMQ体系中的作用。在启动NameServer时,我们会使用类似下面的命令:

nohup sh bin/mqnamesrv &

在mqnamesrv脚本中的最后一行,我们看到这样的语句:

sh ${ROCKETMQ_HOME}/bin/runserver.sh org.apache.rocketmq.namesrv.NamesrvStartup $@

这里我们就看到NameServer启动时的主类了,也就是NamesrvStartup,我们来看它的main方法实现:

public static void main(String[] args) {
    main0(args);
}

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

下面会涉及到org.apache.commons.cli的使用,我们来简单说明一下:Apache Commons CLI库提供了一个API,用于解析传递给程序的命令行选项。 它还能够打印命令行工具可用选项的详细说明和帮助消息。

使用时我需要需要引入maven依赖:

<dependency>
    <groupId>commons-cli</groupId>
    <artifactId>commons-cli</artifactId>
    <version>1.4</version>
</dependency>

NamesrvStartup:

public static NamesrvController createNamesrvController(String[] args) throws IOException, JoranException {
    System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));
    // 构建命令行选项,添加-h -n选项
    Options options = ServerUtil.buildCommandlineOptions(new Options());
    // 添加-c -p选项,解析命令行
    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);
    // -c选项可以指定NameServer配置文件的地址
    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);
            // 将配置文件中的配置通过反射setter方法设置到NamesrvConfig和NettyServerConfig的对应属性上
            MixAll.properties2Object(properties, namesrvConfig);
            MixAll.properties2Object(properties, nettyServerConfig);
            namesrvConfig.setConfigStorePath(file);
            System.out.printf("load config properties file OK, %s%n", file);
            in.close();
        }
    }
    // -p选项可以把所有配置的条目打印到控制台
    if (commandLine.hasOption('p')) {
        InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_CONSOLE_NAME);
        MixAll.printObjectProperties(console, namesrvConfig);
        MixAll.printObjectProperties(console, nettyServerConfig);
        System.exit(0);
    }
    // 将命令行选项的完整名称(eg:-n --> namesrvAddr)对应的选项值设置到NettyServerConfig的对应属性上
    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);
    // 日志打印NamesrvConfig和NettyServerConfig和配置
    MixAll.printObjectProperties(log, namesrvConfig);
    MixAll.printObjectProperties(log, nettyServerConfig);
    final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);
    // 记住所有的配置,以防止被丢弃掉
    controller.getConfiguration().registerConfig(properties);
    return controller;
}

NamesrvStartup:

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);
    }
    // 添加关闭钩子,在停止系统时关闭控制器
    Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
        @Override
        public Void call() throws Exception {
            controller.shutdown();
            return null;
        }
    }));
    /* 启动控制器 */
    controller.start();
    return controller;
}

NamesrvController:

public boolean initialize() {
    // 加载KV配置表
    this.kvConfigManager.load();
    /* 构造NettyRemotingServer */
    this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);
    // remoting线程池
    this.remotingExecutor =
        Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_"));
    // 注册默认请求处理器,用于每个请求代码在处理器表中没有完全匹配的情况,处理请求时使用的线程池即为remotingExecutor
    this.registerProcessor();
    this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            /* 每隔10秒扫描未激活的broker */
            NamesrvController.this.routeInfoManager.scanNotActiveBroker();
        }
    }, 5, 10, TimeUnit.SECONDS);
    this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            // 每隔10分钟打印KV配置表的内容
            NamesrvController.this.kvConfigManager.printAllPeriodically();
        }
    }, 1, 10, TimeUnit.MINUTES);
    if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {
        // 注册一个监听器以重新加载SslContext
        try {
            fileWatchService = new FileWatchService(
                new String[] {
                    TlsSystemConfig.tlsServerCertPath,
                    TlsSystemConfig.tlsServerKeyPath,
                    TlsSystemConfig.tlsServerTrustCertPath
                },
                new FileWatchService.Listener() {
                    boolean certChanged, keyChanged = false;
                    @Override
                    public void onChanged(String path) {
                        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;
}

NettyRemotingServer:

public NettyRemotingServer(final NettyServerConfig nettyServerConfig,
    final ChannelEventListener channelEventListener) {
    // 指定单向和异步信号量的容量
    super(nettyServerConfig.getServerOnewaySemaphoreValue(), nettyServerConfig.getServerAsyncSemaphoreValue());
    this.serverBootstrap = new ServerBootstrap();
    this.nettyServerConfig = nettyServerConfig;
    this.channelEventListener = channelEventListener;
    int publicThreadNums = nettyServerConfig.getServerCallbackExecutorThreads();
    // 公共线程池数量,默认4
    if (publicThreadNums <= 0) {
        publicThreadNums = 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());
        }
    });
    // 判断是否使用epoll构建selector线程池(其实就是我们常说的worker线程)
    // 判断条件:linux平台 && 用户指定了使用epoll && epoll可用
    if (useEpoll()) {
        // boss线程池
        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 {
        // boss线程池
        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()));
            }
        });
        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();
}

这里涉及到netty的API调用,不熟悉netty可以查阅相关资料,也可以查看笔者关于netty的源码分析文章。
RouteInfoManager:

public void scanNotActiveBroker() {
    Iterator<Entry<String, BrokerLiveInfo>> it = this.brokerLiveTable.entrySet().iterator();
    // 遍历存活的broker表
    while (it.hasNext()) {
        Entry<String, BrokerLiveInfo> next = it.next();
        // 最新的更新时间
        long last = next.getValue().getLastUpdateTimestamp();
        // broker channel过期时间,两分钟
        if ((last + BROKER_CHANNEL_EXPIRED_TIME) < System.currentTimeMillis()) {
            // 关闭channel
            RemotingUtil.closeChannel(next.getValue().getChannel());
            // 从存活的broker表中移除
            it.remove();
            log.warn("The broker channel expired, {} {}ms", next.getKey(), BROKER_CHANNEL_EXPIRED_TIME);
            /* 触发channel销毁动作 */
            this.onChannelDestroy(next.getKey(), next.getValue().getChannel());
        }
    }
}

channel销毁后的动作主要就是一些资源的清理工作:
RouteInfoManager:

public void onChannelDestroy(String remoteAddr, Channel channel) {
    String brokerAddrFound = null;
    if (channel != null) {
        try {
            try {
                this.lock.readLock().lockInterruptibly();
                // 遍历存活的broker列表
                Iterator<Entry<String, BrokerLiveInfo>> itBrokerLiveTable =
                    this.brokerLiveTable.entrySet().iterator();
                while (itBrokerLiveTable.hasNext()) {
                    Entry<String, BrokerLiveInfo> entry = itBrokerLiveTable.next();
                    if (entry.getValue().getChannel() == channel) {
                        // 记录本次要销毁的broker地址
                        brokerAddrFound = entry.getKey();
                        break;
                    }
                }
            } finally {
                this.lock.readLock().unlock();
            }
        } catch (Exception e) {
            log.error("onChannelDestroy Exception", e);
        }
    }
    if (null == brokerAddrFound) {
        brokerAddrFound = remoteAddr;
    } else {
        log.info("the broker's channel destroyed, {}, clean it's data structure at once", brokerAddrFound);
    }
    if (brokerAddrFound != null && brokerAddrFound.length() > 0) {
        try {
            try {
                this.lock.writeLock().lockInterruptibly();
                // 从存活的broker列表中移除
                this.brokerLiveTable.remove(brokerAddrFound);
                // 从过滤器服务列表中移除
                this.filterServerTable.remove(brokerAddrFound);
                String brokerNameFound = null;
                boolean removeBrokerName = false;
                // 遍历broker地址列表 key:brokerName value:broker名称、地址等数据
                Iterator<Entry<String, BrokerData>> itBrokerAddrTable =
                    this.brokerAddrTable.entrySet().iterator();
                while (itBrokerAddrTable.hasNext() && (null == brokerNameFound)) {
                    BrokerData brokerData = itBrokerAddrTable.next().getValue();
                    // 遍历BrokerData中的地址列表
                    Iterator<Entry<Long, String>> it = brokerData.getBrokerAddrs().entrySet().iterator();
                    while (it.hasNext()) {
                        Entry<Long, String> entry = it.next();
                        Long brokerId = entry.getKey();
                        String brokerAddr = entry.getValue();
                        // 匹配本次要移除的地址
                        if (brokerAddr.equals(brokerAddrFound)) {
                            // 记录broker的名称
                            brokerNameFound = brokerData.getBrokerName();
                            // 移除
                            it.remove();
                            log.info("remove brokerAddr[{}, {}] from brokerAddrTable, because channel destroyed",
                                brokerId, brokerAddr);
                            break;
                        }
                    }
                    // 如果移除后BrokerData中的地址列表为空了,则此brokerName需要移除
                    if (brokerData.getBrokerAddrs().isEmpty()) {
                        removeBrokerName = true;
                        // 移除
                        itBrokerAddrTable.remove();
                        log.info("remove brokerName[{}] from brokerAddrTable, because channel destroyed",
                            brokerData.getBrokerName());
                    }
                }
                if (brokerNameFound != null && removeBrokerName) {
                    // 集群名称 --> broker列表
                    Iterator<Entry<String, Set<String>>> it = this.clusterAddrTable.entrySet().iterator();
                    while (it.hasNext()) {
                        Entry<String, Set<String>> entry = it.next();
                        String clusterName = entry.getKey();
                        Set<String> brokerNames = entry.getValue();
                        // 从集群的broker列表中移除此broker
                        boolean removed = brokerNames.remove(brokerNameFound);
                        if (removed) {
                            log.info("remove brokerName[{}], clusterName[{}] from clusterAddrTable, because channel destroyed",
                                brokerNameFound, clusterName);
                            // 如果移除broker后集群的broker列表为空,则移除此集群名称
                            if (brokerNames.isEmpty()) {
                                log.info("remove the clusterName[{}] from clusterAddrTable, because channel destroyed and no broker in this cluster",
                                    clusterName);
                                it.remove();
                            }
                            break;
                        }
                    }
                }
                if (removeBrokerName) {
                    // topic --> 队列信息
                    Iterator<Entry<String, List<QueueData>>> itTopicQueueTable =
                        this.topicQueueTable.entrySet().iterator();
                    while (itTopicQueueTable.hasNext()) {
                        Entry<String, List<QueueData>> entry = itTopicQueueTable.next();
                        String topic = entry.getKey();
                        List<QueueData> queueDataList = entry.getValue();
                        Iterator<QueueData> itQueueData = queueDataList.iterator();
                        while (itQueueData.hasNext()) {
                            QueueData queueData = itQueueData.next();
                            // 判断队列信息中是否包含本次移除的brokerName,包含则移除此队列
                            if (queueData.getBrokerName().equals(brokerNameFound)) {
                                itQueueData.remove();
                                log.info("remove topic[{} {}], from topicQueueTable, because channel destroyed",
                                    topic, queueData);
                            }
                        }
                        // 如果移除后队列信息列表为空,则移除此topic队列映射
                        if (queueDataList.isEmpty()) {
                            itTopicQueueTable.remove();
                            log.info("remove topic[{}] all queue, from topicQueueTable, because channel destroyed",
                                topic);
                        }
                    }
                }
            } finally {
                this.lock.writeLock().unlock();
            }
        } catch (Exception e) {
            log.error("onChannelDestroy Exception", e);
        }
    }
}

接下来就是控制器的启动:
NamesrvController:

public void start() throws Exception {
    /* 启动netty server */
    this.remotingServer.start();
    if (this.fileWatchService != null) {
        // 启动重新加载SslContext监听器线程
        this.fileWatchService.start();
    }
}

NettyRemotingServer:

public void start() {
    // 构建默认事件执行器线程组
    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());
            }
        });
    // netty api的调用,构建ServerBootstrap
    ServerBootstrap childHandler =
        this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector)
            .channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 1024)
            .option(ChannelOption.SO_REUSEADDR, true)
            .option(ChannelOption.SO_KEEPALIVE, false)
            .childOption(ChannelOption.TCP_NODELAY, true)
            .childOption(ChannelOption.SO_SNDBUF, nettyServerConfig.getServerSocketSndBufSize())
            .childOption(ChannelOption.SO_RCVBUF, nettyServerConfig.getServerSocketRcvBufSize())
            .localAddress(new InetSocketAddress(this.nettyServerConfig.getListenPort()))
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline()
                        // 添加握手请求处理器
                        .addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME,
                            new HandshakeHandler(TlsSystemConfig.tlsMode))
                        .addLast(defaultEventExecutorGroup,
                            // 编码器
                            new NettyEncoder(),
                            // 解码器
                            new NettyDecoder(),
                            // netty提供的handler,用于当Channel没有执行读、写或两者操作时,触发IdleStateEvent
                            new IdleStateHandler(0, 0, nettyServerConfig.getServerChannelMaxIdleTimeSeconds()),
                            // 连接管理
                            new NettyConnectManageHandler(),
                            // 请求和响应处理器
                            new NettyServerHandler()
                        );
                }
            });
    if (nettyServerConfig.isServerPooledByteBufAllocatorEnable()) {
        childHandler.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
    }
    try {
        // 将channel的socket绑定到本地地址,并配置socket以侦听连接,同步等待
        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);
}

NettyRemotingAbstract:

public void scanResponseTable() {
    // 记录所有移除的请求
    final List<ResponseFuture> rfList = new LinkedList<ResponseFuture>();
    // responseTable缓存所有正在进行的请求
    Iterator<Entry<Integer, ResponseFuture>> it = this.responseTable.entrySet().iterator();
    while (it.hasNext()) {
        Entry<Integer, ResponseFuture> next = it.next();
        ResponseFuture rep = next.getValue();
        // 判断请求是否过期
        if ((rep.getBeginTimestamp() + rep.getTimeoutMillis() + 1000) <= System.currentTimeMillis()) {
            // 释放信号量
            rep.release();
            // 移除当前请求
            it.remove();
            // 添加到移除请求列表中
            rfList.add(rep);
            log.warn("remove timeout request, " + rep);
        }
    }
    for (ResponseFuture rf : rfList) {
        try {
            // 在回调执行器中执行回调。如果回调执行器为null,则直接在当前线程中运行
            executeInvokeCallback(rf);
        } catch (Throwable e) {
            log.warn("scanResponseTable, operationComplete Exception", e);
        }
    }
}

最后我们来看停止动作:

public void shutdown() {
    /* 停止NettyRemotingServer */
    this.remotingServer.shutdown();
    // 停止remoting线程池
    this.remotingExecutor.shutdown();
    // 停止定时任务线程池
    this.scheduledExecutorService.shutdown();
    if (this.fileWatchService != null) {
        // 停止SslContext监听器
        this.fileWatchService.shutdown();
    }
}
public void shutdown() {
    try {
        if (this.timer != null) {
            // 取消timer
            this.timer.cancel();
        }
        // 停止boss线程池
        this.eventLoopGroupBoss.shutdownGracefully();
        // 停止worker线程池
        this.eventLoopGroupSelector.shutdownGracefully();
        if (this.nettyEventExecutor != null) {
            // 停止netty事件执行器
            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);
        }
    }
}

到这里,NameServer的启动和停止流程就分析完成了。

猜你喜欢

转载自blog.csdn.net/heroqiang/article/details/85988438