RocketMQ消息存储--ConsumeQueue构建过程

        通过源码追溯,我们发现Store包里的DefaultMessageStore是Consumer的构建入口(DefaultMessageStore做了很多工作并不仅限于构建ConsumerQueue)。DefaultMessageStore提供ConcurrentMap(private final ConcurrentMap> consumeQueueTable;)来对Consumerqueue进行管理,使用ReputMessageService作为构建ConsumerQueue的触发器,最终通过调用ConsumerQueue.putMessagePositionInfoWrapper进行ConsumerQueue的构建。

1.ReputMessageService继承ServiceThread是一个线程服务,服务启动后每间隔1毫秒调用一次doReput方法,doReput方法会调用CommitLogDispatcher进行消息分发。

class ReputMessageService extends ServiceThread {

        /**省略非关键代码**/
        private void doReput() {
            for (boolean doNext = true; this.isCommitLogAvailable() && doNext; ) {

                if (DefaultMessageStore.this.getMessageStoreConfig().isDuplicationEnable()
                    && this.reputFromOffset >= DefaultMessageStore.this.getConfirmOffset()) {
                    break;
                }

                SelectMappedBufferResult result = DefaultMessageStore.this.commitLog.getData(reputFromOffset);
                if (result != null) {
                    try {
                        this.reputFromOffset = result.getStartOffset();

                        for (int readSize = 0; readSize < result.getSize() && doNext; ) {
                            DispatchRequest dispatchRequest =
                                DefaultMessageStore.this.commitLog.checkMessageAndReturnSize(result.getByteBuffer(), false, false);
                            int size = dispatchRequest.getMsgSize();

                            if (dispatchRequest.isSuccess()) {
                                if (size > 0) {
								    /**DefaultMessageStore在初始化时会维护一个dispatcherList,默认加载两个类;
									*CommitLogDispatcherBuildConsumeQueue用于构建ConsumerQueue
									*CommitLogDispatcherBuildIndex用于构建Index索引
									
									**/
                                    DefaultMessageStore.this.doDispatch(dispatchRequest);

                                    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());
                                    }

                                    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());
                                    }
                                } 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 (DefaultMessageStore.this.brokerConfig.getBrokerId() == MixAll.MASTER_ID) {
                                        log.error("[BUG]the master dispatch message to consume queue error, COMMITLOG OFFSET: {}",
                                            this.reputFromOffset);

                                        this.reputFromOffset += result.getSize() - readSize;
                                    }
                                }
                            }
                        }
                    } finally {
                        result.release();
                    }
                } else {
                    doNext = false;
                }
            }
        }

        @Override
        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");
        }

    }

2.dispatch分发后CommitLogDispatcherBuildConsumeQueue用于触发构建ConsumerQueue的操作。

class CommitLogDispatcherBuildConsumeQueue implements CommitLogDispatcher {

        @Override
        public void dispatch(DispatchRequest request) {
            final int tranType = MessageSysFlag.getTransactionValue(request.getSysFlag());
            switch (tranType) {
			    //非事务消息和已提交的事务消息,调用putMessagePositionInfo方法,触发构建ConsumerQueue的操作。
                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;
            }
        }
    }

3.putMessagePositionInfo方法首先在consumeQueueTable中找到对应TOPIC和queueID的ConsumerQueue然后调用ConsumerQueue的putMessagePositionInfoWrapper方法构建ConsumerQueue。

//DefaultMessageStore
public void putMessagePositionInfo(DispatchRequest dispatchRequest) {
        ConsumeQueue cq = this.findConsumeQueue(dispatchRequest.getTopic(), dispatchRequest.getQueueId());
        cq.putMessagePositionInfoWrapper(dispatchRequest);
    }

4.ConsumeQueue的putMessagePositionInfoWrapper完成ConsumerQueue的构建。

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();
	    //如果配置启动了ConsumeQueue扩展类型,则为ConsumerQueue构建扩展索引ConsumeQueueExt。
		
		if (isExtWriteEnable()) {
			ConsumeQueueExt.CqExtUnit cqExtUnit = new ConsumeQueueExt.CqExtUnit();
			cqExtUnit.setFilterBitMap(request.getBitMap());
			cqExtUnit.setMsgStoreTime(request.getStoreTimestamp());
			cqExtUnit.setTagsCode(request.getTagsCode());
            //如果配置的扩展类型地址,则ConsumerQueue中tagsCode保存的值是扩展信息的偏移量。
			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());
			}
		}
		//ConsumerQueue准备完成后构建ConsumerQueue。
		boolean result = this.putMessagePositionInfo(request.getCommitLogOffset(),
			request.getMsgSize(), tagsCode, request.getConsumeQueueOffset());
		if (result) {
			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();
}

5.putMessagePositionInfo构建consumerQueue

private boolean putMessagePositionInfo(final long offset, final int size, final long tagsCode,
        final long cqOffset) {

	if (offset <= this.maxPhysicOffset) {
		return true;
	}
    /*
	*1.构建ConsumerQueue索引
	*补充一下:ConsumerQueue索引的数据结构=CommitLog偏移(long 类型 8字节)+消息的大小(int类型,4字节)+消息标签对应的hashCode(long类型,8字节)
	*
	*/
	this.byteBufferIndex.flip();
	this.byteBufferIndex.limit(CQ_STORE_UNIT_SIZE);
	this.byteBufferIndex.putLong(offset);
	this.byteBufferIndex.putInt(size);
	this.byteBufferIndex.putLong(tagsCode);

	final long expectLogicOffset = cqOffset * CQ_STORE_UNIT_SIZE;
    /*
	*2.构建mappedFileQueue,写入消息物理偏移、消息大小和tagsCode
	*/
	MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile(expectLogicOffset);
	if (mappedFile != null) {

		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;
		return mappedFile.appendMessage(this.byteBufferIndex.array());
	}
	return false;
}

 

你可能感兴趣的:(RocketMQ)