/** * $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); } }