RocketMq——BrokerStartup启动流程以及注册broker至Namesrv
1、创建controller
2、进入BrokerController构造方法
2.1、 new ConsumerOffsetManager(this);
创建偏移量管理类,将topic@group作为key,value是一个map(key为queueId,value为offset偏移量)
2.2、new TopicConfigManager(this);
创建topic配置管理类,将系统内部的topic放入topicConfigTable(key为topic的name value为TopicConfig)-默认读写队列数量都是1
2.3、new PullMessageProcessor(this);
创建拉取消息的处理器
2.4、new BrokerOuterAPI(nettyClientConfig);
作为客户端,主要用于和namesrv交互
3、controller初始化
创建DefaultMessageStore对象,进入DefaultMessageStore构造方法
- 进入commitLog构造方法
看下mapperFileQueue类
构造方法
3.1、this.topicConfigManager.load();
因为被抽象出模板方法类 ConfigManager, 统一管理加载流程,稍后我们可以看到, ${user.home}\store\config\topics.json
3.2、this.consumerOffsetManager.load();
加载消费偏移, ${user.home}\store\config\consumerOffset.json
3.3、加载存储服务,默认情况使用 DefaultMessageStore
调用存储服务load方法 result = result && this.messageStore.load();
3.3.1、加载commitLog文件
3.3.2、加载consumerqueue文件
创建consumequeue对象时,创建了一个MapperFileQueue对象
继续调用consumequeue的load方法
3.3.3、调用恢复方法
图1↓
-
恢复consumequeue
循环结束最终 this.maxPhysicOffset会记录当前文件记录的最大的物理偏移量,回到之前代码流程如下,会将文件的最大物理偏移量进行比较,取出最大的赋值给messageStore的属性变量maxPhysicOffset,并返回
-
恢复commitLog
回到图1位置
进入commitLog的recoverNormally方法
图2↓
最后会组装消息到DispatchRequest,并返回
回到图2位置,如下,进行消息分发
简单看下CommitLogDispatcherBuildConsumeQueue类的dispathc方法
根据topic和queueId查找ConsumeQueue
offset参数是commitlogOffset,即commitLog文件中当前请求读取的位置,下图是commitLog存储结构
3.4、this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService);
初始化 ServerBootstrap,作为netty服务端
3.5、registerProcessor注册处理器
3.6、 定时记录broker状态, 默认 86400s 调度一次
3.7、定时持久化 offset, 5s 持久化一次
4、controller启动
4.1、 this.messageStore.start();
从consumequeue中获取最大物理偏移量,首先从commitLog文件中获取可用文件的最小偏移量,commitLog的文件名称是以二进制来表示的0000000000000000表示第一个文件,每个文件1G
设置分发服务的起始分发位置,并启动分发服务,reputMessageService是一个线程类,启动就是执行run方法,然后执行doReput方法,进行分发服务
一旦有生产者发送了消息到commitLog文件中后,就会判定isCommitLogAvailable是true
分发会给到consumequeue和index等文件
consumequeue文件的单个数据结构是20个字节,分别为long int long
4.2、this.remotingServer.start();
启动netty服务端
4.3、this.brokerOuterAPI.start();
@Override
public void start() {
this.defaultEventExecutorGroup = new DefaultEventExecutorGroup(
nettyClientConfig.getClientWorkerThreads(),
new ThreadFactory() {
private AtomicInteger threadIndex = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "NettyClientWorkerThread_" + this.threadIndex.incrementAndGet());
}
});
Bootstrap handler = this.bootstrap.group(this.eventLoopGroupWorker).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_KEEPALIVE, false)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, nettyClientConfig.getConnectTimeoutMillis())
.option(ChannelOption.SO_SNDBUF, nettyClientConfig.getClientSocketSndBufSize())
.option(ChannelOption.SO_RCVBUF, nettyClientConfig.getClientSocketRcvBufSize())
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
if (nettyClientConfig.isUseTLS()) {
if (null != sslContext) {
pipeline.addFirst(defaultEventExecutorGroup, "sslHandler", sslContext.newHandler(ch.alloc()));
log.info("Prepend SSL handler");
} else {
log.warn("Connections are insecure as SSLContext is null!");
}
}
pipeline.addLast(
defaultEventExecutorGroup,
new NettyEncoder(),
new NettyDecoder(),
new IdleStateHandler(0, 0, nettyClientConfig.getClientChannelMaxIdleTimeSeconds()),
new NettyConnectManageHandler(),
new NettyClientHandler());
}
});
this.timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
NettyRemotingClient.this.scanResponseTable();
} catch (Throwable e) {
log.error("scanResponseTable exception", e);
}
}
}, 1000 * 3, 1000);
if (this.channelEventListener != null) {
this.nettyEventExecutor.start();
}
}