RocketMQ源码阅读-Name Server

1,147 阅读3分钟

1、 RocketMQ 源码包说明

RocketMQ源码包含以下文件夹:

RocketMQ源码包说明

2、源码阅读

以 rocketmq-all-4.2.0版本做源码解读:

2.1 Namer Server启动流程图

核心类:

  • NamesrvStartup;
  • NamesrvController;

2.1.1 NamesrvStartup类

  1. 启动入口方法 public static NamesrvController main0(String[] args)
  • 第一步, 脚本以及启动参数配置。
  • 第二步, new一个NamesrvController,initialize()初始化NamesrvController。
  • 第三步, 添加JVM Hook,Hook 调用shutdown()方法关闭Namesrv服务。
  • 第四步, 调用start()方法。 如下代码:
创建NamesrvController, 初始化initialize()
            final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);

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

            boolean initResult = controller.initialize();
            if (!initResult) {
                controller.shutdown();
                System.exit(-3);
            }

初始化方法如下:

    public boolean initialize() {

        this.kvConfigManager.load();  //代码解释1

        this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService); //代码解释2

        this.remotingExecutor =
            Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_")); //代码解释1

        this.registerProcessor(); //代码解释3

        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                NamesrvController.this.routeInfoManager.scanNotActiveBroker();
            }
        }, 5, 10, TimeUnit.SECONDS);//代码解释 4

        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                NamesrvController.this.kvConfigManager.printAllPeriodically();
            }
        }, 1, 10, TimeUnit.MINUTES);        //代码解释5

        return true;
    }
  • //代码解释1: 加载KV配置;
  • //代码解释3: 注册namesrv process处理类,用于处理namesrv 接收的请求命令,具体处理类 DefaultRequestProcessor.java

优雅关机
添加JVM Hook
            Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    controller.shutdown();
                    return null;
                }
            }));
关闭代码:
    public void shutdown() {
        this.remotingServer.shutdown(); 
        this.remotingExecutor.shutdown();
        this.scheduledExecutorService.shutdown();
    }
  • 关闭Netty服务;
  • 关闭Namesrv接口处理线程;
  • 关闭定时任务线程池;
启动NamesrvController
controller.start();

2.1.2 NamesrvController 类

类图核心属性及方法

核心属性:

  • namesrvConfig: Name Server 配置参数;

  • nettyServerConfig: RocketMQ各组件基于Netty通信,netty参数;

  • scheduledExecutorService: 定时任务线程池;

  • remotingServer: Name Server 作为Netty服务端实现类;

  • routeInfoManager: 路由元数据管理器;

2.2 剖析RouteInfoManager 代码:

    public class RouteInfoManager {
        private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fd9d3661729749f99f7f8fe292d4cf2d~tplv-k3u1fbpfcp-watermark.image)
        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;

        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);
        }
  • topicQueueTable: Topic消息队列路由信息;
  • brokerAddrTable: Broker基础信息;
  • clusterAddrTable: 集群基础信息;
  • brokerLiveTable: Broker状态信息;
  • filterServerTable: Broker的FliterServer列表信息;

相关元数据类图

2.3 路由注册

根据上文可知道Namesrv 处理类 DefaultRequestProcessor.java,用于处理接收命令:

@Override
    public RemotingCommand processRequest(ChannelHandlerContext ctx,
        RemotingCommand request) throws RemotingCommandException {
        if (log.isDebugEnabled()) {
            log.debug("receive request, {} {} {}",
                request.getCode(),
                RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
                request);
        }

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

可知路由注册命令 RequestCode.REGISTER_BROKER,执行registerBrokerWithFilterServer()方法

public RemotingCommand registerBrokerWithFilterServer(ChannelHandlerContext ctx, RemotingCommand request)
        throws RemotingCommandException {
        final RemotingCommand response = RemotingCommand.createResponseCommand(RegisterBrokerResponseHeader.class);
        final RegisterBrokerResponseHeader responseHeader = (RegisterBrokerResponseHeader) response.readCustomHeader();
        final RegisterBrokerRequestHeader requestHeader =
            (RegisterBrokerRequestHeader) request.decodeCommandCustomHeader(RegisterBrokerRequestHeader.class);

        RegisterBrokerBody registerBrokerBody = new RegisterBrokerBody();

        if (request.getBody() != null) {
            registerBrokerBody = RegisterBrokerBody.decode(request.getBody(), RegisterBrokerBody.class);
        } else {
            registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setCounter(new AtomicLong(0));
            registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setTimestamp(0);
        }

        RegisterBrokerResult result = this.namesrvController.getRouteInfoManager().registerBroker(
            requestHeader.getClusterName(),
            requestHeader.getBrokerAddr(),
            requestHeader.getBrokerName(),
            requestHeader.getBrokerId(),
            requestHeader.getHaServerAddr(),
            registerBrokerBody.getTopicConfigSerializeWrapper(),
            registerBrokerBody.getFilterServerList(),
            ctx.channel());

        responseHeader.setHaServerAddr(result.getHaServerAddr());
        responseHeader.setMasterAddr(result.getMasterAddr());

        byte[] jsonValue = this.namesrvController.getKvConfigManager().getKVListByNamespace(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG);
        response.setBody(jsonValue);

        response.setCode(ResponseCode.SUCCESS);
        response.setRemark(null);
        return response;
    }

RouteInfoManager#registerBroker方法:

public RegisterBrokerResult registerBroker(
            final String clusterName,
            final String brokerAddr,
            final String brokerName,
            final long brokerId,
            final String haServerAddr,
            final TopicConfigSerializeWrapper topicConfigWrapper,
            final List<String> filterServerList,
            final Channel channel) {
        RegisterBrokerResult result = new RegisterBrokerResult();
        try {
            try {
                this.lock.writeLock().lockInterruptibly();

                Set<String> brokerNames = this.clusterAddrTable.get(clusterName);
                if (null == brokerNames) {
                    brokerNames = new HashSet<String>();
                    this.clusterAddrTable.put(clusterName, brokerNames);
                }
                brokerNames.add(brokerName);

                boolean registerFirst = false;

                BrokerData brokerData = this.brokerAddrTable.get(brokerName);
                if (null == brokerData) {
                    registerFirst = true;
                    brokerData = new BrokerData(clusterName, brokerName, new HashMap<Long, String>());
                    this.brokerAddrTable.put(brokerName, brokerData);
                }
                log.info("broker data is {}", JSONObject.toJSONString(brokerData));
                String oldAddr = brokerData.getBrokerAddrs().put(brokerId, brokerAddr);
                registerFirst = registerFirst || (null == oldAddr);

2.4 心跳检测

2.5 路由剔除

Namesrv 路由剔除有两种方式:

  • 第一种,Broker主动关闭,调用 Namesrv的接口命令 RequestCode.UNREGISTER_BROKER。
  • 第二种,Namesrv通过定时扫描已经下线的Broker,将其剔除。
      this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                NamesrvController.this.routeInfoManager.scanNotActiveBroker();
            }
        }, 5, 10, TimeUnit.SECONDS);
    public void scanNotActiveBroker() {
        Iterator<Entry<String, BrokerLiveInfo>> it = this.brokerLiveTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, BrokerLiveInfo> next = it.next();
            long last = next.getValue().getLastUpdateTimestamp();
            if ((last + BROKER_CHANNEL_EXPIRED_TIME) < System.currentTimeMillis()) {
                RemotingUtil.closeChannel(next.getValue().getChannel());
                it.remove();
                log.warn("The broker channel expired, {} {}ms", next.getKey(), BROKER_CHANNEL_EXPIRED_TIME);
                this.onChannelDestroy(next.getKey(), next.getValue().getChannel());
            }
        }
    }

频率每10s执行一次,便利brokerLiveInfo表,检查lastUpdateTimestamp上次收到心跳包的时间如果超过120s,Namesrv就认为该Broker不可用,将其remove(), 关闭Channel,删除相关信息。