RocketMQ源码(一):NameServer的启动
RocketMQ源码(二):broker的启动(一)
RocketMQ源码(四):producer的启动
RocketMQ源码(五):producer发送消息
RocketMQ源码(六):broker接收消息
RocketMQ源码(七):consumer的启动
RocketMQ源码(八):consumer消息拉取(一)
RocketMQ源码(九):consumer消息拉取(二)
上一篇文章说了BrokerController的创建,现在开始分析BrokerController的start过程
public void start() throws Exception {
// 获取整个文件夹锁对象,保证只有一个 messageStore 操作存储文件
lock = lockFile.getChannel().tryLock(0, 1, false);
if (lock == null || lock.isShared() || !lock.isValid()) {
throw new RuntimeException("Lock failed,MQ already started");
}
lockFile.getChannel().write(ByteBuffer.wrap("lock".getBytes()));
lockFile.getChannel().force(true);
{
// 获取commitLog中最小的Offset
long maxPhysicalPosInLogicQueue = commitLog.getMinOffset();
// 替换为所有consumerQueue中最大的消费offset
for (ConcurrentMap maps : this.consumeQueueTable.values()) {
for (ConsumeQueue logic : maps.values()) {
if (logic.getMaxPhysicOffset() > maxPhysicalPosInLogicQueue) {
maxPhysicalPosInLogicQueue = logic.getMaxPhysicOffset();
}
}
}
if (maxPhysicalPosInLogicQueue < 0) {
maxPhysicalPosInLogicQueue = 0;
}
// 如果有人删除了所有的 consumequeue 文件或磁盘被损坏。
if (maxPhysicalPosInLogicQueue < this.commitLog.getMinOffset()) {
maxPhysicalPosInLogicQueue = this.commitLog.getMinOffset();
log.warn("[TooSmallCqOffset] maxPhysicalPosInLogicQueue={} clMinOffset={}", maxPhysicalPosInLogicQueue, this.commitLog.getMinOffset());
}
log.info("[SetReputOffset] maxPhysicalPosInLogicQueue={} clMinOffset={} clMaxOffset={} clConfirmedOffset={}",
maxPhysicalPosInLogicQueue, this.commitLog.getMinOffset(), this.commitLog.getMaxOffset(), this.commitLog.getConfirmOffset());
// 设置consumerQueue的构造起始offset
this.reputMessageService.setReputFromOffset(maxPhysicalPosInLogicQueue);
// 根据commitLog构造consumerQueue和index的内容
this.reputMessageService.start();
// 等待消息分配完毕
while (true) {
if (dispatchBehindBytes() <= 0) {
break;
}
Thread.sleep(1000);
log.info("Try to finish doing reput the messages fall behind during the starting, reputOffset={} maxOffset={} behind={}", this.reputMessageService.getReputFromOffset(), this.getMaxPhyOffset(), this.dispatchBehindBytes());
}
// 由于前面的消息分配,这里将ConsumeQueue的Topic和QueueId,以及MaxOffset保存在table中,
// 同时调用correctMinOffset方法根据物理队列最小offset计算修正逻辑队列最小offset
this.recoverTopicQueueTable();
}
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
this.haService.start();
this.handleScheduleMessageService(messageStoreConfig.getBrokerRole());
}
// 刷新 consumerQueue 的数据到磁盘上
this.flushConsumeQueueService.start();
// commitLog 刷盘服务启动
this.commitLog.start();
// 存储统计服务
this.storeStatsService.start();
this.createTempFile();
// 添加定时器
this.addScheduleTask();
this.shutdown = false;
}
可以看到brokercontroller的启动,其实也就是启动各种服务的过程,接下来分别看看这些服务都是做什么的。
1. DefaultMessageStore.start
public void start() throws Exception {
// 获取整个文件夹锁对象,保证只有一个 messageStore 操作存储文件
lock = lockFile.getChannel().tryLock(0, 1, false);
if (lock == null || lock.isShared() || !lock.isValid()) {
throw new RuntimeException("Lock failed,MQ already started");
}
lockFile.getChannel().write(ByteBuffer.wrap("lock".getBytes()));
lockFile.getChannel().force(true);
{
// 获取commitLog中最小的Offset
long maxPhysicalPosInLogicQueue = commitLog.getMinOffset();
// 替换为所有consumerQueue中最大的消费offset
for (ConcurrentMap maps : this.consumeQueueTable.values()) {
for (ConsumeQueue logic : maps.values()) {
if (logic.getMaxPhysicOffset() > maxPhysicalPosInLogicQueue) {
maxPhysicalPosInLogicQueue = logic.getMaxPhysicOffset();
}
}
}
if (maxPhysicalPosInLogicQueue < 0) {
maxPhysicalPosInLogicQueue = 0;
}
// 如果有人删除了所有的 consumequeue 文件或磁盘被损坏。
if (maxPhysicalPosInLogicQueue < this.commitLog.getMinOffset()) {
maxPhysicalPosInLogicQueue = this.commitLog.getMinOffset();
log.warn("[TooSmallCqOffset] maxPhysicalPosInLogicQueue={} clMinOffset={}", maxPhysicalPosInLogicQueue, this.commitLog.getMinOffset());
}
log.info("[SetReputOffset] maxPhysicalPosInLogicQueue={} clMinOffset={} clMaxOffset={} clConfirmedOffset={}",
maxPhysicalPosInLogicQueue, this.commitLog.getMinOffset(), this.commitLog.getMaxOffset(), this.commitLog.getConfirmOffset());
// 设置consumerQueue的构造起始offset
this.reputMessageService.setReputFromOffset(maxPhysicalPosInLogicQueue);
// 根据commitLog构造consumerQueue和index的内容
this.reputMessageService.start();
// 等待消息分配完毕
while (true) {
if (dispatchBehindBytes() <= 0) {
break;
}
Thread.sleep(1000);
log.info("Try to finish doing reput the messages fall behind during the starting, reputOffset={} maxOffset={} behind={}", this.reputMessageService.getReputFromOffset(), this.getMaxPhyOffset(), this.dispatchBehindBytes());
}
// 由于前面的消息分配,这里将ConsumeQueue的Topic和QueueId,以及MaxOffset保存在table中,
// 同时调用correctMinOffset方法根据物理队列最小offset计算修正逻辑队列最小offset
this.recoverTopicQueueTable();
}
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
this.haService.start();
this.handleScheduleMessageService(messageStoreConfig.getBrokerRole());
}
// 刷新 consumerQueue 的数据到磁盘上
this.flushConsumeQueueService.start();
// commitLog 刷盘服务启动
this.commitLog.start();
// 存储统计服务
this.storeStatsService.start();
this.createTempFile();
// 添加定时器
this.addScheduleTask();
this.shutdown = false;
}
这里主要就是整个store文件夹里面文件的构造过程,以及物理文件和逻辑对象之间内容的加载和刷盘等,这里分成3部分来分析
1.1 reputMessageService.start()
ReputMessageService的功能可以概述为:发送消息的再次分发
接下来看看ReputMessageService是如何发挥作用的
public void run() {
DefaultMessageStore.log.info(this.getServiceName() + " service started");
while (!this.isStopped()) {
try {
Thread.sleep(1);
this.doReput();
} catch (Exception e) {
DefaultMessageStore.log.warn(this.getServiceName() + " service has exception. ", e);
}
}
DefaultMessageStore.log.info(this.getServiceName() + " service end");
}
可以看到这里就是重复的执行doReput方法。doReput方法其实就是从消费的最大offset开始,把未消费的commitLog消息取出来,然后根据取出来的消息不断的构建consumerQueue和indexFile。consumerQueue和indexFile详细的文件结构就不描述了,可通过链接自行了解。
private void doReput() {
if (this.reputFromOffset < DefaultMessageStore.this.commitLog.getMinOffset()) {
log.warn("The reputFromOffset={} is smaller than minPyOffset={}, this usually indicate that the dispatch behind too much and the commitlog has expired.",
this.reputFromOffset, DefaultMessageStore.this.commitLog.getMinOffset());
this.reputFromOffset = DefaultMessageStore.this.commitLog.getMinOffset();
}
// isCommitLogAvailable 方法,就是判断 reputFromOffset 是否达到了最后一个文件能访问的地方
for (boolean doNext = true; this.isCommitLogAvailable() && doNext; ) {
// 根据偏移量,读取偏移量 到 偏移量+ commitlog文件中有效数据的最大偏移量。如果未找到数据,结束doReput 方法
if (DefaultMessageStore.this.getMessageStoreConfig().isDuplicationEnable()
&& this.reputFromOffset >= DefaultMessageStore.this.getConfirmOffset()) {
break;
}
// 根据reputFromOffset获取commitLog中对应的mappedFile的对应位置起始的所有消息数据
SelectMappedBufferResult result = DefaultMessageStore.this.commitLog.getData(reputFromOffset);
if (result != null) {
try {
this.reputFromOffset = result.getStartOffset();
// 循环从 SelectMappedBufferResult 中读取消息,每次读取一条
for (int readSize = 0; readSize < result.getSize() && doNext; ) {
// 从 SelectMappedBufferResult 中读取一条消息,生成 DispatchRequest 对象
DispatchRequest dispatchRequest =
DefaultMessageStore.this.commitLog.checkMessageAndReturnSize(result.getByteBuffer(), false, false);
// 这条消息占用的字节数组长度
int size = dispatchRequest.getBufferSize() == -1 ? dispatchRequest.getMsgSize() : dispatchRequest.getBufferSize();
if (dispatchRequest.isSuccess()) {
if (size > 0) {
// 根据 commitlog 文件内容实时构建 consumequeue、index文件的关键所在
DefaultMessageStore.this.doDispatch(dispatchRequest);
// 如果开启了长轮询并且角色为主节点,则通知有新消息到达,执行一次 pullRequest 验证
// 消费端拉取消息的一种方式
if (BrokerRole.SLAVE != DefaultMessageStore.this.getMessageStoreConfig().getBrokerRole()
&& DefaultMessageStore.this.brokerConfig.isLongPollingEnable()) {
DefaultMessageStore.this.messageArrivingListener.arriving(dispatchRequest.getTopic(),
dispatchRequest.getQueueId(), dispatchRequest.getConsumeQueueOffset() + 1,
dispatchRequest.getTagsCode(), dispatchRequest.getStoreTimestamp(),
dispatchRequest.getBitMap(), dispatchRequest.getPropertiesMap());
}
// 滚动增加reputFromOffset
this.reputFromOffset += size;
readSize += size;
// 从节点进行相应的统计工作
if (DefaultMessageStore.this.getMessageStoreConfig().getBrokerRole() == BrokerRole.SLAVE) {
DefaultMessageStore.this.storeStatsService
.getSinglePutMessageTopicTimesTotal(dispatchRequest.getTopic()).incrementAndGet();
DefaultMessageStore.this.storeStatsService
.getSinglePutMessageTopicSizeTotal(dispatchRequest.getTopic())
.addAndGet(dispatchRequest.getMsgSize());
}
}
// 如果当前文件没有可读取消息数据,滚动到下一个mappedFile的起始位置
else if (size == 0) {
this.reputFromOffset = DefaultMessageStore.this.commitLog.rollNextFile(this.reputFromOffset);
readSize = result.getSize();
}
} else if (!dispatchRequest.isSuccess()) {
if (size > 0) {
log.error("[BUG]read total count not equals msg total size. reputFromOffset={}", reputFromOffset);
this.reputFromOffset += size;
} else {
doNext = false;
// If user open the dledger pattern or the broker is master node,
// it will not ignore the exception and fix the reputFromOffset variable
if (DefaultMessageStore.this.getMessageStoreConfig().isEnableDLegerCommitLog() ||
DefaultMessageStore.this.brokerConfig.getBrokerId() == MixAll.MASTER_ID) {
log.error("[BUG]dispatch message to consume queue error, COMMITLOG OFFSET: {}",
this.reputFromOffset);
this.reputFromOffset += result.getSize() - readSize;
}
}
}
}
} finally {
result.release();
}
} else {
doNext = false;
}
}
}
这里主要做了三件事:
- 从commitLog读取数据
- 通过CommitLogDispatcher将读取的数据写入到consumerQueue和indexFile中
- 发出消息达到通知,触发在长或短轮询中等待的链接。
加下来继续看看前两件事,消息达到通知在后续的consumer消息拉取中再继续分析
1.1.1 commitLog.getData
public SelectMappedBufferResult getData(final long offset) {
return this.getData(offset, offset == 0);
}
public SelectMappedBufferResult getData(final long offset, final boolean returnFirstOnNotFound) {
// mappedFile大小,默认1G
int mappedFileSize = this.defaultMessageStore.getMessageStoreConfig().getMappedFileSizeCommitLog();
// 根据 offset 通过mappedFileQueue的findMappedFileByOffset方法定位物理文件的逻辑MappedFile对象
MappedFile mappedFile = this.mappedFileQueue.findMappedFileByOffset(offset, returnFirstOnNotFound);
if (mappedFile != null) {
// 通过offset和mappedFileSize(1G)取余,得到文件指针起始位置
int pos = (int) (offset % mappedFileSize);
// 从pos位置开始,读取mappedFile的剩余所有数据
SelectMappedBufferResult result = mappedFile.selectMappedBuffer(pos);
return result;
}
return null;
}
获取offset对应位置的mappedFile,以及mappedFile从offerset开始到文件结束之间的缓存数据
1.1.2 doDispatch
public void doDispatch(DispatchRequest req) {
// 这里的dispatcherList,在DefaultMessageStore初始化的时候添加了
// CommitLogDispatcherBuildConsumeQueue 和 CommitLogDispatcherBuildIndex 实例
for (CommitLogDispatcher dispatcher : this.dispatcherList) {
dispatcher.dispatch(req);
}
}
这里挑CommitLogDispatcherBuildConsumeQueue来分析,看看是如何通过commitLog中的数据构造consumerQueue文件的
class CommitLogDispatcherBuildConsumeQueue implements CommitLogDispatcher {
@Override
public void dispatch(DispatchRequest request) {
final int tranType = MessageSysFlag.getTransactionValue(request.getSysFlag());
switch (tranType) {
// 当消息满足 TRANSACTION_NOT_TYPE 和 TRANSACTION_COMMIT_TYPE 时,调用 putMessagePositionInfo 方法
case MessageSysFlag.TRANSACTION_NOT_TYPE:
case MessageSysFlag.TRANSACTION_COMMIT_TYPE:
DefaultMessageStore.this.putMessagePositionInfo(request);
break;
case MessageSysFlag.TRANSACTION_PREPARED_TYPE:
case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE:
break;
}
}
}
针对普通消息以及提交类的事务消息,需要通过putMessagePositionInfo,将消息的信息刷新到consumerQueue中。其实可以从这里看出来一些事务消息的实现方式,如果是事务消息或者是事务回滚消息,那么是不会在consumerQueue中写入数据的,自然而然consumer也就无法消费对应的数据。
public void putMessagePositionInfo(DispatchRequest dispatchRequest) {
// 根据 topic 和 queueId 查询到 对应的ConsumeQueue,如果没有则新建
ConsumeQueue cq = this.findConsumeQueue(dispatchRequest.getTopic(), dispatchRequest.getQueueId());
cq.putMessagePositionInfoWrapper(dispatchRequest);
}
第一步就是通过topic和queueId查询consumerQueue,如果为空则先创建对应的consumerQueue对象,然后再把消息写入到consumerQueue中
public ConsumeQueue findConsumeQueue(String topic, int queueId) {
// 通过topic获取queueId-consumerQueue的map对象
ConcurrentMap map = consumeQueueTable.get(topic);
if (null == map) {
ConcurrentMap newMap = new ConcurrentHashMap(128);
ConcurrentMap oldMap = consumeQueueTable.putIfAbsent(topic, newMap);
if (oldMap != null) {
map = oldMap;
} else {
map = newMap;
}
}
ConsumeQueue logic = map.get(queueId);
// 如果没有则新建
if (null == logic) {
ConsumeQueue newLogic = new ConsumeQueue(
topic,
queueId,
// consumerQueue 的物理地址
StorePathConfigHelper.getStorePathConsumeQueue(this.messageStoreConfig.getStorePathRootDir()),
// 文件大小
this.getMessageStoreConfig().getMappedFileSizeConsumeQueue(),
this);
ConsumeQueue oldLogic = map.putIfAbsent(queueId, newLogic);
if (oldLogic != null) {
logic = oldLogic;
} else {
logic = newLogic;
}
}
return logic;
}
比较简单继续往下看
public void putMessagePositionInfoWrapper(DispatchRequest request) {
// 最大的重试次数
final int maxRetries = 30;
boolean canWrite = this.defaultMessageStore.getRunningFlags().isCQWriteable();
for (int i = 0; i < maxRetries && canWrite; i++) {
long tagsCode = request.getTagsCode();
// 是否写入consumerQueueExt数据
if (isExtWriteEnable()) {
ConsumeQueueExt.CqExtUnit cqExtUnit = new ConsumeQueueExt.CqExtUnit();
cqExtUnit.setFilterBitMap(request.getBitMap());
cqExtUnit.setMsgStoreTime(request.getStoreTimestamp());
cqExtUnit.setTagsCode(request.getTagsCode());
long extAddr = this.consumeQueueExt.put(cqExtUnit);
if (isExtAddr(extAddr)) {
tagsCode = extAddr;
} else {
log.warn("Save consume queue extend fail, So just save tagsCode! {}, topic:{}, queueId:{}, offset:{}", cqExtUnit,
topic, queueId, request.getCommitLogOffset());
}
}
// 将 commitLog 的offset、消息大小、tag信息写入consumerQueue文件
boolean result = this.putMessagePositionInfo(request.getCommitLogOffset(),
request.getMsgSize(), tagsCode, request.getConsumeQueueOffset());
// 记录消息写入时间
if (result) {
if (this.defaultMessageStore.getMessageStoreConfig().getBrokerRole() == BrokerRole.SLAVE ||
this.defaultMessageStore.getMessageStoreConfig().isEnableDLegerCommitLog()) {
this.defaultMessageStore.getStoreCheckpoint().setPhysicMsgTimestamp(request.getStoreTimestamp());
}
this.defaultMessageStore.getStoreCheckpoint().setLogicsMsgTimestamp(request.getStoreTimestamp());
return;
} else {
// XXX: warn and notify me
log.warn("[BUG]put commit log position info to " + topic + ":" + queueId + " " + request.getCommitLogOffset()
+ " failed, retry " + i + " times");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.warn("", e);
}
}
}
// XXX: warn and notify me
log.error("[BUG]consume queue can not write, {} {}", this.topic, this.queueId);
this.defaultMessageStore.getRunningFlags().makeLogicsQueueError();
}
这里就是在for循环中重复最多30次,直到将 commitLog 的offset、消息大小、tag信息通过putMessagePositionInfo写入consumerQueue文件中
private boolean putMessagePositionInfo(final long offset, final int size, final long tagsCode,
final long cqOffset) {
if (offset + size <= this.maxPhysicOffset) {
log.warn("Maybe try to build consume queue repeatedly maxPhysicOffset={} phyOffset={}", maxPhysicOffset, offset);
return true;
}
// ConsumeQueue文件是通过20字节来存放对应CommitLog文件中的消息映射
this.byteBufferIndex.flip();
this.byteBufferIndex.limit(CQ_STORE_UNIT_SIZE);
this.byteBufferIndex.putLong(offset);
this.byteBufferIndex.putInt(size);
this.byteBufferIndex.putLong(tagsCode);
// 根据 ConsumeQueueOffset 即 cqOffset * CQ_STORE_UNIT_SIZE(20)计算expectLogicOffset
final long expectLogicOffset = cqOffset * CQ_STORE_UNIT_SIZE;
// expectLogicOffset就是ConsumeQueue文件逻辑Offset,由此可以通过getLastMappedFile找到对应的文件映射MappedFile
MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile(expectLogicOffset);
if (mappedFile != null) {
// 第一个mappedFile,并且是写入的第一个数据
if (mappedFile.isFirstCreateInQueue() && cqOffset != 0 && mappedFile.getWrotePosition() == 0) {
// 设置最小的逻辑地址
this.minLogicOffset = expectLogicOffset;
// 设置刷盘起始位置
this.mappedFileQueue.setFlushedWhere(expectLogicOffset);
// 设置数据提交位置
this.mappedFileQueue.setCommittedWhere(expectLogicOffset);
// 填充一个空数据在文件最开始处
this.fillPreBlank(mappedFile, expectLogicOffset);
log.info("fill pre blank space " + mappedFile.getFileName() + " " + expectLogicOffset + " "
+ mappedFile.getWrotePosition());
}
if (cqOffset != 0) {
long currentLogicOffset = mappedFile.getWrotePosition() + mappedFile.getFileFromOffset();
if (expectLogicOffset < currentLogicOffset) {
log.warn("Build consume queue repeatedly, expectLogicOffset: {} currentLogicOffset: {} Topic: {} QID: {} Diff: {}",
expectLogicOffset, currentLogicOffset, this.topic, this.queueId, expectLogicOffset - currentLogicOffset);
return true;
}
if (expectLogicOffset != currentLogicOffset) {
LOG_ERROR.warn(
"[BUG]logic queue order maybe wrong, expectLogicOffset: {} currentLogicOffset: {} Topic: {} QID: {} Diff: {}",
expectLogicOffset,
currentLogicOffset,
this.topic,
this.queueId,
expectLogicOffset - currentLogicOffset
);
}
}
// 最大的物理地址
this.maxPhysicOffset = offset + size;
// 将 byteBufferIndex 追加到 mappedFile 后
return mappedFile.appendMessage(this.byteBufferIndex.array());
}
return false;
}
至此消息就已经写入到了consumerQueue中,接下来回到DefaultMessageStore的start方法中
1.2 handleScheduleMessageService
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
this.haService.start();
this.handleScheduleMessageService(messageStoreConfig.getBrokerRole());
}
这里如果没有开启 DLegerCommitLog 的存储消息模式,就会执行下面的两个方法
haService与rocketmq的高可用架构实现有关暂不分析
public void handleScheduleMessageService(final BrokerRole brokerRole) {
if (this.scheduleMessageService != null) {
if (brokerRole == BrokerRole.SLAVE) {
this.scheduleMessageService.shutdown();
} else {
this.scheduleMessageService.start();
}
}
}
可以看到这里判断如果broker的角色是Mater那么就会启动scheduleMessageService服务,scheduleMessageService是实现延迟消息的核心服务,简单来说就是consumer的延迟消息都会进入一个 SCHEDULE_TOPIC_XXXX 开头的topic中(消息推送过程中会提到相关部分),然后scheduleMessageService通过一个定时器判断消息是否到了延迟时间,如果到了就会把消息从 SCHEDULE_TOPIC_XXXX 中取出,重新投递到consumer的消费队列中。
先简单的认识下ScheduleMessageService,看下它的属性
// 第一次执行时的延迟时间
private static final long FIRST_DELAY_TIME = 1000L;
// 处理完一次consumerQueue所有消息时暂停时间
private static final long DELAY_FOR_A_WHILE = 100L;
// 发送异常时的消息处理时间
private static final long DELAY_FOR_A_PERIOD = 10000L;
// 延迟等级和对应延迟时间
private final ConcurrentMap delayLevelTable = new ConcurrentHashMap(32);
// 延迟等级和对应consumerQueue下次处理的offset
private final ConcurrentMap offsetTable = new ConcurrentHashMap(32);
private final DefaultMessageStore defaultMessageStore;
private final AtomicBoolean started = new AtomicBoolean(false);
private Timer timer;
private MessageStore writeMessageStore;
private int maxDelayLevel;
加下来看看它是如何实现延迟消息的,看下它的start方法
public void start() {
// 标识已经start
if (started.compareAndSet(false, true)) {
this.timer = new Timer("ScheduleMessageTimerThread", true);
for (Map.Entry entry : this.delayLevelTable.entrySet()) {
Integer level = entry.getKey();
Long timeDelay = entry.getValue();
Long offset = this.offsetTable.get(level);
if (null == offset) {
offset = 0L;
}
// 每个延迟等级使用一个DeliverDelayedMessageTimerTask,在1s之后执行
if (timeDelay != null) {
this.timer.schedule(new DeliverDelayedMessageTimerTask(level, offset), FIRST_DELAY_TIME);
}
}
this.timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
if (started.get()) ScheduleMessageService.this.persist();
} catch (Throwable e) {
log.error("scheduleAtFixedRate flush exception", e);
}
}
}, 10000, this.defaultMessageStore.getMessageStoreConfig().getFlushDelayOffsetInterval());
}
}
这里有两部分的内容
- 处理延迟队列中的消息
- 持久化offsetTable(每个延迟consumerQueue的)到磁盘C:\Users\xxx\store\config\delayOffset.json文件中
继续看下DeliverDelayedMessageTimerTask这个线程做了什么事情
@Override
public void run() {
try {
if (isStarted()) {
this.executeOnTimeup();
}
} catch (Exception e) {
// XXX: warn and notify me
log.error("ScheduleMessageService, executeOnTimeup exception", e);
ScheduleMessageService.this.timer.schedule(new DeliverDelayedMessageTimerTask(
this.delayLevel, this.offset), DELAY_FOR_A_PERIOD);
}
}
public void executeOnTimeup() {
// 获取延迟等级对应的consumerQueue
ConsumeQueue cq = ScheduleMessageService.this.defaultMessageStore.findConsumeQueue(
TopicValidator.RMQ_SYS_SCHEDULE_TOPIC, delayLevel2QueueId(delayLevel));
long failScheduleOffset = offset;
if (cq != null) {
SelectMappedBufferResult bufferCQ = cq.getIndexBuffer(this.offset);
if (bufferCQ != null) {
try {
long nextOffset = offset;
int i = 0;
ConsumeQueueExt.CqExtUnit cqExtUnit = new ConsumeQueueExt.CqExtUnit();
// 循环的取出每一条延迟消息
for (; i < bufferCQ.getSize(); i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
long offsetPy = bufferCQ.getByteBuffer().getLong();
int sizePy = bufferCQ.getByteBuffer().getInt();
// 注意这个tagCode,不再是普通的tag的hashCode,而是该延时消息到期的时间
long tagsCode = bufferCQ.getByteBuffer().getLong();
if (cq.isExtAddr(tagsCode)) {
if (cq.getExt(tagsCode, cqExtUnit)) {
tagsCode = cqExtUnit.getTagsCode();
} else {
//can't find ext content.So re compute tags code.
log.error("[BUG] can't find consume queue extend file content!addr={}, offsetPy={}, sizePy={}",
tagsCode, offsetPy, sizePy);
long msgStoreTime = defaultMessageStore.getCommitLog().pickupStoreTimestamp(offsetPy, sizePy);
tagsCode = computeDeliverTimestamp(delayLevel, msgStoreTime);
}
}
long now = System.currentTimeMillis();
long deliverTimestamp = this.correctDeliverTimestamp(now, tagsCode);
// 下次需要判断的offset
nextOffset = offset + (i / ConsumeQueue.CQ_STORE_UNIT_SIZE);
// 延迟时间减去当前时间
long countdown = deliverTimestamp - now;
// 到延迟时间
if (countdown <= 0) {
// 从commitLog取出延迟消息
MessageExt msgExt = ScheduleMessageService.this.defaultMessageStore.lookMessageByOffset(offsetPy, sizePy);
// 如果数据取出
if (msgExt != null) {
try {
// MessageExt对象转换成MessageExtBrokerInner对象
MessageExtBrokerInner msgInner = this.messageTimeup(msgExt);
// 判断消息的真实队列是否是半事务队列
if (TopicValidator.RMQ_SYS_TRANS_HALF_TOPIC.equals(msgInner.getTopic())) {
log.error("[BUG] the real topic of schedule msg is {}, discard the msg. msg={}", msgInner.getTopic(), msgInner);
continue;
}
// 把延迟消息存入真实消息队列
PutMessageResult putMessageResult = ScheduleMessageService.this.writeMessageStore.putMessage(msgInner);
if (putMessageResult != null && putMessageResult.getPutMessageStatus() == PutMessageStatus.PUT_OK) {
continue;
} else {
// 如果消息放入失败,结束当前task,重新投递DeliverDelayedMessageTimerTask从当前offset再次开始
log.error("ScheduleMessageService, a message time up, but reput it failed, topic: {} msgId {}",
msgExt.getTopic(), msgExt.getMsgId());
ScheduleMessageService.this.timer.schedule(
new DeliverDelayedMessageTimerTask(this.delayLevel, nextOffset), DELAY_FOR_A_PERIOD);
ScheduleMessageService.this.updateOffset(this.delayLevel, nextOffset);
return;
}
} catch (Exception e) {
log.error("ScheduleMessageService, messageTimeup execute error, drop it. msgExt="
+ msgExt + ", nextOffset=" + nextOffset + ",offsetPy="
+ offsetPy + ",sizePy=" + sizePy, e);
}
}
}
// 未到延迟时间
else {
// 设定到达延迟时间之后执行当前消息
ScheduleMessageService.this.timer.schedule(new DeliverDelayedMessageTimerTask(this.delayLevel, nextOffset), countdown);
// 更新下一个需要处理的延迟消息
ScheduleMessageService.this.updateOffset(this.delayLevel, nextOffset);
return;
}
} // end of for
nextOffset = offset + (i / ConsumeQueue.CQ_STORE_UNIT_SIZE);
// 处理完当前MappedFile中的消息后,重新启动TimerTask,从下一个消息开始处理
ScheduleMessageService.this.timer.schedule(new DeliverDelayedMessageTimerTask(
this.delayLevel, nextOffset), DELAY_FOR_A_WHILE);
ScheduleMessageService.this.updateOffset(this.delayLevel, nextOffset);
return;
} finally {
bufferCQ.release();
}
} // end of if (bufferCQ != null)
else {
long cqMinOffset = cq.getMinOffsetInQueue();
if (offset < cqMinOffset) {
failScheduleOffset = cqMinOffset;
log.error("schedule CQ offset invalid. offset=" + offset + ", cqMinOffset="
+ cqMinOffset + ", queueId=" + cq.getQueueId());
}
}
} // end of if (cq != null)
ScheduleMessageService.this.timer.schedule(new DeliverDelayedMessageTimerTask(this.delayLevel,
failScheduleOffset), DELAY_FOR_A_WHILE);
}
总的来说就是根据offset从对应的consumerQueue中取出所有的数据,然后遍历这些数据如果达到了延迟时间则把消息投递到真实队列中,如果没有到延迟时间则结束当前任务,重新投递DeliverDelayedMessageTimerTask在延时时间到达时从offset再次开始执行。
接下来回到DefaultMessageStore的start方法中
1.3 flushConsumeQueueService.start
主要是用于将内存中的consumerQueue刷新到磁盘上
public void run() {
DefaultMessageStore.log.info(this.getServiceName() + " service started");
while (!this.isStopped()) {
try {
// 默认1秒
int interval = DefaultMessageStore.this.getMessageStoreConfig().getFlushIntervalConsumeQueue();
this.waitForRunning(interval);
// 默认只尝试一次
this.doFlush(1);
} catch (Exception e) {
DefaultMessageStore.log.warn(this.getServiceName() + " service has exception. ", e);
}
}
this.doFlush(RETRY_TIMES_OVER);
DefaultMessageStore.log.info(this.getServiceName() + " service end");
}
private void doFlush(int retryTimes) {
int flushConsumeQueueLeastPages = DefaultMessageStore.this.getMessageStoreConfig().getFlushConsumeQueueLeastPages();
// 服务停止时,把consumerQueue的所有数据刷到磁盘
if (retryTimes == RETRY_TIMES_OVER) {
flushConsumeQueueLeastPages = 0;
}
long logicsMsgTimestamp = 0;
// 刷盘时间
int flushConsumeQueueThoroughInterval = DefaultMessageStore.this.getMessageStoreConfig().getFlushConsumeQueueThoroughInterval();
long currentTimeMillis = System.currentTimeMillis();
// 如果到了刷盘时间,不管consumerQueue数据量是否到达刷盘限制,都进行刷盘
if (currentTimeMillis >= (this.lastFlushTimestamp + flushConsumeQueueThoroughInterval)) {
this.lastFlushTimestamp = currentTimeMillis;
flushConsumeQueueLeastPages = 0;
logicsMsgTimestamp = DefaultMessageStore.this.getStoreCheckpoint().getLogicsMsgTimestamp();
}
ConcurrentMap> tables = DefaultMessageStore.this.consumeQueueTable;
for (ConcurrentMap maps : tables.values()) {
for (ConsumeQueue cq : maps.values()) {
boolean result = false;
for (int i = 0; i < retryTimes && !result; i++) {
// 刷盘
result = cq.flush(flushConsumeQueueLeastPages);
}
}
}
// 保存最后的刷盘时间
if (0 == flushConsumeQueueLeastPages) {
if (logicsMsgTimestamp > 0) {
DefaultMessageStore.this.getStoreCheckpoint().setLogicsMsgTimestamp(logicsMsgTimestamp);
}
DefaultMessageStore.this.getStoreCheckpoint().flush();
}
}
当满足两种情况时调用ConsumerQueue.flush方法,进行刷盘
- 达到flushConsumeQueueThoroughInterval刷盘时间
- consumerQueue中的数据量达到此次刷盘所需的page量
继续看下ConsumerQueue的flush方法
public boolean flush(final int flushLeastPages) {
boolean result = this.mappedFileQueue.flush(flushLeastPages);
if (isExtReadEnable()) {
result = result & this.consumeQueueExt.flush(flushLeastPages);
}
return result;
}
其实也就是调用mappedFileQueue的flush方法
public boolean flush(final int flushLeastPages) {
boolean result = true;
// 根据上次刷新的位置,得到当前的 MappedFile 对象
MappedFile mappedFile = this.findMappedFileByOffset(this.flushedWhere, this.flushedWhere == 0);
if (mappedFile != null) {
long tmpTimeStamp = mappedFile.getStoreTimestamp();
// 执行 MappedFile 的 flush 方法
int offset = mappedFile.flush(flushLeastPages);
long where = mappedFile.getFileFromOffset() + offset;
result = where == this.flushedWhere;
// 更新上次刷新的位置
this.flushedWhere = where;
if (0 == flushLeastPages) {
this.storeTimestamp = tmpTimeStamp;
}
}
return result;
}
回到DefaultMessageStore的start方法中
1.4 commitLog.start
先来看下commitLog的属性
// Message's MAGIC CODE daa320a7
public final static int MESSAGE_MAGIC_CODE = -626843481;
protected static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.STORE_LOGGER_NAME);
// End of file empty MAGIC CODE cbd43194
protected final static int BLANK_MAGIC_CODE = -875286124;
// 真实消息的逻辑队列
protected final MappedFileQueue mappedFileQueue;
// 消息存储服务
protected final DefaultMessageStore defaultMessageStore;
// 刷commitLog数据到磁盘文件的服务
private final FlushCommitLogService flushCommitLogService;
private final FlushCommitLogService commitLogService;
private final AppendMessageCallback appendMessageCallback;
private final ThreadLocal batchEncoderThreadLocal;
protected HashMap topicQueueTable = new HashMap(1024);
protected volatile long confirmOffset = -1L;
private volatile long beginTimeInLock = 0;
protected final PutMessageLock putMessageLock;
可以把commitLog理解为一个包装,用于处理消息的存取,消息的持久化策略等,接下来看看它的持久化方法
public void start() {
// 默认异步刷盘
this.flushCommitLogService.start();
// 是否开启暂存池,异步刷盘
if (defaultMessageStore.getMessageStoreConfig().isTransientStorePoolEnable()) {
this.commitLogService.start();
}
}
这里的FlushCommitLogService有三个实现类,对应了commitLog的三种刷盘方式:
- CommitRealTimeService:异步使用暂存池刷盘
- FlushRealTimeService:异步不使用暂存池刷盘
- GroupCommitService:同步刷盘
和consumerQueue的刷盘方法大同小异,就不详细的展开说了
继续回到DefaultMessageStore
1.5 addScheduleTask
private void addScheduleTask() {
// 定期清除文件,会定期删除掉长时间(默认72小时)未被引用的CommitLog文件
// 并且删除对应的 consumerQueue 和 indexFile
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
DefaultMessageStore.this.cleanFilesPeriodically();
}
}, 1000 * 60, this.messageStoreConfig.getCleanResourceInterval(), TimeUnit.MILLISECONDS);
// 定期检查CommitLog和ConsumeQueue文件有否损坏、丢失,做日志打印
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
DefaultMessageStore.this.checkSelf();
}
}, 1, 10, TimeUnit.MINUTES);
// 定期虚拟机堆栈使用日志记录
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
if (DefaultMessageStore.this.getMessageStoreConfig().isDebugLockEnable()) {
try {
if (DefaultMessageStore.this.commitLog.getBeginTimeInLock() != 0) {
long lockTime = System.currentTimeMillis() - DefaultMessageStore.this.commitLog.getBeginTimeInLock();
if (lockTime > 1000 && lockTime < 10000000) {
String stack = UtilAll.jstack();
final String fileName = System.getProperty("user.home") + File.separator + "debug/lock/stack-"
+ DefaultMessageStore.this.commitLog.getBeginTimeInLock() + "-" + lockTime;
MixAll.string2FileNotSafe(stack, fileName);
}
}
} catch (Exception e) {
}
}
}
}, 1, 1, TimeUnit.SECONDS);
// this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
// @Override
// public void run() {
// DefaultMessageStore.this.cleanExpiredConsumerQueue();
// }
// }, 1, 1, TimeUnit.HOURS);
this.diskCheckScheduledExecutorService.scheduleAtFixedRate(new Runnable() {
public void run() {
DefaultMessageStore.this.cleanCommitLogService.isSpaceFull();
}
}, 1000L, 10000L, TimeUnit.MILLISECONDS);
}
之后回到brokerController,接下来是remotingServer和fastRemotingServer的启动,这里在NameServer的启动过程中已经分析过了,不在赘述。值得一提的是虽然启动是一样的不过broker的processorTable绑定了对应事件的处理器,因此不会使用DefaultRequestProcessor来处理provider和consumer客户端的事件
1.6 brokerOuterAPI.start
BrokerOuterAPI用于处理Broker到NameSrv的各种连接事件
public void start() {
// 创建 EventExecutorGroup
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() {
@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()),
// 当nettyclient监听到Netty事件,比如连接、断开、异常等情况,执行客户端自定义的事件处理
new NettyConnectManageHandler(),
new NettyClientHandler());
}
});
// 过滤超时的response
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();
}
}
启动netty客户端,处理和NameSrv通信的事件。
回到brokerController,pullRequestHoldService是处理消息拉取相关请求的(后续分析)。
1.7 clientHousekeepingService.start
public void start() {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
ClientHousekeepingService.this.scanExceptionChannel();
} catch (Throwable e) {
log.error("Error occurred when scan not active client channels.", e);
}
}
}, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);
}
private void scanExceptionChannel() {
this.brokerController.getProducerManager().scanNotActiveChannel();
this.brokerController.getConsumerManager().scanNotActiveChannel();
this.brokerController.getFilterServerManager().scanNotActiveChannel();
}
清除异常的Channel缓存
回到BrokerController,接下来是如下代码
// 启动 mqfiltersrv.exe
if (this.filterServerManager != null) {
this.filterServerManager.start();
}
// 向 NameSrv 注册自己
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
// Master会启动事务消息检查,遍历未提交、未回滚的部分消息并向生产者发送检查请求以获取事务状态
// 进行偏移量的检查和计算等操作,并移除掉需要丢弃的消息
startProcessorByHa(messageStoreConfig.getBrokerRole());
// Slave会启动同步操作
handleSlaveSynchronize(messageStoreConfig.getBrokerRole());
this.registerBrokerAll(true, false, true);
}
这里重点看看registerBrokerAll方法
1.8 registerBrokerAll
public synchronized void registerBrokerAll(final boolean checkOrderConfig, boolean oneway, boolean forceRegister) {
// 封装 topicConfigTable 到 TopicConfigSerializeWrapper 中
TopicConfigSerializeWrapper topicConfigWrapper = this.getTopicConfigManager().buildTopicConfigSerializeWrapper();
if (!PermName.isWriteable(this.getBrokerConfig().getBrokerPermission())
|| !PermName.isReadable(this.getBrokerConfig().getBrokerPermission())) {
ConcurrentHashMap topicConfigTable = new ConcurrentHashMap();
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);
}
// 判断是否需要注册,和NameSrv比较Broker的version是否变化
if (forceRegister || needRegister(this.brokerConfig.getBrokerClusterName(),
this.getBrokerAddr(),
this.brokerConfig.getBrokerName(),
this.brokerConfig.getBrokerId(),
this.brokerConfig.getRegisterBrokerTimeoutMills())) {
doRegisterBrokerAll(checkOrderConfig, oneway, topicConfigWrapper);
}
}
首先是包装 ConcurrentMap
然后通过 needRegister 判断是否需要需要想Namesrv注册
if (forceRegister || needRegister(this.brokerConfig.getBrokerClusterName(),
this.getBrokerAddr(),
this.brokerConfig.getBrokerName(),
this.brokerConfig.getBrokerId(),
this.brokerConfig.getRegisterBrokerTimeoutMills())) {
doRegisterBrokerAll(checkOrderConfig, oneway, topicConfigWrapper);
}
private boolean needRegister(final String clusterName,
final String brokerAddr,
final String brokerName,
final long brokerId,
final int timeoutMills) {
// 封装topicManage到topicConfigWrapper中
TopicConfigSerializeWrapper topicConfigWrapper = this.getTopicConfigManager().buildTopicConfigSerializeWrapper();
List changeList = brokerOuterAPI.needRegister(clusterName, brokerAddr, brokerName, brokerId, topicConfigWrapper, timeoutMills);
boolean needRegister = false;
// 只要相比任何一个NameServer有了改变,就需要注册
for (Boolean changed : changeList) {
if (changed) {
needRegister = true;
break;
}
}
return needRegister;
}
public List needRegister(
final String clusterName,
final String brokerAddr,
final String brokerName,
final long brokerId,
final TopicConfigSerializeWrapper topicConfigWrapper,
final int timeoutMills) {
final List changedList = new CopyOnWriteArrayList<>();
// 获取NameServer的地址列表
List nameServerAddressList = this.remotingClient.getNameServerAddressList();
if (nameServerAddressList != null && nameServerAddressList.size() > 0) {
// CountDownLatch 用于控制和每个NameServer比较完成
final CountDownLatch countDownLatch = new CountDownLatch(nameServerAddressList.size());
// 遍历 NameServer
// 循环比较每个NameServer上和当前broker的dataVersion,并且记录每个比较的结果
for (final String namesrvAddr : nameServerAddressList) {
brokerOuterExecutor.execute(new Runnable() {
@Override
public void run() {
try {
// 封装QueryDataVersionRequestHeader请求头
QueryDataVersionRequestHeader requestHeader = new QueryDataVersionRequestHeader();
requestHeader.setBrokerAddr(brokerAddr);
requestHeader.setBrokerId(brokerId);
requestHeader.setBrokerName(brokerName);
requestHeader.setClusterName(clusterName);
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.QUERY_DATA_VERSION, requestHeader);
request.setBody(topicConfigWrapper.getDataVersion().encode());
// 向NameServer发送 QUERY_DATA_VERSION 类型的请求,在NameServer端比较dataVersion
RemotingCommand response = remotingClient.invokeSync(namesrvAddr, request, timeoutMills);
DataVersion nameServerDataVersion = null;
Boolean changed = false;
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
QueryDataVersionResponseHeader queryDataVersionResponseHeader =
(QueryDataVersionResponseHeader) response.decodeCommandCustomHeader(QueryDataVersionResponseHeader.class);
changed = queryDataVersionResponseHeader.getChanged();
byte[] body = response.getBody();
if (body != null) {
nameServerDataVersion = DataVersion.decode(body, DataVersion.class);
if (!topicConfigWrapper.getDataVersion().equals(nameServerDataVersion)) {
changed = true;
}
}
if (changed == null || changed) {
changedList.add(Boolean.TRUE);
}
}
default:
break;
}
log.warn("Query data version from name server {} OK,changed {}, broker {},name server {}", namesrvAddr, changed, topicConfigWrapper.getDataVersion(), nameServerDataVersion == null ? "" : nameServerDataVersion);
} catch (Exception e) {
changedList.add(Boolean.TRUE);
log.error("Query data version from name server {} Exception, {}", namesrvAddr, e);
} finally {
countDownLatch.countDown();
}
}
});
}
try {
countDownLatch.await(timeoutMills, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
log.error("query dataversion from nameserver countDownLatch await Exception", e);
}
}
return changedList;
}
当判断为需要注册之后,就会调用doRegisterBrokerAll方法,将broker信息注册到NameServer上
private void doRegisterBrokerAll(boolean checkOrderConfig, boolean oneway,
TopicConfigSerializeWrapper topicConfigWrapper) {
// 返回的是向每个NameServer注册的结果,其中包含了haServerAddr、masterAddr、kvTable等信息
List registerBrokerResultList = this.brokerOuterAPI.registerBrokerAll(
this.brokerConfig.getBrokerClusterName(),
this.getBrokerAddr(),
this.brokerConfig.getBrokerName(),
this.brokerConfig.getBrokerId(),
this.getHAServerAddr(),
topicConfigWrapper,
this.filterServerManager.buildNewFilterServerList(),
oneway,
this.brokerConfig.getRegisterBrokerTimeoutMills(),
this.brokerConfig.isCompressedRegister());
if (registerBrokerResultList.size() > 0) {
RegisterBrokerResult registerBrokerResult = registerBrokerResultList.get(0);
if (registerBrokerResult != null) {
// 更新主从节点信息
if (this.updateMasterHAServerAddrPeriodically && registerBrokerResult.getHaServerAddr() != null) {
this.messageStore.updateHaMasterAddress(registerBrokerResult.getHaServerAddr());
}
// 保存master地址信息
this.slaveSynchronize.setMasterAddr(registerBrokerResult.getMasterAddr());
// 更新配置信息
if (checkOrderConfig) {
this.getTopicConfigManager().updateOrderTopicConfig(registerBrokerResult.getKvTable());
}
}
}
}
接下来brokerController会启动一个定时器定时调用registerBrokerAll以便实时更新信息
接下来还会启动两个定时器
// broker的统计,待实现
if (this.brokerStatsManager != null) {
this.brokerStatsManager.start();
}
// 当读写磁盘忙碌的时候,结束掉一些发送消息的请求
if (this.brokerFastFailure != null) {
this.brokerFastFailure.start();
}
继续看下brokerFastFailure
public void start() {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
if (brokerController.getBrokerConfig().isBrokerFastFailureEnable()) {
cleanExpiredRequest();
}
}
}, 1000, 10, TimeUnit.MILLISECONDS);
}
private void cleanExpiredRequest() {
// 如果os内存缓存繁忙
// 快速失败,返回发送消息请求队列中的请求
while (this.brokerController.getMessageStore().isOSPageCacheBusy()) {
try {
if (!this.brokerController.getSendThreadPoolQueue().isEmpty()) {
final Runnable runnable = this.brokerController.getSendThreadPoolQueue().poll(0, TimeUnit.SECONDS);
if (null == runnable) {
break;
}
final RequestTask rt = castRunnable(runnable);
rt.returnResponse(RemotingSysResponseCode.SYSTEM_BUSY, String.format("[PCBUSY_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: %sms, size of queue: %d", System.currentTimeMillis() - rt.getCreateTimestamp(), this.brokerController.getSendThreadPoolQueue().size()));
} else {
break;
}
} catch (Throwable ignored) {
}
}
// 结束对应队列中,超过对应等待时间的请求
cleanExpiredRequestInQueue(this.brokerController.getSendThreadPoolQueue(),
this.brokerController.getBrokerConfig().getWaitTimeMillsInSendQueue());
cleanExpiredRequestInQueue(this.brokerController.getPullThreadPoolQueue(),
this.brokerController.getBrokerConfig().getWaitTimeMillsInPullQueue());
cleanExpiredRequestInQueue(this.brokerController.getHeartbeatThreadPoolQueue(),
this.brokerController.getBrokerConfig().getWaitTimeMillsInHeartbeatQueue());
cleanExpiredRequestInQueue(this.brokerController.getEndTransactionThreadPoolQueue(), this
.brokerController.getBrokerConfig().getWaitTimeMillsInTransactionQueue());
}
比较简单就不详细分析了
到此broker的启动过程就结束了