rocketmq异步刷盘源码分析

文章主要分析的是rocketmq的异步刷盘

//commitlog.java.asyncPutMessage()方法
//提交刷盘的请求,这里封装了一个CompletableFuture,后面可以并行的去处理
CompletableFuture<PutMessageStatus> flushResultFuture = submitFlushRequest(result, putMessageResult, msg);

submitFlushRequest()提交刷盘的请求

public CompletableFuture<PutMessageStatus> submitFlushRequest(AppendMessageResult result, PutMessageResult putMessageResult,
                                                              MessageExt messageExt) {
    // Synchronization flush 同步刷盘
    if (FlushDiskType.SYNC_FLUSH == this.defaultMessageStore.getMessageStoreConfig().getFlushDiskType()) {
        //GroupCommitService()
        final GroupCommitService service = (GroupCommitService) this.flushCommitLogService;
        //这个message是需要等待storeMsg的结果 ,默认是true
        if (messageExt.isWaitStoreMsgOK()) {
          	//封装成一个GroupCommitRequest
            //syncFlushTimeout = 1000 * 5; 同步刷盘的时间
            GroupCommitRequest request = new GroupCommitRequest(result.getWroteOffset() + result.getWroteBytes(),this.defaultMessageStore.getMessageStoreConfig().getSyncFlushTimeout());
            //GroupCommitService提交一个request,见下面分析
            service.putRequest(request);
            return request.future();
        } else {
            service.wakeup();
            return CompletableFuture.completedFuture(PutMessageStatus.PUT_OK);
        }
    }
    // Asynchronous flush 异步刷盘
    else {
      	//如果是一步刷盘,如果开启了对外内存池
        if (!this.defaultMessageStore.getMessageStoreConfig().isTransientStorePoolEnable()) {
            //FlushRealTimeService
            flushCommitLogService.wakeup();
        } else  {
          	//没有开启对外内存池,不直接到pagecache,先存在缓冲区里面,这个会使用我们的writeBuffer,我们暂时先不讲
            commitLogService.wakeup();
        }
        return CompletableFuture.completedFuture(PutMessageStatus.PUT_OK);
    }
}

FlushRealTimeService 异步刷盘

FlushRealTimeService异步刷盘,这边直接flushCommitLogService.wakeup(),然后返回CompletableFuture.completedFuture(PutMessageStatus.PUT_OK)返回成功

class FlushRealTimeService extends FlushCommitLogService {
  private long lastFlushTimestamp = 0;
  private long printTimes = 0;
	//还是看我们的run方法
  public void run() {
    CommitLog.log.info(this.getServiceName() + " service started");

    while (!this.isStopped()) {
      //是否定时刷盘,默认为false即实时刷盘
      boolean flushCommitLogTimed = CommitLog.this.defaultMessageStore.getMessageStoreConfig().isFlushCommitLogTimed();
			// 刷盘时间间隔,默认500ms
      int interval = CommitLog.this.defaultMessageStore.getMessageStoreConfig().getFlushIntervalCommitLog();
      //每次刷盘最小刷入页,数默认4页,脏页达不到4页,不进行刷盘
      int flushPhysicQueueLeastPages = CommitLog.this.defaultMessageStore.getMessageStoreConfig().getFlushCommitLogLeastPages();
			//两次刷盘之间的最长时间间隔默认10s
      int flushPhysicQueueThoroughInterval =
        CommitLog.this.defaultMessageStore.getMessageStoreConfig().getFlushCommitLogThoroughInterval();

      boolean printFlushProgress = false;

      // Print flush progress
      long currentTimeMillis = System.currentTimeMillis();
      //超过flushPhysicQueueThoroughInterval这个时间,无论脏页是否够都去刷盘,flushPhysicQueueLeastPages = 0这个之前说过只要0,就是强制刷盘
      if (currentTimeMillis >= (this.lastFlushTimestamp + flushPhysicQueueThoroughInterval)) {
        this.lastFlushTimestamp = currentTimeMillis;
        flushPhysicQueueLeastPages = 0;
        printFlushProgress = (printTimes++ % 10) == 0;
      }

      try {
        if (flushCommitLogTimed) {
          Thread.sleep(interval);
        } else {
          this.waitForRunning(interval);
        }

        if (printFlushProgress) {
          this.printFlushProgress();
        }

        long begin = System.currentTimeMillis();
        //刷盘,正常情况下flushPhysicQueueLeastPages = 4,同步刷盘是0
        CommitLog.this.mappedFileQueue.flush(flushPhysicQueueLeastPages);
        long storeTimestamp = CommitLog.this.mappedFileQueue.getStoreTimestamp();
        if (storeTimestamp > 0) {
          CommitLog.this.defaultMessageStore.getStoreCheckpoint().setPhysicMsgTimestamp(storeTimestamp);
        }
        long past = System.currentTimeMillis() - begin;
        if (past > 500) {
          log.info("Flush data to disk costs {} ms", past);
        }
      } catch (Throwable e) {
        CommitLog.log.warn(this.getServiceName() + " service has exception. ", e);
        this.printFlushProgress();
      }
    }

    // Normal shutdown, to ensure that all the flush before exit
    boolean result = false;
    for (int i = 0; i < RETRY_TIMES_OVER && !result; i++) {
      result = CommitLog.this.mappedFileQueue.flush(0);
      CommitLog.log.info(this.getServiceName() + " service shutdown, retry " + (i + 1) + " times " + (result ? "OK" : "Not OK"));
    }

    this.printFlushProgress();

    CommitLog.log.info(this.getServiceName() + " service end");
  }

  @Override
  public String getServiceName() {
    return FlushRealTimeService.class.getSimpleName();
  }

  private void printFlushProgress() {
    // CommitLog.log.info("how much disk fall behind memory, "
    // + CommitLog.this.mappedFileQueue.howMuchFallBehind());
  }

  @Override
  public long getJointime() {
    return 1000 * 60 * 5;
  }
}

flush() 刷盘

public boolean flush(final int flushLeastPages) {
  boolean result = true;//返回一个mappedFile就是返回一个commitLog的对象
  //获得我们刷盘要的MappedFile
  MappedFile mappedFile = this.findMappedFileByOffset(this.flushedWhere, this.flushedWhere == 0);
  if (mappedFile != null) {
    long tmpTimeStamp = mappedFile.getStoreTimestamp();
    //这里去刷新进去,因为是flushLeastPages是0mappedFile.flush(0),这个方法就代表都是强制刷盘
    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;
}


public int flush(final int flushLeastPages) {
  if (this.isAbleToFlush(flushLeastPages)) {
    if (this.hold()) {
      int value = getReadPosition();

      try {
        //We only append data to fileChannel or mappedByteBuffer, never both.
        if (writeBuffer != null || this.fileChannel.position() != 0) {
          this.fileChannel.force(false);
        } else {
          //mappedByteBuffer强制刷新
          this.mappedByteBuffer.force();
        }
      } catch (Throwable e) {
        log.error("Error occurred when force data to disk.", e);
      }
      //更新flushwhere为当前位置
      this.flushedPosition.set(value);
      this.release();
    } else {
      log.warn("in flush, hold failed, flush offset = " + this.flushedPosition.get());
      this.flushedPosition.set(getReadPosition());
    }
  }
  //返回当前位置
  return this.getFlushedPosition();
}

//是否值得谁像你
private boolean isAbleToFlush(final int flushLeastPages) {
  //刷新的位置
  int flush = this.flushedPosition.get();
  //当前写的位置
  int write = getReadPosition();

  if (this.isFull()) {
    return true;
  }
	//如果不是0,会判断是否大于1页,因为我们是0,所以强制刷新
  if (flushLeastPages > 0) {
    //大于4K了
    return ((write / OS_PAGE_SIZE) - (flush / OS_PAGE_SIZE)) >= flushLeastPages;
  }

  return write > flush;
}

总结:

异步刷盘机制,submitFlushRequest提交一个刷盘请求后,直接返回结果,真正处理的是FlushRealTimeService,FlushRealTimeService会判断当前的脏页和离上一次刷盘时间是否超过了10s去判断是否真正要刷盘写进去, 如果没有超时只有脏页数量超过4个,就是16K的数据,才会真正的刷盘
  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值