在上一章分析了NamesrvController的构造函数时,会生成一个RouteInfoManager对象,该对象存放着整个消息集群的相关消息,所以这里单独拿出来分析。其实试想一下namesrv的功能不就是一个提供了通信功能的一个队列嘛,而RouteInfoManager保留了所有信息的路由。所以要想弄明白RocketMQ,RouteInfoManager必须要攻下。
  
  RouteInfoManager的构造函数
  
  主要提供了topic、broker、cluster、liveBroker的路由信息。
  
  public class RouteInfoManager {
  
  private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
  
  private final static long BROKER_CHANNEL_EXPIRED_TIME = 1000 * 60 * 2;
  
  // 读写锁
  
  private final ReadWriteLock lock = new ReentrantReadWriteLock();
  
  // Topic,以及对应的队列信息
  
  private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;
  
  // 以Broker Name为单位的Broker集合
  
  private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
  
  // 集群以及属于该集群的Broker列表
  
  private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
  
  // 存活的Broker地址列表
  
  private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;
  
  // Broker对应的Filter Server列表
  
  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
  
  private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;
  
  1
  
  存放了Topic的一个hashMap,在RocketMQ的插件中RocketMQ控制台,可以看到Topic的列表,这个就是topicQueueTable的一个可视化:
  
  查看QueueData的类信息:
  
  public class QueueData implements Comparable<QueueData> {
  
  // 队列所属的Broker名称
  
  private String brokerName;
  
  // 读队列数量
  
  private int readQueueNums;
  
  // 写队列数量
  
  private int writeQueueNums;
  
  // Topic的读写权限(2是写 4是读 6是读写)
  
  private int perm;
  
  private int topicSynFlag;
  
  点开一个一个topic的配置可以看到和上面类对应的变量:
  
  clusterAddrTable
  
  private final HashMap<String/* clusterName www.quwanyule157.com*/, Set<String/* brokerName */>> clusterAddrTable;
  
  1
  
  根据一个集群名,获得对应的一组BrokerName的列表,在可视化界面就是如下:
  
  虽然只得到了BrokerName,只要调用对应的接口就能得到Broker对应的属性值。
  
  brokerAddrTable
  
  private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
  
  1
  
  以Broker Name为Key,BrokerData为Value:
  
  public class BrokerData implements Comparable<BrokerData> {
  
  private String cluster;
  
  private String brokerName;
  
  //同一个brokerName下可以有一个Master和多个Slave,所以brokerAddrs是一个集合
  
  private HashMap<Long/* brokerId */, String/* broker address */> brokerAddrs;
  
  private final Random random = new Random();
  
  Master与Slave 的对应关系通过指定相同的BrokerName,不同的BrokerId来定 义,BrokerId为0 表示Master,非0 表示Slave。
  
  brokerLiveTable
  
  private final HashMap<String/*www.yongshiyule178.com brokerAddr */, BrokerLiveInfo> brokerLiveTable;
  
  1
  
  用于存放存活的Broker信息,BrokerLiveInfo:
  
  class BrokerLiveInfo {
  
  // 最后一次更新时间
  
  private long lastUpdateTimestamp;
  
  // 版本号
  
  private DataVersion dataVersion;
  
  // Netty的Channel
  
  private Channel channel;
  
  /**
  
  * HA Broker的地址
  
  * 是Slave从Master拉取数据时链接的地址,由brokerIp2+HA端口构成 */
  
  private String haServerAddr;
  
  registerBroker
  
  上面的类的解释可能还存在很多遗憾,下面以registerBroker方法,来实际操作一下上面提到类,看看注册一个Broker发生了什么:
  
  public RegisterBrokerResult registerBroker(
  
  final String clusterName,
  
  final String brokerAddr,
  
  final String brokerName,
  
  final long brokerId,
  
  final String haServerAddr,
  
  //TopicConfigSerializeWrapper比较复杂的数据结构,主要包含了broker上所有的topic信息
  
  final TopicConfigSerializeWrapper topicConfigWrapper,
  
  final List<String> filterServerList,
  
  final Channel channel) {
  
  RegisterBrokerResult result = new RegisterBrokerResult();
  
  try {
  
  try {
  
  this.lock.writeLock(www.feifanyule.cn/).lockInterruptibly(www.haom178.com);
  
  // 更新集群信息(根据集群名字,获取当前集群下面的所有brokerName)
  
  Set<String> brokerNames = this.clusterAddrTable.get(clusterName);
  
  // 如果当前集群下面brokerNames为空,则将当前请求broker加入到clusterAddrTable中
  
  if (null == brokerNames) {
  
  brokerNames = new HashSet<String>();
  
  this.clusterAddrTable.put(clusterName, brokerNames);
  
  }
  
  brokerNames.add(brokerName);
  
  boolean registerFirst = false;
  
  //获取所有的名称为brokerNamed 的brokerData,brokerData保留着了。也就是说同一个BrokerName,有可能有多条的brokerId和broker address与之对应
  
  // HashMap<Long/* brokerId */, String/* broker address */> brokerAddrs
  
  BrokerData brokerData = this.brokerAddrTable.get(brokerName);
  
  if (null == brokerData) {
  
  registerFirst = true;
  
  brokerData = new BrokerData(www.michenggw.com clusterName, brokerName, new HashMap<Long, String>());
  
  this.brokerAddrTable.put(brokerName, brokerData);
  
  }
  
  String oldAddr = brokerData.getBrokerAddrs(www.wanmeiyuele.cn).put(brokerId, brokerAddr);
  
  registerFirst = registerFirst || (null == oldAddr);
  
  // 更新Topic信息
  
  //如果topicConfigWrapper不为空,且当前brokerId == 0,即为当前broker为master
  
  //这里会判断只有master才会创建QueueData,因为只有master才包含了读写队列的信息
  
  if (null != topicConfigWrapper
  
  && MixAll.MASTER_ID == brokerId) {
  
  // 如果Topic配置信息发生变更或者该broker为第一次注册
  
  if (this.isBrokerTopicConfigChanged(brokerAddr, topicConfigWrapper.getDataVersion())
  
  || registerFirst)www.furggw.com {
  
  // 获取所有topic信息
  
  ConcurrentMap<String, TopicConfig> tcTable =
  
  topicConfigWrapper.getTopicConfigTable();
  
  if (tcTable != null) {
  
  // 遍历所有Topic
  
  for (Map.Entry<String, TopicConfig> entry : tcTable.entrySet()) {
  
  // 根据brokername及topicconfig(read、write queue数量等)新增或者更新到topicQueueTable中
  
  this.createAndUpdateQueueData(brokerName, entry.getValue());
  
  }
  
  }
  
  }
  
  }
  
  // 更新最后变更时间(将brokerLiveTable中保存的对应的broker的更新时间戳,设置为当前时间)
  
  BrokerLiveInfo prevBrokerLiveInfo = this.brokerLiveTable.put(brokerAddr,
  
  new BrokerLiveInfo(
  
  System.currentTimeMillis(),
  
  topicConfigWrapper.getDataVersion(),
  
  channel,
  
  haServerAddr));
  
  if (null == prevBrokerLiveInfo) {
  
  log.info("new broker registered, {} HAServer: {}", brokerAddr, haServerAddr);
  
  }
  
  if (filterServerList != null) {
  
  if (filterServerList.isEmpty()) {
  
  this.filterServerTable.remove(brokerAddr);
  
  } else {
  
  this.filterServerTable.put(brokerAddr, filterServerList);
  
  }
  
  }
  
  // 返回值(如果当前broker为slave节点)则将haServerAddr、masterAddr等信息设置到result返回值中
  
  if (MixAll.MASTER_ID != brokerId) {
  
  // 通过brokename的brokedate获取当前slave节点的master节点addr
  
  String masterAddr = brokerData.getBrokerAddrs().get(MixAll.MASTER_ID);
  
  if (masterAddr != null) {
  
  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;
  
  插入过程可以看如下这张图:
  
  疑惑
  
  topicQueueTable有一点不明白,为什么一个topic对应的是一个List,难道不是一个topic对应一个QueueData吗?难道可以存在相同的topic?
  
  因为每一个topic可以存在不同的broker中,不同的broker就有不同的QueueData。
  
  brokerAddrTable根据一个brokername获得一个BrokerData,可是为什么一个BrokerData中有一个hashMap的brokerAddrs呢?我能理解的是一个brokername,会有多个address和id,但是cluster为什么又是同一个呢?
  
  这是因为你理解错了,以为BrokerName是唯一的,实际上是指定多个broker为一个name,只不过指定了不同的id和addres来区分是master还是slave。

RocketMQ 源码分析 RouteInfoManager(四)的更多相关文章

  1. 【RocketMQ源码分析】深入消息存储(3)

    前文回顾 CommitLog篇 --[RocketMQ源码分析]深入消息存储(1) ConsumeQueue篇 --[RocketMQ源码分析]深入消息存储(2) 前面两篇已经说过了消息如何存储到Co ...

  2. RocketMQ 源码分析 —— Message 发送与接收

    1.概述 Producer 发送消息.主要是同步发送消息源码,涉及到 异步/Oneway发送消息,事务消息会跳过. Broker 接收消息.(存储消息在<RocketMQ 源码分析 —— Mes ...

  3. RocketMQ源码分析之从官方示例窥探:RocketMQ事务消息实现基本思想

    摘要: RocketMQ源码分析之从官方示例窥探RocketMQ事务消息实现基本思想. 在阅读本文前,若您对RocketMQ技术感兴趣,请加入RocketMQ技术交流群 RocketMQ4.3.0版本 ...

  4. 【RocketMQ源码分析】深入消息存储(2)

    前文回顾 CommitLog篇 --[RocketMQ源码分析]深入消息存储(1) MappedFile篇 --[RocketMQ源码分析]深入消息存储(3) 前文说完了一条消息如何被持久化到本地磁盘 ...

  5. Java集合源码分析(四)HashMap

    一.HashMap简介 1.1.HashMap概述 HashMap是基于哈希表的Map接口实现的,它存储的是内容是键值对<key,value>映射.此类不保证映射的顺序,假定哈希函数将元素 ...

  6. 插件开发之360 DroidPlugin源码分析(四)Activity预注册占坑

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52258434 在了解系统的activity,service,broa ...

  7. RocketMQ源码分析之RocketMQ事务消息实现原理上篇(二阶段提交)

    在阅读本文前,若您对RocketMQ技术感兴趣,请加入 RocketMQ技术交流群 根据上文的描述,发送事务消息的入口为: TransactionMQProducer#sendMessageInTra ...

  8. Docker源码分析(四):Docker Daemon之NewDaemon实现

    1. 前言 Docker的生态系统日趋完善,开发者群体也在日趋庞大,这让业界对Docker持续抱有极其乐观的态度.如今,对于广大开发者而言,使用Docker这项技术已然不是门槛,享受Docker带来的 ...

  9. 【RocketMQ源码分析】深入消息存储(1)

    最近在学习RocketMQ相关的东西,在学习之余沉淀几篇笔记. RocketMQ有很多值得关注的设计点,消息发送.消息消费.路由中心NameServer.消息过滤.消息存储.主从同步.事务消息等等. ...

随机推荐

  1. LeetCode997. Find the Town Judge

    题目 在一个小镇里,按从 1 到 N 标记了 N 个人.传言称,这些人中有一个是小镇上的秘密法官. 如果小镇的法官真的存在,那么: 小镇的法官不相信任何人. 每个人(除了小镇法官外)都信任小镇的法官. ...

  2. 【动态规划】cf1034C. Region Separation

    质因数分解套路的复杂度分析的动态规划 题目大意 有一颗$n$个节点有点权的树,初始整棵树为$1$号区域,要求满足下列规则: 除非$i$是最后一个等级,否则每一个$i$级区域都要被分成至少两个$i+1$ ...

  3. ssm整合-图片上传功能(转)

    本文介绍 ssm (Spring+SpringMVC+Mybatis)实现上传功能. 以一个添加用户的案例介绍(主要是将上传文件). 一.需求介绍 我们要实现添加用户的时候上传图片(其实任何文件都可以 ...

  4. v-cloak

    v-cloak 不需要表达式 用法: 这个指令保持在元素上直到关联实例结束编译.和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Must ...

  5. libvirt笔记(未完待续)

    参考源地址:http://libvirt.org/formatdomain.html http://blog.csdn.net/qq250941970/article/details/6022094 ...

  6. PHP关闭notice级别的错误提示

    1.在php.ini文件中改动error_reporting改为: error_reporting=E_ALL & ~E_NOTICE 2.如果你不能操作php.ini文件,你可以使用如下方法 ...

  7. Linux基本的指令操作

    绝对路径: 路径的写法,由根目录/写起,例如:/usr/share/doc这个目录. 相对路径: 路径的写法,不由/写起,例如由/usr/share/doc要到/usr/share/man底下时,可以 ...

  8. POJ:3684-Physics Experiment(弹性碰撞)

    Physics Experiment Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 3392 Accepted: 1177 Sp ...

  9. ACM 最大化平均值问题总结

    主要是应用c(x)的满足条件有共通之处: c(x)表示要求解的那个表达式不小于x 可以找到表达式 v/w>=x 如果 v-x*w>0 说明有贡献 那就把贡献最大的找出来 如果找出来之后 s ...

  10. Card Hand Sorting 18中南多校第一场C题

    一.题意 随机给你一堆牌(标准扑克牌),之后让你按照: 第一优先规则:所有相同花色的在一起 第二优先规则:所有相同花色的必须按照升序或者降序排列 问,你最少要拿出多少张牌插入到其他的地方以维持这个状况 ...