RocketMQ源码深度解析二之Name Server篇

来源:互联网 发布:转换视频格式的软件 编辑:程序博客网 时间:2024/06/11 01:35

前言:主要是用于管理所有的broker信息,让producer和consumer都能获取到正确的broker信息,进行业务处理。这是一个类似于zookeeper的服务治理中心。

(一)功能
1,Broker启动时候会向Namesrv发送注册请求,Namesrv接收broker的请求注册路由信息和保存活跃的broker列表,包括Master和Slave
2,用来保存所有topic和该topic所有队列的列表
3,NameServer用来保存所有broker的Filter列表
4,接收Producer和Consumer的请求,根据某个topic获取到broker的路由信息。

(二)初始化和启动过程
1,KVConfigManager类
该类会加载NameServer的配置参数,将配置参数加载保存到一个HashMap中

2,以初始化 BrokerHousekeepingService 对象为参数初始化
NettyRemotingServer 对象, BrokerHousekeepingService 对象作为该 Netty 连接中 Socket 链接的监听器( ChannelEventListener);监听与 Broker 建立的渠道的状态(空闲、关闭、异常三个状态),并调用 BrokerHousekeepingService的相应 onChannel****方法。其中渠道的空闲、关闭、异常状态均调用RouteInfoManager.onChannelDestory 方法处理。清理 RouteInfoManager 类的几个成员变量数据
这里写图片描述

3,注册默认的处理类 DefaultRequestProcessor,所有的请求均由该处理类的 processRequest 方法来处理。

4,设置两个定时任务
(1)每隔十秒钟遍历brokerLiveTable集合,查看每个broker的最后更新时间是否超过了两分钟,超过则关闭broker的渠道并清理 RouteInfoManager 类的topicQueueTable、 brokerAddrTable、 clusterAddrTable、 filterServerTable成员变量
(2)每隔 10 分钟打印一次 NameServer 的配置参数。即KVConfigManager.configTable 变量的内容。

5,启动 NameServer 的 Netty 服务端( NettyRemotingServer),监听渠道的请求信息。当收到客户端的请求信息之后会初始化一个线程,并放入线程池中进行处理,该线程调用 DefaultRequestProcessor. processRequest 方法来处理请求。

(三)处理broker的注册请求
如果收到REGISTER_BROKER请求,那么最终会调用到RouteInfoManager.registerBroker。注册完成后,返回给Broker端主用Broker的地址和主用Broker的HA服务地址

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();                /*                    维护 RouteInfoManager.clusterAddrTable 变量; 若 Broker 集群名字不                    在该 Map 变量中,则初始化一个 Set 集合,将 brokerName 存入该 Set 集合中,                    然后以 clusterName 为 key 值,该 Set 集合为 values 值存入此 Map 变量中                */                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;                /*                    维护 RouteInfoManager.brokerAddrTable 变量,该变量是维护 Broker                    的名称、 ID、地址等信息的。 若该 brokername 不在该 Map 变量中,则创建                    BrokerData 对象,该对象包含了 brokername,以及 brokerId 和 brokerAddr 为                    K-V 的 brokerAddrs 变量;然后以 brokername 为 key 值将 BrokerData 对象存入                    该 brokerAddrTable 变量中; 说明同一个 BrokerName 下面可以有多个不同                    BrokerId 的 Broker 存在,表示一个 BrokerName 有多个 Broker 存在,通过                    BrokerId 来区分主备                */                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);                }                String oldAddr = brokerData.getBrokerAddrs().put(brokerId, brokerAddr);                registerFirst = registerFirst || (null == oldAddr);                //若 Broker 的注册请求消息中 topic 的配置不为空,并且该 Broker 是主用(即 brokerId=0)                if (null != topicConfigWrapper //                    && MixAll.MASTER_ID == brokerId) {                    //则根据 NameServer 存储的 Broker 版本信息来判断是否需要更新 NameServer 端的 topic 配置信息                    if (this.isBrokerTopicConfigChanged(brokerAddr, topicConfigWrapper.getDataVersion())//                        || registerFirst) {                        ConcurrentMap<String, TopicConfig> tcTable =                            topicConfigWrapper.getTopicConfigTable();                        if (tcTable != null) {                            for (Map.Entry<String, TopicConfig> entry : tcTable.entrySet()) {                                this.createAndUpdateQueueData(brokerName, entry.getValue());                            }                        }                    }                }                //初始化 BrokerLiveInfo 对象并以 broker 地址为 key 值存入brokerLiveTable:HashMap<String/* brokerAddr */, BrokerLiveInfo>变量中                BrokerLiveInfo prevBrokerLiveInfo = this.brokerLiveTable.put(brokerAddr,                    new BrokerLiveInfo(                        System.currentTimeMillis(),                        topicConfigWrapper.getDataVersion(),                        channel,                        haServerAddr));                if (null == prevBrokerLiveInfo) {                    log.info("new broker registerd, {} HAServer: {}", brokerAddr, haServerAddr);                }                //对于 filterServerList 不为空的, 以 broker 地址为 key 值存入                if (filterServerList != null) {                    if (filterServerList.isEmpty()) {                        this.filterServerTable.remove(brokerAddr);                    } else {                        this.filterServerTable.put(brokerAddr, filterServerList);                    }                }                //找到该 BrokerName 下面的主用 Broker( BrokerId=0)                if (MixAll.MASTER_ID != brokerId) {                    String masterAddr = brokerData.getBrokerAddrs().get(MixAll.MASTER_ID);                    if (masterAddr != null) {                        //主用 Broker 地址从brokerLiveTable 中获取 BrokerLiveInfo 对象,取该对象的 HaServerAddr 值                        BrokerLiveInfo brokerLiveInfo = this.brokerLiveTable.get(masterAddr);                        if (brokerLiveInfo != null) {                            result.setHaServerAddr(brokerLiveInfo.getHaServerAddr());                            result.setMasterAddr(masterAddr);                        }                    }                }            } finally {                this.lock.writeLock().unlock();            }        } catch (Exception e) {            log.error("registerBroker Exception", e);        }        return result;    }

(四)根据Topic获取Broker信息和topic配置信息
接收到GET_ROUTEINTO_BY_TOPIC请求之后,间接调用了RouteInfoManager.pickupTopicRouteData方法来获取Broker和topic信息。
1、获取 topic 配置信息,根据 topic 从 RouteInfoManager.topicQueueTable变量中获取 List队列, 赋值给返回对象 TopicRouteData 的QueueDatas 变量。 表示该 topic 对应的所有 topic 配置信息以及每个配置所属的 BrokerName。
2、 从上一步获取到的 List队列中获取 BrokerName 集合,该集合是去重之后的 BrokerName 集合,然后以该 BrokerName 集合的每个 BrokerName从 RouteInfoManager.brokerAddrTable 变量中获取 BrokerData 对象,将所有获取到的 BrokerData 对象集合赋值给返回对象 TopicRouteData 的 BrokerDatas集合变量。表示该 topic 是由哪些 Broker 提供的服务,以及每个 Broker 的名字、BrokerId、 IP 地址。
3、然后以“ ORDER_TOPIC_CONFIG”和请求消息中的 topic 值为参数从NamesrvController.kvConfigManager.configTable: HashMap变量中获取orderTopiconf 值(即 broker 的顺序),并赋值给TopicRouteData.orderTopicConf 变量;该 orderTopiconf 的参数格式为:以“ ;”解析成数组,数组的每个成员是以“ :”分隔的,构成数据 “ brokerName:queueNum”;最后将 TopicRouteData 对象返回给调用者

(五)为什么放弃zookeeper
(1)在RocketMQ中,topic在每个master的数据是对等的,没有哪个master有全部的topic数据,所以选举是没有意义的
(2)NameServer与NameServer直接完全没有信息同步,所以与其依赖重量级的zookeeper,不如开发轻量级的NameServer

原创粉丝点击