NameServerController
主要属性
NamesrvConfig
是nameserver全局的一些配置属性,定义了从哪些运行环境的path获取配置
NettyServerConfig
定义了netty server的配置参数,包括监听端口,工作线程数量,一些阀值等
ScheduledExecutorService
执行定时任务的线程池
KVConfigManager
本地的kv存储工具,使用读写锁 + hashmap实现
RouteInfoManager
路由信息的管理类。主要属性如下
// topic和包含topic的broker name之间的映射
private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;
// brokerName和主从broker的映射
private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
//集群名和broker名的映射。主从broker使用相同的cluster name,相同的name,不同的id,主为0,从为其他
private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
//每个broker的broker上报信息,和netty的channel
private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;
//broker和对应的过滤服务器(不知道过滤服务器具体是什么)
private final HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;
RemotingServer
netty的轻量级封装,我感觉主要是为了符合面向接口设计,并且方便替换其他通信框架,当然这发生的概率很低。
Nameserver的启动主要分为两大步骤。
BrokerHousekeepingService
实现了netty的ChannelEventListener,对channel的close、exception、idle事件(不是很懂这些事件发生的场景,看来得看看netty)主动告知RouteInfoManager,调用其onChannelDestroy方法。把触发当前事件的broker从RouteInfoManager的缓存中,挨个移除。
在一个对象的状态变化时,通知依赖他的对象。这个设计有点观察者模式的味道。
remotingExecutor
netty的工作线程
Configuration
保存了一份启动nameserver 的全量配置
FileWatchService
ssl相关文件的监视器,如果ssl相关的配置文件更换,会更换netty的tls信息。
用到了很多并发控制的技巧,后面关注下。
启动流程
启动主要分为init和start两步。这个设计感觉是松耦合的设计。
init方法
主要是下面几步,还有一个tls模块的加载没贴,一般这种内网的服务,https应该都是关闭的。
kv.load()
这个方法会去加载在运行环境中预定义位置的property配置,一般都没有,所以略过
new NettyRemotingServer
会把netty的启动辅助类serverBootstrap创建好,保存了channelEventListener。
新建了netty的boss线程。
创建publicExecutor线程池。
remotingExecutor
这个应该应该应该是netty的worker线程
this.registerProcessor();
创建DefaultRequestProcessor 作为netty server 请求处理器。
org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor#processRequest 处理所有已知request code类型的请求
定时任务
每10分钟会打印kvManager的所有配置信息
每10s执行routeInfoManager.scanNotActiveBroker()
遍历brokerLiveTable,剔除120s以上没有上报心跳的broker。
这个任务比较关键,是broker故障发现机制中一环
nameController的start方法
容易理解的start方法,启动netty server,如果运行环境启动了tls选项,会启动fileWatchService
remoteServer的start方法
发现新大陆
DefaultEventExecutorGroup 这个事件执行组之前没有了解过。
使用ServerBootstrap配置了netty的boss和worker线程,一些通信参数
添加了channelHandler。HandshakeHandler、encoder、NettyDecoder、IdleStateHandler、connectionManageHandler、serverHandler。
使用Timer,每秒扫描responseTable,处理过期的请求。
namserver处理请求的逻辑
nameserver处理请求的逻辑主要封装在org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor#processRequest中
RemotingCommand
remotingCommand是rocketmq使用netty发送远程消息的封装体
// 请求类型code @see RequestCode
private int code;
private LanguageCode language = LanguageCode.JAVA;
private int version = 0;
// 唯一标识
private int opaque = requestId.getAndIncrement();
private int flag = 0;
private String remark;
// 请求头中的字段
private HashMap<String, String> extFields;
//请求头
private transient CommandCustomHeader customHeader;
private SerializeType serializeTypeCurrentRPC = serializeTypeConfigInThisServer;
//请求体
private transient byte[] body;
processRequest
public RemotingCommand processRequest(ChannelHandlerContext ctx,
RemotingCommand request) throws RemotingCommandException {
switch (request.getCode()) {
case RequestCode.PUT_KV_CONFIG:
return this.putKVConfig(ctx, request);
case RequestCode.GET_KV_CONFIG:
return this.getKVConfig(ctx, request);
case RequestCode.DELETE_KV_CONFIG:
return this.deleteKVConfig(ctx, request);
case RequestCode.QUERY_DATA_VERSION:
return queryBrokerTopicConfig(ctx, request);
case RequestCode.REGISTER_BROKER:
Version brokerVersion = MQVersion.value2Version(request.getVersion());
if (brokerVersion.ordinal() >= MQVersion.Version.V3_0_11.ordinal()) {
return this.registerBrokerWithFilterServer(ctx, request);
} else {
return this.registerBroker(ctx, request);
}
case RequestCode.UNREGISTER_BROKER:
return this.unregisterBroker(ctx, request);
case RequestCode.GET_ROUTEINTO_BY_TOPIC:
return this.getRouteInfoByTopic(ctx, request);
case RequestCode.GET_BROKER_CLUSTER_INFO:
return this.getBrokerClusterInfo(ctx, request);
case RequestCode.WIPE_WRITE_PERM_OF_BROKER:
return this.wipeWritePermOfBroker(ctx, request);
case RequestCode.GET_ALL_TOPIC_LIST_FROM_NAMESERVER:
return getAllTopicListFromNameserver(ctx, request);
case RequestCode.DELETE_TOPIC_IN_NAMESRV:
return deleteTopicInNamesrv(ctx, request);
case RequestCode.GET_KVLIST_BY_NAMESPACE:
return this.getKVListByNamespace(ctx, request);
case RequestCode.GET_TOPICS_BY_CLUSTER:
return this.getTopicsByCluster(ctx, request);
case RequestCode.GET_SYSTEM_TOPIC_LIST_FROM_NS:
return this.getSystemTopicListFromNs(ctx, request);
case RequestCode.GET_UNIT_TOPIC_LIST:
return this.getUnitTopicList(ctx, request);
case RequestCode.GET_HAS_UNIT_SUB_TOPIC_LIST:
return this.getHasUnitSubTopicList(ctx, request);
case RequestCode.GET_HAS_UNIT_SUB_UNUNIT_TOPIC_LIST:
return this.getHasUnitSubUnUnitTopicList(ctx, request);
case RequestCode.UPDATE_NAMESRV_CONFIG:
return this.updateConfig(ctx, request);
case RequestCode.GET_NAMESRV_CONFIG:
return this.getConfig(ctx, request);
default:
break;
}
return null;
}
putKVConfig
客户端(broker consumer productor)会在KvConfigManager存放配置并持久化
public RemotingCommand putKVConfig(ChannelHandlerContext ctx,
RemotingCommand request) throws RemotingCommandException {
final RemotingCommand response = RemotingCommand.createResponseCommand(null);
final PutKVConfigRequestHeader requestHeader =
(PutKVConfigRequestHeader) request.decodeCommandCustomHeader(PutKVConfigRequestHeader.class);
this.namesrvController.getKvConfigManager().putKVConfig(
requestHeader.getNamespace(),
requestHeader.getKey(),
requestHeader.getValue()
);
response.setCode(ResponseCode.SUCCESS);
response.setRemark(null);
return response;
}
getKVConfig
deleteKVConfig
向KvConfigManager查询配置和删除配置的方法
queryBrokerTopicConfig
这个方法不知道谁来调用的,看着像是broker来更新BrokerLiveInfo的dataversion
public RemotingCommand queryBrokerTopicConfig(ChannelHandlerContext ctx,
RemotingCommand request) throws RemotingCommandException {
final RemotingCommand response = RemotingCommand.createResponseCommand(QueryDataVersionResponseHeader.class);
final QueryDataVersionResponseHeader responseHeader = (QueryDataVersionResponseHeader) response.readCustomHeader();
final QueryDataVersionRequestHeader requestHeader =
(QueryDataVersionRequestHeader) request.decodeCommandCustomHeader(QueryDataVersionRequestHeader.class);
DataVersion dataVersion = DataVersion.decode(request.getBody(), DataVersion.class);
Boolean changed = this.namesrvController.getRouteInfoManager().isBrokerTopicConfigChanged(requestHeader.getBrokerAddr(), dataVersion);
if (!changed) {
this.namesrvController.getRouteInfoManager().updateBrokerInfoUpdateTimestamp(requestHeader.getBrokerAddr());
}
DataVersion nameSeverDataVersion = this.namesrvController.getRouteInfoManager().queryBrokerTopicConfig(requestHeader.getBrokerAddr());
response.setCode(ResponseCode.SUCCESS);
response.setRemark(null);
if (nameSeverDataVersion != null) {
response.setBody(nameSeverDataVersion.encode());
}
responseHeader.setChanged(changed);
return response;
}
registerBrokerWithFilterServer
broker注册的方法。我们思考一下需要初始化哪些信息
- 添加或者覆盖clusterAddrTable中当前broker的brokername
- 首次注册添加,后续注册更新brokerData。
- 如果是slave变master,则把master和自己先从brokerData移除,再添加当前的broker。
- 如果broker有topic配置,那么会往topicQueueTable 插入注册broker的queuedata的信息。从插入的代码来看,queueData与broker的关系是一对一
- 如果是master第一次注册或者topic的dataversion变了(fixme 什么情况下会变),找到当前topic并且是属于该broker的queueData。不存在则新增,有修改则覆盖
- 添加或者更新brokerLiveTable中当前broker的BrokerLiveInfo
- 当前broker没有携带filterServerList就移除filterServerTable,否则就添加
- 如果不是master就返回master的地址
RegisterBrokerResult result = this.namesrvController.getRouteInfoManager().registerBroker(
requestHeader.getClusterName(),
requestHeader.getBrokerAddr(),
requestHeader.getBrokerName(),
requestHeader.getBrokerId(),
requestHeader.getHaServerAddr(),
registerBrokerBody.getTopicConfigSerializeWrapper(),
registerBrokerBody.getFilterServerList(),
ctx.channel());
unregisterBroker
删除注册
- 删除brokerLiveTable中的自己
- 删除filterServerTable中的自己
- 删除brokerAddrTable中自己的brokerid,如果删除后是空,就把brokerdata删除
- 如果主从都删除了,那么删除clusterAddrTable中的brokername,如果删除后是空,就把clustername删除
- 如果主从都删除了,那么删除topicqueue中属于当前brokername的queuedata
getRouteInfoByTopic
org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager#pickupTopicRouteData
根据topic去 topicQueueTable找到所有的brokername,再去brolerAddrTable找到主从的broker地址
getBrokerClusterInfo
找到nameserver管理的clusterAddrTable和brokerAddrTable
wipeWritePermOfBroker
擦除当前brokerName的所有topic的写权限
getAllTopicListFromNameserver
查询所有topic的信息
返回topicQueueTable的topic
deleteTopicInNamesrv
删除一个topic
根据topicName删除topicQueue中的entry
getKVListByNamespace
根据namespace获取kvConfigManager中的所有配置
//fixme namespace是什么概念 难道是适配k8s?
getTopicsByCluster
查询集群下的所有topic
获取clusterAddrTable中当前集群的所有brokerName,从topicQueueTable挨个找属于当前brokerName的topic
getSystemTopicListFromNs
这个方法最骚的是使用了brokerId为0时,entry是排在HashMap中table的第一个,这个特性。
获取了所有的clustername,所有的brokername,brokername的master brokerAddr
getUnitTopicList
获取topic的topicSynFlag属性是FLAG_UNIT的所有topicname
getHasUnitSubTopicList
获取topic的topicSynFlag属性是FLAG_UNIT_SUB的所有topicname
updateConfig
更新configuration这个类维护的property
nameServer中,这个类维护的是namesrvConfig,nettyServerConfig这两份配置
getConfig
查询configuration这个类维护的property
总结
NameServer处理的请求类型
来自broker的请求
- 注册
- 删除注册
- 更新broker的brokerLiveTable
- 主从切换时,更新brokerData
- 查询kv存储
- 移除broker的写权限
来自producer/consumer/管理界面
- 根据topic查询broker
- 获取所有topic
- 获取系统的topic
- 更新和查询nameServer的参数配置
- 获取两种类型的topic FLAG_UNIT_SUB/FLAG_UNIT
- 查询kv存储
- 删除topic
疑问???
没有看到创建topic的动作?topic是创建在broker然后注册到nameServer?