RocketMq存储之___消息写入内存

一、消息存入缓存 入口接口: putMessage()

  • location: org.apache.rocketmq.store.MessageStore#putMessage :
/**
     * Store a message into store.
     *
     * @param msg Message instance to store
     * @return result of store operation.
     */
    PutMessageResult putMessage(final MessageExtBrokerInner msg);

二、实现类:DefaultMessageStore#putMessage

  • location: org.apache.rocketmq.store.DefaultMessageStore#putMessage:
public PutMessageResult putMessage(MessageExtBrokerInner msg) {
    // 1. 校验 broker
		PutMessageStatus checkStoreStatus = this.checkStoreStatus();
		if (checkStoreStatus != PutMessageStatus.PUT_OK) {
		    return new PutMessageResult(checkStoreStatus, null);
		}
		//2. 校验topic 
		PutMessageStatus msgCheckStatus = this.checkMessage(msg);
		if (msgCheckStatus == PutMessageStatus.MESSAGE_ILLEGAL) {
		    return new PutMessageResult(msgCheckStatus, null);
		}
		
		long beginTime = this.getSystemClock().now();
    //3. 存储
		PutMessageResult result = this.commitLog.putMessage(msg);
		long elapsedTime = this.getSystemClock().now() - beginTime;
		if (elapsedTime > 500) {
		    log.warn("not in lock elapsed time(ms)={}, bodyLength={}", elapsedTime, msg.getBody().length);
		}
   // 4. 后续处理
			// 记录写commitlog时间,大于最大时间则设置为这个最新的最大时间 putMessageEntireTimeMax
			this.storeStatsService.setPutMessageEntireTimeMax(elapsedTime);
			
			if (null == result || !result.isOk()) {
			    // 记录写commitlog 失败次数
			    this.storeStatsService.getPutMessageFailedTimes().incrementAndGet();
			}
		
		return result;
}

三、存储前校验

1. 校验broker 的方法checkStoreStatus():

private PutMessageStatus checkStoreStatus() {
        //1. 如果当前broker 停止工作,返回
        if (this.shutdown) {
            log.warn("message store has shutdown, so putMessage is forbidden");
            return PutMessageStatus.SERVICE_NOT_AVAILABLE;
        }
       // 2. 如果当前broker 是 Slave 角色
        if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {
            long value = this.printTimes.getAndIncrement();
            if ((value % 50000) == 0) {
                log.warn("broke role is slave, so putMessage is forbidden");
            }
            return PutMessageStatus.SERVICE_NOT_AVAILABLE;
        }
       //3. 如果当前broker 不提供写的能力,返回
        if (!this.runningFlags.isWriteable()) {
            long value = this.printTimes.getAndIncrement();
            if ((value % 50000) == 0) {
                log.warn("the message store is not writable. It may be caused by one of the following reasons: " +
                    "the broker's disk is full, write to logic queue error, write to index file error, etc");
            }
            return PutMessageStatus.SERVICE_NOT_AVAILABLE;
        } else {
            this.printTimes.set(0);
        }
      // 计算CommitLog锁持有时间,是否超过了默认的设置1s 并且小于 10000000
        if (this.isOSPageCacheBusy()) {
            return PutMessageStatus.OS_PAGECACHE_BUSY;
        }
        return PutMessageStatus.PUT_OK;
}

isOSPageCacheBusy() :

计算CommitLog锁持有时间 = 当前时间 - 获取锁开始时间,page cache timeout默认配置的时间是1000ms,也即1s,所以broker在处理上一个消息写入的时候占有锁的时间超过1s的时候就会被认为page cache busy。

public boolean isOSPageCacheBusy() {
    long begin = this.getCommitLog().getBeginTimeInLock();
    long diff = this.systemClock.now() - begin;
    return diff < 10000000
        && diff > this.messageStoreConfig.getOsPageCacheBusyTimeOutMills();
}

2. 校验 topic 的长度和属性的长度:

private PutMessageStatus checkMessage(MessageExtBrokerInner msg) {
      // topic 长度是否大于 127
      if (msg.getTopic().length() > Byte.MAX_VALUE) {
          log.warn("putMessage message topic length too long " + msg.getTopic().length());
          return PutMessageStatus.MESSAGE_ILLEGAL;
      }
      // 消息属性的长度 是否大于  32767
      if (msg.getPropertiesString() != null && msg.getPropertiesString().length() > Short.MAX_VALUE) {
          log.warn("putMessage message properties length too long " + msg.getPropertiesString().length());
          return PutMessageStatus.MESSAGE_ILLEGAL;
      }
      return PutMessageStatus.PUT_OK;
}

四、存储前-组装消息体信息 和 存储结果处理:

commitLog.putMessage()

  • location: org.apache.rocketmq.store.CommitLog#putMessage
public PutMessageResult putMessage(final MessageExtBrokerInner msg) {
    // Set the storage time
    msg.setStoreTimestamp(System.currentTimeMillis());
    // Set the message body BODY CRC (consider the most appropriate setting
    // on the client)
    msg.setBodyCRC(UtilAll.crc32(msg.getBody()));
    // Back to Results
    AppendMessageResult result = null;

    StoreStatsService storeStatsService = this.defaultMessageStore.getStoreStatsService();

    String topic = msg.getTopic();
    int queueId = msg.getQueueId();

    final int tranType = MessageSysFlag.getTransactionValue(msg.getSysFlag());
    //1. 非事务消息 或 已commit事务消息
    if (tranType == MessageSysFlag.TRANSACTION_NOT_TYPE
        || tranType == MessageSysFlag.TRANSACTION_COMMIT_TYPE) {
        // Delay Delivery
        if (msg.getDelayTimeLevel() > 0) {
            if (msg.getDelayTimeLevel() > this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel()) {
                msg.setDelayTimeLevel(this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel());
            }

            topic = TopicValidator.RMQ_SYS_SCHEDULE_TOPIC;
            queueId = ScheduleMessageService.delayLevel2QueueId(msg.getDelayTimeLevel());

            // Backup real topic, queueId
            MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_TOPIC, msg.getTopic());
            MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_QUEUE_ID, String.valueOf(msg.getQueueId()));
            msg.setPropertiesString(MessageDecoder.messageProperties2String(msg.getProperties()));

            msg.setTopic(topic);
            msg.setQueueId(queueId);
        }
    }

    InetSocketAddress bornSocketAddress = (InetSocketAddress) msg.getBornHost();
    if (bornSocketAddress.getAddress() instanceof Inet6Address) {
        msg.setBornHostV6Flag();
    }

    InetSocketAddress storeSocketAddress = (InetSocketAddress) msg.getStoreHost();
    if (storeSocketAddress.getAddress() instanceof Inet6Address) {
        msg.setStoreHostAddressV6Flag();
    }

    long elapsedTimeInLock = 0;
  
    MappedFile unlockMappedFile = null;
    // 2. 获取可以写入的commitLog 文件
    MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile();
    // 3. 获取写入的锁
    putMessageLock.lock(); //spin or ReentrantLock ,depending on store config
    try {
        long beginLockTimestamp = this.defaultMessageStore.getSystemClock().now();
        this.beginTimeInLock = beginLockTimestamp;

        // Here settings are stored timestamp, in order to ensure an orderly
        // global
        // 4. 设置存储时间,为了保证全局有序
        msg.setStoreTimestamp(beginLockTimestamp);
        // 5. 校验获取的mappedFile 是否有值或者已经满了,如果是,创建新的
        if (null == mappedFile || mappedFile.isFull()) {
            mappedFile = this.mappedFileQueue.getLastMappedFile(0); // Mark: NewFile may be cause noise
        }
        if (null == mappedFile) {
            log.error("create mapped file1 error, topic: " + msg.getTopic() + " clientAddr: " + msg.getBornHostString());
            beginTimeInLock = 0;
            return new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, null);
        }
        // 6. 组装消息完成,将消息追加到 获取到的或者创建的commitLog 文件中
        result = mappedFile.appendMessage(msg, this.appendMessageCallback);
        switch (result.getStatus()) {
            case PUT_OK:
                break;
            case END_OF_FILE:
                unlockMappedFile = mappedFile;
                // Create a new file, re-write the message
                mappedFile = this.mappedFileQueue.getLastMappedFile(0);
                if (null == mappedFile) {
                    // XXX: warn and notify me
                    log.error("create mapped file2 error, topic: " + msg.getTopic() + " clientAddr: " + msg.getBornHostString());
                    beginTimeInLock = 0;
                    return new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, result);
                }
                result = mappedFile.appendMessage(msg, this.appendMessageCallback);
                break;
            case MESSAGE_SIZE_EXCEEDED:
            case PROPERTIES_SIZE_EXCEEDED:
                beginTimeInLock = 0;
                return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, result);
            case UNKNOWN_ERROR:
                beginTimeInLock = 0;
                return new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result);
            default:
                beginTimeInLock = 0;
                return new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result);
        }

        elapsedTimeInLock = this.defaultMessageStore.getSystemClock().now() - beginLockTimestamp;
        beginTimeInLock = 0;
    } finally {
        putMessageLock.unlock();
    }

    if (elapsedTimeInLock > 500) {
        log.warn("[NOTIFYME]putMessage in lock cost time(ms)={}, bodyLength={} AppendMessageResult={}", elapsedTimeInLock, msg.getBody().length, result);
    }

    if (null != unlockMappedFile && this.defaultMessageStore.getMessageStoreConfig().isWarmMapedFileEnable()) {
        this.defaultMessageStore.unlockMappedFile(unlockMappedFile);
    }

    PutMessageResult putMessageResult = new PutMessageResult(PutMessageStatus.PUT_OK, result);

    // Statistics
    storeStatsService.getSinglePutMessageTopicTimesTotal(msg.getTopic()).incrementAndGet();
    storeStatsService.getSinglePutMessageTopicSizeTotal(topic).addAndGet(result.getWroteBytes());

    handleDiskFlush(result, putMessageResult, msg);
    handleHA(result, putMessageResult, msg);

    return putMessageResult;
}

核心流程:

  1. 判断消息的类型,如果是 非事务消息 或 已commit事务消息,判断消息是否设置延迟,如果设置了延迟,将消息的topic 设置为延时消息的topic SCHEDULE_TOPIC_XXXX, QueueId 设置为延时消息的 queueId(一个延迟级别对应一个Queue,根据消息的延迟等级获取对应的延迟队列)。

    同时,把消息的原始topic 和 queue保存在消息的property中。

  2. 获取可以写入的commitLog 文件;

  3. 获取putMessageLock 锁,因为写入CommitLog 文件是串行的。

    1. spin or ReentrantLock ,depending on store config; 获取哪种锁取决于配置文件。
  4. 设置存储时间,为了保证全局有序;

  5. 校验获取的mappedFile(commitLog文件) 是否没值或者已经满了;如果是,根据偏移量 0创建一个新的commitLog 文件,文件为 00000000000000000000 ;如果创建失败,抛出异常。

  6. 组装消息完成,发送 将消息追加到 获取到的或者创建的commitLog 文件中的请求,等待结果;

  7. 如果 6 请求:

    1. 成功:
      1. 释放锁;
      2. 保存对应topic 的添加时间和写入长度;
      3. 刷盘;
      4. HA;
    2. END_OF_FILE (满了,当前commitLog 不够空间了)
      1. 创建一个新的commitLog 去写;继续执行 第 6步;
    3. MESSAGE_SIZE_EXCEEDED,PROPERTIES_SIZE_EXCEEDED,UNKNOWN_ERROR,其他
      1. 直接返回异常。
重要方法介绍: getLastMappedFile()

获取当前可以写入的 Commitlog 件。MappedFile 对应的就是commitLog 文件夹中一个一个文件。

public MappedFile getLastMappedFile() {
    MappedFile mappedFileLast = null;

    while (!this.mappedFiles.isEmpty()) {
        try {
            mappedFileLast = this.mappedFiles.get(this.mappedFiles.size() - 1);
            break;
        } catch (IndexOutOfBoundsException e) {
            //continue;
        } catch (Exception e) {
            log.error("getLastMappedFile has exception.", e);
            break;
        }
    }

    return mappedFileLast;
}

五、信息存储到CommitLog 文件的链路

5.1 前置操作

接口定义:mappedFile.appendMessage(msg,this.appendMessageCallback)

  • location: org.apache.rocketmq.store.MappedFile#appendMessages
public AppendMessageResult appendMessages(final MessageExtBatch messageExtBatch, final AppendMessageCallback cb) {
     return appendMessagesInner(messageExtBatch, cb);
}

实现类:appendMessagesInner(final MessageExt messageExt, final AppendMessageCallback cb) org.apache.rocketmq.store.MappedFile#appendMessagesInner

public AppendMessageResult appendMessagesInner(final MessageExt messageExt, final AppendMessageCallback cb) {
    assert messageExt != null;
    assert cb != null;
    // 1. 获取 MappedFile 当前写指针
    int currentPos = this.wrotePosition.get();
    // 2. 判断 ,如果指针表示的size 小于文件大小
    if (currentPos < this.fileSize) {
        // 3. 创建共享内存区
        //  如果 ByteBuffer 不是null,调用slice 方法创建一个 MappedFile 的共享内存区 否则调用 mappedByteBuffer 的slice 方法;
        
        ByteBuffer byteBuffer = writeBuffer != null ? writeBuffer.slice() : this.mappedByteBuffer.slice();
        //  4. 设置共享内存区的 position 为 第一步 获取的当前指针。
        byteBuffer.position(currentPos);
        AppendMessageResult result;
        if (messageExt instanceof MessageExtBrokerInner) {
             // 5. 执行写入操作。
            result = cb.doAppend(this.getFileFromOffset(), byteBuffer, this.fileSize - currentPos, (MessageExtBrokerInner) messageExt);
        } else if (messageExt instanceof MessageExtBatch) {
            // 5. 执行写入操作。
            result = cb.doAppend(this.getFileFromOffset(), byteBuffer, this.fileSize - currentPos, (MessageExtBatch) messageExt);
        } else {
            return new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR);
        }
        this.wrotePosition.addAndGet(result.getWroteBytes());
        this.storeTimestamp = result.getStoreTimestamp();
        return result;
    }
    log.error("MappedFile.appendMessage return null, wrotePosition: {} fileSize: {}", currentPos, this.fileSize);
    return new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR);
}

核心流程:

  1. 获取 MappedFile 当前指针 ;
  2. 校验 1 获取的指针 是否 大于 文件大小,如果大于,直接返回异常。
  3. 创建共享内存区。如果ByteBuffer 不为null,用ByteBuffer 创建,否则用 MappedByteBuffer 创建。
    1. ByteBuffer writeBuffer :堆内存 ByteBuffer 如果不为空,数 先将存储在该
      Buffer 中, 然后提交到 appedFile 对应的内存映射 Buffer transientStorePoolEnable
      为 true 不为空;
    2. MappedByteBuffer mappedByteBuffer :物理文件对应的内存映射 Buffer;
  4. 设置共享内存区的 position 为 第一步 获取的当前指针。
  5. 执行写入操作。

5.2 执行操作

接口定义:AppendMessageCallback#doAppend():

  • location: org.apache.rocketmq.store.AppendMessageCallback#doAppend(long, java.nio.ByteBuffer, int, org.apache.rocketmq.store.MessageExtBrokerInner)

实现类: DefaultAppendMessageCallback#doAppend()

  • location: org.apache.rocketmq.store.CommitLog.DefaultAppendMessageCallback#doAppend(long, java.nio.ByteBuffer, int, org.apache.rocketmq.store.MessageExtBrokerInner)

参数介绍:

  1. long fileFromOffset 最后一个文件的偏移量,也是文件的
  2. ByteBuffer byteBuffer MappedFile 的共享内存区
  3. int maxBlank commitLog 文件中剩余的空间
  4. MessageExtBrokerInner msgInner 消息体
public AppendMessageResult doAppend(final long fileFromOffset, final ByteBuffer byteBuffer, final int maxBlank,
        final MessageExtBrokerInner msgInner) {
      // STORETIMESTAMP + STOREHOSTADDRESS + OFFSET <br>

      // PHY OFFSET
      // 最后一个文件的偏移量 + 当前指针位置 =  存储文件当前的写指针
      long wroteOffset = fileFromOffset + byteBuffer.position();

      int sysflag = msgInner.getSysFlag();

      int bornHostLength = (sysflag & MessageSysFlag.BORNHOST_V6_FLAG) == 0 ? 4 + 4 : 16 + 4;
      int storeHostLength = (sysflag & MessageSysFlag.STOREHOSTADDRESS_V6_FLAG) == 0 ? 4 + 4 : 16 + 4;
      ByteBuffer bornHostHolder = ByteBuffer.allocate(bornHostLength);
      ByteBuffer storeHostHolder = ByteBuffer.allocate(storeHostLength);

      this.resetByteBuffer(storeHostHolder, storeHostLength);
      // 1. 生成一个唯一的消息id;
      String msgId;
      if ((sysflag & MessageSysFlag.STOREHOSTADDRESS_V6_FLAG) == 0) {
          msgId = MessageDecoder.createMessageId(this.msgIdMemory, msgInner.getStoreHostBytes(storeHostHolder), wroteOffset);
      } else {
          msgId = MessageDecoder.createMessageId(this.msgIdV6Memory, msgInner.getStoreHostBytes(storeHostHolder), wroteOffset);
      }

      // Record ConsumeQueue information
      keyBuilder.setLength(0);
      keyBuilder.append(msgInner.getTopic());
      keyBuilder.append('-');
      keyBuilder.append(msgInner.getQueueId());
      String key = keyBuilder.toString();
      //2. 获取该消息在消息队列的偏移量。 CommitLog 中保存了当前所有消息队列的当前待写入偏移量
      // HashMap<String/* topic-queueid */, Long/* offset */>
      Long queueOffset = CommitLog.this.topicQueueTable.get(key);
      // 如果偏移量为null ,放入新的
      if (null == queueOffset) {
          queueOffset = 0L;
          CommitLog.this.topicQueueTable.put(key, queueOffset);
      }

      // Transaction messages that require special handling
      final int tranType = MessageSysFlag.getTransactionValue(msgInner.getSysFlag());
      // 3. 判断消息类型 Prepared and Rollback message is not consumed, will not enter the consumer queuec, 偏移量置为 0
      switch (tranType) {
          // Prepared and Rollback message is not consumed, will not enter the
          // consumer queuec
          case MessageSysFlag.TRANSACTION_PREPARED_TYPE:
          case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE:
              queueOffset = 0L;
              break;
          case MessageSysFlag.TRANSACTION_NOT_TYPE:
          case MessageSysFlag.TRANSACTION_COMMIT_TYPE:
          default:
              break;
      }
			/**
       * Serialize message
       */
      final byte[] propertiesData =
          msgInner.getPropertiesString() == null ? null : msgInner.getPropertiesString().getBytes(MessageDecoder.CHARSET_UTF8);
      //4. 校验消息长度
      final int propertiesLength = propertiesData == null ? 0 : propertiesData.length;
      // 如果消息的额外信息的长度 大于 32767
       //4.1   校验消息的额外信息的长度 
      if (propertiesLength > Short.MAX_VALUE) {
          log.warn("putMessage message properties length too long. length={}", propertiesData.length);
          return new AppendMessageResult(AppendMessageStatus.PROPERTIES_SIZE_EXCEEDED);
      }

      final byte[] topicData = msgInner.getTopic().getBytes(MessageDecoder.CHARSET_UTF8);
      final int topicLength = topicData.length;

      final int bodyLength = msgInner.getBody() == null ? 0 : msgInner.getBody().length;
      // 根据消息、体的长度 主题的长度、属性的长度结合消息存储格式计算消息的总长度
      final int msgLen = calMsgLength(msgInner.getSysFlag(), bodyLength, topicLength, propertiesLength);
        //4.2   校验消息的总长度 
      // Exceeds the maximum message,如果消息的总长度 大于 设置的消息最大值抛出异常
      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);
      }

       //4.3   校验消息的总长度 + END_FILE_MIN_BLANK_LENGTH
      // Determines whether there is sufficient free space
      // 如果消息的总长度 +  大于当前CommitLog 文件中可用的空间; 返回 END_OF_FILE
      if ((msgLen + END_FILE_MIN_BLANK_LENGTH) > maxBlank) {
          this.resetByteBuffer(this.msgStoreItemMemory, maxBlank);
          // 1 TOTALSIZE
          this.msgStoreItemMemory.putInt(maxBlank);
          // 2 MAGICCODE
          this.msgStoreItemMemory.putInt(CommitLog.BLANK_MAGIC_CODE);
          // 3 The remaining space may be any value
          // Here the length of the specially set maxBlank
          final long beginTimeMills = CommitLog.this.defaultMessageStore.now();
          byteBuffer.put(this.msgStoreItemMemory.array(), 0, maxBlank);
          return new AppendMessageResult(AppendMessageStatus.END_OF_FILE, wroteOffset, maxBlank, msgId, msgInner.getStoreTimestamp(),
              queueOffset, CommitLog.this.defaultMessageStore.now() - beginTimeMills);
      }

      // Initialization of storage space
      this.resetByteBuffer(msgStoreItemMemory, msgLen);
      // 1 TOTALSIZE
      this.msgStoreItemMemory.putInt(msgLen);
      // 2 MAGICCODE
      this.msgStoreItemMemory.putInt(CommitLog.MESSAGE_MAGIC_CODE);
      // 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.resetByteBuffer(bornHostHolder, bornHostLength);
      this.msgStoreItemMemory.put(msgInner.getBornHostBytes(bornHostHolder));
      // 11 STORETIMESTAMP
      this.msgStoreItemMemory.putLong(msgInner.getStoreTimestamp());
      // 12 STOREHOSTADDRESS
      this.resetByteBuffer(storeHostHolder, storeHostLength);
      this.msgStoreItemMemory.put(msgInner.getStoreHostBytes(storeHostHolder));
      // 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) topicLength);
      this.msgStoreItemMemory.put(topicData);
      // 17 PROPERTIES
      this.msgStoreItemMemory.putShort((short) propertiesLength);
      if (propertiesLength > 0)
          this.msgStoreItemMemory.put(propertiesData);

      final long beginTimeMills = CommitLog.this.defaultMessageStore.now();
      // Write messages to the queue buffer
      byteBuffer.put(this.msgStoreItemMemory.array(), 0, msgLen);

      AppendMessageResult result = new AppendMessageResult(AppendMessageStatus.PUT_OK, wroteOffset, msgLen, msgId,
          msgInner.getStoreTimestamp(), queueOffset, CommitLog.this.defaultMessageStore.now() - beginTimeMills);

      switch (tranType) {
          case MessageSysFlag.TRANSACTION_PREPARED_TYPE:
          case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE:
              break;
          case MessageSysFlag.TRANSACTION_NOT_TYPE:
          case MessageSysFlag.TRANSACTION_COMMIT_TYPE:
              // The next update ConsumeQueue information
              CommitLog.this.topicQueueTable.put(key, ++queueOffset);
              break;
          default:
              break;
      }
      return result;
}

核心流程:

  1. 生成一个唯一的消息id;
  2. 获取该消息在消息队列的偏移量。 CommitLog 中保存了当前所有消息队列的当前待写入偏移量;
    1. 消息队列是HashMap 的形式,key = topic + queueId;
  3. 判断消息类型 Prepared and Rollback message is not consumed, will not enter the consumer queuec, 偏移量置为 0;
  4. 校验消息的长度
    1. 4.1 校验消息的额外信息的长度 是否大于 32767;抛出异常
    2. 4.2 校验消息的总长度 是否大于 定义的长度;抛出异常
    3. 4.3 校验消息的总长度 + END_FILE_MIN_BLANK_LENGTH 是否大于 当前CommitLog 文件中可用的空间; 如果大于,创建新的commitLog 文件,重新保存
  5. 将消息内容存储到 ByteBuffer 中,然后创建 AppendMessageResult;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值