2、RocketMQ NameSrc 源码

启动

org.apache.rocketmq.namesrv.NamesrvStartup#start

加载参数、配置文件…
System.getProperty() 略过

初始化

public boolean initialize() {  
    //加载kvConfigPath下kvConfig.json配置文件里的KV配置,然后将这些配置放到KVConfigManager#configTable属性中  
    loadConfig();  
    // 初始化 netty 服务器  
    initiateNetworkComponents();  
    // 初始化负责处理 Netty 网络交互的线程池,默认线程树 16 个,队列 LinkedBlockingQueue 10000  
    initiateThreadExecutors();  
    // 注册 Netty 服务端业务处理逻辑,如果开启了clusterTest,那么注册的请求处理类是ClusterTestRequestProcessor,否则请求处理类是DefaultRequestProcessor  
    registerProcessor();  
    // 开启定时任务线程池,1、扫描下线的 Broker 2、打印 KV 配置  
    startScheduleService();  
    // Tls 提高数据传输安全性 是否启用  
    initiateSslContext();  
    // rpc 钩子函数  
    initiateRpcHooks();  
    return true;}

优雅停机:

Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
    @Override
    public Void call() throws Exception {
        controller.shutdown();
        return null;
    }
}));

接着是

controller.start();

是启动 NettySerser 和 NettyClient 进行远程通信。

还有一个是启动路由 manager 的 BatchUnregistrationService 对 Borker 的一个快速下线的管理。
但是 RemoteManager 这个类的变量配置是非常重要的,包括存储了重要的元数据信息。

private final Map<String/* topic */, Map<String, QueueData>> topicQueueTable;  
private final Map<String/* brokerName */, BrokerData> brokerAddrTable;  
private final Map<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;  
private final Map<BrokerAddrInfo/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;  
private final Map<BrokerAddrInfo/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;  
private final Map<String/* topic */, Map<String/*brokerName*/, TopicQueueMappingInfo>> topicQueueMappingInfoTable;
1)topicQueueTable:topic消息队列的路由信息,消息发送时根据路由表进行负载均衡。
2)brokerAddrTable:Broker基础信息,包含brokerName、所属集群名称、主备Broker地址。
3)clusterAddrTable:Broker集群信息,存储集群中所有Broker的名称。
4)brokerLiveTable:Broker状态信息,NameServer每次收到心跳包时会替换该信息。
5)filterServerTable:Broker上的FilterServer列表,用于类模式消息过滤。类模式过滤机制在4.4及以后版本被废弃。

TopicQueueTable

数据结构:HashMap 结构,key 是 Topic 名字,value 是一个类型是 QueueData 的队列集合。在第一章就讲过,一个 Topic 中有多个队列。QueueData 的数据结构如下:
image.png

topicQueueTable:{
    "topic1": [
        {
            "brokerName": "broker-a",
            "readQueueNums":4,
            "writeQueueNums":4,
            "perm":6,
            "topicSynFlag":0,
        },
        {
            "brokerName": "broker-b",
            "readQueueNums":4,
            "writeQueueNums":4,
            "perm":6,
            "topicSynFlag":0,
        }
    ]
}

疑问🤔️:为什么要搞读、写队列数目?

BrokerAddrTable

数据结构:HashMap 结构,key 是 BrokerName,value 是一个类型是 BrokerData 的对象。BrokerData 的数据结构如下(可以结合下面 Broker 主从结构逻辑图来理解):
image.png
![[image-20230401151758242.png]]

Broker 主从结构逻辑图:
image.png

BrokerAddrTable:{
    "broker-a": {
        "cluster": "c 1",
        "brokerName": "broker-a",
        "brokerAddrs": {
            0: "192.168.1.1:10000",
            1: "192.168.1.2:10000"
        }
    },
    "broker-b": {
        "cluster": "c 1",
        "brokerName": "broker-b",
        "brokerAddrs": {
            0: "192.168.1.3:10000",
            1: "192.168.1.4:10000"
        }
    }
}

ClusterAddrTable

数据结构:HashMap 结构,key 是 ClusterName,value 是存储 BrokerName 的 Set 结构。

clusterAddrTable:{
    "c1": ["broker-a","broker-b"]
}

BrokerLiveTable

说明:Broker 状态信息。NameServer 每次收到心跳包时会替换该信息
数据结构:HashMap 结构,key 是 Broker 的地址,value 是 BrokerLiveInfo 结构的该 Broker 信息对象。BrokerLiveInfo 的数据结构如下:
image.png

brokerLiveTable:{
    "192.168.1.1:10000": {
            "lastUpdateTimestamp": 1518270318980,
            "dataVersion":versionObj1,
            "channel":channelObj,
            "haServerAddr":""
    },
    "192.168.1.2:10000": {
            "lastUpdateTimestamp": 1518270318980,
            "dataVersion":versionObj1,
            "channel":channelObj,
            "haServerAddr":"192.168.1.1:10000"
     },
    "192.168.1.3:10000": {
            "lastUpdateTimestamp": 1518270318980,
            "dataVersion":versionObj1,
            "channel":channelObj,
            "haServerAddr":""
     },
    "192.168.1.4:10000": {
            "lastUpdateTimestamp": 1518270318980,
            "dataVersion":versionObj1,
            "channel":channelObj,
            "haServerAddr":"192.168.1.3:10000"
     }
}

FilterServerTable

说明:Broker 上的 FilterServer 列表,消息过滤服务器列表,后续介绍 Consumer 时会介绍,consumer 拉取数据是通过 filterServer 拉取,consumer 向 Broker 注册。
数据结构:HashMap 结构,key 是 Broker 地址,value 是记录了 filterServer 地址的 List 集合。

注册


分为两步
1、BorkerCntroller 发起注册请求
2、NameSrcController 接受处理

Broker 发送心跳

BrokerStartup 是 Broker 启动类,加载参数、配置文件和 NameSpace 一样,不再过多的说明。
比如会创建心跳线程池等等都是在
首先是找到核心的入口 org.apache.rocketmq.broker.BrokerController#start()

可以看到使用定时任务线程池,10s 延迟启动,每隔 30s 执行一次心跳请求。

scheduledFutures.add(this.scheduledExecutorService.scheduleAtFixedRate(new AbstractBrokerRunnable(this.getBrokerIdentity()) {  
    @Override  
    public void run0() {  
        try {  
	        // 心跳发送逻辑
            BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());  
        } catch (Throwable e) {  
            BrokerController.LOG.error("registerBrokerAll Exception", e);  
        }  
    }  
}, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS));

注册心跳请求:

public List<Boolean> needRegister(  
    final String clusterName,  
    final String brokerAddr,  
    final String brokerName,  
    final long brokerId,  
    final TopicConfigSerializeWrapper topicConfigWrapper,  
    final int timeoutMills,  
    final boolean isInBrokerContainer) {  
    final List<Boolean> changedList = new CopyOnWriteArrayList<>();  
    // 获取 nameServerAddressList  
    List<String> nameServerAddressList = this.remotingClient.getNameServerAddressList();  
    if (nameServerAddressList != null && nameServerAddressList.size() > 0) {  
        final CountDownLatch countDownLatch = new CountDownLatch(nameServerAddressList.size());  
        // 并发请求 每个 NameServer   
for (final String namesrvAddr : nameServerAddressList) {  
            brokerOuterExecutor.execute(new AbstractBrokerRunnable(new BrokerIdentity(clusterName, brokerName, brokerId, isInBrokerContainer)) {  
                @Override  
                public void run0() {  
                    try {  
                        // 封装 header  
                        QueryDataVersionRequestHeader requestHeader = new QueryDataVersionRequestHeader();  
                        requestHeader.setBrokerAddr(brokerAddr);  
                        requestHeader.setBrokerId(brokerId);  
                        requestHeader.setBrokerName(brokerName);  
                        requestHeader.setClusterName(clusterName);  
                        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.QUERY_DATA_VERSION, requestHeader);  
                        // 封装 Body  
                        request.setBody(topicConfigWrapper.getDataVersion().encode());  
                        // 调用得到 response  
                        RemotingCommand response = remotingClient.invokeSync(namesrvAddr, request, timeoutMills);  
                        DataVersion nameServerDataVersion = null;  
                        Boolean changed = false;  
                        switch (response.getCode()) {  
                            // 存储 NameServer 结果  
                            case ResponseCode.SUCCESS: {  
                                QueryDataVersionResponseHeader queryDataVersionResponseHeader =  
                                    (QueryDataVersionResponseHeader) response.decodeCommandCustomHeader(QueryDataVersionResponseHeader.class);  
                                changed = queryDataVersionResponseHeader.getChanged();  
                                byte[] body = response.getBody();  
                                if (body != null) {  
                                    nameServerDataVersion = DataVersion.decode(body, DataVersion.class);  
                                    if (!topicConfigWrapper.getDataVersion().equals(nameServerDataVersion)) {  
                                        changed = true;  
                                    }  
                                }  
                                if (changed == null || changed) {  
                                    changedList.add(Boolean.TRUE);  
                                }  
                            }  
                            default:  
                                break;  
                        }  
                        LOGGER.warn("Query data version from name server {} OK, changed {}, broker {},name server {}", namesrvAddr, changed, topicConfigWrapper.getDataVersion(), nameServerDataVersion == null ? "" : nameServerDataVersion);  
                    } catch (Exception e) {  
                        changedList.add(Boolean.TRUE);  
                        LOGGER.error("Query data version from name server {}  Exception, {}", namesrvAddr, e);  
                    } finally {  
                        countDownLatch.countDown();  
                    }  
                }  
            });  
  
        }  
        try {  
            countDownLatch.await(timeoutMills, TimeUnit.MILLISECONDS);  
        } catch (InterruptedException e) {  
            LOGGER.error("query dataversion from nameserver countDownLatch await Exception", e);  
        }  
    }  
    return changedList;  
}

最后去处理返回结果,更新本地存储信息

NameServer 接受处理心跳包

主要的处理逻辑在 org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor#processRequest

public RemotingCommand processRequest(ChannelHandlerContext ctx,  
    RemotingCommand request) throws RemotingCommandException {  
    switch (request.getCode()) {  
        ... 
        case RequestCode.REGISTER_BROKER:  
            // 注册 Broker  
            return this.registerBroker(ctx, request);  
        ...
        default:  
            String error = " request type " + request.getCode() + " not supported";  
            return RemotingCommand.createResponseCommand(RemotingSysResponseCode.REQUEST_CODE_NOT_SUPPORTED, error);  
    }  
}

this.registerBroker(ctx, request) 这里面

public RegisterBrokerResult registerBroker(  
    final String clusterName,  
    final String brokerAddr,  
    final String brokerName,  
    final long brokerId,  
    final String haServerAddr,  
    final String zoneName,  
    final Long timeoutMillis,  
    final Boolean enableActingMaster,  
    final TopicConfigSerializeWrapper topicConfigWrapper,  
    final List<String> filterServerList,  
    final Channel channel) {  
    RegisterBrokerResult result = new RegisterBrokerResult();  
    try {  
        //加写锁,防止并发写RoutInfoManager中的路由表信息。  
        this.lock.writeLock().lockInterruptibly();  
  
        //init or update the cluster info  
        //根据clusterName从clusterAddrTable中获取所有broker名字集合        Set<String> brokerNames = ConcurrentHashMapUtils.computeIfAbsent((ConcurrentHashMap<String, Set<String>>) this.clusterAddrTable, clusterName, k -> new HashSet<>());  
        brokerNames.add(brokerName);  
  
        boolean registerFirst = false;  
  
        //根据brokerName尝试从brokerAddrTable中获取brokerData  
        BrokerData brokerData = this.brokerAddrTable.get(brokerName);  
        if (null == brokerData) {  
            //如果没获取到brokerData,新建BrokerData并放入brokerAddrTable,registerFirst设为true;  
            registerFirst = true;  
            brokerData = new BrokerData(clusterName, brokerName, new HashMap<>());  
            this.brokerAddrTable.put(brokerName, brokerData);  
        }  
  
        boolean isOldVersionBroker = enableActingMaster == null;  
        brokerData.setEnableActingMaster(!isOldVersionBroker && enableActingMaster);  
        brokerData.setZoneName(zoneName);  
  
        //更新brokerData中的brokerAddrs  
        Map<Long, String> brokerAddrsMap = brokerData.getBrokerAddrs();  
  
        boolean isMinBrokerIdChanged = false;  
        long prevMinBrokerId = 0;  
        if (!brokerAddrsMap.isEmpty()) {  
            prevMinBrokerId = Collections.min(brokerAddrsMap.keySet());  
        }  
  
        if (brokerId < prevMinBrokerId) {  
            isMinBrokerIdChanged = true;  
        }  
  
        //Switch slave to master: first remove <1, IP:PORT> in namesrv, then add <0, IP:PORT>  
        //The same IP:PORT must only have one record in brokerAddrTable        brokerAddrsMap.entrySet().removeIf(item -> null != brokerAddr && brokerAddr.equals(item.getValue()) && brokerId != item.getKey());  
  
        //If Local brokerId stateVersion bigger than the registering one,  
        String oldBrokerAddr = brokerAddrsMap.get(brokerId);  
        if (null != oldBrokerAddr && !oldBrokerAddr.equals(brokerAddr)) {  
            BrokerLiveInfo oldBrokerInfo = brokerLiveTable.get(new BrokerAddrInfo(clusterName, oldBrokerAddr));  
  
            if (null != oldBrokerInfo) {  
                long oldStateVersion = oldBrokerInfo.getDataVersion().getStateVersion();  
                long newStateVersion = topicConfigWrapper.getDataVersion().getStateVersion();  
                if (oldStateVersion > newStateVersion) {  
                    log.warn("Registered Broker conflicts with the existed one, just ignore.: Cluster:{}, BrokerName:{}, BrokerId:{}, " +  
                            "Old BrokerAddr:{}, Old Version:{}, New BrokerAddr:{}, New Version:{}.",  
                        clusterName, brokerName, brokerId, oldBrokerAddr, oldStateVersion, brokerAddr, newStateVersion);  
                    //Remove the rejected brokerAddr from brokerLiveTable.  
                    brokerLiveTable.remove(new BrokerAddrInfo(clusterName, brokerAddr));  
                    return result;  
                }  
            }  
        }    
        boolean isMaster = MixAll.MASTER_ID == brokerId;  
        boolean isPrimeSlave = !isOldVersionBroker && !isMaster  
            && brokerId == Collections.min(brokerAddrsMap.keySet());  
        //如过Broker是Master,并且Broker的Topic配置信息发生变化或者是首次注册,需要创建或更新Topic路由元数据,填充topicQueueTable  
        if (null != topicConfigWrapper && (isMaster || isPrimeSlave)) {  
            ConcurrentMap<String, TopicConfig> tcTable =  
                topicConfigWrapper.getTopicConfigTable();  
            if (tcTable != null) {  
                for (Map.Entry<String, TopicConfig> entry : tcTable.entrySet()) {  
                    if (registerFirst || this.isTopicConfigChanged(clusterName, brokerAddr,  
                        topicConfigWrapper.getDataVersion(), brokerName,  
                        entry.getValue().getTopicName())) {  
                        final TopicConfig topicConfig = entry.getValue();  
                        if (isPrimeSlave) {  
                            // Wipe write perm for prime slave  
                            topicConfig.setPerm(topicConfig.getPerm() & (~PermName.PERM_WRITE));  
                        }  
                        //创建或更新Topic路由元数据  
                        this.createAndUpdateQueueData(brokerName, topicConfig);  
                    }  
                }  
            }  
  
        // 注册到 brokerLiveTable 存活心跳表  
        BrokerAddrInfo brokerAddrInfo = new BrokerAddrInfo(clusterName, brokerAddr);  
        BrokerLiveInfo prevBrokerLiveInfo = this.brokerLiveTable.put(brokerAddrInfo,  
            new BrokerLiveInfo(  
                System.currentTimeMillis(),  
                timeoutMillis == null ? DEFAULT_BROKER_CHANNEL_EXPIRED_TIME : timeoutMillis,  
                topicConfigWrapper == null ? new DataVersion() : topicConfigWrapper.getDataVersion(),  
                channel,  
                haServerAddr));  
        if (null == prevBrokerLiveInfo) {  
            log.info("new broker registered, {} HAService: {}", brokerAddrInfo, haServerAddr);  
        }  
  
        // 更新过滤 List filterServerList  
        if (filterServerList != null) {  
            if (filterServerList.isEmpty()) {  
                this.filterServerTable.remove(brokerAddrInfo);  
            } else {  
                this.filterServerTable.put(brokerAddrInfo, filterServerList);  
            }  
        }  
  
        // 如果 broker 是从节点,则需要查找Broker Master的节点信息,并更新对应masterAddr属性  
        if (MixAll.MASTER_ID != brokerId) {  
            String masterAddr = brokerData.getBrokerAddrs().get(MixAll.MASTER_ID);  
            if (masterAddr != null) {  
                BrokerAddrInfo masterAddrInfo = new BrokerAddrInfo(clusterName, masterAddr);  
                BrokerLiveInfo masterLiveInfo = this.brokerLiveTable.get(masterAddrInfo);  
                if (masterLiveInfo != null) {  
                    result.setHaServerAddr(masterLiveInfo.getHaServerAddr());  
                    result.setMasterAddr(masterAddr);  
                }  
            }  
        }  
    } catch (Exception e) {  
        log.error("registerBroker Exception", e);  
    } finally {  
        this.lock.writeLock().unlock();  
    }  
  
    return result;  
}

路由删除


NameServer 会每隔10s 扫描一次 brokerLiveTable 状态表,如果 BrokerLive 的 lastUpdate-Timestamp 时间戳距当前时间超过120s,则认为 Broker 失效,移除该 Broker,关闭与 Broker 的连接,同时更新 topicQueueTable、brokerAddrTable、brokerLiveTable、filterServerTable。

两种操作:
1、定时扫描,超过 120s 则移除该 Broker 信息
2、正常关闭

第一种定时扫描:
org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager#scanNotActiveBroker

public void scanNotActiveBroker() {  
    try {  
        log.info("start scanNotActiveBroker");  
        for (Entry<BrokerAddrInfo, BrokerLiveInfo> next : this.brokerLiveTable.entrySet()) {  
            long last = next.getValue().getLastUpdateTimestamp();  
            long timeoutMillis = next.getValue().getHeartbeatTimeoutMillis();  
            if ((last + timeoutMillis) < System.currentTimeMillis()) {  
                RemotingHelper.closeChannel(next.getValue().getChannel());  
                log.warn("The broker channel expired, {} {}ms", next.getKey(), timeoutMillis);  
                this.onChannelDestroy(next.getKey());  
            }  
        }  
    } catch (Exception e) {  
        log.error("scanNotActiveBroker exception", e);  
    }  
}

RouteInfoManager#onChannelDestroy :// remove Broker 入口,主要是投递到一个 Queue 中,然后有一个线程来拉 Queue 中需要删除的 broker 关键的代码就不给了
RouteInfoManager#unRegisterBroker:删除 Broker

路由删除整体逻辑主要分为 6 步:
Step 1:加 readlock,通过 channel 从 BrokerLiveTable 中找出对应的 Broker 地址,释放 readlock,若该 Broker 已经从存活的 Broker 地址列表中被清除,则直接使用 remoteAddr。
Step 2:申请写锁,根据 BrokerAddress 从 BrokerLiveTable、filterServerTable 移除。
Step 3:遍历 BrokerAddrTable,根据 BrokerAddress 找到对应的 brokerData,并将 brokerData 中对应的 brokerAddress 移除,如果移除后,整个 brokerData 的 brokerAddress 空了,那么将整个 brokerData 移除。
Step 4:遍历 clusterAddrTable,根据第三步中获取的需要移除的 BrokerName,将对应的 brokerName 移除了。如果移除后,该集合为空,那么将整个集群从 clusterAddrTable 中移除。
Step 5:遍历 TopicQueueTable,根据 BrokerName,将 Topic 下对应的 Broker 移除掉,如果该 Topic 下只有一个待移除的 Broker,那么该 Topic 也从 table 中移除。
Step 6:释放写锁。

深入剖析 RocketMQ 源码-NameServer
原文链接: https://xie.infoq.cn/article/3289c20816913170b160b82a6

路由发现


说明:RocketMQ 路由发现是非实时的,当 topic 路由出现变化后,NameServer 不主动推送给客户端,而是由客户端定时拉取主题最新的路由。为了降低 NameSpace 实现的复杂性

Producer 主动拉取

代码入口是 MQClientInstance#start-ScheduledTask()

private void startScheduledTask() {
    ......
    this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
​
        @Override
        public void run() {
            try {
                //从nameserver更新最新的topic路由信息
                MQClientInstance.this.updateTopicRouteInfoFromNameServer();
            } catch (Exception e) {
                log.error("ScheduledTask updateTopicRouteInfoFromNameServer exception", e);
            }
        }
    }, 10, this.clientConfig.getPollNameServerInterval(), TimeUnit.MILLISECONDS);
​
    ......
}
​
/**
    * 从nameserver获取topic路由信息
    */
public TopicRouteData getTopicRouteInfoFromNameServer(final String topic, final long timeoutMillis,
                                                      boolean allowTopicNotExist) throws MQClientException, InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException {
    ......
    //向nameserver发送请求包,requestCode为RequestCode.GET_ROUTEINFO_BY_TOPIC
    RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ROUTEINFO_BY_TOPIC, requestHeader);
  ......
}

可以看到
1、定时任务线程池是 30s 拉一次。
2、更新使用的是 tryLock () 过期时间也是 30s,防止高并发场景
3、请求的 RequestCode 是 RequestCode.GET_ROUTEINFO_BY_TOPIC

NameServer 返回路由信息

NameServer 收到 producer 发送的请求后,会根据请求中的 requestCode 进行处理。处理 requestCode 同样是在默认的网络处理器 DefaultRequestProcessor 中进行处理,最终通过 RouteInfoManager#pickupTopicRouteData 来实现。

TopicRouteData 结构
image.png

public TopicRouteData pickupTopicRouteData(final String topic) {
    TopicRouteData topicRouteData = new TopicRouteData();
    boolean foundQueueData = false;
    boolean foundBrokerData = false;
    Set<String> brokerNameSet = new HashSet<String>();
    List<BrokerData> brokerDataList = new LinkedList<BrokerData>();
    topicRouteData.setBrokerDatas(brokerDataList);
​
    HashMap<String, List<String>> filterServerMap = new HashMap<String, List<String>>();
    topicRouteData.setFilterServerTable(filterServerMap);
​
    try {
        try {
            //加读锁
            this.lock.readLock().lockInterruptibly();
            //从元数据topicQueueTable中根据topic名字获取队列集合
            List<QueueData> queueDataList = this.topicQueueTable.get(topic);
            if (queueDataList != null) {
                //将获取到的队列集合写入topicRouteData的queueDatas中
                topicRouteData.setQueueDatas(queueDataList);
                foundQueueData = true;
​
                Iterator<QueueData> it = queueDataList.iterator();
                while (it.hasNext()) {
                    QueueData qd = it.next();
                    brokerNameSet.add(qd.getBrokerName());
                }
        //遍历从QueueData集合中提取的brokerName
                for (String brokerName : brokerNameSet) {
                    //根据brokerName从brokerAddrTable获取brokerData
                    BrokerData brokerData = this.brokerAddrTable.get(brokerName);
                    if (null != brokerData) {
                        //克隆brokerData对象,并写入到topicRouteData的brokerDatas中
                        BrokerData brokerDataClone = new BrokerData(brokerData.getCluster(), brokerData.getBrokerName(), (HashMap<Long, String>) brokerData.getBrokerAddrs().clone());
                        brokerDataList.add(brokerDataClone);
                        foundBrokerData = true;
                        //遍历brokerAddrs
                        for (final String brokerAddr : brokerDataClone.getBrokerAddrs().values()) {
                            //根据brokerAddr获取filterServerList,封装后写入到topicRouteData的filterServerTable中
                            List<String> filterServerList = this.filterServerTable.get(brokerAddr);
                            filterServerMap.put(brokerAddr, filterServerList);
                        }
                    }
                }
            }
        } finally {
            //释放读锁
            this.lock.readLock().unlock();
        }
    } catch (Exception e) {
        log.error("pickupTopicRouteData Exception", e);
    }
​
    log.debug("pickupTopicRouteData {} {}", topic, topicRouteData);
​
    if (foundBrokerData && foundQueueData) {
        return topicRouteData;
    }
​
    return null;
}

上面代码封装了 TopicRouteData 的 queueDatas、BrokerDatas 和 filterServerTable,还有 orderTopicConf 字段没封装,我们再看下这个字段是在什么时候封装的,我们向上看 RouteInfoManager#pickupTopicRouteData 的调用方法 DefaultRequestProcessor#getRouteInfoByTopic 如下:

public RemotingCommand getRouteInfoByTopic(ChannelHandlerContext ctx,
                                           RemotingCommand request) throws RemotingCommandException {
    ......
  //这块代码就是上面解析的代码,获取到topicRouteData对象
    TopicRouteData topicRouteData = this.namesrvController.getRouteInfoManager().pickupTopicRouteData(requestHeader.getTopic());
​
    if (topicRouteData != null) {
        //判断nameserver的orderMessageEnable配置是否打开
        if (this.namesrvController.getNamesrvConfig().isOrderMessageEnable()) {
            //如果配置打开了,根据namespace和topic名字获取kvConfig配置文件中顺序消息配置内容
            String orderTopicConf =
                this.namesrvController.getKvConfigManager().getKVConfig(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG,
                                                                        requestHeader.getTopic());
            //封装orderTopicConf
            topicRouteData.setOrderTopicConf(orderTopicConf);
        }
​
        byte[] content = topicRouteData.encode();
        response.setBody(content);
        response.setCode(ResponseCode.SUCCESS);
        response.setRemark(null);
        return response;
    }
  //如果没有获取到topic路由,那么reponseCode为TOPIC_NOT_EXIST
    response.setCode(ResponseCode.TOPIC_NOT_EXIST);
    response.setRemark("No topic route info in name server for the topic: " + requestHeader.getTopic()
                       + FAQUrl.suggestTodo(FAQUrl.APPLY_TOPIC_URL));
    return response;
}

结合这两个方法,我们可以总结出查找 Topic 路由主要分为 3 个步骤:

调用 RouteInfoManager#pickupTopicRouteData ,从 topicQueueTable, brokerAddrTabl,filterServerTable 中获取信息,分别填充 queue-Datas、BrokerDatas、filterServerTable。
如果 topic 为顺序消息,那么从 KVconfig 中获取关于顺序消息先关的配置填充到 orderTopicConf 中。
如果找不到路由信息,那么返回 code 为 ResponseCode.TOPIC_NOT_EXIST。

总结


给我们很多提示
1、JVM 优雅停机 + hook 函数的使用
2、读写锁防止并发编程 lockInterruptibly()、tryLock ()…

注意的点
需要注意的点是 NameSpace 中 RouteManger 的变量很关键。
基本上所有路由信息都是对变量进行操作的。

todo
1、netty 通信

问题

1、RocketMQ 如何保证路由发现、删除时的并发呢?
2、lockInterruptibly()、tryLock()、tryLock(过期时间) 的区别?
3、底层为什么要保持长链接?是怎么做的?
4、讲讲 RocketMQ 如何实现路由注册、发现、删除的?
5、RocketMQ 为什么采取让 Product 自己主动拉的策略?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值