在上一节 RocketMQ源码详解 | Producer篇 · 其一:Start,然后 Send 一条消息 中,我们了解了 Producer 在发送消息的流程。这次我们再来具体下看消息的构成与其发送的链路
在 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;
}
变量名 | 含义 |
---|---|
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 较多,只摘了一小段
属性 | 含义 |
---|---|
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 生成了这样的消息后,会直接将其发出去吗?
让我们继续跟踪上一篇没讲完的内容
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;
}
具体的消息结构如下图:
其中每个字段在 Request 和 Response 中都有不同的含义
在 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