RocketMQ源码详解 | Producer篇 · 其二:消息组成、发送链路

概述

在上一节 RocketMQ源码详解 | Producer篇 · 其一:Start,然后 Send 一条消息 中,我们了解了 Producer 在发送消息的流程。这次我们再来具体下看消息的构成与其发送的链路


Message

在 RocketMQ 的使用中,Message 类是在发送消息时必须用到的,其中 body 即是消息的存放位置,还有的就是消息的 标识(flag) 和 属性(properties)

public class Message {
   
  private String topic;
  private int flag;
  private Map<String, String> properties;
  private byte[] body;
  private String transactionId;
}

消息的标识(flag)

变量名 含义
COMPRESSED_FLAG 压缩消息。消息为批量的时候,就会进行压缩,默认使用5级的 zip
MULTI_TAGS_FLAG 有多个 tag。
TRANSACTION_NOT_TYPE 事务为未知状态。当 Broker 回查 Producer 的时候,如果为 Commit 应该提交,为 Rollback 应该回滚,为 Unknown 时应该继续回查
TRANSACTION_PREPARED_TYPE 事务的运行状态。当前消息是事务的一部分
TRANSACTION_COMMIT_TYPE 事务的提交消息。要求提交事务
TRANSACTION_ROLLBACK_TYPE 事务的回滚消息。要求回滚事务
BORNHOST_V6_FLAG 生成该消息的 host 是否 ipv6 的地址
STOREHOSTADDRESS_V6_FLAG 持久化该消息的 host 是否是 ipv6 的地址

消息的属性(properties)

而消息的 properties 较多,只摘了一小段

属性 含义
KEYS 消息的 Key。服务器会通过 key 设置索引,应用可以通过 Topic 和 Key 来查找这条消息以及被谁消费
TAGS 消息的子类型,可以根据 tag 选择性消费
DELAY 延迟消息的延迟级别(共16级,理论上可以有18级)
RETRY_TOPIC 需要重试的 Topic(在 Broker 中会存放到 SCHEDULE_TOPIC_XXXX Topic,其中有 18 个 queue,对应 18 个重试延迟)
REAL_TOPIC 真实的 Topic (RocketMQ 经常使用更换目的 Topic 的"把戏",如事务消息和延时消息,这个字段记录了真正的 Topic)
PGROUP 生产者组
MAX_OFFSET\MIN_OFFSET 在 pull 中的最大偏移量和最小偏移量
TRANSFER_FLAG 事务有关标识
MSG_TYPE 消息类型,是否为回复消息
BUYER_ID 嗯…买家ID?

当然,这只是在生产者中的消息的样子,在 Broker 和消费者的眼中中,它是这样的

public class MessageExt extends Message {
   
  private static final long serialVersionUID = 5720810158625748049L;

  private String brokerName;

  private int queueId;

  // 存盘的大小
  private int storeSize;

  // 在 ConsumerQueue 中的偏移量
  private long queueOffset;
  private int sysFlag;
  // 消息创建时间
  private long bornTimestamp;
  // 创建地址
  private SocketAddress bornHost;

  // 存盘时间
  private long storeTimestamp;
  private SocketAddress storeHost;
  private String msgId;
  // 在 commitLog 中的偏移量
  private long commitLogOffset;
  // crc 校验
  private int bodyCRC;
  // 消费重试次数
  private int reconsumeTimes;

  private long preparedTransactionOffset;
}

消息的包装

那么,producer 生成了这样的消息后,会直接将其发出去吗?

让我们继续跟踪上一篇没讲完的内容


MQClientAPIImpl#sendMessage
long beginStartTime = System.currentTimeMillis();
RemotingCommand request = null;
String msgType = msg.getProperty(MessageConst.PROPERTY_MESSAGE_TYPE);
// 是否为 reply 消息
boolean isReply = msgType != null && msgType.equals(MixAll.REPLY_MESSAGE_FLAG);
if (isReply) {
   
  // 是 smart 消息则加上请求头
  if (sendSmartMsg) {
   
    SendMessageRequestHeaderV2 requestHeaderV2 = SendMessageRequestHeaderV2.createSendMessageRequestHeaderV2(requestHeader);
    request = RemotingCommand.createRequestCommand(RequestCode.SEND_REPLY_MESSAGE_V2, requestHeaderV2);
  } else {
   
    request = RemotingCommand.createRequestCommand(RequestCode.SEND_REPLY_MESSAGE, requestHeader);
  }
} else {
   
  if (sendSmartMsg || msg instanceof MessageBatch) {
   
    SendMessageRequestHeaderV2 requestHeaderV2 = SendMessageRequestHeaderV2.createSendMessageRequestHeaderV2(requestHeader);
    request = RemotingCommand.createRequestCommand(msg instanceof MessageBatch ? RequestCode.SEND_BATCH_MESSAGE : RequestCode.SEND_MESSAGE_V2, requestHeaderV2);
  } else {
   
    request = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE, requestHeader);
  }
}
request.setBody(msg.getBody());

/* -- pass -- */

在这里,我们可以看到在这又加了层套娃(只保留了body),然后才发送

RemotingCommand 的具体属性如下

private int code;
private LanguageCode language = LanguageCode.JAVA;
private int version = 0;
private int opaque = requestId.getAndIncrement();
private int flag = 0;
private String remark;
private HashMap<String, String> extFields;
private transient CommandCustomHeader customHeader;
private transient byte[] body;

我们还在他的方法中找到了一个叫 encode 的方法,并且返回的是 ByteBuffer 。因此这就是实际发送的消息。

public ByteBuffer encode() {
   
  // 1> header length size
  int length = 4;

  // 2> header data length
  byte[] headerData = this.headerEncode();
  length += headerData.length;

  // 3> body data length
  if (this.body != null) {
   
    length += body.length;
  }

  ByteBuffer result = ByteBuffer.allocate(4 + length);

  // length
  result.putInt(length);

  // header length
  result.put(markProtocolType(headerData.length, serializeTypeCurrentRPC));

  // header data
  result.put(headerData);

  // body data;
  if (this.body != null) {
   
    result.put(this.body);
  }

  result.flip();

  return result;
}

消息的结构

具体的消息结构如下图:

RocketMQ源码详解 | Producer篇 · 其二:消息组成、发送链路_第1张图片

其中每个字段在 Request 和 Response 中都有不同的含义

code

在 Request 中,为请求的操作码

public class RequestCode {
   
  // 发送消息
  public static final int SEND_MESSAGE = 10;
  // 拉取消息
  public static final int PULL_MESSAGE = 11;
  // 查询消息(所在topic, 需要的 key, 最大数量, 开始偏移量, 结束偏移量)
  public static final int QUERY_MESSAGE = 12;
  // 查询 Broker 偏移量(未使用)
  public static final int QUERY_BROKER_OFFSET = 13;
  /*
     * 查询消费者偏移量
     *      消费者会将偏移量存储在内存中,当使用主从架构时,会默认由主 Broker 负责读于写
     *      为避免消息堆积,堆积消息超过指定的值时,会由从服务器来接管读,但会导致消费进度问题
     *      所以主从消费进度的一致性由 从服务器主动上报 和 消费者内存进度优先 来保证
     */
  // 查询消费者自己的偏移量
  public static final int QUERY_CONSUMER_OFFSET = 14;
  // 提交自己的偏移量
  public static final int UPDATE_CONSUMER_OFFSET = 15;
  // 创建或更新Topic


  public static final int UPDATE_AND_CREATE_TOPIC = 17;
  // 获取所有的Topic信息
  public static final int GET_ALL_TOPIC_CONFIG = 21;
  /*   unused  */
  public static final int GET_TOPIC_CONFIG_LIST = 22;
  public static final int GET_TOPIC_NAME_LIST = 23;
  // 更新 Broker 配置
  public static final int UPDATE_BROKER_CONFIG = 25;
  // 获取 Broker 配置
  public static final int GET_BROKER_CONFIG = 26;
  public static final int TRIGGER_DELETE_FILES = 27;
  // 获取 Broker 运行时信息
  public static final int GET_BROKER_RUNTIME_INFO = 28;
  // 通过时间戳查找偏移量
  public static final int SEARCH_OFFSET_BY_TIMESTAMP = 29;
  // 获取最大偏移量
  public static final int GET_MAX_OFFSET = 30;
  // 获取最小偏移量
  public static final int GET_MIN_OFFSET = 31;
  //
  public static final int GET_EARLIEST_MSG_STORETIME = 32;

  /*       由 Broker 处理        */
  // 通过消息ID查询消息
  public static final int VIEW_MESSAGE_BY_ID = 33;
  // 心跳消息
  public static final int HEART_BEAT = 34;
  // 注销客户端
  public static final int UNREGISTER_CLIENT = 35;
  // 报告消费失败(一段时间后重试) (Deprecated)
  public static final int CONSUMER_SEND_MSG_BACK = 36;
  // 事务结果(可能是 commit 或 rollback)
  public static final int END_TRANSACTION = 37;
  // 通过消费者组获取消费者列表
  public static final int GET_CONSUMER_LIST_BY_GROUP = 38;

  // 检查事务状态; Broker对于事务的未知状态的回查操作
  public static final int CHECK_TRANSACTION_STATE = 39;
  // 通知消费者的ID已经被更改
  public static final int NOTIFY_CONSUMER_IDS_CHANGED = 40;

  // 批量锁定 Queue (rebalance使用)
  public static final int LOCK_BATCH_MQ = 41;
  // 解锁 Queue
  public static final int UNLOCK_BATCH_MQ = 42;
  // 获得该 Broker 上的所有的消费者偏移量
  public static final int GET_ALL_CONSUMER_OFFSET = 43;
  // 获得延迟 Topic 上的偏移量
  public static final int GET_ALL_DELAY_OFFSET = 45;
  // 检查客户端配置
  public static final int CHECK_CLIENT_CONFIG = 46;
  // 更新或创建 ACL
  public static final int UPDATE_AND_CREATE_ACL_CONFIG = 50;
  // 删除 ACL 配置
  public static final int DELETE_ACL_CONFIG = 51;
  // 获取 Broker 集群的 ACL 信息
  public static final int GET_BROKER_CLUSTER_ACL_INFO = 52;
  // 更新全局白名单
  public static final int UPDATE_GLOBAL_WHITE_ADDRS_CONFIG = 53;
  // 获取 Broker 集群的 ACL 配置
  public static final int GET_BROKER_CLUSTER_ACL_CONFIG = 54;


  /*      NameServer 相关      */

  // 放入键值配置
  public static final int PUT_KV_CONFIG = 100;
  // 获取键值配置
  public static final int GET_KV_CONFIG = 101;
  // 删除键值配置
  public static final int DELETE_KV_CONFIG = 102;
  // 注册 Broker
  public static final int REGISTER_BROKER = 103;
  // 注销 Broker
  public static final int UNREGISTER_BROKER = 104;
  // 获取指定 Topic 的路由信息
  public static final int GET_ROUTEINFO_BY_TOPIC = 105;
  // 获取 Broker 的集群信息
  public static final int GET_BROKER_CLUSTER_INFO = 106;



  // 更新或创建订阅组
  public static final int UPDATE_AND_CREATE_SUBSCRIPTIONGROUP = 200;
  // 获取所有订阅组的配置
  public static final int GET_ALL_SUBSCRIPTIONGROUP_CONFIG = 201;
  // 获取 Topic 的度量指标
  public static final int GET_TOPIC_STATS_INFO = 202;
  // 获取消费者在线列表(rpc)
  public static final int GET_CONSUMER_CONNECTION_LIST = 203;
  // 获取生产者在线列表
  public static final int GET_PRODUCER_CONNECTION_LIST = 204;
  public static final int WIPE_WRITE_PERM_OF_BROKER = 205;
  // 从 NameSrv 获取所有 Topic
  public static final int GET_ALL_TOPIC_LIST_FROM_NAMESERVER = 206;
  // 删除订阅组
  public static final int DELETE_SUBSCRIPTIONGROUP = 207;
  // 获取消费者的度量指标
  public static final int GET_CONSUME_STATS = 208;

  public static final int SUSPEND_CONSUMER = 209;
  public static final int RESUME_CONSUMER = 210;
  public static final int RESET_CONSUMER_OFFSET_IN_CONSUMER = 211;
  public static final int RESET_CONSUMER_OFFSET_IN_BROKER = 212;

  public static final int ADJUST_CONSUMER_THREAD_POOL = 213;

  public static final int WHO_CONSUME_THE_MESSAGE = 214;

  // 删除 Broker 中的 Topic
  public static final int DELETE_TOPIC_IN_BROKER = 215;
  // 删除 NameSrv 中的 Topic
  public static final int DELETE_TOPIC_IN_NAMESRV = 216;
  // 获取键值列表
  public static final int GET_KVLIST_BY_NAMESPACE = 219;

  // 重置消费者的消费进度
  public static final int RESET_CONSUMER_CL

你可能感兴趣的:(RocketMQ,后端,源码,中间件,消息队列)