roketMQ学习小结

125 阅读10分钟

作用: 削峰填谷(主要解决瞬时写压力大于应用服务能力导致消息丢失、系统奔溃等问题) 系统解耦(解决不同重要程度、不同能力级别系统之间依赖导致一死全死) 提升性能(当存在一对多调用时,可以发一条消息给消息系统,让消息系统通知相关系统) 蓄流压测(线上有些链路不好压测,可以通过堆积一定量消息再放开来压测) 链接:www.jianshu.com/p/2838890f3…

- rocketMq基础

1. mq模式

producor-consumer-nameServer-broker
producor和consumer都是从nameServer中获取broker路由信息和维护topic心跳

2. 同步与异步发送消息

同步是直接调用sendMessage,异步需要添加sendCallBack回调参数;

3. 顺序消费

只有在同一个queue下的消息才能实现消息的顺序消费,为了实现这个消息的顺序需要生产者和消费者的配合
生产者通过queue的选择器来确定对应的queue,消费者选择顺序消息

4. 事务型消息

特定的消息类型

5. 内存空间

commitLog 消息主体,consumer queue存储索引
有异步刷盘和同步刷盘,异步的时候是先i将数据保存到内存,当数据达到一定的量的时候,内存线程会将数据加载到磁盘,缺点是会丢数据,但是效率高;
同步刷盘:需要等待数据保存的返回,很影响性能,优点是不会丢失数据;

6. 消息的高可用:

分别从Producer发送机制、Broker的持久化机制,以及消费者的offSet机制来最大程度保证消息不易丢失
  1. 从Producer的视角来看:如果消息未能正确的存储在MQ中,或者消费者未能正确的消费到这条消息,都是消息丢失。从Broker的视角来看:如果消息已经存在Broker里面了,如何保证不会丢失呢(宕机、磁盘崩溃) 从Consumer的视角来看:如果消息已经完成持久化了,但是Consumer取了,但是未消费成功且没有反馈,就是消息丢失 从Producer分析:如何确保消息正确的发送到了Broker?默认情况下,可以通过同步的方式阻塞式的发送,check SendStatus,状态是OK,表示消息一定成功的投递到了Broker,状态超时或者失败,则会触发默认的2次重试。此方法的发送结果,可能Broker存储成功了,也可能没成功采取事务消息的投递方式,并不能保证消息100%投递成功到了Broker,但是如果消息发送Ack失败的话,此消息会存储在CommitLog当中,但是对ConsumerQueue是不可见的。可以在日志中查看到这条异常的消息,严格意义上来讲,也并没有完全丢失RocketMQ支持 日志的索引,如果一条消息发送之后超时,也可以通过查询日志的API,来check是否在Broker存储成功
  2. 从Broker分析:如果确保接收到的消息不会丢失? 消息支持持久化到Commitlog里面,即使宕机后重启,未消费的消息也是可以加载出来的 Broker自身支持同步刷盘、异步刷盘的策略,可以保证接收到的消息一定存储在本地的内存中 Broker集群支持 1主N从的策略,支持同步复制和异步复制的方式,同步复制可以保证即使Master 磁盘崩溃,消息仍然不会丢失
  3. 从Cunmser分析:如何确保拉取到的消息被成功消费?消费者可以根据自身的策略批量Pull消息 Consumer自身维护一个持久化的offset(对应MessageQueue里面的min offset),标记已经成功消费或者已经成功发回到broker的消息下标 如果Consumer消费失败,那么它会把这个消息发回给Broker,发回成功后,再更新自己的offset 如果Consumer消费失败,发回给broker时,broker挂掉了,那么Consumer会定时重试这个操作 如果Consumer和broker一起挂了,消息也不会丢失,因为consumer 里面的offset是定时持久化的,重启之后,继续拉取offset之前的消息到本地
7.roketmq源码

producer: producer消息发送

producer源码

DefaultMQProducer#send(Message)
@Override
public SendResult send(Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
return this.defaultMQProducerImpl.send(msg);
}
发送同步消息,DefaultMQProducer#send(Message) 对 DefaultMQProducerImpl#send(Message) 进行封装。\
DefaultMQProducerImpl#sendDefaultImpl()

public SendResult send(Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
return send(msg, this.defaultMQProducer.getSendMsgTimeout());
}

public SendResult send(Message msg, long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
return this.sendDefaultImpl(msg, CommunicationMode.SYNC, null, timeout);
}

private SendResult sendDefaultImpl(Message msg,final CommunicationMode communicationMode,final SendCallback sendCallback, final long timeout) throws MQClientException, RemotingException,MQBrokerException, InterruptedException {
// 校验 Producer 处于运行状态
this.makeSureStateOK();
// 校验消息格式
Validators.checkMessage(msg, this.defaultMQProducer);
//
final long invokeID = random.nextLong(); // 调用编号;用于下面打印日志,标记为同一次发送消息
long beginTimestampFirst = System.currentTimeMillis();
long beginTimestampPrev = beginTimestampFirst;
long endTimestamp = beginTimestampFirst;
// 获取 Topic路由信息
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
if (topicPublishInfo != null && topicPublishInfo.ok()) {
MessageQueue mq = null; // 最后选择消息要发送到的队列
Exception exception = null;
SendResult sendResult = null; // 最后一次发送结果
int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1; // 同步多次调用
int times = 0; // 第几次发送
String\[] brokersSent = new String\[timesTotal]; // 存储每次发送消息选择的broker名
// 循环调用发送消息,直到成功
for (; times < timesTotal; times++) {
String lastBrokerName = null == mq ? null : mq.getBrokerName();
MessageQueue tmpmq = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName); // 选择消息要发送到的队列
if (tmpmq != null) {
mq = tmpmq;
brokersSent\[times] = mq.getBrokerName();
try {
    beginTimestampPrev = System.currentTimeMillis();
    // 调用发送消息核心方法
    sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout);
    endTimestamp = System.currentTimeMillis();
    // 更新Broker可用性信息
    this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);
    switch (communicationMode) {
case ASYNC:
    return null;
case ONEWAY:
    return null;
case SYNC:
if (sendResult.getSendStatus() != SendStatus.SEND\_OK) {
if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) { // 同步发送成功但存储有问题时 && 配置存储异常时重新发送开关 时,进行重试
    continue;
}
}
    return sendResult;
    default:
    break;
}
} catch (RemotingException e) { // 打印异常,更新Broker可用性信息,更新继续循环
    endTimestamp = System.currentTimeMillis();
    this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true);
    log.warn(String.format("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq), e);
    log.warn(msg.toString());
    exception = e;
    continue;
} catch (MQClientException e) { // 打印异常,更新Broker可用性信息,继续循环
    endTimestamp = System.currentTimeMillis();
    this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true);
    log.warn(String.format("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq), e);
    log.warn(msg.toString());
    exception = e;
    continue;
} catch (MQBrokerException e) { // 打印异常,更新Broker可用性信息,部分情况下的异常,直接返回,结束循环
    endTimestamp = System.currentTimeMillis();
    this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true);
    log.warn(String.format("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq), e);
    log.warn(msg.toString());
    exception = e;
switch (e.getResponseCode()) {
// 如下异常continue,进行发送消息重试
    case ResponseCode.TOPIC\_NOT\_EXIST:
    case ResponseCode.SERVICE\_NOT\_AVAILABLE:
    case ResponseCode.SYSTEM\_ERROR:
    case ResponseCode.NO\_PERMISSION:
    case ResponseCode.NO\_BUYER\_ID:
    case ResponseCode.NOT\_IN\_CURRENT\_UNIT:
continue;
// 如果有发送结果,进行返回,否则,抛出异常;
default:
if (sendResult != null) {
    return sendResult;
}
    throw e;
}
} catch (InterruptedException e) {
    endTimestamp = System.currentTimeMillis();
    this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);
    log.warn(String.format("sendKernelImpl exception, throw exception, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq), e);
    log.warn(msg.toString());
    throw e;
}
} else {
    break;
}
}
// 返回发送结果
if (sendResult != null) {
    return sendResult;
}
// 根据不同情况,抛出不同的异常
String info = String.format("Send \[%d] times, still failed, cost \[%d]ms, Topic: %s, BrokersSent: %s", times, System.currentTimeMillis() - beginTimestampFirst,
msg.getTopic(), Arrays.toString(brokersSent)) + FAQUrl.suggestTodo(FAQUrl.SEND\_MSG\_FAILED);
MQClientException mqClientException = new MQClientException(info, exception);
if (exception instanceof MQBrokerException) {
    mqClientException.setResponseCode(((MQBrokerException) exception).getResponseCode());
} else if (exception instanceof RemotingConnectException) {
    mqClientException.setResponseCode(ClientErrorCode.CONNECT\_BROKER\_EXCEPTION);
} else if (exception instanceof RemotingTimeoutException) {
    mqClientException.setResponseCode(ClientErrorCode.ACCESS\_BROKER\_TIMEOUT);
} else if (exception instanceof MQClientException) {
    mqClientException.setResponseCode(ClientErrorCode.BROKER\_NOT\_EXIST\_EXCEPTION);
}
    throw mqClientException;
}
// Namesrv找不到异常
List<String> nsList = this.getmQClientFactory().getMQClientAPIImpl().getNameServerAddressList();
if (null == nsList || nsList.isEmpty()) {
    throw new MQClientException(
     "No name server address, please set it." + FAQUrl.suggestTodo(FAQUrl.NAME\_SERVER\_ADDR\_NOT\_EXIST\_URL), null).setResponseCode(ClientErrorCode.NO\_NAME\_SERVER\_EXCEPTION);
}
// 消息路由找不到异常
throw new MQClientException("No route info of this topic, " + msg.getTopic() + FAQUrl.suggestTodo(FAQUrl.NO\_TOPIC\_ROUTE\_INFO),
null).setResponseCode(ClientErrorCode.NOT\_FOUND\_TOPIC\_EXCEPTION);
说明 :发送消息。步骤:获取消息路由信息,选择要发送到的消息队列,执行消息发送核心方法,并对发送结果进行封装返回。
第 17 行:对sendsendDefaultImpl(...)进行封装。
第 20 行 :invokeID仅仅用于打印日志,无实际的业务用途。
第 25 行 :获取 Topic路由信息, 详细解析见:DefaultMQProducerImpl#tryToFindTopicPublishInfo()
第 30 & 34 行 :计算调用发送消息到成功为止的最大次数,并进行循环。同步或异步发送消息会调用多次,默认配置为3次。
第 36 行 :选择消息要发送到的队列,详细解析见:MQFaultStrategy43 行 :调用发送消息核心方法,详细解析见:DefaultMQProducerImpl#sendKernelImpl()
第 46 行 :更新Broker可用性信息。在选择发送到的消息队列时,会参考Broker发送消息的延迟,详细解析见:MQFaultStrategy6268 行:当抛出RemotingException时,如果进行消息发送失败重试,则可能导致消息发送重复。例如,发送消息超时(RemotingTimeoutException),实际Broker接收到该消息并处理成功。因此,Consumer在消费时,需要保证幂等性。

DefaultMQProducerImpl#tryToFindTopicPublishInfo()
private TopicPublishInfo tryToFindTopicPublishInfo(final String topic) {
// 缓存中获取 Topic发布信息
TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic);
// 当无可用的 Topic发布信息时,从Namesrv获取一次
if (null == topicPublishInfo || !topicPublishInfo.ok()) {
    this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());
    this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);
    topicPublishInfo = this.topicPublishInfoTable.get(topic);
}
// 若获取的 Topic发布信息时候可用,则返回
if (topicPublishInfo.isHaveTopicRouterInfo() || topicPublishInfo.ok()) {
    return topicPublishInfo;
} else { // 使用 {@link DefaultMQProducer#createTopicKey} 对应的 Topic发布信息。用于 Topic发布信息不存在 && Broker支持自动创建Topic
    this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer);
    topicPublishInfo = this.topicPublishInfoTable.get(topic);
    return topicPublishInfo;
}
}
说明 :获得 Topic发布信息。优先从缓存topicPublishInfoTable,其次从Namesrv中获得。
第 3 行 :从缓存topicPublishInfoTable中获得 Topic发布信息。
第 59 行 :从 Namesrv 中获得 Topic发布信息。
第 1317 行 :当从 Namesrv 无法获取时,使用 {@link DefaultMQProducer#createTopicKey} 对应的 Topic发布信息。目的是当 Broker 开启自动创建 Topic开关时,Broker 接收到消息后自动创建Topic

DefaultMQProducerImpl#sendKernelImpl()

private SendResult sendKernelImpl(final Message msg, //
final MessageQueue mq, //
final CommunicationMode communicationMode, //
final SendCallback sendCallback, //
final TopicPublishInfo topicPublishInfo, //
final long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
// 获取 broker地址
    String brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(mq.getBrokerName());
if (null == brokerAddr) {
    tryToFindTopicPublishInfo(mq.getTopic());
    brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(mq.getBrokerName());
}
//
SendMessageContext context = null;
if (brokerAddr != null) {
// 是否使用broker vip通道。broker会开启两个端口对外服务。
brokerAddr = MixAll.brokerVIPChannel(this.defaultMQProducer.isSendMessageWithVIPChannel(), brokerAddr);
byte\[] prevBody = msg.getBody(); // 记录消息内容。下面逻辑可能改变消息内容,例如消息压缩。
try {
// 设置唯一编号
MessageClientIDSetter.setUniqID(msg);
// 消息压缩
int sysFlag = 0;
if (this.tryToCompressMessage(msg)) {
    sysFlag |= MessageSysFlag.COMPRESSED\_FLAG;
}
// 事务
final String tranMsg = msg.getProperty(MessageConst.PROPERTY\_TRANSACTION\_PREPARED);
if (tranMsg != null && Boolean.parseBoolean(tranMsg)) {
    sysFlag |= MessageSysFlag.TRANSACTION\_PREPARED\_TYPE;
}
// hook:发送消息校验
if (hasCheckForbiddenHook()) {
CheckForbiddenContext checkForbiddenContext = new CheckForbiddenContext();
    checkForbiddenContext.setNameSrvAddr(this.defaultMQProducer.getNamesrvAddr());
    checkForbiddenContext.setGroup(this.defaultMQProducer.getProducerGroup());
    checkForbiddenContext.setCommunicationMode(communicationMode);
    checkForbiddenContext.setBrokerAddr(brokerAddr);
    checkForbiddenContext.setMessage(msg);
    checkForbiddenContext.setMq(mq);
    checkForbiddenContext.setUnitMode(this.isUnitMode());
    this.executeCheckForbiddenHook(checkForbiddenContext);
}
// hook:发送消息前逻辑
if (this.hasSendMessageHook()) {
    context = new SendMessageContext();
    context.setProducer(this);
    context.setProducerGroup(this.defaultMQProducer.getProducerGroup());
    context.setCommunicationMode(communicationMode);
    context.setBornHost(this.defaultMQProducer.getClientIP());
    context.setBrokerAddr(brokerAddr);
    context.setMessage(msg);
    context.setMq(mq);
    String isTrans = msg.getProperty(MessageConst.PROPERTY\_TRANSACTION\_PREPARED);
if (isTrans != null && isTrans.equals("true")) {
    context.setMsgType(MessageType.Trans\_Msg\_Half);
}
    if (msg.getProperty("\_\_STARTDELIVERTIME") != null || msg.getProperty(MessageConst.PROPERTY\_DELAY\_TIME\_LEVEL) != null) {
    context.setMsgType(MessageType.Delay\_Msg);
}
    this.executeSendMessageHookBefore(context);
}
// 构建发送消息请求
SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup());
requestHeader.setTopic(msg.getTopic());
requestHeader.setDefaultTopic(this.defaultMQProducer.getCreateTopicKey());
requestHeader.setDefaultTopicQueueNums(this.defaultMQProducer.getDefaultTopicQueueNums());
requestHeader.setQueueId(mq.getQueueId());
requestHeader.setSysFlag(sysFlag);
requestHeader.setBornTimestamp(System.currentTimeMillis());
requestHeader.setFlag(msg.getFlag());
requestHeader.setProperties(MessageDecoder.messageProperties2String(msg.getProperties()));
requestHeader.setReconsumeTimes(0);
requestHeader.setUnitMode(this.isUnitMode());
if (requestHeader.getTopic().startsWith(MixAll.RETRY\_GROUP\_TOPIC\_PREFIX)) { // 消息重发Topic
    String reconsumeTimes = MessageAccessor.getReconsumeTime(msg);
if (reconsumeTimes != null) {
    requestHeader.setReconsumeTimes(Integer.valueOf(reconsumeTimes));
    MessageAccessor.clearProperty(msg, MessageConst.PROPERTY\_RECONSUME\_TIME);
}
String maxReconsumeTimes = MessageAccessor.getMaxReconsumeTimes(msg);
if (maxReconsumeTimes != null) {
    requestHeader.setMaxReconsumeTimes(Integer.valueOf(maxReconsumeTimes));
    MessageAccessor.clearProperty(msg, MessageConst.PROPERTY\_MAX\_RECONSUME\_TIMES);
}
}
// 发送消息
SendResult sendResult = null;
switch (communicationMode) {
case ASYNC:
    sendResult = this.mQClientFactory.getMQClientAPIImpl().sendMessage(//
    brokerAddr, // 1
    mq.getBrokerName(), // 2
    msg, // 3
    requestHeader, // 4
    timeout, // 5
    communicationMode, // 6
    sendCallback, // 7
    topicPublishInfo, // 8
    this.mQClientFactory, // 9
    this.defaultMQProducer.getRetryTimesWhenSendAsyncFailed(), // 10
    context, //
    this);
    break;
case ONEWAY:
case SYNC:
    sendResult = this.mQClientFactory.getMQClientAPIImpl().sendMessage(
    brokerAddr,
    mq.getBrokerName(),
    msg,
    requestHeader,
    timeout,
    communicationMode,
    context,
    this);
    break;
default:
assert false;
break;
}
    // hook:发送消息后逻辑
if (this.hasSendMessageHook()) {
    context.setSendResult(sendResult);
    this.executeSendMessageHookAfter(context);
}
// 返回发送结果
    return sendResult;
} catch (RemotingException e) {
    if (this.hasSendMessageHook()) {
        context.setException(e);
        this.executeSendMessageHookAfter(context);
}
    throw e;
} catch (MQBrokerException e) {
    if (this.hasSendMessageHook()) {
        context.setException(e);
        this.executeSendMessageHookAfter(context);
}
    throw e;
} catch (InterruptedException e) {
    if (this.hasSendMessageHook()) {
        context.setException(e);
        this.executeSendMessageHookAfter(context);
}
    throw e;
} finally {
    msg.setBody(prevBody);
}
}
// broker为空抛出异常
throw new MQClientException("The broker\[" + mq.getBrokerName() + "] not exist", null);


Broker 接收消息

`SendMessageProcessor#sendMessage
@Override
public RemotingCommand processRequest(ChannelHandlerContext ctx, RemotingCommand request) throws RemotingCommandException {
SendMessageContext mqtraceContext;
switch (request.getCode()) {
case RequestCode.CONSUMER\_SEND\_MSG\_BACK:
return this.consumerSendMsgBack(ctx, request);
default:
// 解析请求
SendMessageRequestHeader requestHeader = parseRequestHeader(request);
if (requestHeader == null) {
return null;
}
// 发送请求Context。在 hook 场景下使用
mqtraceContext = buildMsgContext(ctx, requestHeader);
// hook:处理发送消息前逻辑
this.executeSendMessageHookBefore(ctx, request, mqtraceContext);
// 处理发送消息逻辑
final RemotingCommand response = this.sendMessage(ctx, request, mqtraceContext, requestHeader);
// hook:处理发送消息后逻辑
this.executeSendMessageHookAfter(response, mqtraceContext);
return response;
}
}

```js
private RemotingCommand sendMessage(final ChannelHandlerContext ctx, //
final RemotingCommand request, //
final SendMessageContext sendMessageContext, //
final SendMessageRequestHeader requestHeader) throws RemotingCommandException {
`
      // 初始化响应
      final RemotingCommand response = RemotingCommand.createResponseCommand(SendMessageResponseHeader.class);
      final SendMessageResponseHeader responseHeader = (SendMessageResponseHeader) response.readCustomHeader();
      response.setOpaque(request.getOpaque());
      response.addExtField(MessageConst.PROPERTY_MSG_REGION, this.brokerController.getBrokerConfig().getRegionId());
      response.addExtField(MessageConst.PROPERTY_TRACE_SWITCH, String.valueOf(this.brokerController.getBrokerConfig().isTraceOn()));

      if (log.isDebugEnabled()) {
          log.debug("receive SendMessage request command, {}", request);
      }

      // 如果未开始接收消息,抛出系统异常
      @SuppressWarnings("SpellCheckingInspection")
      final long startTimstamp = this.brokerController.getBrokerConfig().getStartAcceptSendRequestTimeStamp();
      if (this.brokerController.getMessageStore().now() < startTimstamp) {
          response.setCode(ResponseCode.SYSTEM_ERROR);
          response.setRemark(String.format("broker unable to service, until %s", UtilAll.timeMillisToHumanString2(startTimstamp)));
          return response;
      }

      // 消息配置(Topic配置)校验
      response.setCode(-1);
      super.msgCheck(ctx, requestHeader, response);
      if (response.getCode() != -1) {
          return response;
      }

      final byte[] body = request.getBody();

      // 如果队列小于0,从可用队列随机选择
      int queueIdInt = requestHeader.getQueueId();
      TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());
      if (queueIdInt < 0) {
          queueIdInt = Math.abs(this.random.nextInt() % 99999999) % topicConfig.getWriteQueueNums();
      }

      //
      int sysFlag = requestHeader.getSysFlag();
      if (TopicFilterType.MULTI_TAG == topicConfig.getTopicFilterType()) {
          sysFlag |= MessageSysFlag.MULTI_TAGS_FLAG;
      }

      // 对RETRY类型的消息处理。如果超过最大消费次数,则topic修改成"%DLQ%" + 分组名,即加入 死信队列(Dead Letter Queue)
      String newTopic = requestHeader.getTopic();
      if (null != newTopic && newTopic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {
          // 获取订阅分组配置
          String groupName = newTopic.substring(MixAll.RETRY_GROUP_TOPIC_PREFIX.length());
          SubscriptionGroupConfig subscriptionGroupConfig =
              this.brokerController.getSubscriptionGroupManager().findSubscriptionGroupConfig(groupName);
          if (null == subscriptionGroupConfig) {
              response.setCode(ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST);
              response.setRemark("subscription group not exist, " + groupName + " " + FAQUrl.suggestTodo(FAQUrl.SUBSCRIPTION_GROUP_NOT_EXIST));
              return response;
          }
          // 计算最大可消费次数
          int maxReconsumeTimes = subscriptionGroupConfig.getRetryMaxTimes();
          if (request.getVersion() >= MQVersion.Version.V3_4_9.ordinal()) {
              maxReconsumeTimes = requestHeader.getMaxReconsumeTimes();
          }
          int reconsumeTimes = requestHeader.getReconsumeTimes() == null ? 0 : requestHeader.getReconsumeTimes();
          if (reconsumeTimes >= maxReconsumeTimes) { // 超过最大消费次数
              newTopic = MixAll.getDLQTopic(groupName);
              queueIdInt = Math.abs(this.random.nextInt() % 99999999) % DLQ_NUMS_PER_GROUP;
              topicConfig = this.brokerController.getTopicConfigManager().createTopicInSendMessageBackMethod(newTopic, //
                 DLQ_NUMS_PER_GROUP, //
                 PermName.PERM_WRITE, 0
             );
             if (null == topicConfig) {
                 response.setCode(ResponseCode.SYSTEM_ERROR);
                 response.setRemark("topic[" + newTopic + "] not exist");
                 return response;
             }
         }
     }

     // 创建MessageExtBrokerInner
     MessageExtBrokerInner msgInner = new MessageExtBrokerInner();
     msgInner.setTopic(newTopic);
     msgInner.setBody(body);
     msgInner.setFlag(requestHeader.getFlag());
     MessageAccessor.setProperties(msgInner, MessageDecoder.string2messageProperties(requestHeader.getProperties()));
     msgInner.setPropertiesString(requestHeader.getProperties());
     msgInner.setTagsCode(MessageExtBrokerInner.tagsString2tagsCode(topicConfig.getTopicFilterType(), msgInner.getTags()));
    msgInner.setQueueId(queueIdInt);
    msgInner.setSysFlag(sysFlag);
    msgInner.setBornTimestamp(requestHeader.getBornTimestamp());
    msgInner.setBornHost(ctx.channel().remoteAddress());
    msgInner.setStoreHost(this.getStoreHost());
    msgInner.setReconsumeTimes(requestHeader.getReconsumeTimes() == null ? 0 : requestHeader.getReconsumeTimes());

     // 校验是否不允许发送事务消息
     if (this.brokerController.getBrokerConfig().isRejectTransactionMessage()) {
         String traFlag = msgInner.getProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED);
         if (traFlag != null) {
             response.setCode(ResponseCode.NO_PERMISSION);
             response.setRemark(
                 "the broker[" + this.brokerController.getBrokerConfig().getBrokerIP1() + "] sending transaction message is forbidden");
             return response;
         }
     }

     // 添加消息
     PutMessageResult putMessageResult = this.brokerController.getMessageStore().putMessage(msgInner);
     if (putMessageResult != null) {
         boolean sendOK = false;

         switch (putMessageResult.getPutMessageStatus()) {
             // Success
             case PUT_OK:
                 sendOK = true;
                 response.setCode(ResponseCode.SUCCESS);
                 break;
             case FLUSH_DISK_TIMEOUT:
                 response.setCode(ResponseCode.FLUSH_DISK_TIMEOUT);
                 sendOK = true;
                 break;
             case FLUSH_SLAVE_TIMEOUT:
                 response.setCode(ResponseCode.FLUSH_SLAVE_TIMEOUT);
                 sendOK = true;
                 break;
             case SLAVE_NOT_AVAILABLE:
                 response.setCode(ResponseCode.SLAVE_NOT_AVAILABLE);
                 sendOK = true;
                 break;

             // Failed
             case CREATE_MAPEDFILE_FAILED:
                 response.setCode(ResponseCode.SYSTEM_ERROR);
                 response.setRemark("create mapped file failed, server is busy or broken.");
                 break;
             case MESSAGE_ILLEGAL:
             case PROPERTIES_SIZE_EXCEEDED:
                 response.setCode(ResponseCode.MESSAGE_ILLEGAL);
                 response.setRemark(
                     "the message is illegal, maybe msg body or properties length not matched. msg body length limit 128k, msg properties length limit 32k.");
                 break;
             case SERVICE_NOT_AVAILABLE:
                 response.setCode(ResponseCode.SERVICE_NOT_AVAILABLE);
                 response.setRemark(
                     "service not available now, maybe disk full, " + diskUtil() + ", maybe your broker machine memory too small.");
                 break;
             case OS_PAGECACHE_BUSY:
                 response.setCode(ResponseCode.SYSTEM_ERROR);
                 response.setRemark("[PC_SYNCHRONIZED]broker busy, start flow control for a while");
                 break;
             case UNKNOWN_ERROR:
                 response.setCode(ResponseCode.SYSTEM_ERROR);
                 response.setRemark("UNKNOWN_ERROR");
                 break;
             default:
                 response.setCode(ResponseCode.SYSTEM_ERROR);
                 response.setRemark("UNKNOWN_ERROR DEFAULT");
                 break;
         }

         String owner = request.getExtFields().get(BrokerStatsManager.COMMERCIAL_OWNER);
         if (sendOK) {
             // 统计
             this.brokerController.getBrokerStatsManager().incTopicPutNums(msgInner.getTopic());
             this.brokerController.getBrokerStatsManager().incTopicPutSize(msgInner.getTopic(), putMessageResult.getAppendMessageResult().getWroteBytes());
             this.brokerController.getBrokerStatsManager().incBrokerPutNums();

             // 响应
             response.setRemark(null);
             responseHeader.setMsgId(putMessageResult.getAppendMessageResult().getMsgId());
             responseHeader.setQueueId(queueIdInt);
             responseHeader.setQueueOffset(putMessageResult.getAppendMessageResult().getLogicsOffset());
             doResponse(ctx, request, response);

             // hook:设置发送成功到context
             if (hasSendMessageHook()) {
                 sendMessageContext.setMsgId(responseHeader.getMsgId());
                 sendMessageContext.setQueueId(responseHeader.getQueueId());
                 sendMessageContext.setQueueOffset(responseHeader.getQueueOffset());

                 int commercialBaseCount = brokerController.getBrokerConfig().getCommercialBaseCount();
                 int wroteSize = putMessageResult.getAppendMessageResult().getWroteBytes();
                 int incValue = (int) Math.ceil(wroteSize / BrokerStatsManager.SIZE_PER_COUNT) * commercialBaseCount;

                 sendMessageContext.setCommercialSendStats(BrokerStatsManager.StatsType.SEND_SUCCESS);
                 sendMessageContext.setCommercialSendTimes(incValue);
                 sendMessageContext.setCommercialSendSize(wroteSize);
                 sendMessageContext.setCommercialOwner(owner);
             }
             return null;
         } else {
             // hook:设置发送失败到context
             if (hasSendMessageHook()) {
                 int wroteSize = request.getBody().length;
                 int incValue = (int) Math.ceil(wroteSize / BrokerStatsManager.SIZE_PER_COUNT);

                 sendMessageContext.setCommercialSendStats(BrokerStatsManager.StatsType.SEND_FAILURE);
                 sendMessageContext.setCommercialSendTimes(incValue);
                 sendMessageContext.setCommercialSendSize(wroteSize);
                 sendMessageContext.setCommercialOwner(owner);
             }
         }
     } else {
         response.setCode(ResponseCode.SYSTEM_ERROR);
         response.setRemark("store putMessage return null");
     }

     return response;}

```js
AbstractSendMessageProcessor#msgCheck
protected RemotingCommand msgCheck(final ChannelHandlerContext ctx,
final SendMessageRequestHeader requestHeader, final RemotingCommand response) {
// 检查 broker 是否有写入权限
if (!PermName.isWriteable(this.brokerController.getBrokerConfig().getBrokerPermission())
&& this.brokerController.getTopicConfigManager().isOrderTopic(requestHeader.getTopic())) {
response.setCode(ResponseCode.NO\_PERMISSION);
response.setRemark("the broker\[" + this.brokerController.getBrokerConfig().getBrokerIP1()
\+ "] sending message is forbidden");
return response;
}
// 检查topic是否可以被发送。目前是{@link MixAll.DEFAULT\_TOPIC}不被允许发送
if (!this.brokerController.getTopicConfigManager().isTopicCanSendMessage(requestHeader.getTopic())) {
String errorMsg = "the topic\[" + requestHeader.getTopic() + "] is conflict with system reserved words.";
log.warn(errorMsg);
response.setCode(ResponseCode.SYSTEM\_ERROR);
response.setRemark(errorMsg);
return response;
}
TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());
if (null == topicConfig) { // 不能存在topicConfig,则进行创建
int topicSysFlag = 0;
if (requestHeader.isUnitMode()) {
if (requestHeader.getTopic().startsWith(MixAll.RETRY\_GROUP\_TOPIC\_PREFIX)) {
topicSysFlag = TopicSysFlag.buildSysFlag(false, true);
} else {
topicSysFlag = TopicSysFlag.buildSysFlag(true, false);
}
}
// 创建topic配置
log.warn("the topic {} not exist, producer: {}", requestHeader.getTopic(), ctx.channel().remoteAddress());
topicConfig = this.brokerController.getTopicConfigManager().createTopicInSendMessageMethod(//
requestHeader.getTopic(), //
requestHeader.getDefaultTopic(), //
RemotingHelper.parseChannelRemoteAddr(ctx.channel()), //
requestHeader.getDefaultTopicQueueNums(), topicSysFlag);
if (null == topicConfig) {
if (requestHeader.getTopic().startsWith(MixAll.RETRY\_GROUP\_TOPIC\_PREFIX)) {
topicConfig =
this.brokerController.getTopicConfigManager().createTopicInSendMessageBackMethod(
requestHeader.getTopic(), 1, PermName.PERM\_WRITE | PermName.PERM\_READ,
topicSysFlag);
}
}
// 如果没配置
if (null == topicConfig) {
response.setCode(ResponseCode.TOPIC\_NOT\_EXIST);
response.setRemark("topic\[" + requestHeader.getTopic() + "] not exist, apply first please!"
\+ FAQUrl.suggestTodo(FAQUrl.APPLY\_TOPIC\_URL));
return response;
}
}
// 队列编号是否正确
int queueIdInt = requestHeader.getQueueId();
int idValid = Math.max(topicConfig.getWriteQueueNums(), topicConfig.getReadQueueNums());
if (queueIdInt >= idValid) {
String errorInfo = String.format("request queueId\[%d] is illegal, %s Producer: %s",
queueIdInt,
topicConfig.toString(),
RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
log.warn(errorInfo);
response.setCode(ResponseCode.SYSTEM\_ERROR);
response.setRemark(errorInfo);
return response;
}
return response;
}

DefaultMessageStore#putMessage
public PutMessageResult putMessage(MessageExtBrokerInner msg) {
if (this.shutdown) {
log.warn("message store has shutdown, so putMessage is forbidden");
return new PutMessageResult(PutMessageStatus.SERVICE\_NOT\_AVAILABLE, null);
}

     // 从节点不允许写入
     if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {
         long value = this.printTimes.getAndIncrement();
         if ((value % 50000) == 0) {
             log.warn("message store is slave mode, so putMessage is forbidden ");
         }

         return new PutMessageResult(PutMessageStatus.SERVICE_NOT_AVAILABLE, null);
     }

     // store是否允许写入
     if (!this.runningFlags.isWriteable()) {
         long value = this.printTimes.getAndIncrement();
         if ((value % 50000) == 0) {
             log.warn("message store is not writeable, so putMessage is forbidden " + this.runningFlags.getFlagBits());
         }

         return new PutMessageResult(PutMessageStatus.SERVICE_NOT_AVAILABLE, null);
     } else {
         this.printTimes.set(0);
     }

     // 消息过长
     if (msg.getTopic().length() > Byte.MAX_VALUE) {
         log.warn("putMessage message topic length too long " + msg.getTopic().length());
         return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, null);
     }

     // 消息附加属性过长
     if (msg.getPropertiesString() != null && msg.getPropertiesString().length() > Short.MAX_VALUE) {
         log.warn("putMessage message properties length too long " + msg.getPropertiesString().length());
         return new PutMessageResult(PutMessageStatus.PROPERTIES_SIZE_EXCEEDED, null);
     }

     if (this.isOSPageCacheBusy()) {
         return new PutMessageResult(PutMessageStatus.OS_PAGECACHE_BUSY, null);
     }

     long beginTime = this.getSystemClock().now();
     // 添加消息到commitLog
     PutMessageResult result = this.commitLog.putMessage(msg);

     long eclipseTime = this.getSystemClock().now() - beginTime;
     if (eclipseTime > 500) {
         log.warn("putMessage not in lock eclipse time(ms)={}, bodyLength={}", eclipseTime, msg.getBody().length);
     }
     this.storeStatsService.setPutMessageEntireTimeMax(eclipseTime);

     if (null == result || !result.isOk()) {
         this.storeStatsService.getPutMessageFailedTimes().incrementAndGet();
     }

     return result;

}
```