RocketMq 消息查询

系列

  • RocketMq 消息Tag过滤
  • RocketMq 广播模式
  • RocketMQ 同步调用的新特性
  • RocketMq 事务消息
  • RocketMq 堆积查询
  • RocketMq 消息查询
  • RocketMq 消费位点上报

开篇

  • 这篇文章主要解析下RocketMq的两张消息查询方式,分别是queryMsgById和queryMsgByKey。
  • queryMsgById根据MsgId查询消息体。
  • queryMsgByKey根据key查询消息体。
  • queryMsgByUniqueKey根据uniqueKey进行查询。

queryMsgById

usage: mqadmin queryMsgById [-d ] [-g ] [-h] -i  [-n ] [-s ] [-u ]
 -d,--clientId         The consumer's client id
 -g,--consumerGroup    consumer group name
 -h,--help                  Print help
 -i,--msgId            Message Id
 -n,--namesrvAddr      Name server address list, eg: 192.168.0.1:9876;192.168.0.2:9876
 -s,--sendMessage      resend message
 -u,--unitName         unit name
  • queryMsgById根据msgId去查询实际消息内容,命令形式如上所示。


mqadmin queryMsgById -n localhost -i C0A8010300002A9F0000000000D80335

查询结果
OffsetID:            C0A8010300002A9F0000000000D80335
Topic:               SCHEDULE_TOPIC_XXXX
Tags:                [TagA]
Keys:                [unix_1]
Queue ID:            2
Queue Offset:        11795
CommitLog Offset:    14156597
Reconsume Times:     0
Born Timestamp:      2020-05-12 22:54:45,771
Store Timestamp:     2020-05-12 22:54:45,784
Born Host:           192.168.1.3:55209
Store Host:          192.168.1.3:10911
System Flag:         0
Properties:          {REAL_TOPIC=TopicTest, KEYS=unix_1, UNIQ_KEY=24098A2802202740D87C1B9F2622006B000018B4AAC23D9096070000, CLUSTER=DefaultCluster, WAIT=true, DELAY=3, TAGS=TagA, REAL_QID=3}
Message Body Path:   /tmp/rocketmq/msgbodys/24098A2802202740D87C1B9F2622006B000018B4AAC23D9096070000
  • msgId如C0A8010300002A9F0000000000D80335,msgId由ip:port:offset三者组成。


public class MessageDecoder {

    public static String createMessageId(final ByteBuffer input, final ByteBuffer addr, final long offset) {
        input.flip();
        // ByteBuffer addr包含ip和端口
        // 针对ipv4地址,ip为4字节,端口为4字节,offset为8字节,总计16个字节。
        // 针对ipv6地址,ip地址为16个字节,端口为4字节,offset为8字节,总结28个字节。
        int msgIDLength = addr.limit() == 8 ? 16 : 28;
        input.limit(msgIDLength);

        input.put(addr);
        input.putLong(offset);

        return UtilAll.bytes2string(input.array());
    }

    public static MessageId decodeMessageId(final String msgId) throws UnknownHostException {
        SocketAddress address;
        long offset;
        // ipv4地址为32bit,等于4个字节,等于8个16进制码
        // ipv6地址为128bit,等于16个字节,等于32个16进制码
        int ipLength = msgId.length() == 32 ? 4 * 2 : 16 * 2;
        // 取除ip地址
        byte[] ip = UtilAll.string2bytes(msgId.substring(0, ipLength));
        // 取出port端口
        byte[] port = UtilAll.string2bytes(msgId.substring(ipLength, ipLength + 8));
        ByteBuffer bb = ByteBuffer.wrap(port);
        int portInt = bb.getInt(0);
        address = new InetSocketAddress(InetAddress.getByAddress(ip), portInt);

        // 取出offset偏移
        byte[] data = UtilAll.string2bytes(msgId.substring(ipLength + 8, ipLength + 8 + 16));
        bb = ByteBuffer.wrap(data);
        offset = bb.getLong(0);

        return new MessageId(address, offset);
    }
}


public class MQAdminImpl {

    public MessageExt viewMessage(
        String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {

        MessageId messageId = null;
        try {
            messageId = MessageDecoder.decodeMessageId(msgId);
        } catch (Exception e) {
            throw new MQClientException(ResponseCode.NO_MESSAGE, "query message by id finished, but no message.");
        }
        return this.mQClientFactory.getMQClientAPIImpl().viewMessage(RemotingUtil.socketAddress2String(messageId.getAddress()),
            messageId.getOffset(), timeoutMillis);
    }
}
  • 针对ipv4地址,ip为4字节,端口为4字节,offset为8字节,总计16个字节。
  • 针对ipv6地址,ip地址为16个字节,端口为4字节,offset为8字节,总结28个字节。
  • createMessageId的组成包括ip:port:offset三者。
  • viewMessage#decodeMessageId会解析成ip:port和offset,通过ip:port查询指定的broker,根据offset查询指定的commitLog的内容。


public class QueryMessageProcessor implements NettyRequestProcessor {

    public RemotingCommand viewMessageById(ChannelHandlerContext ctx, RemotingCommand request)
        throws RemotingCommandException {
        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
        final ViewMessageRequestHeader requestHeader =
            (ViewMessageRequestHeader) request.decodeCommandCustomHeader(ViewMessageRequestHeader.class);

        response.setOpaque(request.getOpaque());
        // 根据offset去commitLog查询实际存储数据
        final SelectMappedBufferResult selectMappedBufferResult =
            this.brokerController.getMessageStore().selectOneMessageByOffset(requestHeader.getOffset());
        if (selectMappedBufferResult != null) {
            response.setCode(ResponseCode.SUCCESS);
            response.setRemark(null);

            try {
                FileRegion fileRegion =
                    new OneMessageTransfer(response.encodeHeader(selectMappedBufferResult.getSize()),
                        selectMappedBufferResult);
                ctx.channel().writeAndFlush(fileRegion).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        selectMappedBufferResult.release();
                        if (!future.isSuccess()) {
                            log.error("Transfer one message from page cache failed, ", future.cause());
                        }
                    }
                });
            } catch (Throwable e) {
                log.error("", e);
                selectMappedBufferResult.release();
            }

            return null;
        } else {
            response.setCode(ResponseCode.SYSTEM_ERROR);
            response.setRemark("can not find message by the offset, " + requestHeader.getOffset());
        }

        return response;
    }
}
  • viewMessageById在根据requestHeader.getOffset()的位移去commitLog获取消息体。


queryMsgByKey

usage: mqadmin queryMsgByKey [-h] -k  [-n ] -t 
 -h,--help                Print help
 -k,--msgKey         Message Key
 -n,--namesrvAddr    Name server address list, eg: 192.168.0.1:9876;192.168.0.2:9876
 -t,--topic          topic name
  • queryMsgByKey根据topic 和 msgKey 去查询实际消息内容,命令形式如上所示。


mqadmin queryMsgByKey -k unix_1 -t TopicTest -n localhost:9876 

#Message ID                                        #QID                                  #Offset
C0A80008000018B4AAC23D6A91EA0000                      7                                     1256
C0A80008000018B4AAC23D6BDB330000                      0                                     1405
24098A2802202740D87C1B9F2622006B000018B4AAC23D7948F00000    3                                     1516
24098A2802202740D87C1B9F2622006B000018B4AAC23D7D2BA50000    6                                     1626
24098A2802202740D87C1B9F2622006B000018B4AAC23D7F844F0000    6                                     1751
24098A2802202740D87C1B9F2622006B000018B4AAC23D8089980000    0                                     1905
24098A2802202740D87C1B9F2622006B000018B4AAC23D81031F0000    0                                     1906
24098A2802202740D87C1B9F2622006B000018B4AAC23D81BBB00000    2                                     1873
24098A2802202740D87C1B9F2622006B000018B4AAC23D82D0BB0000    2                                     1874
24098A2802202740D87C1B9F2622006B000018B4AAC23D83B57F0000    0                                     1907
24098A2802202740D87C1B9F2622006B000018B4AAC23D850EC70000    0                                     1908
24098A2802202740D87C1B9F2622006B000018B4AAC23D8C99B10000    1                                     1909
24098A2802202740D87C1B9F2622006B000018B4AAC23D9096070000    3                                     1891
24098A2802202740D87C1B9F2622006B000018B4AAC23D9553AF0000    5                                     1867
24098A2802202740D87C1B9F2622006B000018B4AAC23D9640380000    1                                     1910
24098A2802202740D87C1B9F2622006B000018B4AAC23D96F68B0000    1                                     1911
24098A2802202740D87C1B9F2622006B000018B4AAC23D9822BE0000    2                                     1875


public class DefaultMessageStore implements MessageStore {

    @Override
    public QueryMessageResult queryMessage(String topic, String key, int maxNum, long begin, long end) {
        QueryMessageResult queryMessageResult = new QueryMessageResult();

        long lastQueryMsgTime = end;

        for (int i = 0; i < 3; i++) {
            // 去indexService根据topic和key去查询
            QueryOffsetResult queryOffsetResult = this.indexService.queryOffset(topic, key, maxNum, begin, lastQueryMsgTime);
            if (queryOffsetResult.getPhyOffsets().isEmpty()) {
                break;
            }

            Collections.sort(queryOffsetResult.getPhyOffsets());

            queryMessageResult.setIndexLastUpdatePhyoffset(queryOffsetResult.getIndexLastUpdatePhyoffset());
            queryMessageResult.setIndexLastUpdateTimestamp(queryOffsetResult.getIndexLastUpdateTimestamp());

            for (int m = 0; m < queryOffsetResult.getPhyOffsets().size(); m++) {
                long offset = queryOffsetResult.getPhyOffsets().get(m);

                try {

                    boolean match = true;
                    MessageExt msg = this.lookMessageByOffset(offset);
                    if (0 == m) {
                        lastQueryMsgTime = msg.getStoreTimestamp();
                    }

                    if (match) {
                        SelectMappedBufferResult result = this.commitLog.getData(offset, false);
                        if (result != null) {
                            int size = result.getByteBuffer().getInt(0);
                            result.getByteBuffer().limit(size);
                            result.setSize(size);
                            queryMessageResult.addMessage(result);
                        }
                    } else {
                        log.warn("queryMessage hash duplicate, {} {}", topic, key);
                    }
                } catch (Exception e) {
                    log.error("queryMessage exception", e);
                }
            }

            if (queryMessageResult.getBufferTotalSize() > 0) {
                break;
            }

            if (lastQueryMsgTime < begin) {
                break;
            }
        }

        return queryMessageResult;
    }
}
  • 根据topic和key去indexService查询索引信息。
  • 根据查询结果的phyoffset去commitLog查询实际存储的消息。


queryMsgByUniqueKey

queryMsgByUniqueKey -t TopicTest -n 192.168.0.8:9876 -i C0A80008000018B4AAC23DBBCAD00000 

Topic:               TopicTest
Tags:                [TagA]
Keys:                [unix_1]
Queue ID:            2
Queue Offset:        1876
CommitLog Offset:    14159198
Reconsume Times:     0
Born Timestamp:      2020-05-12 23:41:57,329
Store Timestamp:     2020-05-12 23:41:57,344
Born Host:           192.168.0.8:61405
Store Host:          192.168.0.8:10911
System Flag:         0
Properties:          {KEYS=unix_1, UNIQ_KEY=C0A80008000018B4AAC23DBBCAD00000, CLUSTER=DefaultCluster, WAIT=true, TAGS=TagA}
Message Body Path:   /tmp/rocketmq/msgbodys/C0A80008000018B4AAC23DBBCAD00000


MessageTrack [consumerGroup=order_consumer, trackType=NOT_ONLINE, exceptionDesc=CODE:206 DESC:the consumer group[order_consumer] not online]MessageTrack [consumerGroup=consumer_group_test, trackType=NOT_ONLINE, exceptionDesc=CODE:206 DESC:the consumer group[consumer_group_test] not online]MessageTrack [consumerGroup=ResponseConsumerGroup, trackType=NOT_ONLINE, exceptionDesc=CODE:206 DESC:the consumer group[ResponseConsumerGroup] not online]MessageTrack [consumerGroup=please_rename_unique_group_name_4, trackType=UNKNOWN, exceptionDesc=org.apache.rocketmq.client.exception.MQClientException: CODE: 17  DESC: No topic route info in name server for the topic: %RETRY%please_rename_unique_group_name_4
See http://rocketmq.apache.org/docs/faq/ for further details., org.apache.rocketmq.client.impl.MQClientAPIImpl.getTopicRouteInfoFromNameServer(MQClientAPIImpl.java:1389)]
  • producer发送消息后返回的msgId=C0A80008000018B4AAC23DC95F940000本质上是UniqueKey,而非queryMsgById中使用的Id

你可能感兴趣的:(RocketMq 消息查询)