RocketMQ消息存储二:数据文件存组件储层

数据文件存储组件层只有一个类DefaultMessageStore,它负责所有存储相关的工作,上层业务中的消息发送处理、查询处理、拉取处理,最终都委托给DefaultMessageStore操作。

核心属性

    // 存储配置
    private final MessageStoreConfig messageStoreConfig;
    // CommitLog原始消息存储
    private final CommitLog commitLog;
    // 消费队列信息
    private final ConcurrentMap<String/* topic */, ConcurrentMap<Integer/* queueId */, ConsumeQueue>> consumeQueueTable;
    // ConsumeQueue刷盘
    private final FlushConsumeQueueService flushConsumeQueueService;
    // 清理commitLog
    private final CleanCommitLogService cleanCommitLogService;
    // 清理ConsumeQueue刷
    private final CleanConsumeQueueService cleanConsumeQueueService;
    // 消息索引服务
    private final IndexService indexService;
    // 新建 MappedFile 服务
    private final AllocateMappedFileService allocateMappedFileService;
    // 消息分发
    private final ReputMessageService reputMessageService;
    // 主从同步高可用
    private final HAService haService;
    // 定时消息
    private final ScheduleMessageService scheduleMessageService;
    // 统计服务
    private final StoreStatsService storeStatsService;
    // 短暂存储池
    private final TransientStorePool transientStorePool;

    private final RunningFlags runningFlags = new RunningFlags();
    private final SystemClock systemClock = new SystemClock();
    // 定时任务
    private final ScheduledExecutorService scheduledExecutorService =
        Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("StoreScheduledThread"));
    private final BrokerStatsManager brokerStatsManager;
    private final MessageArrivingListener messageArrivingListener;
    private final BrokerConfig brokerConfig;

    private volatile boolean shutdown = true;

    private StoreCheckpoint storeCheckpoint;

    private AtomicLong printTimes = new AtomicLong(0);

    private final LinkedList<CommitLogDispatcher> dispatcherList;
    // 文件 
    private RandomAccessFile lockFile;

内部方法

start()方法

start方法是启动入口,主要逻辑为:(1)加锁,一个broker只能启动一个DefaultMessageStore;(2)CommitLog、ConsumeQueue检查与恢复;(3)启动DefaultMessageStore内的各种服务。

putMessage()、putMessages()方法

存储消息的方法,逻辑比较简单,首先检查是否可写,接着将发送消息委托给CommitLog处理。最后统计耗时情况和失败情况。

isOSPageCacheBusy()判断系统是否繁忙

    public boolean isOSPageCacheBusy() {
   
        // putMessage 加锁的时间,如果没有正在写入,则值为0
        long begin = this.getCommitLog().getBeginTimeInLock();
        long diff = this.systemClock.now() - begin;
        // 超过配置时间,则为系统繁忙
        return diff < 10000000
                && diff > this.messageStoreConfig.getOsPageCacheBusyTimeOutMills();
    }

getMessage() 获取消息

逻辑见注释

    public GetMessageResult getMessage(final String group, final String topic, final int queueId, final long offset,
        final int maxMsgNums,
        final MessageFilter messageFilter) {
   

        // 前置校验,省略

        // 获取ConsumeQueue
        ConsumeQueue consumeQueue = findConsumeQueue(topic, queueId);
        if (consumeQueue != null) {
   
            // 检查offset合法性
            minOffset = consumeQueue.getMinOffsetInQueue();
            maxOffset = consumeQueue.getMaxOffsetInQueue();

            if (maxOffset == 0) {
   
                status = GetMessageStatus.NO_MESSAGE_IN_QUEUE;
                nextBeginOffset = nextOffsetCorrection(offset, 0);
            } else if (offset < minOffset) {
   
                status = GetMessageStatus.OFFSET_TOO_SMALL;
                nextBeginOffset = nextOffsetCorrection(offset, minOffset);
            } else if (offset == maxOffset) {
   
                status = GetMessageStatus.OFFSET_OVERFLOW_ONE;
                nextBeginOffset = nextOffsetCorrection(offset, offset);
            } else if (offset > maxOffset) {
   
                status = GetMessageStatus.OFFSET_OVERFLOW_BADLY;
                if (0 == minOffset) {
   
                    nextBeginOffset = nextOffsetCorrection(offset, minOffset);
                } else {
   
                    nextBeginOffset = nextOffsetCorrection(offset, maxOffset);
                }
            } else {
   
                // offset合法,从ConsumeQueue中获取可读的bytebuffer
                SelectMappedBufferResult bufferConsumeQueue = consumeQueue.getIndexBuffer(offset);
                if (bufferConsumeQueue != null) {
   
                    try {
   
                        status = GetMessageStatus.NO_MATCHED_MESSAGE;
                        long nextPhyFileStartOffset = Long.MIN_VALUE;
                        long maxPhyOffsetPulling = 0;

                        int i= 0;
                        final int maxFilterMessageCount = Math.max(16000, maxMsgNums * ConsumeQueue.CQ_STORE_UNIT_SIZE);
                        final boolean diskFallRecorded = this.messageStoreConfig.isDiskFallRecorded();
                        ConsumeQueueExt.CqExtUnit cqExtUnit = new ConsumeQueueExt.CqExtUnit();
                        // 遍历bytebuffer,对每条消息进程处理。ConsumeQueue消息定长,每个20字节
                        for (; i < bufferConsumeQueue.getSize() && i < maxFilterMessageCount; i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
   
                            long offsetPy = bufferConsumeQueue.getByteBuffer().getLong();
                            int sizePy = bufferConsumeQueue.getByteBuffer().getInt();
                            long tagsCode = bufferConsumeQueue.getByteBuffer().getLong();

                            maxPhyOffsetPulling = offsetPy;

                            if (nextPhyFileStartOffset != Long.MIN_VALUE) {
   
                                if (offsetPy < nextPhyFileStartOffset)
                                    continue;
                            }
                            // 消息在磁盘
                            boolean isInDisk = checkInDiskByCommitOffset(offsetPy, maxOffsetPy);
                            // 此批消息拉取到上限,结束
                            if (
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值