说在前面
继续解析管理请求之SEARCH_OFFSET_BY_TIMESTAMP 按时间查询offset
源码解析
进入这个方法org.apache.rocketmq.broker.processor.AdminBrokerProcessor#searchOffsetByTimestamp 按时间查询offset
private RemotingCommand searchOffsetByTimestamp(ChannelHandlerContext ctx, RemotingCommand request) throws RemotingCommandException { final RemotingCommand response = RemotingCommand.createResponseCommand(SearchOffsetResponseHeader.class); final SearchOffsetResponseHeader responseHeader = (SearchOffsetResponseHeader) response.readCustomHeader(); final SearchOffsetRequestHeader requestHeader = (SearchOffsetRequestHeader) request.decodeCommandCustomHeader(SearchOffsetRequestHeader.class); // 按时间查询offset=》 long offset = this.brokerController.getMessageStore().getOffsetInQueueByTime(requestHeader.getTopic(), requestHeader.getQueueId(), requestHeader.getTimestamp()); responseHeader.setOffset(offset); response.setCode(ResponseCode.SUCCESS); response.setRemark(null); return response; }
进入到这个方法org.apache.rocketmq.store.DefaultMessageStore#getOffsetInQueueByTime 按时间查询offset
public long getOffsetInQueueByTime(String topic, int queueId, long timestamp) { // 根据topic找到队列群 =》 ConsumeQueue logic = this.findConsumeQueue(topic, queueId); if (logic != null) { // 按时间查询offset=》 return logic.getOffsetInQueueByTime(timestamp); } return 0; }
进入这个方法org.apache.rocketmq.store.DefaultMessageStore#findConsumeQueue 查询消费队列
public ConsumeQueue findConsumeQueue(String topic, int queueId) { ConcurrentMap<Integer, ConsumeQueue> map = consumeQueueTable.get(topic); if (null == map) { ConcurrentMap<Integer, ConsumeQueue> newMap = new ConcurrentHashMap<Integer, ConsumeQueue>(128); ConcurrentMap<Integer, ConsumeQueue> oldMap = consumeQueueTable.putIfAbsent(topic, newMap); if (oldMap != null) { map = oldMap; } else { map = newMap; } } // 按queue id查找消费者队列 ConsumeQueue logic = map.get(queueId); if (null == logic) { ConsumeQueue newLogic = new ConsumeQueue( topic, queueId, // 消费者队列存储地址 user.home/store/consumequeue StorePathConfigHelper.getStorePathConsumeQueue(this.messageStoreConfig.getStorePathRootDir()), // 每个文件存储默认30W this.getMessageStoreConfig().getMapedFileSizeConsumeQueue(), this); ConsumeQueue oldLogic = map.putIfAbsent(queueId, newLogic); if (oldLogic != null) { logic = oldLogic; } else { logic = newLogic; } } return logic; }
往上返回到这个方法org.apache.rocketmq.store.ConsumeQueue#getOffsetInQueueByTime 按时间查询offset
public long getOffsetInQueueByTime(final long timestamp) { // 根据时间找到映射的文件,文件可以知道最后一次修改的时间 =》 MappedFile mappedFile = this.mappedFileQueue.getMappedFileByTime(timestamp); if (mappedFile != null) { long offset = 0; int low = minLogicOffset > mappedFile.getFileFromOffset() ? (int) (minLogicOffset - mappedFile.getFileFromOffset()) : 0; int high = 0; int midOffset = -1, targetOffset = -1, leftOffset = -1, rightOffset = -1; long leftIndexValue = -1L, rightIndexValue = -1L; // 获取最小的物理偏移量 =》 long minPhysicOffset = this.defaultMessageStore.getMinPhyOffset(); // =》 SelectMappedBufferResult sbr = mappedFile.selectMappedBuffer(0); if (null != sbr) { ByteBuffer byteBuffer = sbr.getByteBuffer(); high = byteBuffer.limit() - CQ_STORE_UNIT_SIZE; try { while (high >= low) { midOffset = (low + high) / (2 * CQ_STORE_UNIT_SIZE) * CQ_STORE_UNIT_SIZE; byteBuffer.position(midOffset); long phyOffset = byteBuffer.getLong(); int size = byteBuffer.getInt(); if (phyOffset < minPhysicOffset) { low = midOffset + CQ_STORE_UNIT_SIZE; leftOffset = midOffset; continue; } // 按物理offset从commitLog中获取存储时间=》 long storeTime = this.defaultMessageStore.getCommitLog().pickupStoreTimestamp(phyOffset, size); if (storeTime < 0) { return 0; } else if (storeTime == timestamp) { targetOffset = midOffset; break; } else if (storeTime > timestamp) { high = midOffset - CQ_STORE_UNIT_SIZE; rightOffset = midOffset; rightIndexValue = storeTime; } else { low = midOffset + CQ_STORE_UNIT_SIZE; leftOffset = midOffset; leftIndexValue = storeTime; } } if (targetOffset != -1) { offset = targetOffset; } else { if (leftIndexValue == -1) { offset = rightOffset; } else if (rightIndexValue == -1) { offset = leftOffset; } else { offset = Math.abs(timestamp - leftIndexValue) > Math.abs(timestamp - rightIndexValue) ? rightOffset : leftOffset; } } return (mappedFile.getFileFromOffset() + offset) / CQ_STORE_UNIT_SIZE; } finally { // 映射文件释放=》 sbr.release(); } } } return 0; }
进入这个方法org.apache.rocketmq.store.MappedFileQueue#getMappedFileByTime 按时间查询映射文件
public MappedFile getMappedFileByTime(final long timestamp) { Object[] mfs = this.copyMappedFiles(0); if (null == mfs) return null; for (int i = 0; i < mfs.length; i++) { MappedFile mappedFile = (MappedFile) mfs[i]; // 如果文件的最后修改时间大于等于参数时间 if (mappedFile.getLastModifiedTimestamp() >= timestamp) { return mappedFile; } } return (MappedFile) mfs[mfs.length - 1]; }
往上返回进入到这个方法org.apache.rocketmq.store.DefaultMessageStore#getMinPhyOffset 获取最小的物理offset
@Override public long getMinPhyOffset() { // 获取commitLog的最小偏移量 =》 return this.commitLog.getMinOffset(); }
进入这个方法org.apache.rocketmq.store.CommitLog#getMinOffset
public long getMinOffset() { MappedFile mappedFile = this.mappedFileQueue.getFirstMappedFile(); if (mappedFile != null) { if (mappedFile.isAvailable()) { // 获取映射文件的起始偏移量 return mappedFile.getFileFromOffset(); } else { // 获取下个文件的起始偏移量 return this.rollNextFile(mappedFile.getFileFromOffset()); } } return -1; }
往上返回到这个方法org.apache.rocketmq.store.CommitLog#pickupStoreTimestamp 按offset查询存储时间
public long pickupStoreTimestamp(final long offset, final int size) { if (offset >= this.getMinOffset()) { // =》 SelectMappedBufferResult result = this.getMessage(offset, size); if (null != result) { try { // 获取消息存储时间 return result.getByteBuffer().getLong(MessageDecoder.MESSAGE_STORE_TIMESTAMP_POSTION); } finally { result.release(); } } } return -1; }
进入这个方法org.apache.rocketmq.store.CommitLog#getMessage 获取时间、offset属性存储的映射buffer
public SelectMappedBufferResult getMessage(final long offset, final int size) { int mappedFileSize = this.defaultMessageStore.getMessageStoreConfig().getMapedFileSizeCommitLog(); // 根据offset找到映射文件 =》 MappedFile mappedFile = this.mappedFileQueue.findMappedFileByOffset(offset, offset == 0); if (mappedFile != null) { int pos = (int) (offset % mappedFileSize); return mappedFile.selectMappedBuffer(pos, size); } return null; }
进入这个方法org.apache.rocketmq.store.MappedFileQueue#findMappedFileByOffset(long, boolean) 按offset获取映射文件
public MappedFile findMappedFileByOffset(final long offset, final boolean returnFirstOnNotFound) { try { // 获取队列中第一个映射文件 MappedFile firstMappedFile = this.getFirstMappedFile(); // 获取队列中最后一个映射文件 MappedFile lastMappedFile = this.getLastMappedFile(); if (firstMappedFile != null && lastMappedFile != null) { // 如果offset不在索引文件的offset范围内 if (offset < firstMappedFile.getFileFromOffset() || offset >= lastMappedFile.getFileFromOffset() + this.mappedFileSize) { LOG_ERROR.warn("Offset not matched. Request offset: {}, firstOffset: {}, lastOffset: {}, mappedFileSize: {}, mappedFiles count: {}", offset, firstMappedFile.getFileFromOffset(), lastMappedFile.getFileFromOffset() + this.mappedFileSize, this.mappedFileSize, this.mappedFiles.size()); } else { // 找到映射文件在队列中的索引位置 int index = (int) ((offset / this.mappedFileSize) - (firstMappedFile.getFileFromOffset() / this.mappedFileSize)); MappedFile targetFile = null; try { // 获取索引文件 targetFile = this.mappedFiles.get(index); } catch (Exception ignored) { } // offset在目标文件的起始offset和结束offset范围内 if (targetFile != null && offset >= targetFile.getFileFromOffset() && offset < targetFile.getFileFromOffset() + this.mappedFileSize) { return targetFile; } // 如果按索引在队列中找不到映射文件就遍历队列查找映射文件 for (MappedFile tmpMappedFile : this.mappedFiles) { if (offset >= tmpMappedFile.getFileFromOffset() && offset < tmpMappedFile.getFileFromOffset() + this.mappedFileSize) { return tmpMappedFile; } } } // 如果offset=0获取队列中第一个映射文件,个人感觉这个逻辑是否放在前面判断更为合理,还是放在这里另有深意 if (returnFirstOnNotFound) { return firstMappedFile; } } } catch (Exception e) { log.error("findMappedFileByOffset Exception", e); } return null; }
往上返回到这个方法org.apache.rocketmq.broker.processor.AdminBrokerProcessor#searchOffsetByTimestamp 解析完毕。
说在最后
本次解析仅代表个人观点,仅供参考。
加入技术微信群
钉钉技术群