1 创建commitedLog文件,和瞬时内存池建立映射
- 发送消息的时候需要获取队列映射文件MappedFile(CommitLog#asyncPutMessage)
- 新创建的文件或者文件已满需要创建新的文件 (MappedFileQueue#getLastMappedFile)
- 构建申请文件的请求,将请求存放到requestQueue队列中,等待指定线程创建映射文件Mapped. ile (AllocateMappedFileService#putRequestAndReturnMappedFile)
- 等待分配线程创建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 接着看看是瞬时缓存池是怎么复用的
- 运行线程池提交mappedFileQueue队列
- 在将缓存刷新到PageCache中之后就可以将使用的瞬时缓存归还
- 将使用完的buffer进行重置放置到队列第一个元素中
- 在需要创建的CommitLog文件不断增加超出commit提交的速度的时候,瞬时缓存池才会用完
- 在创建commitLog的需求不是很快的时候一般之后使用缓存池中的第一个buffer,以此达到复用堆外内存的目的
3 堆外内存的锁定
调用C的函数锁定内存 、和内存进行底层交互