com.alibaba.rocketmq.store.AllocateMapedFileService Maven / Gradle / Ivy
/**
* Copyright (C) 2010-2013 Alibaba Group Holding Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.rocketmq.store;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.rocketmq.common.ServiceThread;
import com.alibaba.rocketmq.common.UtilAll;
import com.alibaba.rocketmq.common.constant.LoggerName;
/**
* 预分配MapedFile服务
*
* @author shijia.wxr
* @since 2013-7-21
*/
public class AllocateMapedFileService extends ServiceThread {
private static final Logger log = LoggerFactory.getLogger(LoggerName.StoreLoggerName);
private static int WaitTimeOut = 1000 * 5;
private ConcurrentHashMap requestTable =
new ConcurrentHashMap();
private PriorityBlockingQueue requestQueue =
new PriorityBlockingQueue();
private volatile boolean hasException = false;
public MapedFile putRequestAndReturnMapedFile(String nextFilePath, String nextNextFilePath, int fileSize) {
AllocateRequest nextReq = new AllocateRequest(nextFilePath, fileSize);
AllocateRequest nextNextReq = new AllocateRequest(nextNextFilePath, fileSize);
boolean nextPutOK = (this.requestTable.putIfAbsent(nextFilePath, nextReq) == null);
boolean nextNextPutOK = (this.requestTable.putIfAbsent(nextNextFilePath, nextNextReq) == null);
if (nextPutOK) {
boolean offerOK = this.requestQueue.offer(nextReq);
if (!offerOK) {
log.warn("add a request to preallocate queue failed");
}
}
if (nextNextPutOK) {
boolean offerOK = this.requestQueue.offer(nextNextReq);
if (!offerOK) {
log.warn("add a request to preallocate queue failed");
}
}
if (hasException) {
log.warn(this.getServiceName() + " service has exception. so return null");
return null;
}
AllocateRequest result = this.requestTable.get(nextFilePath);
try {
if (result != null) {
boolean waitOK = result.getCountDownLatch().await(WaitTimeOut, TimeUnit.MILLISECONDS);
if (!waitOK) {
log.warn("create mmap timeout " + result.getFilePath() + " " + result.getFileSize());
}
this.requestTable.remove(nextFilePath);
return result.getMapedFile();
}
else {
log.error("find preallocate mmap failed, this never happen");
}
}
catch (InterruptedException e) {
log.warn(this.getServiceName() + " service has exception. ", e);
}
return null;
}
@Override
public String getServiceName() {
return AllocateMapedFileService.class.getSimpleName();
}
public void shutdown() {
this.stoped = true;
this.thread.interrupt();
try {
this.thread.join(this.getJointime());
}
catch (InterruptedException e) {
e.printStackTrace();
}
for (AllocateRequest req : this.requestTable.values()) {
if (req.mapedFile != null) {
log.info("delete pre allocated maped file, {}", req.mapedFile.getFileName());
req.mapedFile.destroy(1000);
}
}
}
public void run() {
log.info(this.getServiceName() + " service started");
while (!this.isStoped() && this.mmapOperation())
;
log.info(this.getServiceName() + " service end");
}
/**
* 只有被外部线程中断,才会返回false
*/
private boolean mmapOperation() {
AllocateRequest req = null;
try {
req = this.requestQueue.take();
if (null == this.requestTable.get(req.getFilePath())) {
log.warn("this mmap request expired, maybe cause timeout " + req.getFilePath() + " "
+ req.getFileSize());
return true;
}
if (req.getMapedFile() == null) {
long beginTime = System.currentTimeMillis();
MapedFile mapedFile = new MapedFile(req.getFilePath(), req.getFileSize());
long eclipseTime = UtilAll.computeEclipseTimeMilliseconds(beginTime);
// 记录大于10ms的
if (eclipseTime > 10) {
int queueSize = this.requestQueue.size();
log.warn("create mapedFile spent time(ms) " + eclipseTime + " queue size " + queueSize
+ " " + req.getFilePath() + " " + req.getFileSize());
}
req.setMapedFile(mapedFile);
this.hasException = false;
}
}
catch (InterruptedException e) {
log.warn(this.getServiceName() + " service has exception, maybe by shutdown");
this.hasException = true;
return false;
}
catch (IOException e) {
log.warn(this.getServiceName() + " service has exception. ", e);
this.hasException = true;
}
finally {
if (req != null)
req.getCountDownLatch().countDown();
}
return true;
}
class AllocateRequest implements Comparable {
// 文件全路径
private String filePath;
// 文件大小
private int fileSize;
// 计数器
private CountDownLatch countDownLatch = new CountDownLatch(1);
// MapedFile
private volatile MapedFile mapedFile = null;
public AllocateRequest(String filePath, int fileSize) {
this.filePath = filePath;
this.fileSize = fileSize;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public int getFileSize() {
return fileSize;
}
public void setFileSize(int fileSize) {
this.fileSize = fileSize;
}
public CountDownLatch getCountDownLatch() {
return countDownLatch;
}
public void setCountDownLatch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
public MapedFile getMapedFile() {
return mapedFile;
}
public void setMapedFile(MapedFile mapedFile) {
this.mapedFile = mapedFile;
}
public int compareTo(AllocateRequest other) {
return this.fileSize < other.fileSize ? 1 : this.fileSize > other.fileSize ? -1 : 0;
}
}
}