RocketMQ Broker 瞬时存储池TransientStorePool的复用、源码分析 (04)

1 创建commitedLog文件,和瞬时内存池建立映射

  1. 发送消息的时候需要获取队列映射文件MappedFile(CommitLog#asyncPutMessage)
  2. 新创建的文件或者文件已满需要创建新的文件 (MappedFileQueue#getLastMappedFile)
  3. 构建申请文件的请求,将请求存放到requestQueue队列中,等待指定线程创建映射文件Mapped. ile (AllocateMappedFileService#putRequestAndReturnMappedFile)
  4. 等待分配线程创建MappedFile,创建之后唤醒获取映射文件的线程,返回映射文件 ,存放入队列 MappedFileQueue

存放申请文件请求的时候,会等待申请线程创建映射文件,当映射文件创建完成时才会返回对应的映射文件,并将创建的映射文件存入MappedFileQueue队列中

 public MappedFile putRequestAndReturnMappedFile(String nextFilePath, String nextNextFilePath, int fileSize) {
        int canSubmitRequests = 2; // TODO 换算需要分配文件的请求数量
        if (this.messageStore.isTransientStorePoolEnable()) {
            if (this.messageStore.getMessageStoreConfig().isFastFailIfNoBufferInStorePool()
                && BrokerRole.SLAVE != this.messageStore.getMessageStoreConfig().getBrokerRole()) { //if broker is slave, don't fast fail even no buffer in pool
                canSubmitRequests = this.messageStore.remainTransientStoreBufferNumbs() - this.requestQueue.size(); //TODO 瞬时缓存中的buffer数量 - 请求队列中的数量 = 需要提交的请求
            }   // TODO 可以提交的请求最多不能超过 瞬时缓存池中的遗留的buffer数量
        }
        // TODO 下个文件的请求的 创建
        AllocateRequest nextReq = new AllocateRequest(nextFilePath, fileSize);
        boolean nextPutOK = this.requestTable.putIfAbsent(nextFilePath, nextReq) == null;

        if (nextPutOK) {
            if (canSubmitRequests <= 0) {
                log.warn("[NOTIFYME]TransientStorePool is not enough, so create mapped file error, " +
                    "RequestQueueSize : {}, StorePoolSize: {}", this.requestQueue.size(), this.messageStore.remainTransientStoreBufferNumbs());
                this.requestTable.remove(nextFilePath);
                return null;
            }
            boolean offerOK = this.requestQueue.offer(nextReq);
            if (!offerOK) {
                log.warn("never expected here, add a request to preallocate queue failed");
            }
            canSubmitRequests--;
        }
        // TODO 下下个文件的请求的 创建
        AllocateRequest nextNextReq = new AllocateRequest(nextNextFilePath, fileSize);
        boolean nextNextPutOK = this.requestTable.putIfAbsent(nextNextFilePath, nextNextReq) == null;
        if (nextNextPutOK) {
            if (canSubmitRequests <= 0) {
                log.warn("[NOTIFYME]TransientStorePool is not enough, so skip preallocate mapped file, " +
                    "RequestQueueSize : {}, StorePoolSize: {}", this.requestQueue.size(), this.messageStore.remainTransientStoreBufferNumbs());
                this.requestTable.remove(nextNextFilePath);
            } else {
                boolean offerOK = this.requestQueue.offer(nextNextReq);
                if (!offerOK) {
                    log.warn("never expected here, add a request to preallocate queue failed");
                }
            }
        }

        if (hasException) {
            log.warn(this.getServiceName() + " service has exception. so return null");
            return null;
        }
        // TODO 先获取第一个分配的文件,需要确定 AllocateRequest中mappedFile的初始化在线程中循环进行初始化创建文件和创建文件映射
        AllocateRequest result = this.requestTable.get(nextFilePath);
        try {
            if (result != null) {
                messageStore.getPerfCounter().startTick("WAIT_MAPFILE_TIME_MS");
                boolean waitOK = result.getCountDownLatch().await(waitTimeOut, TimeUnit.MILLISECONDS);  // TODO 设置等待创建对应的MappedFile文件,等待线程处理填充,然后唤醒继续返回对应的MappedFile
                messageStore.getPerfCounter().endTick("WAIT_MAPFILE_TIME_MS");
                if (!waitOK) {
                    log.warn("create mmap timeout " + result.getFilePath() + " " + result.getFileSize());
                    return null;
                } else {
                    this.requestTable.remove(nextFilePath);
                    return result.getMappedFile();
                }
            } else {
                log.error("find preallocate mmap failed, this never happen");
            }
        } catch (InterruptedException e) {
            log.warn(this.getServiceName() + " service has exception. ", e);
        }

        return null;
    }

线程轮训取出创建文件的请求进行创建文件

2 接着看看是瞬时缓存池是怎么复用的

  1. 运行线程池提交mappedFileQueue队列
  2. 在将缓存刷新到PageCache中之后就可以将使用的瞬时缓存归还
  3. 将使用完的buffer进行重置放置到队列第一个元素中

  1. 在需要创建的CommitLog文件不断增加超出commit提交的速度的时候,瞬时缓存池才会用完
  2. 在创建commitLog的需求不是很快的时候一般之后使用缓存池中的第一个buffer,以此达到复用堆外内存的目的

3 堆外内存的锁定

调用C的函数锁定内存 、和内存进行底层交互

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值