All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.alibaba.rocketmq.store.index.IndexService 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.index;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
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;
import com.alibaba.rocketmq.common.message.MessageConst;
import com.alibaba.rocketmq.common.sysflag.MessageSysFlag;
import com.alibaba.rocketmq.store.DefaultMessageStore;
import com.alibaba.rocketmq.store.DispatchRequest;
/**
* 消息索引服务
*
* @author shijia.wxr
* @since 2013-7-21
*/
public class IndexService extends ServiceThread {
private static final Logger log = LoggerFactory.getLogger(LoggerName.StoreLoggerName);
private final DefaultMessageStore defaultMessageStore;
// 索引配置
private final int hashSlotNum;
private final int indexNum;
private final String storePath;
// 索引文件集合
private final ArrayList indexFileList = new ArrayList();
// 读写锁(针对indexFileList)
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private LinkedBlockingQueue requestQueue = new LinkedBlockingQueue(300000);
public IndexService(final DefaultMessageStore store) {
this.defaultMessageStore = store;
this.hashSlotNum = store.getMessageStoreConfig().getMaxHashSlotNum();
this.indexNum = store.getMessageStoreConfig().getMaxIndexNum();
this.storePath = store.getMessageStoreConfig().getStorePathIndex();
}
public boolean load(final boolean lastExitOK) {
File dir = new File(this.storePath);
File[] files = dir.listFiles();
if (files != null) {
// ascending order
Arrays.sort(files);
for (File file : files) {
try {
IndexFile f = new IndexFile(file.getPath(), this.hashSlotNum, this.indexNum, 0, 0);
f.load();
if (!lastExitOK) {
if (f.getEndTimestamp() > this.defaultMessageStore.getStoreCheckpoint()
.getIndexMsgTimestamp()) {
f.destroy(0);
continue;
}
}
log.info("load index file OK, " + f.getFileName());
this.indexFileList.add(f);
}
catch (IOException e) {
log.error("load file " + file + " error", e);
return false;
}
}
}
return true;
}
/**
* 删除索引文件
*/
public void deleteExpiredFile(long offset) {
Object[] files = null;
try {
this.readWriteLock.readLock().lock();
if (this.indexFileList.isEmpty()) {
return;
}
long endPhyOffset = this.indexFileList.get(0).getEndPhyOffset();
if (endPhyOffset < offset) {
files = this.indexFileList.toArray();
}
}
catch (Exception e) {
log.error("destroy exception", e);
}
finally {
this.readWriteLock.readLock().unlock();
}
if (files != null) {
List fileList = new ArrayList();
for (int i = 0; i < (files.length - 1); i++) {
IndexFile f = (IndexFile) files[i];
if (f.getEndPhyOffset() < offset) {
fileList.add(f);
}
else {
break;
}
}
this.deleteExpiredFile(fileList);
}
}
/**
* 删除文件只能从头开始删
*/
private void deleteExpiredFile(List files) {
if (!files.isEmpty()) {
try {
this.readWriteLock.writeLock().lock();
for (IndexFile file : files) {
boolean destroyed = file.destroy(3000);
destroyed = destroyed && this.indexFileList.remove(file);
if (!destroyed) {
log.error("deleteExpiredFile remove failed.");
break;
}
}
}
catch (Exception e) {
log.error("deleteExpiredFile has exception.", e);
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
}
public void destroy() {
try {
this.readWriteLock.readLock().lock();
for (IndexFile f : this.indexFileList) {
f.destroy(1000 * 3);
}
this.indexFileList.clear();
}
catch (Exception e) {
log.error("destroy exception", e);
}
finally {
this.readWriteLock.readLock().unlock();
}
}
public QueryOffsetResult queryOffset(String topic, String key, int maxNum, long begin, long end) {
List phyOffsets = new ArrayList(maxNum);
// TODO 可能需要返回给最终用户
long indexLastUpdateTimestamp = 0;
long indexLastUpdatePhyoffset = 0;
maxNum = Math.min(maxNum, this.defaultMessageStore.getMessageStoreConfig().getMaxMsgsNumBatch());
try {
this.readWriteLock.readLock().lock();
if (!this.indexFileList.isEmpty()) {
for (int i = this.indexFileList.size(); i > 0; i--) {
IndexFile f = this.indexFileList.get(i - 1);
boolean lastFile = i == this.indexFileList.size();
if (lastFile) {
indexLastUpdateTimestamp = f.getEndTimestamp();
indexLastUpdatePhyoffset = f.getEndPhyOffset();
}
if (f.isTimeMatched(begin, end)) {
// 最后一个文件需要加锁
f.selectPhyOffset(phyOffsets, this.buildKey(topic, key), maxNum, begin, end, lastFile);
}
// 再往前遍历时间更不符合
if (f.getBeginTimestamp() > end) {
break;
}
if (phyOffsets.size() >= maxNum) {
break;
}
}
}
}
catch (Exception e) {
log.error("queryMsg exception", e);
}
finally {
this.readWriteLock.readLock().unlock();
}
return new QueryOffsetResult(phyOffsets, indexLastUpdateTimestamp, indexLastUpdatePhyoffset);
}
private String buildKey(final String topic, final String key) {
return topic + "#" + key;
}
/**
* 向队列中添加请求,队列满情况下,丢弃请求
*/
public void putRequest(final Object[] reqs) {
boolean offer = this.requestQueue.offer(reqs);
if (!offer) {
if (log.isDebugEnabled()) {
log.debug("putRequest index failed, {}", reqs);
}
}
}
@Override
public void run() {
log.info(this.getServiceName() + " service started");
while (!this.isStoped()) {
try {
Object[] req = this.requestQueue.poll(3000, TimeUnit.MILLISECONDS);
if (req != null) {
this.buildIndex(req);
}
}
catch (Exception e) {
log.warn(this.getServiceName() + " service has exception. ", e);
}
}
log.info(this.getServiceName() + " service end");
}
public void buildIndex(Object[] req) {
boolean breakdown = false;
IndexFile indexFile = retryGetAndCreateIndexFile();
if (indexFile != null) {
long endPhyOffset = indexFile.getEndPhyOffset();
MSG_WHILE: for (Object o : req) {
DispatchRequest msg = (DispatchRequest) o;
String topic = msg.getTopic();
String keys = msg.getKeys();
if (msg.getCommitLogOffset() < endPhyOffset) {
continue;
}
final int tranType = MessageSysFlag.getTransactionValue(msg.getSysFlag());
switch (tranType) {
case MessageSysFlag.TransactionNotType:
case MessageSysFlag.TransactionPreparedType:
break;
case MessageSysFlag.TransactionCommitType:
case MessageSysFlag.TransactionRollbackType:
continue;
}
if (keys != null && keys.length() > 0) {
String[] keyset = keys.split(MessageConst.KEY_SEPARATOR);
for (String key : keyset) {
// TODO 是否需要TRIM
if (key.length() > 0) {
for (boolean ok =
indexFile.putKey(buildKey(topic, key), msg.getCommitLogOffset(),
msg.getStoreTimestamp()); !ok;) {
log.warn("index file full, so create another one, " + indexFile.getFileName());
indexFile = retryGetAndCreateIndexFile();
if (null == indexFile) {
breakdown = true;
break MSG_WHILE;
}
ok =
indexFile.putKey(buildKey(topic, key), msg.getCommitLogOffset(),
msg.getStoreTimestamp());
}
}
}
}
}
}
// IO发生故障,build索引过程中断,需要人工参与处理
else {
breakdown = true;
}
if (breakdown) {
log.error("build index error, stop building index");
}
}
public IndexFile retryGetAndCreateIndexFile() {
IndexFile indexFile = null;
// 如果创建失败,尝试重建3次
for (int times = 0; null == indexFile && times < 3; times++) {
indexFile = this.getAndCreateLastIndexFile();
if (null != indexFile)
break;
try {
log.error("try to create index file, " + times + " times");
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
// 重试多次,仍然无法创建索引文件
if (null == indexFile) {
this.defaultMessageStore.getAccessRights().makeIndexFileError();
log.error("mark index file can not build flag");
}
return indexFile;
}
/**
* 获取最后一个索引文件,如果集合为空或者最后一个文件写满了,则新建一个文件
* 只有一个线程调用,所以不存在写竟争问题
*/
public IndexFile getAndCreateLastIndexFile() {
IndexFile indexFile = null;
IndexFile prevIndexFile = null;
long lastUpdateEndPhyOffset = 0;
long lastUpdateIndexTimestamp = 0;
// 先尝试使用读锁
{
this.readWriteLock.readLock().lock();
if (!this.indexFileList.isEmpty()) {
IndexFile tmp = this.indexFileList.get(this.indexFileList.size() - 1);
if (!tmp.isWriteFull()) {
indexFile = tmp;
}
else {
lastUpdateEndPhyOffset = tmp.getEndPhyOffset();
lastUpdateIndexTimestamp = tmp.getEndTimestamp();
prevIndexFile = tmp;
}
}
this.readWriteLock.readLock().unlock();
}
// 如果没找到,使用写锁创建文件
if (indexFile == null) {
try {
String fileName =
this.storePath + File.separator
+ UtilAll.timeMillisToHumanString(System.currentTimeMillis());
indexFile =
new IndexFile(fileName, this.hashSlotNum, this.indexNum, lastUpdateEndPhyOffset,
lastUpdateIndexTimestamp);
this.readWriteLock.writeLock().lock();
this.indexFileList.add(indexFile);
}
catch (Exception e) {
log.error("getLastIndexFile exception ", e);
}
finally {
this.readWriteLock.writeLock().unlock();
}
// 每创建一个新文件,之前文件要刷盘
if (indexFile != null) {
final IndexFile flushThisFile = prevIndexFile;
Thread flushThread = new Thread(new Runnable() {
@Override
public void run() {
IndexService.this.flush(flushThisFile);
}
}, "FlushIndexFileThread");
flushThread.setDaemon(true);
flushThread.start();
}
}
return indexFile;
}
public void flush(final IndexFile f) {
if (null == f)
return;
long indexMsgTimestamp = 0;
if (f.isWriteFull()) {
indexMsgTimestamp = f.getEndTimestamp();
}
f.flush();
if (indexMsgTimestamp > 0) {
this.defaultMessageStore.getStoreCheckpoint().setIndexMsgTimestamp(indexMsgTimestamp);
this.defaultMessageStore.getStoreCheckpoint().flush();
}
}
@Override
public String getServiceName() {
return IndexService.class.getSimpleName();
}
}