/**
* $Id: CommitLog.java 1831 2013-05-16 01:39:51Z shijia.wxr $
*/
package com.alibaba.rocketmq.store;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.rocketmq.common.Message;
import com.alibaba.rocketmq.common.MessageExt;
import com.alibaba.rocketmq.common.MessageDecoder;
import com.alibaba.rocketmq.common.MixAll;
import com.alibaba.rocketmq.common.UtilALl;
import com.alibaba.rocketmq.common.ServiceThread;
import com.alibaba.rocketmq.common.sysflag.MessageSysFlag;
import com.alibaba.rocketmq.store.config.BrokerRole;
import com.alibaba.rocketmq.store.config.FlushDiskType;
import com.alibaba.rocketmq.store.ha.HAService;
import com.alibaba.rocketmq.store.schedule.ScheduleMessageService;
/**
* CommitLogʵ��
*
* @author vintage.wang@gmail.com shijia.wxr@taobao.com
*/
public class CommitLog {
private static final Logger log = LoggerFactory.getLogger(MixAll.StoreLoggerName);
// ��������ÿ��ConsumeQueue�ĵ�ǰ���Offset��Ϣ
private HashMap<String/* topic-queueid */, Long/* offset */> topicQueueTable = new HashMap<String, Long>(1024);
// �洢��Ϣ�Ķ���
private final MapedFileQueue mapedFileQueue;
// �洢�������
private final DefaultMessageStore defaultMessageStore;
// CommitLogˢ�̷���
private final FlushCommitLogService flushCommitLogService;
// ÿ����Ϣ��Ӧ��MAGIC CODE daa320a7
private final static int MessageMagicCode = 0xAABBCCDD ^ 1880681586 + 8;
// �ļ�ĩβ�ն���Ӧ��MAGIC CODE cbd43194
private final static int BlankMagicCode = 0xBBCCDDEE ^ 1880681586 + 8;
// �洢��Ϣʱ�Ļص��ӿ�
private final AppendMessageCallback appendMessageCallback;
abstract class FlushCommitLogService extends ServiceThread {
}
/**
* �첽ʵʱˢ�̷���
*/
class FlushRealTimeService extends FlushCommitLogService {
private static final int RetryTimesOver = 3;
private long lastFlushTimestamp = 0;
private long printTimes = 0;
private void printFlushProgress() {
CommitLog.log.info("how much disk fall behind memory, "
+ CommitLog.this.mapedFileQueue.howMuchFallBehind());
}
public void run() {
CommitLog.log.info(this.getServiceName() + " service started");
while (!this.isStoped()) {
int interval =
CommitLog.this.defaultMessageStore.getMessageStoreConfig().getFlushIntervalCommitLog();
int flushPhysicQueueLeastPages =
CommitLog.this.defaultMessageStore.getMessageStoreConfig().getFlushCommitLogLeastPages();
int flushPhysicQueueThoroughInterval =
CommitLog.this.defaultMessageStore.getMessageStoreConfig()
.getFlushCommitLogThoroughInterval();
boolean printFlushProgress = false;
// ��ʱˢ�̣���ʱ��ӡˢ�̽���
long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis >= (this.lastFlushTimestamp + flushPhysicQueueThoroughInterval)) {
this.lastFlushTimestamp = currentTimeMillis;
flushPhysicQueueLeastPages = 0;
printFlushProgress = ((printTimes++ % 10) == 0);
}
try {
this.waitForRunning(interval);
if (printFlushProgress) {
this.printFlushProgress();
}
CommitLog.this.mapedFileQueue.commit(flushPhysicQueueLeastPages);
long storeTimestamp = CommitLog.this.mapedFileQueue.getStoreTimestamp();
if (storeTimestamp > 0) {
CommitLog.this.defaultMessageStore.getStoreCheckpoint().setPhysicMsgTimestamp(
storeTimestamp);
}
}
catch (Exception e) {
CommitLog.log.warn(this.getServiceName() + " service has exception. ", e);
this.printFlushProgress();
}
}
// ����shutdownʱ��Ҫ��֤ȫ��ˢ�̲��˳�
boolean result = false;
for (int i = 0; i < RetryTimesOver && !result; i++) {
result = CommitLog.this.mapedFileQueue.commit(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 FlushCommitLogService.class.getSimpleName();
}
@Override
public long getJointime() {
// ����CommitLog�������ϴ����Ի���ʱ��Ҫ����
return 1000 * 60 * 5;
}
}
public class GroupCommitRequest {
// ��ǰ��Ϣ��Ӧ����һ��Offset
private final long nextOffset;
// �첽֪ͨ����
private final CountDownLatch countDownLatch = new CountDownLatch(1);
// ˢ���Ƿ�ɹ�
private volatile boolean flushOK = false;
public GroupCommitRequest(long nextOffset) {
this.nextOffset = nextOffset;
}
public long getNextOffset() {
return nextOffset;
}
public void wakeupCustomer(final boolean flushOK) {
this.flushOK = flushOK;
this.countDownLatch.countDown();
}
public boolean waitForFlush(long timeout) {
try {
boolean result = this.countDownLatch.await(timeout, TimeUnit.MILLISECONDS);
return result || this.flushOK;
}
catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
}
/**
* GroupCommit Service
*/
class GroupCommitService extends FlushCommitLogService {
private volatile List<GroupCommitRequest> requestsWrite = new ArrayList<GroupCommitRequest>();
private volatile List<GroupCommitRequest> requestsRead = new ArrayList<GroupCommitRequest>();
private void swapRequests() {
List<GroupCommitRequest> tmp = this.requestsWrite;
this.requestsWrite = this.requestsRead;
this.requestsRead = tmp;
}
public void putRequest(final GroupCommitRequest request) {
synchronized (this) {
this.requestsWrite.add(request);
if (!this.hasNotified) {
this.hasNotified = true;
this.notify();
}
}
}
private void doCommit() {
if (!this.requestsRead.isEmpty()) {
for (GroupCommitRequest req : this.requestsRead) {
// ��Ϣ�п�������һ���ļ����������ˢ��2��
boolean flushOK = false;
for (int i = 0; (i < 2) && !flushOK; i++) {
flushOK = (CommitLog.this.mapedFileQueue.getCommittedWhere() >= req.getNextOffset());
if (!flushOK) {
CommitLog.this.mapedFileQueue.commit(0);
}
}
req.wakeupCustomer(flushOK);
}
long storeTimestamp = CommitLog.this.mapedFileQueue.getStoreTimestamp();
if (storeTimestamp > 0) {
CommitLog.this.defaultMessageStore.getStoreCheckpoint().setPhysicMsgTimestamp(storeTimestamp);
}
this.requestsRead.clear();
}
else {
// ���ڸ�����Ϣ����Ϊ��ͬ��ˢ�̣����Ի��ߵ�������
CommitLog.this.mapedFileQueue.commit(0);
}
}
public void run() {
CommitLog.log.info(this.getServiceName() + " service started");
while (!this.isStoped()) {
try {
this.waitForRunning(0);
this.doCommit();
}
catch (Exception e) {
CommitLog.log.warn(this.getServiceName() + " service has exception. ", e);
}
}
// ������shutdown����£��ȴ���������Ȼ����ˢ��
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
CommitLog.log.warn("GroupCommitService Exception, ", e);
}
synchronized (this) {
this.swapRequests();
}
this.doCommit();
CommitLog.log.info(this.getServiceName() + " service end");
}
@Override
protected void onWaitEnd() {
this.swapRequests();
}
@Override
public String getServiceName() {
return GroupCommitService.class.getSimpleName();
}
@Override
public long getJointime() {
// ����CommitLog�������ϴ����Ի���ʱ��Ҫ����
return 1000 * 60 * 5;
}
}
class DefaultAppendMessageCallback implements AppendMessageCallback {
// �洢��ϢID
private final ByteBuffer msgIdMemory;
// �洢��Ϣ����
private final ByteBuffer msgStoreItemMemory;
// ��Ϣ�����
private final int maxMessageSize;
// �ļ�ĩβ�ն���С����
private static final int END_FILE_MIN_BLANK_LENGTH = 4 + 4;
DefaultAppendMessageCallback(final int size) {
this.msgIdMemory = ByteBuffer.allocate(MessageDecoder.MSG_ID_LENGTH);
this.msgStoreItemMemory = ByteBuffer.allocate(size + END_FILE_MIN_BLANK_LENGTH);
this.maxMessageSize = size;
}
public ByteBuffer getMsgStoreItemMemory() {
return msgStoreItemMemory;
}
private void resetMsgStoreItemMemory(final int length) {
this.msgStoreItemMemory.flip();
this.msgStoreItemMemory.limit(length);
}
public AppendMessageResult doAppend(final long fileFromOffset, final ByteBuffer byteBuffer,
final int maxBlank, final Object msg) {
/**
* ������ϢID STORETIMESTAMP + STOREHOSTADDRESS + OFFSET <br>
*/
MessageExtBrokerInner msgInner = (MessageExtBrokerInner) msg;
// PHY OFFSET
long wroteOffset = fileFromOffset + byteBuffer.position();
String msgId =
MessageDecoder.createMessageId(this.msgIdMemory, (int) (msgInner.getStoreTimestamp() / 1000),
msgInner.getStoreHostBytes(), wroteOffset);
/**
* ��¼ConsumeQueue��Ϣ
*/
String key = msgInner.getTopic() + "-" + msgInner.getQueueId();
Long queueOffset = CommitLog.this.topicQueueTable.get(key);
if (null == queueOffset) {
queueOffset = 0L;
CommitLog.this.topicQueueTable.put(key, queueOffset);
}
/**
* ������Ϣ��Ҫ�����
*/
final int tranType = MessageSysFlag.getTransactionValue(msgInner.getSysFlag());
switch (tranType) {
case MessageSysFlag.TransactionPreparedType:
queueOffset =
CommitLog.this.defaultMessageStore.getTransactionStateService().getTranStateTableOffset()
.get();
break;
case MessageSysFlag.TransactionRollbackType:
queueOffset = msgInner.getQueueOffset();
break;
case MessageSysFlag.TransactionNotType:
case MessageSysFlag.TransactionCommitType:
default:
break;
}
/**
* ���л���Ϣ
*/
int propertiesLength =
msgInner.getPropertiesString() == null ? 0 : msgInner.getPropertiesString().length();
int bodyLength = msgInner.getBody() == null ? 0 : msgInner.getBody().length;
int msgLen = 4 // 1 TOTALSIZE
+ 4 // 2 MAGICCODE
+ 4 // 3 BODYCRC
+ 4 // 4 QUEUEID
+ 4 // 5 FLAG
+ 8 // 6 QUEUEOFFSET
+ 8 // 7 PHYSICALOFFSET
+ 4 // 8 SYSFLAG
+ 8 // 9 BORNTIMESTAMP
+ 8 // 10 BORNHOST
+ 8 // 11 STORETIMESTAMP
+ 8 // 12 STOREHOSTADDRESS
+ 4 // 13 RECONSUMETIMES
+ 8 // 14 Prepared Transaction Offset
+ 4 + bodyLength // 14 BODY
+ 1 + msgInner.getTopic().length() // 15 TOPIC
+ 2 + propertiesLength // 16 propertiesLength
+ 0;
// ��Ϣ�����趨�����ֵ
if (msgLen > this.maxMessageSize) {
CommitLog.log.warn("message size exceeded, msg total size: " + msgLen + ", msg body size: "
+ bodyLength + ", maxMessageSize: " + this.maxMessageSize);
return new AppendMessageResult(AppendMessageStatus.MESSAGE_SIZE_EXCEEDED);
}
// �ж��Ƿ����㹻����ռ�
if ((msgLen + END_FILE_MIN_BLANK_LENGTH) > maxBlank) {
this.resetMsgStoreItemMemory(maxBlank);
// 1 TOTALSIZE
this.msgStoreItemMemory.putInt(maxBlank);
// 2 MAGICCODE
this.msgStoreItemMemory.putInt(CommitLog.BlankMagicCode);
// 3 ʣ��ռ�������κ�ֵ
//
// �˴�������������ΪmaxBlank
byteBuffer.put(this.msgStoreItemMemory.array(), 0, maxBlank);
return new AppendMessageResult(AppendMessageStatus.END_OF_FILE, wroteOffset, maxBlank, msgId,
msgInner.getStoreTimestamp(), queueOffset);
}
// ��ʼ���洢�ռ�
this.resetMsgStoreItemMemory(msgLen);
// 1 TOTALSIZE
this.msgStoreItemMemory.putInt(msgLen);
// 2 MAGICCODE
this.msgStoreItemMemory.putInt(CommitLog.MessageMagicCode);
// 3 BODYCRC
this.msgStoreItemMemory.putInt(msgInner.getBodyCRC());
// 4 QUEUEID
this.msgStoreItemMemory.putInt(msgInner.getQueueId());
// 5 FLAG
this.msgStoreItemMemory.putInt(msgInner.getFlag());
// 6 QUEUEOFFSET
this.msgStoreItemMemory.putLong(queueOffset);
// 7 PHYSICALOFFSET
this.msgStoreItemMemory.putLong(fileFromOffset + byteBuffer.position());
// 8 SYSFLAG
this.msgStoreItemMemory.putInt(msgInner.getSysFlag());
// 9 BORNTIMESTAMP
this.msgStoreItemMemory.putLong(msgInner.getBornTimestamp());
// 10 BORNHOST
this.msgStoreItemMemory.put(msgInner.getBornHostBytes());
// 11 STORETIMESTAMP
this.msgStoreItemMemory.putLong(msgInner.getStoreTimestamp());
// 12 STOREHOSTADDRESS
this.msgStoreItemMemory.put(msgInner.getStoreHostBytes());
// 13 RECONSUMETIMES
this.msgStoreItemMemory.putInt(msgInner.getReconsumeTimes());
// 14 Prepared Transaction Offset
this.msgStoreItemMemory.putLong(msgInner.getPreparedTransactionOffset());
// 15 BODY
this.msgStoreItemMemory.putInt(bodyLength);
if (bodyLength > 0)
this.msgStoreItemMemory.put(msgInner.getBody());
// 16 TOPIC
this.msgStoreItemMemory.put((byte) msgInner.getTopic().length());
this.msgStoreItemMemory.put(msgInner.getTopic().getBytes());
// 17 PROPERTIES
this.msgStoreItemMemory.putShort((short) propertiesLength);
if (propertiesLength > 0)
this.msgStoreItemMemory.put(msgInner.getPropertiesString().getBytes());
// ����л�����д����Ϣ
byteBuffer.put(this.msgStoreItemMemory.array(), 0, msgLen);
AppendMessageResult result =
new AppendMessageResult(AppendMessageStatus.PUT_OK, wroteOffset, msgLen, msgId,
msgInner.getStoreTimestamp(), queueOffset);
switch (tranType) {
case MessageSysFlag.TransactionPreparedType:
CommitLog.this.defaultMessageStore.getTransactionStateService().getTranStateTableOffset()
.incrementAndGet();
break;
case MessageSysFlag.TransactionRollbackType:
break;
case MessageSysFlag.TransactionNotType:
case MessageSysFlag.TransactionCommitType:
// ������һ�ε�ConsumeQueue��Ϣ
CommitLog.this.topicQueueTable.put(key, ++queueOffset);
break;
default:
break;
}
// ���ؽ��
return result;
}
}
/**
* ���캯��
*/
public CommitLog(final DefaultMessageStore defaultMessageStore) {
this.mapedFileQueue =
new MapedFileQueue(defaultMessageStore.getMessageStoreConfig().getStorePathCommitLog(),
defaultMessageStore.getMessageStoreConfig().getMapedFileSizeCommitLog(),
defaultMessageStore.getAllocateMapedFileService());
this.defaultMessageStore = defaultMessageStore;
if (FlushDiskType.SYNC_FLUSH == defaultMessageStore.getMessageStoreConfig().getFlushDiskType()) {
this.flushCommitLogService = new GroupCommitService();
}
else {
this.flushCommitLogService = new FlushRealTimeService();
}
this.appendMessageCallback =
new DefaultAppendMessageCallback(defaultMessageStore.getMessageStoreConfig().getMaxMessageSize());
}
public boolean load() {
boolean result = this.mapedFileQueue.load();
log.info("load physic queue " + (result ? "OK" : "Failed"));
return result;
}
public void start() {
this.flushCommitLogService.start();
}
public void shutdown() {
this.flushCommitLogService.shutdown();
}
public long getMinOffset() {
MapedFile mapedFile = this.mapedFileQueue.getFirstMapedFileOnLock();
if (mapedFile != null) {
if (mapedFile.isAvailable()) {
return mapedFile.getFileFromOffset();
}
else {
return this.rollNextFile(mapedFile.getFileFromOffset());
}
}
return -1;
}
public long getMaxOffset() {
return this.mapedFileQueue.getMaxOffset();
}
public int deleteExpiredFile(final long expiredTime, final int deleteFilesInterval, final long intervalForcibly) {
return this.mapedFileQueue.deleteExpiredFileByTime(expiredTime, deleteFilesInterval, intervalForcibly);
}
/**
* ��ȡ��Ϣ
*/
public SelectMapedBufferResult getMessage(final long offset, final int size) {
int mapedFileSize = this.defaultMessageStore.getMessageStoreConfig().getMapedFileSizeCommitLog();
MapedFile mapedFile = this.mapedFileQueue.findMapedFileByOffset(offset, (0 == offset ? true : false));
if (mapedFile != null) {
int pos = (int) (offset % mapedFileSize);
SelectMapedBufferResult result = mapedFile.selectMapedBuffer(pos, size);
return result;
}
return null;
}
public long rollNextFile(final long offset) {
int mapedFileSize = this.defaultMessageStore.getMessageStoreConfig().getMapedFileSizeCommitLog();
return (offset + mapedFileSize - offset % mapedFileSize);
}
/**
* ��ȡCommitLog���ݣ����ݸ���ʱʹ��
*/
public SelectMapedBufferResult getData(final long offset) {
return this.getData(offset, (0 == offset ? true : false));
}
public SelectMapedBufferResult getData(final long offset, final boolean returnFirstOnNotFound) {
int mapedFileSize = this.defaultMessageStore.getMessageStoreConfig().getMapedFileSizeCommitLog();
MapedFile mapedFile = this.mapedFileQueue.findMapedFileByOffset(offset, returnFirstOnNotFound);
if (mapedFile != null) {
int pos = (int) (offset % mapedFileSize);
SelectMapedBufferResult result = mapedFile.selectMapedBuffer(pos);
return result;
}
return null;
}
public DispatchRequest checkMessageAndReturnSize(java.nio.ByteBuffer byteBuffer, final boolean checkCRC) {
return this.checkMessageAndReturnSize(byteBuffer, checkCRC, true);
}
/**
* �����ʹ�� �����Ϣ��������Ϣ��С
*
* @return 0 ��ʾ�ߵ��ļ�ĩβ >0 ������Ϣ -1 ��ϢУ��ʧ��
*/
public DispatchRequest checkMessageAndReturnSize(java.nio.ByteBuffer byteBuffer, final boolean checkCRC,
final boolean readBody) {
try {
java.nio.ByteBuffer byteBufferMessage =
((DefaultAppendMessageCallback) this.appendMessageCallback).getMsgStoreItemMemory();
byte[] bytesContent = byteBufferMessage.array();
// 1 TOTALSIZE
int totalSize = byteBuffer.getInt();
// 2 MAGICCODE
int magicCode = byteBuffer.getInt();
switch (magicCode) {
case MessageMagicCode:
break;
case BlankMagicCode:
return new DispatchRequest(0);
default:
log.warn("found a illegal magic code 0x" + Integer.toHexString(magicCode));
return new DispatchRequest(-1);
}
// 3 BODYCRC
int bodyCRC = byteBuffer.getInt();
// 4 QUEUEID
int queueId = byteBuffer.getInt();
// 5 FLAG
int flag = byteBuffer.getInt();
flag = flag + 0;
// 6 QUEUEOFFSET
long queueOffset = byteBuffer.getLong();
// 7 PHYSICALOFFSET
long physicOffset = byteBuffer.getLong();
// 8 SYSFLAG
int sysFlag = byteBuffer.getInt();
// 9 BORNTIMESTAMP
long bornTimeStamp = byteBuffer.getLong();
bornTimeStamp = bornTimeStamp + 0;
// 10 BORNHOST��IP+PORT��
byteBuffer.get(bytesContent, 0, 8);
// 11 STORETIMESTAMP
long storeTimestamp = byteBuffer.getLong();
// 12 STOREHOST��IP+PORT��
byteBuffer.get(bytesContent, 0, 8);
// 13 RECONSUMETIMES
int reconsumeTimes = byteBuffer.getInt();
// 14 Prepared Transaction Offset
long preparedTransactionOffset = byteBuffer.getLong();
// 15 BODY
int bodyLen = byteBuffer.getInt();
if (bodyLen > 0) {
if (readBody) {
byteBuffer.get(bytesContent, 0, bodyLen);
// ��CRC
if (checkCRC) {
int crc = UtilALl.crc32(bytesContent, 0, bodyLen);
if (crc != bodyCRC) {
log.warn("CRC check failed " + crc + " " + bodyCRC);
return new DispatchRequest(-1);
}
}
}
else {
byteBuffer.position(byteBuffer.position() + bodyLen);
}
}
// 16 TOPIC
byte topicLen = byteBuffer.get();
byteBuffer.get(bytesContent, 0, topicLen);
String topic = new String(bytesContent, 0, topicLen);
long tagsCode = 0;
String keys = "";
// 17 properties
short propertiesLength = byteBuffer.getShort();
if (propertiesLength > 0) {
byteBuffer.get(bytesContent, 0, propertiesLength);
String properties = new String(bytesContent, 0, propertiesLength);
Map<String, String> propertiesMap = MessageDecoder.string2messageProperties(properties);
keys = propertiesMap.get(Message.PROPERTY_KEYS);
String tags = propertiesMap.get(Message.PROPERTY_TAGS);
if (tags != null && tags.length() > 0) {
tagsCode =
MessageExtBrokerInner.tagsString2tagsCode(MessageExt.parseTopicFilterType(sysFlag),
tags);
}
}
return new DispatchRequest(//
topic,// 1
queueId,// 2
physicOffset,// 3
totalSize,// 4
tagsCode,// 5
storeTimestamp,// 6
queueOffset,// 7
keys,// 8
sysFlag,// 9
0L,// 10
preparedTransactionOffset,// 11
""// 12
);
}
catch (BufferUnderflowException e) {
byteBuffer.position(byteBuffer.limit());
}
catch (Exception e) {
byteBuffer.position(byteBuffer.limit());
}
return new DispatchRequest(-1);
}
/**
* �����˳�ʱ�����ݻָ��������ڴ����ݶ��Ѿ�ˢ��
*/
public void recoverNormally() {
boolean checkCRCOnRecover = this.defaultMessageStore.getMessageStoreConfig().isCheckCRCOnRecover();
final List<MapedFile> mapedFiles = this.mapedFileQueue.getMapedFiles();
if (!mapedFiles.isEmpty()) {
// �ӵ����������ļ���ʼ�ָ�
int index = mapedFiles.size() - 3;
if (index < 0)
index = 0;
MapedFile mapedFile = mapedFiles.get(index);
ByteBuffer byteBuffer = mapedFile.sliceByteBuffer();
long processOffset = mapedFile.getFileFromOffset();
long mapedFileOffset = 0;
while (true) {
DispatchRequest dispatchRequest = this.checkMessageAndReturnSize(byteBuffer, checkCRCOnRecover);
int size = dispatchRequest.getMsgSize();
// ��������
if (size > 0) {
mapedFileOffset += size;
}
// �ļ��м��������
else if (size == -1) {
log.info("recover physics file end, " + mapedFile.getFileName());
break;
}
// �ߵ��ļ�ĩβ���л�����һ���ļ�
// ���ڷ���0���������������Ŀն���������Բ�����truncate offset��
else if (size == 0) {
index++;
if (index >= mapedFiles.size()) {
// ��ǰ������֧�����ܷ���
log.info("recover last 3 physics file over, last maped file " + mapedFile.getFileName());
break;
}
else {
mapedFile = mapedFiles.get(index);
byteBuffer = mapedFile.sliceByteBuffer();
processOffset = mapedFile.getFileFromOffset();
mapedFileOffset = 0;
log.info("recover next physics file, " + mapedFile.getFileName());
}
}
}
processOffset += mapedFileOffset;
this.mapedFileQueue.setCommittedWhere(processOffset);
this.mapedFileQueue.truncateDirtyFiles(processOffset);
}
}
public void recoverAbnormally() {
// ������Сʱ������ָ�
boolean checkCRCOnRecover = this.defaultMessageStore.getMessageStoreConfig().isCheckCRCOnRecover();
final List<MapedFile> mapedFiles = this.mapedFileQueue.getMapedFiles();
if (!mapedFiles.isEmpty()) {
// Ѱ�Ҵ��ĸ��ļ���ʼ�ָ�
int index = mapedFiles.size() - 1;
MapedFile mapedFile = null;
for (; index >= 0; index--) {
mapedFile = mapedFiles.get(index);
if (this.isMapedFileMatchedRecover(mapedFile)) {
log.info("recover from this maped file " + mapedFile.getFileName());
break;
}
}
if (index < 0) {
index = 0;
mapedFile = mapedFiles.get(index);
}
ByteBuffer byteBuffer = mapedFile.sliceByteBuffer();
long processOffset = mapedFile.getFileFromOffset();
long mapedFileOffset = 0;
while (true) {
DispatchRequest dispatchRequest = this.checkMessageAndReturnSize(byteBuffer, checkCRCOnRecover);
int size = dispatchRequest.getMsgSize();
// ��������
if (size > 0) {
mapedFileOffset += size;
this.defaultMessageStore.putDispatchRequest(dispatchRequest);
}
// �ļ��м��������
else if (size == -1) {
log.info("recover physics file end, " + mapedFile.getFileName());
break;
}
// �ߵ��ļ�ĩβ���л�����һ���ļ�
// ���ڷ���0���������������Ŀն���������Բ�����truncate offset��
else if (size == 0) {
index++;
if (index >= mapedFiles.size()) {
// ��ǰ������֧��������²�Ӧ�÷���
log.info("recover physics file over, last maped file " + mapedFile.getFileName());
break;
}
else {
mapedFile = mapedFiles.get(index);
byteBuffer = mapedFile.sliceByteBuffer();
processOffset = mapedFile.getFileFromOffset();
mapedFileOffset = 0;
log.info("recover next physics file, " + mapedFile.getFileName());
}
}
}
processOffset += mapedFileOffset;
this.mapedFileQueue.setCommittedWhere(processOffset);
this.mapedFileQueue.truncateDirtyFiles(processOffset);
// ���ConsumeQueue��������
this.defaultMessageStore.truncateDirtyLogicFiles(processOffset);
}
// �����ļ�����ɾ�������
else {
this.mapedFileQueue.setCommittedWhere(0);
this.defaultMessageStore.destroyLogics();
}
}
private boolean isMapedFileMatchedRecover(final MapedFile mapedFile) {
ByteBuffer byteBuffer = mapedFile.sliceByteBuffer();
int magicCode = byteBuffer.getInt(MessageDecoder.MessageMagicCodePostion);
if (magicCode != MessageMagicCode) {
return false;
}
long storeTimestamp = byteBuffer.getLong(MessageDecoder.MessageStoreTimestampPostion);
if (0 == storeTimestamp) {
return false;
}
if (this.defaultMessageStore.getMessageStoreConfig().isMessageIndexEnable()) {
if (storeTimestamp <= this.defaultMessageStore.getStoreCheckpoint().getMinTimestampIndex()) {
return true;
}
}
else {
if (storeTimestamp <= this.defaultMessageStore.getStoreCheckpoint().getMinTimestamp()) {
return true;
}
}
return false;
}
public PutMessageResult putMessage(final MessageExtBrokerInner msg) {
// ���ô洢ʱ��
msg.setStoreTimestamp(System.currentTimeMillis());
// ������Ϣ��BODY CRC�������ڿͻ�����������ʣ�
msg.setBodyCRC(UtilALl.crc32(msg.getBody()));
// ���ؽ��
AppendMessageResult result = null;
StoreStatsService storeStatsService = this.defaultMessageStore.getStoreStatsService();
String topic = msg.getTopic();
int queueId = msg.getQueueId();
long tagsCode = msg.getTagsCode();
final int tranType = MessageSysFlag.getTransactionValue(msg.getSysFlag());
if (tranType == MessageSysFlag.TransactionNotType//
|| tranType == MessageSysFlag.TransactionCommitType) {
// ��ʱͶ��
if (msg.getDelayTimeLevel() > 0) {
if (msg.getDelayTimeLevel() > this.defaultMessageStore.getScheduleMessageService()
.getMaxDelayLevel()) {
msg.setDelayTimeLevel(this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel());
}
topic = ScheduleMessageService.SCHEDULE_TOPIC;
queueId = ScheduleMessageService.delayLevel2QueueId(msg.getDelayTimeLevel());
tagsCode =
this.defaultMessageStore.getScheduleMessageService().computeDeliverTimestamp(
msg.getDelayTimeLevel(), msg.getStoreTimestamp());
/**
* ������ʵ��topic��queueId
*/
msg.putProperty(Message.PROPERTY_REAL_TOPIC, msg.getTopic());
msg.putProperty(Message.PROPERTY_REAL_QUEUE_ID, String.valueOf(msg.getQueueId()));
msg.setPropertiesString(MessageDecoder.messageProperties2String(msg.getProperties()));
msg.setTopic(topic);
msg.setQueueId(queueId);
}
}
// д�ļ�Ҫ����
synchronized (this) {
long beginLockTimestamp = this.defaultMessageStore.getSystemClock().now();
// �������ô洢ʱ��������ܱ�֤ȫ������
msg.setStoreTimestamp(beginLockTimestamp);
// �����
MapedFile mapedFile = this.mapedFileQueue.getLastMapedFile();
if (null == mapedFile) {
log.error("create maped file1 error, topic: " + msg.getTopic() + " clientAddr: "
+ msg.getBornHostString());
return new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, null);
}
result = mapedFile.appendMessage(msg, this.appendMessageCallback);
switch (result.getStatus()) {
// �ɹ�����Ϣ
case PUT_OK:
break;
// �ߵ��ļ�ĩβ
case END_OF_FILE:
// �������ļ�������д��Ϣ
mapedFile = this.mapedFileQueue.getLastMapedFile();
if (null == mapedFile) {
log.error("create maped file2 error, topic: " + msg.getTopic() + " clientAddr: "
+ msg.getBornHostString());
return new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, result);
}
result = mapedFile.appendMessage(msg, this.appendMessageCallback);
break;
// ��Ϣ��С����
case MESSAGE_SIZE_EXCEEDED:
return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, result);
// δ֪����
case UNKNOWN_ERROR:
return new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result);
default:
return new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result);
}
DispatchRequest dispatchRequest = new DispatchRequest(//
topic,// 1
queueId,// 2
result.getWroteOffset(),// 3
result.getWroteBytes(),// 4
tagsCode,// 5
msg.getStoreTimestamp(),// 6
result.getLogicsOffset(),// 7
msg.getKeys(),// 8
/**
* ����
*/
msg.getSysFlag(),// 9
msg.getQueueOffset(), // 10
msg.getPreparedTransactionOffset(),// 11
msg.getProperty(Message.PROPERTY_PRODUCER_GROUP)// 12
);
this.defaultMessageStore.putDispatchRequest(dispatchRequest);
long eclipseTime = this.defaultMessageStore.getSystemClock().now() - beginLockTimestamp;
if (eclipseTime > 1000) {
log.warn("putMessage in lock eclipse time(ms) " + eclipseTime);
}
}
// ���ؽ��
PutMessageResult putMessageResult = new PutMessageResult(PutMessageStatus.PUT_OK, result);
// ͳ����ϢSIZE
storeStatsService.getPutMessageSizeTotal().addAndGet(result.getWroteBytes());
GroupCommitRequest request = null;
// ͬ��ˢ��
if (FlushDiskType.SYNC_FLUSH == this.defaultMessageStore.getMessageStoreConfig().getFlushDiskType()) {
GroupCommitService service = (GroupCommitService) this.flushCommitLogService;
if (msg.isWaitStoreMsgOK()) {
request = new GroupCommitRequest(result.getWroteOffset() + result.getWroteBytes());
service.putRequest(request);
boolean flushOK =
request.waitForFlush(this.defaultMessageStore.getMessageStoreConfig()
.getSyncFlushTimeout());
if (!flushOK) {
log.error("do groupcommit, wait for flush failed, topic: " + msg.getTopic() + " tags: "
+ msg.getTags() + " client address: " + msg.getBornHostString());
putMessageResult.setPutMessageStatus(PutMessageStatus.FLUSH_DISK_TIMEOUT);
}
}
else {
service.wakeup();
}
}
// �첽ˢ��
else {
this.flushCommitLogService.wakeup();
}
// ͬ��˫д
if (BrokerRole.SYNC_MASTER == this.defaultMessageStore.getMessageStoreConfig().getBrokerRole()) {
HAService service = this.defaultMessageStore.getHaService();
if (msg.isWaitStoreMsgOK()) {
// �ж��Ƿ�Ҫ�ȴ�
if (service.isSlaveOK(result.getWroteOffset() + result.getWroteBytes())) {
if (null == request) {
request = new GroupCommitRequest(result.getWroteOffset() + result.getWroteBytes());
}
service.putRequest(request);
service.getWaitNotifyObject().wakeupAll();
boolean flushOK =
// TODO �˴�������ˢ�̹����Ƿ����
request.waitForFlush(this.defaultMessageStore.getMessageStoreConfig()
.getSyncFlushTimeout());
if (!flushOK) {
log.error("do sync transfer other node, wait return, but failed, topic: " + msg.getTopic()
+ " tags: " + msg.getTags() + " client address: " + msg.getBornHostString());
putMessageResult.setPutMessageStatus(PutMessageStatus.FLUSH_SLAVE_TIMEOUT);
}
}
// Slave�쳣
else {
// ���߷��ͷ���Slave�쳣
putMessageResult.setPutMessageStatus(PutMessageStatus.SLAVE_NOT_AVAILABLE);
}
}
}
// ���ͷ����ؽ��
return putMessageResult;
}
/**
* ����offset��ȡ�ض���Ϣ�Ĵ洢ʱ�� ���������-1
*/
public long pickupStoretimestamp(final long offset, final int size) {
SelectMapedBufferResult result = this.getMessage(offset, size);
if (null != result) {
try {
return result.getByteBuffer().getLong(MessageDecoder.MessageStoreTimestampPostion);
}
finally {
result.release();
}
}
return -1;
}
public HashMap<String, Long> getTopicQueueTable() {
return topicQueueTable;
}
public void setTopicQueueTable(HashMap<String, Long> topicQueueTable) {
this.topicQueueTable = topicQueueTable;
}
public void destroy() {
this.mapedFileQueue.destroy();
}
public boolean appendData(long startOffset, byte[] data) {
// д�ļ�Ҫ����
synchronized (this) {
// �����
MapedFile mapedFile = this.mapedFileQueue.getLastMapedFile(startOffset);
if (null == mapedFile) {
log.error("appendData getLastMapedFile error " + startOffset);
return false;
}
return mapedFile.appendMessage(data);
}
}
public boolean retryDeleteFirstFile(final long intervalForcibly) {
return this.mapedFileQueue.retryDeleteFirstFile(intervalForcibly);
}
}