RocketMQ 源码分析 RouteInfoManager(四)
在上一章分析了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(四)的更多相关文章
- 【RocketMQ源码分析】深入消息存储(3)
前文回顾 CommitLog篇 --[RocketMQ源码分析]深入消息存储(1) ConsumeQueue篇 --[RocketMQ源码分析]深入消息存储(2) 前面两篇已经说过了消息如何存储到Co ...
- RocketMQ 源码分析 —— Message 发送与接收
1.概述 Producer 发送消息.主要是同步发送消息源码,涉及到 异步/Oneway发送消息,事务消息会跳过. Broker 接收消息.(存储消息在<RocketMQ 源码分析 —— Mes ...
- RocketMQ源码分析之从官方示例窥探:RocketMQ事务消息实现基本思想
摘要: RocketMQ源码分析之从官方示例窥探RocketMQ事务消息实现基本思想. 在阅读本文前,若您对RocketMQ技术感兴趣,请加入RocketMQ技术交流群 RocketMQ4.3.0版本 ...
- 【RocketMQ源码分析】深入消息存储(2)
前文回顾 CommitLog篇 --[RocketMQ源码分析]深入消息存储(1) MappedFile篇 --[RocketMQ源码分析]深入消息存储(3) 前文说完了一条消息如何被持久化到本地磁盘 ...
- Java集合源码分析(四)HashMap
一.HashMap简介 1.1.HashMap概述 HashMap是基于哈希表的Map接口实现的,它存储的是内容是键值对<key,value>映射.此类不保证映射的顺序,假定哈希函数将元素 ...
- 插件开发之360 DroidPlugin源码分析(四)Activity预注册占坑
请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52258434 在了解系统的activity,service,broa ...
- RocketMQ源码分析之RocketMQ事务消息实现原理上篇(二阶段提交)
在阅读本文前,若您对RocketMQ技术感兴趣,请加入 RocketMQ技术交流群 根据上文的描述,发送事务消息的入口为: TransactionMQProducer#sendMessageInTra ...
- Docker源码分析(四):Docker Daemon之NewDaemon实现
1. 前言 Docker的生态系统日趋完善,开发者群体也在日趋庞大,这让业界对Docker持续抱有极其乐观的态度.如今,对于广大开发者而言,使用Docker这项技术已然不是门槛,享受Docker带来的 ...
- 【RocketMQ源码分析】深入消息存储(1)
最近在学习RocketMQ相关的东西,在学习之余沉淀几篇笔记. RocketMQ有很多值得关注的设计点,消息发送.消息消费.路由中心NameServer.消息过滤.消息存储.主从同步.事务消息等等. ...
随机推荐
- Java连接数据库的一个问题
问题描述: 利用HTML+servlet+MySQL写一个简单的登录注册案例,抛出了异常No suitable driver found for jdbc 解决方法 将mysql-connector- ...
- IDEA的使用方法(一)(IDEA基本快捷键)
一个软件的快捷键显得尤为重要,接下来来讲讲快捷键 CTR+N 搜索类 CTR+SHIT+N 搜索文件 CTR+ALT+空格 代码提示(类似于 ALT+/) ALT+F7 查询在某处使用 CTR+Q 查 ...
- html5的canvas绘制线条,moveTo和lineTo详解
今天在看html5,里面新增的属性有一个canvas,它相当于一个画布你可以用js在里面画你想要的效果!我在w3c的手册里面看到用moveTo和lineTo绘制线条讲的不是很清楚,尤其是moveTo和 ...
- 爬取多个url页面数据--手动实现
# -*- coding: utf-8 -*- import scrapy from qiubaiByPages.items import QiubaibypagesItem class Qiubai ...
- Jongmah CodeForces - 1110D
传送门 题意:你有n个数字,范围[1, m],你可以选择其中的三个数字构成一个三元组,但是这三个数字必须是连续的或者相同的,每个数字只能用一次,问这n个数字最多构成多少个三元组? 题解:三个一模一样的 ...
- Background Segment CNT
CNT简介 CNT算法是OpenCV Contrib 模块中的背景减除(Background segment)算法之一.相较于OpenCV提供的其他背景减 除算法,该算法具有运行速度快,检测精度高等优 ...
- 读懂CCS链接命令文件(.cmd)
链接器的核心工作就是符号表解析和重定位,链接命令文件则使得编程者可以给链接器提供必要的指导和辅助信息.多数时候,由于集成开发环境的存在,开发者无需了解链接命令文件的编写,使用默认配置即可.但若需要对计 ...
- Java 的单元测试
有点需要注意,当 JUnit 主线程退出,子线程也会跟着退出,需要使用子线程的 join() 方法使主线程等待 Maven 依赖 <dependency> <groupId>j ...
- 在MAC下使用Robotframework+Selenium2【第二枪】如何处理Table点击指定记录
1.通过关键字Get Matching Xpath Count获取table中的记录 2.遍历Table所有记录 3.判断记录是否符合条件,做点击操作
- TCP重组问题
今天问题: vqmon 测试一pcap抓包文件18.pcap.发现实际输出的视频分片信息和抓包不符合. ===>pts : 00:00:33 Too much data in TCP recei ...