- 记录学习过程,方便后续查看,源码基于apache开源项目 rocketmq版本4.4
- namsesrv是什么,既然rocketmq是一款消息中间件,那么必须得有发送端(producer),消息存储端(broker),消费端(consumer),但是这些端是怎么关联起来的呢,这时候就需要一个注册中心了,用来收集各端的信息,进行路由信息的处理,封装。
- namesrv相对rocketmq的其他模块比较简单,namesrv是独立存在运行的,namesrv和其他的namesrv是相互独立的。
namesrv启动类 org.apache.rocketmq.namesrv.NamesrvStartup
org.apache.rocketmq.namesrv.NamesrvStartup#createNamesrvController方法,主要是用来加载一些定义的配置。
默认的监听端口9876,cmd命令 -c加载配置文件命令 ,-p用来打印配置信息
核心启动方法org.apache.rocketmq.namesrv.NamesrvStartup#start
public static NamesrvController start(final NamesrvController controller) throws Exception {
if (null == controller) {
throw new IllegalArgumentException("NamesrvController is null");
}
boolean initResult = controller.initialize();//controller的初始化方法
if (!initResult) {
controller.shutdown();
System.exit(-3);
}
//注册系统勾子方法在jvm关闭的时候调用的方法。
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
@Override
public Void call() throws Exception {
controller.shutdown();
return null;
}
}));
controller.start();//控制器核心启动方法
return controller;
}
public boolean initialize() {
this.kvConfigManager.load();//加载configTable
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);//初始化netty
this.remotingExecutor =
Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_"));
this.registerProcessor();//初始化请求处理器defaultRequestProcessor
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {//线程池没10秒扫描一次,检测broker是否存活
//根据最后收到一次broker心跳的时间,和当前时间比较是否大于间隔2分钟,大于的话关闭channel,移除对应的信息,全部是移除一些本地缓存的,也就是producer是感知不到的
NamesrvController.this.routeInfoManager.scanNotActiveBroker();
}
}, 5, 10, TimeUnit.SECONDS);
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
//每隔一段时间打印下参数。。。
@Override
public void run() {
NamesrvController.this.kvConfigManager.printAllPeriodically();
}
}, 1, 10, TimeUnit.MINUTES);
if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {//tls传输安全不做讨论
// Register a listener to reload SslContext
//省略
}
return true;
}
namesrv的启动就是这么简单,直接。
接下来就是 namesrv接受broker的注册了
org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor#registerBrokerWithFilterServer
public RemotingCommand registerBrokerWithFilterServer(ChannelHandlerContext ctx, RemotingCommand request)
throws RemotingCommandException {
final RemotingCommand response = RemotingCommand.createResponseCommand(RegisterBrokerResponseHeader.class);
final RegisterBrokerResponseHeader responseHeader = (RegisterBrokerResponseHeader) response.readCustomHeader();
final RegisterBrokerRequestHeader requestHeader =
(RegisterBrokerRequestHeader) request.decodeCommandCustomHeader(RegisterBrokerRequestHeader.class);
if (!checksum(ctx, request, requestHeader)) {//对body中的数据做一个crc32校验,tcp传输中也有一个校验和来校验数据。
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("crc32 not match");
return response;
}
RegisterBrokerBody registerBrokerBody = new RegisterBrokerBody();
if (request.getBody() != null) {
try {
registerBrokerBody = RegisterBrokerBody.decode(request.getBody(), requestHeader.isCompressed());//反序列化为注册对象
} catch (Exception e) {
throw new RemotingCommandException("Failed to decode RegisterBrokerBody", e);
}
} else {
registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setCounter(new AtomicLong(0));
registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setTimestamp(0);
}
RegisterBrokerResult result = this.namesrvController.getRouteInfoManager().registerBroker(//注册broker方法
requestHeader.getClusterName(),
requestHeader.getBrokerAddr(),
requestHeader.getBrokerName(),
requestHeader.getBrokerId(),
requestHeader.getHaServerAddr(),
registerBrokerBody.getTopicConfigSerializeWrapper(),
registerBrokerBody.getFilterServerList(),
ctx.channel());
responseHeader.setHaServerAddr(result.getHaServerAddr());
responseHeader.setMasterAddr(result.getMasterAddr());
byte[] jsonValue = this.namesrvController.getKvConfigManager().getKVListByNamespace(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG);
response.setBody(jsonValue);
response.setCode(ResponseCode.SUCCESS);
response.setRemark(null);
return response;
}
org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager#registerBroker
public RegisterBrokerResult registerBroker(
final String clusterName,
final String brokerAddr,
final String brokerName,
final long brokerId,
final String haServerAddr,
final TopicConfigSerializeWrapper topicConfigWrapper,
final List<String> filterServerList,
final Channel channel) {
RegisterBrokerResult result = new RegisterBrokerResult();
try {
try {
this.lock.writeLock().lockInterruptibly();//这个会并发请求
//clusterAddrTable是在RouteInfoManager构造函数初始化的,而routeInfoManager是在NamesrvController构造函数中初始化的
Set<String> brokerNames = this.clusterAddrTable.get(clusterName);
if (null == brokerNames) {
brokerNames = new HashSet<String>();
this.clusterAddrTable.put(clusterName, brokerNames);//同一个集群下会有多个broker
}
brokerNames.add(brokerName);
boolean registerFirst = false;
//brokerAddrTable用来保存broker相关的信息,key是broker名称,
BrokerData brokerData = this.brokerAddrTable.get(brokerName);
if (null == brokerData) {
registerFirst = true;
brokerData = new BrokerData(clusterName, brokerName, new HashMap<Long, String>());
this.brokerAddrTable.put(brokerName, brokerData);
}
String oldAddr = brokerData.getBrokerAddrs().put(brokerId, brokerAddr);
registerFirst = registerFirst || (null == oldAddr);
if (null != topicConfigWrapper
&& MixAll.MASTER_ID == brokerId) {//如果topic配置信息不为空的情况下,并且是master节点的broker
if (this.isBrokerTopicConfigChanged(brokerAddr, topicConfigWrapper.getDataVersion())
|| registerFirst) {//当路由配置变换的时候或者首次注册的时候
ConcurrentMap<String, TopicConfig> tcTable =
topicConfigWrapper.getTopicConfigTable();
if (tcTable != null) {
for (Map.Entry<String, TopicConfig> entry : tcTable.entrySet()) {
//创建或者更新队列信息 即填充topicQueueTable数据,key为topic,vlaue为topic下面的队列数据,为list,因为同一个topic会保存在多个broker
this.createAndUpdateQueueData(brokerName, entry.getValue());
}
}
}
}
//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) {//filterserver不做讨论
if (filterServerList.isEmpty()) {
this.filterServerTable.remove(brokerAddr);
} else {
this.filterServerTable.put(brokerAddr, filterServerList);
}
}
if (MixAll.MASTER_ID != brokerId) {//不是master的时候,
String masterAddr = brokerData.getBrokerAddrs().get(MixAll.MASTER_ID);
if (masterAddr != null) {
BrokerLiveInfo brokerLiveInfo = this.brokerLiveTable.get(masterAddr);
if (brokerLiveInfo != null) {
result.setHaServerAddr(brokerLiveInfo.getHaServerAddr());//设置从节点的ha同步地址,
result.setMasterAddr(masterAddr);
}
}
}
} finally {
this.lock.writeLock().unlock();
}
} catch (Exception e) {
log.error("registerBroker Exception", e);
}
return result;
}
broker下线,broker正常下线 会调用broker的shuntdown方法,
会请求到namesrv的org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor#unregisterBroker
清除各种namesrv保存的缓存信息。
由于每个broker和所有的namesrv保持长连接,当broker意外下线的时候会触发
org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager#onChannelDestroy
同样也是情理各种信息。