这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党
rocketmq版本
- 5.1.0
现象
我们使用官方的发送消息和消费消息demo运行得到如下结果
消息发送
DefaultMQProducer producer = new DefaultMQProducer(PRODUCER_GROUP);
producer.setNamesrvAddr(DEFAULT_NAMESRVADDR);
producer.start();
Message msg = new Message(TOPIC /* Topic */,
TAG /* Tag */,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
SendResult sendResult = producer.send(msg);
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.printf("%s %s%n", sendResult, dtf2.format(LocalDateTime.now()));
打印的结果如下:
可以看到几个重要信息
- msgId: 0A0A003A4833251A69D78E7563400000
- offsetMsgId: AC1963C4000078BF000000001D4E6C2A
消息消费
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(CONSUMER_GROUP);
consumer.setNamesrvAddr(DEFAULT_NAMESRVADDR);
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe(TOPIC, "*");
consumer.registerMessageListener((MessageListenerConcurrently) (msg, context) -> {
System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msg);
// return ConsumeConcurrentlyStatus.RECONSUME_LATER;
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
打印结果如下:
我们也是看看几个重要信息
- msgId:AC1963C4000078BF000000001D4E6C2A
- UNIQ_KEY: 0A0A003A4833251A69D78E7563400000
疑问
这里我们发现了一个问题:
生产者发送的消息中的msgId在消费者中变成了UNIQ_KEY
生产者中的offsetMsgId才是消费者中的msgId
为什么好好的msgId在消费者中变成了UNIQ_KEY
带着这个疑问我们去看看消费者的msgId是如何获取的
注意这里看到的结果都是通过调用
toString()方法打印的
先说msgId和offsetMsgId是什么
msgId: 这个id是由客户端发送消息的时候生成,全局唯一,又叫UNIQ_KEYoffsetMsgId:消息偏移ID,该 ID 记录了消息所在集群的物理地址,主要包含所存储 Broker 服务器的地址( IP 与端口号)以及所在commitlog 文件的物理偏移量(采用”IP地址+Port端口”与“CommitLog的物理偏移量地址”做了一个字符串拼接),在consumer通过调用toString打印的msgId即为offsetMsgId
消费者msgId获取源码分析
我们首先看看producer的msgId是怎么来的
通过查看SendResult的构造方法可以看到
这里构造的SendResult中的
msgId=uniqMsgIdoffsetMsgId= `responseHeader.getMsgId()``
这里看好像msgId就是UNIQ_KEY
我们再看看msgId的获取来确认一下
public static final String PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX = "UNIQ_KEY";
String uniqMsgId = MessageClientIDSetter.getUniqID(msg);
return msg.getProperty(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);
可以很清晰的看到product中的msgId就是uniqMsgId
我们再看看consumer中的UNIQ_KEY是如何获取到的
我们跟踪源码可以看到有如下代码
这里就可以看到UNIQ_KEY是client在消息发送生成的
uniq key生成规则
生成规则我们可以简单看看
public static String createUniqID() {
char[] sb = new char[LEN * 2];
System.arraycopy(FIX_STRING, 0, sb, 0, FIX_STRING.length);
long current = System.currentTimeMillis();
if (current >= nextStartTime) {
setStartTime(current);
}
int diff = (int)(current - startTime);
if (diff < 0 && diff > -1000_000) {
// may cause by NTP
diff = 0;
}
int pos = FIX_STRING.length;
UtilAll.writeInt(sb, pos, diff);
pos += 8;
UtilAll.writeShort(sb, pos, COUNTER.getAndIncrement());
return new String(sb);
}
用到的FIX_STRING主要是在静态代码块中运行的,我们可以看看
static {
byte[] ip;
try {
ip = UtilAll.getIP();
} catch (Exception e) {
ip = createFakeIP();
}
LEN = ip.length + 2 + 4 + 4 + 2;
ByteBuffer tempBuffer = ByteBuffer.allocate(ip.length + 2 + 4);
tempBuffer.put(ip);
tempBuffer.putShort((short) UtilAll.getPid());
tempBuffer.putInt(MessageClientIDSetter.class.getClassLoader().hashCode());
FIX_STRING = UtilAll.bytes2string(tempBuffer.array()).toCharArray();
setStartTime(System.currentTimeMillis());
COUNTER = new AtomicInteger(0);
}
这段代码有生成规则是如下:
- 总长度为20
- 生成策略由客户端的IP、进程ID、加载 MessageClientIDSetter 的类加载器的 hashcode结合
解惑
所以我们这里知道了product中的SendResult返回的msgId实际是uniqID
至于consumer中的msgId很明显就是offsetMsgId
dashboard疑惑
实际我们发现在rocketmq-dashboard无论是使用msgId还是offsetMsgId好像我们都能查询到我们想要的结果,这是为什么呢
如果我们查看源码会发现rocketmq提供的MQAdmin工具类中帮我们做了兼容处理
可以我们先会使用offsetMsgId去查询,如果报错则try catch这个异常,然后用queryMessageByUniqKey去查询
由于在底层屏蔽了这个差异,导致我们很多人会分不清UniqKey和offsetMsgId
总结
- 由于我们在使用
rocketmq-dashboard查询的时候底层帮我们屏蔽了msgId和offsetMsgId,导致我们不会去细分二则的区别 - consumer调用
toString方法打印出来的msgId实际是offsetMsgId,所以会与product中的msgId对不上,因为product中的msgId实际是UniqKey