概述

  所有broker在启动的时候都会向NameServer进行注册,对它进行发送心跳包。

源码阅读

我们先从 NamesrvStartup这个类分析
    public static void main(String[] args) {
main0(args);
} public static NamesrvController main0(String[] args) { try {
//创建NamesrvController
NamesrvController controller = createNamesrvController(args);
//启动
start(controller);
String tip = "The Name Server boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer();
log.info(tip);
System.out.printf("%s%n", tip);
return controller;
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
} return null;
}
看一下 NamesrvController 这个类有什么东西
public class NamesrvController {
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME); private final NamesrvConfig namesrvConfig; private final NettyServerConfig nettyServerConfig; //时间线程池
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
"NSScheduledThread"));
private final KVConfigManager kvConfigManager;
//路由相关
private final RouteInfoManager routeInfoManager;
//远程管理相关
private RemotingServer remotingServer; private BrokerHousekeepingService brokerHousekeepingService; //线程池
private ExecutorService remotingExecutor; private Configuration configuration;
private FileWatchService fileWatchService; ...
}
主要是 配置 + manager + 线程池

路由注册

我们先来看一下 RouteInfoManager .
public class RouteInfoManager {
private static final Logger log = LoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
//过期时间
private final static long BROKER_CHANNEL_EXPIRED_TIME = 1000 * 60 * 2; //读写锁(适合读多写少), 读的时候可以获取写锁吗?
//一个线程获取读锁的时候,另外一个线程是否可以获取到写锁
//答 : 可以
private final ReadWriteLock lock = new ReentrantReadWriteLock(); //主题对应信息
private final HashMap<String/* topic */, List<QueueData>> topicQueueTable; //Broker 基础信息,包括所有的 broker
private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable; //Broker集群信息,存储集群中所有broker名称(注意是集群中!!)
private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable; //存活的 Broker
//收到心跳包时会更新信息会激活,key 是 ip 地址
private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable; //用于过滤
private final HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable; ...
... }

  上面的字段在可以通过下面两张图来理解

路由注册的逻辑在 BrokerController 的 start方法内,
        this.registerBrokerAll(true, false);

        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

            @Override
public void run() {
try {
//向 NameServer 注册所有的 broker,发送心跳包给 NameServer
BrokerController.this.registerBrokerAll(true, false);
} catch (Throwable e) {
log.error("registerBrokerAll Exception", e);
}
}
}, 1000 * 10, 1000 * 30, TimeUnit.MILLISECONDS);

registerBrokerAll 方法

    public synchronized void registerBrokerAll(final boolean checkOrderConfig, boolean oneway) {
TopicConfigSerializeWrapper topicConfigWrapper = this.getTopicConfigManager().buildTopicConfigSerializeWrapper(); if (!PermName.isWriteable(this.getBrokerConfig().getBrokerPermission())
|| !PermName.isReadable(this.getBrokerConfig().getBrokerPermission())) {
ConcurrentHashMap<String, TopicConfig> topicConfigTable = new ConcurrentHashMap<String, TopicConfig>();
for (TopicConfig topicConfig : topicConfigWrapper.getTopicConfigTable().values()) {
TopicConfig tmp =
new TopicConfig(topicConfig.getTopicName(), topicConfig.getReadQueueNums(), topicConfig.getWriteQueueNums(),
this.brokerConfig.getBrokerPermission());
topicConfigTable.put(topicConfig.getTopicName(), tmp);
}
topicConfigWrapper.setTopicConfigTable(topicConfigTable);
} //注册方法
RegisterBrokerResult registerBrokerResult = this.brokerOuterAPI.registerBrokerAll(
this.brokerConfig.getBrokerClusterName(),
this.getBrokerAddr(),
this.brokerConfig.getBrokerName(),
this.brokerConfig.getBrokerId(),
this.getHAServerAddr(),
topicConfigWrapper,
this.filterServerManager.buildNewFilterServerList(),
oneway,
this.brokerConfig.getRegisterBrokerTimeoutMills()); if (registerBrokerResult != null) {
if (this.updateMasterHAServerAddrPeriodically && registerBrokerResult.getHaServerAddr() != null) {
this.messageStore.updateHaMasterAddress(registerBrokerResult.getHaServerAddr());
} this.slaveSynchronize.setMasterAddr(registerBrokerResult.getMasterAddr()); if (checkOrderConfig) {
this.getTopicConfigManager().updateOrderTopicConfig(registerBrokerResult.getKvTable());
}
}
}

brokerOuterAPI 的 registerBrokerAll 方法

    /**
* broker 向 NS 的注册方法
*/
public RegisterBrokerResult registerBrokerAll(
final String clusterName,
final String brokerAddr,
final String brokerName,
final long brokerId,
final String haServerAddr,
final TopicConfigSerializeWrapper topicConfigWrapper,
final List<String> filterServerList,
final boolean oneway,
final int timeoutMills) {
RegisterBrokerResult registerBrokerResult = null; List<String> nameServerAddressList = this.remotingClient.getNameServerAddressList();
if (nameServerAddressList != null) {
for (String namesrvAddr : nameServerAddressList) {
try {
RegisterBrokerResult result = this.registerBroker(namesrvAddr, clusterName, brokerAddr, brokerName, brokerId,
haServerAddr, topicConfigWrapper, filterServerList, oneway, timeoutMills);
if (result != null) {
registerBrokerResult = result;
} log.info("register broker to name server {} OK", namesrvAddr);
} catch (Exception e) {
log.warn("registerBroker Exception, {}", namesrvAddr, e);
}
}
} return registerBrokerResult;
} private RegisterBrokerResult registerBroker(
final String namesrvAddr,
final String clusterName,
final String brokerAddr,
final String brokerName,
final long brokerId,
final String haServerAddr,
final TopicConfigSerializeWrapper topicConfigWrapper,
final List<String> filterServerList,
final boolean oneway,
final int timeoutMills
) throws RemotingCommandException, MQBrokerException, RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
InterruptedException {
//No.1 封装请求头和请求body
RegisterBrokerRequestHeader requestHeader = new RegisterBrokerRequestHeader();
requestHeader.setBrokerAddr(brokerAddr);
requestHeader.setBrokerId(brokerId);
requestHeader.setBrokerName(brokerName);
requestHeader.setClusterName(clusterName);
requestHeader.setHaServerAddr(haServerAddr);
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.REGISTER_BROKER, requestHeader); RegisterBrokerBody requestBody = new RegisterBrokerBody();
requestBody.setTopicConfigSerializeWrapper(topicConfigWrapper);
requestBody.setFilterServerList(filterServerList);
request.setBody(requestBody.encode()); //No.2 发送
if (oneway) {
try {
//发送,remotingClient
this.remotingClient.invokeOneway(namesrvAddr, request, timeoutMills);
} catch (RemotingTooMuchRequestException e) {
// Ignore
}
return null;
} RemotingCommand response = this.remotingClient.invokeSync(namesrvAddr, request, timeoutMills);
assert response != null;
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
RegisterBrokerResponseHeader responseHeader =
(RegisterBrokerResponseHeader) response.decodeCommandCustomHeader(RegisterBrokerResponseHeader.class);
RegisterBrokerResult result = new RegisterBrokerResult();
result.setMasterAddr(responseHeader.getMasterAddr());
result.setHaServerAddr(responseHeader.getHaServerAddr());
if (response.getBody() != null) {
result.setKvTable(KVTable.decode(response.getBody(), KVTable.class));
}
return result;
}
default:
break;
} throw new MQBrokerException(response.getCode(), response.getRemark());
}

  其中 RemotingClient 是个接口,它的结构图如下   具体的子类是 NettyRemotingClient ,这个类留着后续分析。   NS 处理心跳包的逻辑在 RouteInfoManager 的 registerBroker 方法,这里不再分析源码实现(对字段保存的对应信息进行增删改)。

路由发现

  发现路由变化不会主动push到 producer ,而是 producer 主动到 NS 中去获取。RocketMQ路由实体 TopicRouteData

public class TopicRouteData extends RemotingSerializable {
//顺序消息配置信息,来自于 kvConfig
private String orderTopicConf;
//多个broker 订阅了某个 topic ,所以一个 topic可能对应着多个 broker
private List<QueueData> queueDatas;
//多个broker 的信息
private List<BrokerData> brokerDatas;
//过滤
private HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable; ...
...
}

  NameServer 路由发现实 现类 : DefaultRequestProcessor#getRoutelnfoByTopic

    public RemotingCommand getRouteInfoByTopic(ChannelHandlerContext ctx,
RemotingCommand request) throws RemotingCommandException {
final RemotingCommand response = RemotingCommand.createResponseCommand(null);
final GetRouteInfoRequestHeader requestHeader =
(GetRouteInfoRequestHeader) request.decodeCommandCustomHeader(GetRouteInfoRequestHeader.class); TopicRouteData topicRouteData = this.namesrvController.getRouteInfoManager().pickupTopicRouteData(requestHeader.getTopic()); if (topicRouteData != null) {
if (this.namesrvController.getNamesrvConfig().isOrderMessageEnable()) {
String orderTopicConf =
this.namesrvController.getKvConfigManager().getKVConfig(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG,
requestHeader.getTopic());
topicRouteData.setOrderTopicConf(orderTopicConf);
} byte[] content = topicRouteData.encode();
response.setBody(content);
response.setCode(ResponseCode.SUCCESS);
response.setRemark(null);
return response;
} 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;
} //RouteInfoManager#pickupTopicRouteData 方法 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();
List<QueueData> queueDataList = this.topicQueueTable.get(topic);
if (queueDataList != null) {
topicRouteData.setQueueDatas(queueDataList);
foundQueueData = true; Iterator<QueueData> it = queueDataList.iterator();
while (it.hasNext()) {
QueueData qd = it.next();
brokerNameSet.add(qd.getBrokerName());
} for (String brokerName : brokerNameSet) {
BrokerData brokerData = this.brokerAddrTable.get(brokerName);
if (null != brokerData) {
BrokerData brokerDataClone = new BrokerData(brokerData.getCluster(), brokerData.getBrokerName(), (HashMap<Long, String>) brokerData
.getBrokerAddrs().clone());
brokerDataList.add(brokerDataClone);
foundBrokerData = true;
for (final String brokerAddr : brokerDataClone.getBrokerAddrs().values()) {
List<String> filterServerList = this.filterServerTable.get(brokerAddr);
filterServerMap.put(brokerAddr, filterServerList);
}
}
}
}
} finally {
this.lock.readLock().unlock();
}
} catch (Exception e) {
log.error("pickupTopicRouteData Exception", e);
} if (log.isDebugEnabled()) {
log.debug("pickupTopicRouteData {} {}", topic, topicRouteData);
} if (foundBrokerData && foundQueueData) {
return topicRouteData;
} return null;
}

总结

  文章总结 NS 相关的路由管理逻辑。

消息队列(二)--- RocketMQ-NameServer阅读的更多相关文章

  1. 消息队列之-RocketMQ入门

    简介 RocketMQ是阿里开源的消息中间件,目前已经捐献个Apache基金会,它是由Java语言开发的,具备高吞吐量.高可用性.适合大规模分布式系统应用等特点,经历过双11的洗礼,实力不容小觑. 官 ...

  2. RabbitMQ 消息队列 二

    一:查看MQ的用户角色 rabbitmqctl list_users 二:添加新的角色,并授予权限 rabbitmqctl add_user xiaoyao 123456 rabbitmqctl se ...

  3. day43-python消息队列二-queue模块

    Python提供了Queue模块来专门实现消息队列Queue对象 Queue对象实现一个fifo队列(其他的还有lifo.priority队列,这里不再介绍).queue只有maxsize一个构造参数 ...

  4. 剖析nsq消息队列(二) 去中心化代码源码解析

    在上一篇帖子剖析nsq消息队列(一) 简介及去中心化实现原理中,我介绍了nsq的两种使用方式,一种是直接连接,还有一种是通过nslookup来实现去中心化的方式使用,并大概说了一下实现原理,没有什么难 ...

  5. linux网络编程之system v消息队列(二)

    今天继续学习system v消息队列,主要是学习两个函数的使用,开始进入正题: 下面则开始用代码来使用一下该发送函数: 在运行之前,先查看一下1234消息队列是否已经创建: 用上次编写的查看消息队列状 ...

  6. .net core使用rabbitmq消息队列 (二)

    之前有写过.net core集成使用rabbitmq的博文,见.net core使用rabbitmq消息队列,但是里面的使用很简单,而且还有几个bug,想改下,但是后来想了想,还是算了,之前使用的是. ...

  7. [分布式学习]消息队列之rocketmq笔记

    文档地址 RocketMQ架构 哔哩哔哩上的视频 mq有很多,近期买了<分布式消息中间件实践>这本书,学习关于mq的相关知识.mq大致有有4个功能: 异步处理.比如业务端需要给用户发送邮件 ...

  8. 消息队列之--RocketMQ

    序言 资料 https://github.com/alibaba/RocketMQ http://rocketmq.apache.org/

  9. 消息队列中间件 RocketMQ 源码分析 —— Message 存储

  10. 阿里消息队列中间件 RocketMQ源码解析:Message发送&接收

随机推荐

  1. Android 开发 微信分享,登陆,获取信息

    1 获取appid和appsecret.        https://open.weixin.qq.com/cgi-bin/index?t=home/index&lang=zh_CN     ...

  2. SDF数据库

    一.SDF数据库初探 SDF是一个标准缩略数据库格式.这个数据库包含扩展名为.sdf的文件并且以结构化文件格式进行数据存储.这些SDF文件通常用于在不同数据库应用之间移动数据.它允许一个用户将一个软件 ...

  3. util之Set

    1.定义: Set<Integer>set = new TreeSet<Integer>(); 注意: TreeSet 是二差树实现的,Treeset中的数据是自动排好序的,不 ...

  4. UI 自定义布局

    今天继续学习UI布局的布局管理器,昨天学习并练习使用了线性布局,相对布局和帧布局,今天学习表格布局,网格布局以及嵌套布局,相对于前三种布局,后三种布局比较复杂一些. 1.表格布局 TableLayou ...

  5. 「模板」Splay

    代码说明 对于一些变量进行说明: 变量名 说明 rt 树根 ff[u] 点 \(u\) 的父节点,特别地, ff[rt]=0 ch[u][0|1] 点 \(u\) 的 左/右儿子 siz[u] 点 \ ...

  6. php/js将 CST时间转成格式化时间

    PHP :比较简单 $str = 'Wed Jul 24 11:24:33 CST 2019'; echo date('Y-m-d H:i:s', strtotime($str)); echo dat ...

  7. .net core 2.2 使用imagemagick 将pdf转化为png

    工作需要将PDF文件每一页拆分为一个一个的png文件 测试环境:mac,visual studio for mac 2019 nuget:magick.net-Q16-AnyCPU 不能直接支持PDF ...

  8. window10配置远程虚拟机window7上的mysql5.7数据源

    原文链接:http://www.xitongcheng.com/jiaocheng/win10_article_18644.html windows10系统用户想要在电脑中设置ODBC数据源,于是手动 ...

  9. mybatis报错:A query was run and no Result Maps were found for the Mapped Statement、、Property [login_ip] not found on type [com.thinkgem.jeesite.common.permission.entity.PremissUser]问题解决

    今天在做ssm项目的时候出现了: 先是出现 了错误: mybatis报错:A query was run and no Result Maps were found for the Mapped St ...

  10. noobSTL-1-配置器-0

    noobSTL-1-配置器-0 0.前言 STL的配置器(allocator),也叫内存分配器,负责空间配置与管理,简单地说,就是负责管理内存的. 从实现的角度来看,配置器是一个实现了动态空间配置.空 ...