RocketMQ-NameServer路由机制源码分析


上篇文章里提到,NameServer在Mq架构中充当一个路由中心的角色,其角色类似Dubbo中的zookeeper,支持Broker的动态注册与发现。主要包括两个功能

  1. Broker管理,NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活;
  2. 路由信息管理,每个NameServer将保存关于Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费

RouteInfoManager

NameServer对Broker信息的管理,对路由信息的管理,都在RouteInfoManager类中实现。
先看下RouteInfoManager中的存储的路由数据及结构

	// Broker信息过期失效时间(120秒),若 上次心跳时间+BROKER_CHANNEL_EXPIRED_TIME>当前时间,则判断Broker失效
	private final static long BROKER_CHANNEL_EXPIRED_TIME = 1000 * 60 * 2;
	// 读写锁,控制下列各个Map在并发读写下的安全性
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    // topic对应队列信息
    private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;
    // broker信息
    private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
    // broker集群信息,key-集群名称 value-对应集群中所有broker名称
    private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
    // broker地址及对应对应broker存活信息
    private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;
    // broker地址及对应Filter Server列表
    private final HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;

再看下QueueData、BrokerData、BrokerLiveInfo中属性

QueueData:

	//brokerName
	private String brokerName;
	//读队列数量
    private int readQueueNums;
    //写队列数量
    private int writeQueueNums;
    //权限值 参考PermName类
    private int perm;
    //还不知道具体有啥用
    private int topicSynFlag;

一个topic有多个消息队列,这些消息队列分布在不同的broker上,一个Queuedata对应一个broker上某topic对应读写队列的结构
在这里插入图片描述

BrokerData

	// broker集群名称
	private String cluster;
	// broker名称
    private String brokerName;
    // 主备broker地址,主brokerId为0
    private HashMap<Long/* brokerId */, String/* broker address */> brokerAddrs;

BrokerLiveInfo

	// 上次broker心跳检查时的时间
	private long lastUpdateTimestamp;
	// topicConfig的版本号
    private DataVersion dataVersion;
    // Broker与NameServer链接通道
    private Channel channel;
    // ha地址
    private String haServerAddr;

broker在启动时,向NameServer进行注册,启动后也会定时(每隔30秒)定时向NameServer进行注册以维持心跳检测,每次注册,broker的信息都会存储到BrokerLiveInfo中。

假设broker宕机了,那么BrokerLiveInfo中对应lastUpdateTimestamp就不再更新,Nameserver每隔120秒扫描brokerLiveTable中broker存活信息,判断 lastUpdateTimestamp + BROKER_CHANNEL_EXPIRED_TIME(120秒) > 当前时间,即broker在120秒内都未进行心跳注册时,移除broker信息

路由注册(Broker注册)

Broker发起注册请求

介绍完了,RouteInfoManager中的存储数据结构,下面从Broker路由信息注册开始分析源码
Broker启动时,像NameServer进行注册,代码在org.apache.rocketmq.broker.BrokerController#start中,可以看到定时(每隔30秒)向nameServer发起注册请求
在这里插入图片描述
进到registerBrokerAll方法查看具体broker注册逻辑,
在这里插入图片描述
总得来说就是两步

  1. 组织topicConfig信息,判断broker读写权限,若不可读,或不可写,更新topic对应权限信息
  2. 判断是否需要进行注册,由于forceRegister默认为true,所以必然会进行broker信息注册

最后进行路由注册的请求方法在org.apache.rocketmq.broker.out.BrokerOuterAPI#registerBrokerAll中,主要就是封装了请求参数,最后调用NettyClient发送请求进行broker注册,具体Netty请求方法在org.apache.rocketmq.broker.out.BrokerOuterAPI#registerBroker,请求RequestCode为REGISTER_BROKER
在这里插入图片描述

NameServer接收注册请求

上篇文章提到,NameServer模块中默认的Netty事件处理器为DefaultRequestProcessor,查询对应RequestCode为REGISTER_BROKER即可找到接收注册的方法,最后调用的是
RouteInfoManager#registerBroker方法,方法比较长,逐步分析
在这里插入图片描述
在这里插入图片描述

@1:因为每台Broker都会向所有NameServer进行注册,所以存在多台Broker同时向一个NameServer注册的情况,加写锁保证数据安全

@2:维护HashMap<String/* clusterName /, Set<String/ brokerName */>> clusterAddrTable,判断Broker集群中是否有该Broker,没有则加入

@3,@4:维护HashMap<String/* brokerName */, BrokerData> brokerAddrTable,主要是对Broker地址的维护,具体内容看注释

@5:维护HashMap<String/* topic */, List> topicQueueTable,具体维护逻辑在createAndUpdateQueueData中,基本逻辑就是对broker中每个默认topic注册其QueueData信息
在这里插入图片描述

@6: 每次Broker注册时,将最新Broker信息存入HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable中,其中lastUpdateTimestamp是NameServer后续判断Broker是否失效的关键参数

@7:如果当前Broker是从节点,则查找BrokerId=0的主节点,并将HaServer地址及Master地址设为主节点地址

路由查询(QueueData、BrokerData信息查询)

Producer在消息发送时,会先从NameServer查询topic对应路由信息,对应代码实现在org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#sendDefaultImpl中
在这里插入图片描述
查询路由信息的Netty请求封装在org.apache.rocketmq.client.impl.MQClientAPIImpl#getTopicRouteInfoFromNameServer中
在这里插入图片描述
RequestCode为GET_ROUTEINTO_BY_TOPIC

NameServer端对路由信息查询的请求处理方法为RouteInfoManager#getRouteInfoByTopic,根据topic信息查询了QueueData列表,及BrokerData列表,封装成TopicRouteData返回
在这里插入图片描述

路由信息删除

如果Broker宕机,那么NameServer里的该Broker相关路由信息需要被移除,NameServer启动时开启一个定时器,每隔10秒scanNotActiveBroker,遍历brokerLiveTable,判断:若当前时间>上次心跳时间+120秒,则关闭通道,清除broker信息,最终调用RouteInfoManager#onChannelDestroy方法清除路由元信息
在这里插入图片描述
其维护代码与新增路由信息是个相反过程,不再赘述

若Broker与NameServer之间的长连接正常断开,因为Nameserver中的NettyServer启动时绑定了BrokerHousekeepingService,而BrokerHousekeepingService又是继承了ChannelEventListener,所以长连接关闭或者异常时,会触发BrokerHousekeepingService中对应Channel事件,最终也会调用RouteInfoManager#onChannelDestroy清除路由信息
在这里插入图片描述

看完整篇文章,其核心在于Broker路由注册及路由查询,但有一个关键点,Broker失效后,NameServer是定时查询失效Broker并删除路由信息的,也就是说,在某一个时刻,Broker已经宕机了,但是NameServer中还维持有该Broker路由信息,这样Producer在查询路由信息时,就可能从NameServer查询到一个已经失效的路由信息,这不是违背了高可用的原则了?

实际上Mq在消息发送时,针对这种情况做了容错处理,具体分析在下篇Mq消息发送时做具体介绍

发布了43 篇原创文章 · 获赞 134 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/hosaos/article/details/94833101
今日推荐