摘要
消息发送之前,会在客户端创建一个msgId,发送到broker,broker也会创建一个msgId,
- 客户端的msgId 包含 本地IP:本地PORT,本地classloader的hashCode
- broker返回的msgId 包含 brokerIP:brokerPort,brokerOffset
源码阅读
SendResult [sendStatus=SEND_OK, msgId=0A0158434F3473D16E938F000AEE0000 (本地msgId), offsetMsgId=0A01584300002A9F0000000000125CA7(brokerMsgId), messageQueue=MessageQueue [topic=TOPIC_TEST, brokerName=broker-b, queueId=3], queueOffset=479]
客户端消息创建源码
MessageClientIDSetter.setUniqID(msg);
public static String createUniqID() {
StringBuilder sb = new StringBuilder(LEN * 2);//32位
sb.append(FIX_STRING);// 静态方法,在启动的时候初始化,内容包含 IP:PORT,classloader的hashCode
sb.append(UtilAll.bytes2string(createUniqIDBuffer()));// 追加了一个时间差值 和子增值
return sb.toString();
}
static {
LEN = 4 + 2 + 4 + 4 + 2;
ByteBuffer tempBuffer = ByteBuffer.allocate(10);
tempBuffer.position(2);
tempBuffer.putInt(UtilAll.getPid());
tempBuffer.position(0);
try {
tempBuffer.put(UtilAll.getIP());// 获取当前IP:port,转换成16个字符串,之后创建msgId时可以用这个16位作为固定前缀
} catch (Exception e) {
tempBuffer.put(createFakeIP());
}
tempBuffer.position(6);
tempBuffer.putInt(MessageClientIDSetter.class.getClassLoader().hashCode());
FIX_STRING = UtilAll.bytes2string(tempBuffer.array());
setStartTime(System.currentTimeMillis());
COUNTER = new AtomicInteger(0);
}
// 6位,前4位表示时间差值,后面2位表示一个自增值
private static byte[] createUniqIDBuffer() {
ByteBuffer buffer = ByteBuffer.allocate(4 + 2);
long current = System.currentTimeMillis();
if (current >= nextStartTime) {
setStartTime(current);
}
buffer.position(0);
buffer.putInt((int) (System.currentTimeMillis() - startTime));
buffer.putShort((short) COUNTER.getAndIncrement());
return buffer.array();
}
broker 消息ID创建源码
// 根据这个创建commitlog的消息id 前面8位是 brokerIP:PORT,后面8位是commitlog的offset,转码成32位
public static String createMessageId(final ByteBuffer input, final ByteBuffer addr, final long offset) {
input.flip();
input.limit(MessageDecoder.MSG_ID_LENGTH);
input.put(addr);
input.putLong(offset);
return UtilAll.bytes2string(input.array());
}
消息offsetMsgId解码源码
这个port一般是10911
代码入口:
org.apache.rocketmq.common.message.MessageDecoder.decodeMessageId(String)
/**
* 根据消息id反转码得到MessageId对象,MessageId对象包含 SocketAddress(ip:port),消息在索引文件的位置 offset
* 0A01584A483873D16E936A9D8D930000
* 前8位是 ip,接下来8位是port,最后16位是commitLogOffset
*/
public static MessageId decodeMessageId(final String msgId) throws UnknownHostException {
SocketAddress address;
long offset;
//前8位是ip地址
byte[] ip = UtilAll.string2bytes(msgId.substring(0, 8));
//接下来8位是端口
byte[] port = UtilAll.string2bytes(msgId.substring(8, 16));
ByteBuffer bb = ByteBuffer.wrap(port);
int portInt = bb.getInt(0);
address = new InetSocketAddress(InetAddress.getByAddress(ip), portInt);
// offset 接下来16位是offset
byte[] data = UtilAll.string2bytes(msgId.substring(16, 32));
bb = ByteBuffer.wrap(data);
offset = bb.getLong(0);
return new MessageId(address, offset);
}
/**
* 将8位的字符串转换成4个字节
* 转换方法:高字节左移,跟 字节 做| 运算
* 高字节能左移,是因为字节都是16以下的,字节的低4位能表示,字节的高4位是0
* @param hexString
* @return
*/
public static byte[] string2bytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
/**
* 这些字符的必定是 0123456789ABCDEF 这16个数字里面的,最大也就是16位,只需要低4位表示
* 第i个字符的低四位左移4位,拼接 i+1个字符的低四位,构成一个完整的8位byte,这样就是一个256范围的数字,或 操作就是拼接
*/
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}