RocketMQ关于指标数据的总结


前言

在RocketMQ中有很多关于指标数据统计的实现,本篇文章就来总结下其实现的方式以及都统计了哪些数据(源码是4.9.1版本)。


一、BrokerStatsManager

1.BrokerStatsManager初始化及启动

(1)BrokerStatsManager初始化
在broker启动过程中会创建BrokerController对象,在初始化BrokerController时就会初始化BrokerStatsManager,具体如下:

public static BrokerController createBrokerController(String[] args) {
	...
	final BrokerController controller = new BrokerController(
        brokerConfig,
        nettyServerConfig,
        nettyClientConfig,
        messageStoreConfig);
            // remember all configs to prevent discard
    controller.getConfiguration().registerConfig(properties);
	...
}

//在构造BrokerController对象时会初始化BrokerStatsManager
public BrokerController(
        final BrokerConfig brokerConfig,
        final NettyServerConfig nettyServerConfig,
        final NettyClientConfig nettyClientConfig,
        final MessageStoreConfig messageStoreConfig
    ) {
    ...
	this.brokerStatsManager = new BrokerStatsManager(this.brokerConfig.getBrokerClusterName());
	...
}

在BrokerStatsManager中有一个map结构的statsTable是用于存储BrokerStatsManager统计的指标名称及其对应的指标数据,在BrokerStatsManager初始化过程中最重要的操作就是将其统计的所有指标放入这个map结构中。
(2)BrokerStatsManager启动
在启动BrokerController时会启动BrokerStatsManager,具体如下:

public static BrokerController start(BrokerController controller) {
        try {

            controller.start();

            String tip = "The broker[" + controller.getBrokerConfig().getBrokerName() + ", "
                + controller.getBrokerAddr() + "] boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer();

            if (null != controller.getBrokerConfig().getNamesrvAddr()) {
                tip += " and name server is " + controller.getBrokerConfig().getNamesrvAddr();
            }

            log.info(tip);
            System.out.printf("%s%n", tip);
            return controller;
        } catch (Throwable e) {
            e.printStackTrace();
            System.exit(-1);
        }

        return null;
    }

public void start() throws Exception {
	...
	if (this.brokerStatsManager != null) {
        this.brokerStatsManager.start();
    }
    ...
}

2.BrokerStatsManager运转

想要搞清楚BrokerStatsManager是如何统计数据的,需要搞清楚HashMap<String, StatsItemSet> statsTable、StatsItemSet及StatsItem这三个数据结构。在统计指标数据时当然会有指标名称,在每个指标名称下还会有具体的对象,例如topic,最后才是该指标的值。所以在设计上首先是由statsTable来缓存所有的指标,每个指标对应的StatsItemSet中会有一个map结构statsItemTable用来缓存该指标下具体的对象及其指标值,指标值被封装成StatsItem。这三个数据结构详解具体如下。

2.1 statsTable

statsTable在BrokerStatsManager中是用于缓存统计的指标数据,statsTable的key表示统计的指标名称,value是StatsItemSet类型的数据,在BrokerStatsManager初始化时向statsTable中放入了统计的指标及其对应的StatsItemSet。

2.2 StatsItemSet

(1)StatsItemSet初始化
在BrokerStatsManager中每个指标都对应一条StatsItemSet类型的数据,其结构具体如下,其中statsName表示指标的名称,statsItemTable缓存的是该指标下具体的指标对象及其对应的StatsItem类型对象,举个例子,TOPIC_PUT_NUMS指标是在统计写入commitlog的消息数,此时会统计各个topic对应的消息数,所以topic的名称就是statsItemTable的key。

public class StatsItemSet {
    private final ConcurrentMap<String/* key */, StatsItem> statsItemTable =
        new ConcurrentHashMap<String, StatsItem>(128);

    private final String statsName;
    private final ScheduledExecutorService scheduledExecutorService;
    private final InternalLogger log;

    public StatsItemSet(String statsName, ScheduledExecutorService scheduledExecutorService, InternalLogger log) {
        this.statsName = statsName;
        this.scheduledExecutorService = scheduledExecutorService;
        this.log = log;
        this.init();
    }
}

(2)init()
在StatsItemSet的初始化中有个很重要的方法就是init(),该方法中主要是一些定时任务,定时任务可以分为两大类,分别是samplingInxxx()和printAtxxx()。

  • samplingInxxx()方法主要完成的功能是定时根据StatsItem中的指标值value、次数times以及当前的时间点构建一个指标的快照并将该指标快照存储在一个LinkedList中。
  • printAtxxx()方法主要完成的功能是在broker端的日志文件stats.log中打印指标数据,按照定时任务的时间频率即每分钟、每小时、每天打印各指标数据,后面输出的指标数据SUM、TPS以及AVGPT是根据LinkedList中缓存的快照数据进行计算,具体计算逻辑见computeStatsData方法。
[statsName] [statsKey] Stats In One Minute, SUM: xxx TPS: xxx AVGPT: xxx
[statsName] [statsKey] Stats In One Hour, SUM: xxx TPS: xxx AVGPT: xxx
[statsName] [statsKey] Stats In One Day, SUM: xxx TPS: xxx AVGPT: xxx
private static StatsSnapshot computeStatsData(final LinkedList<CallSnapshot> csList) {
        StatsSnapshot statsSnapshot = new StatsSnapshot();
        synchronized (csList) {
            double tps = 0;
            double avgpt = 0;
            long sum = 0;
            long timesDiff = 0;
            if (!csList.isEmpty()) {
                CallSnapshot first = csList.getFirst();
                CallSnapshot last = csList.getLast();
                sum = last.getValue() - first.getValue();
                tps = (sum * 1000.0d) / (last.getTimestamp() - first.getTimestamp());

                timesDiff = last.getTimes() - first.getTimes();
                if (timesDiff > 0) {
                    avgpt = (sum * 1.0d) / timesDiff;
                }
            }

            statsSnapshot.setSum(sum);
            statsSnapshot.setTps(tps);
            statsSnapshot.setAvgpt(avgpt);
            statsSnapshot.setTimes(timesDiff);
        }

        return statsSnapshot;
    }

2.3 StatsItem

StatsItem的数据结构如下,其中value表示指标值,times表示次数,csListMiute、csListHour和csListDay是用来缓存指标快照的,statsName表示指标名称,statsKey表示某一个指标下的具体对象。

public class StatsItem {

    private final AtomicLong value = new AtomicLong(0);

    private final AtomicLong times = new AtomicLong(0);

    private final LinkedList<CallSnapshot> csListMinute = new LinkedList<CallSnapshot>();

    private final LinkedList<CallSnapshot> csListHour = new LinkedList<CallSnapshot>();

    private final LinkedList<CallSnapshot> csListDay = new LinkedList<CallSnapshot>();

    private final String statsName;
    private final String statsKey;
}

3.统计的指标及其含义

指标名称含义
TOPIC_PUT_NUMS成功写入commitlog中的消息条数,按topic维度统计,其statsKey是topicName
TOPIC_PUT_SIZE成功写入commitlog中的消息大小(单位是字节),按topic维度统计,其statsKey是topicName
GROUP_GET_NUMS成功从broker获取消息的条数,其statsKey是topic@group
GROUP_GET_SIZE成功从broker获取消息的大小(单位是字节),其statsKey是topic@group
GROUP_GET_LATENCY消息的消费延迟时间 ,其statsKey是queueId@topic@group
SNDBCK_PUT_NUMSconsumer消费失败的消息成功写入SCHEDULE_TOPIC_XXXX中的消息条数,其statsKey是topic@group
BROKER_PUT_NUMS统计消息存储到broker端的条数 ,按broker维度统计,其statsKey是clusterName
BROKER_GET_NUMS统计从broker端成功获取消息的条数 ,按broker维度统计,其statsKey是clusterName

二、StoreStatsService

StoreStatsService继承了ServiceThread,其统计的是和存储相关的指标数据。由于它本质上是个线程,所以我们直接看其run方法,可以看到它每1分钟会执行sampling和printTps方法。其中sampling方法主要完成的功能是往putTimesList、getTimesFoundList、getTimesMissList和transferedMsgCountList中添加指标的快照数据。

public void run() {
        log.info(this.getServiceName() + " service started");

        while (!this.isStopped()) {
            try {
                this.waitForRunning(FREQUENCY_OF_SAMPLING);

                this.sampling();

                this.printTps();
            } catch (Exception e) {
                log.warn(this.getServiceName() + " service has exception. ", e);
            }
        }

        log.info(this.getServiceName() + " service end");
    }

sampling方法中存储各指标快照的含义具体如下:

指标含义
putTimesList成功写入broker的数据总条数
getTimesFoundList从broker端成功获取数据的次数
getTimesMissList从broker端获取数据失败的次数
transferedMsgCountList该指标含义和getTimesFoundList貌似是一样的,没啥区别,都是在成功获取到消息时自增

printTps方法会每1分钟在日志中打印以下信息:

[STORETPS] put_tps XXX get_found_tps XXX get_miss_tps XXX get_transfered_tps XXX

StoreStatsService - [PAGECACHERT] TotalPut 0, PutMessageDistributeTime [<=0ms]:xxx [0~10ms]:xxx [10~50ms]:xxx [50~100ms]:xxx [100~200ms]:xxx [200~500ms]:xxx [500ms~1s]:xxx [1~2s]:xxx [2~3s]:xxx [3~4s]:xxx [4~5s]:xxx [5~10s]:xxx [10s~]:xxx
指标含义
put_tps成功写入消息的TPS(根据putTimesList中的快照数据计算)
get_found_tps从broker端成功获取到数据的TPS(根据getTimesFoundList中的快照数据计算)
get_miss_tps从broker端获取数据失败的TPS(根据getTimesMissList中的快照数据计算)
get_transfered_tps与get_found_tps类似(根据transferedMsgCountList中的快照数据计算)
TotalPut一分钟内在broker端一共写入消息多少次
PutMessageDistributeTimebroker端普通消息或者批量消息写入内存中花费时间的统计,花费的时间一共分为13个级别 ,分别是[<=0ms]、[0-10ms]、[10-50ms]、[50-100ms]、[100-200ms]、[200-500ms]、[500ms-1s]、[1-2s]、[2-3s]、[3-4s]、[4-5s]、[5-10s]、[10s-]

三、BrokerStats

在创建BrokerController时会初始化BrokerStats,并且还会周期性执行record方法,周期是一天一次,具体如下:

public boolean initialize() throws CloneNotSupportedException {
        boolean result = this.topicConfigManager.load();

        result = result && this.consumerOffsetManager.load();
        result = result && this.subscriptionGroupManager.load();
        result = result && this.consumerFilterManager.load();

        if (result) {
            try {
                this.messageStore =
                    new DefaultMessageStore(this.messageStoreConfig, this.brokerStatsManager, this.messageArrivingListener,
                        this.brokerConfig);
                if (messageStoreConfig.isEnableDLegerCommitLog()) {
                    DLedgerRoleChangeHandler roleChangeHandler = new DLedgerRoleChangeHandler(this, (DefaultMessageStore) messageStore);
                    ((DLedgerCommitLog)((DefaultMessageStore) messageStore).getCommitLog()).getdLedgerServer().getdLedgerLeaderElector().addRoleChangeHandler(roleChangeHandler);
                }
                this.brokerStats = new BrokerStats((DefaultMessageStore) this.messageStore);
                ...
            } catch (IOException e) {
                result = false;
                log.error("Failed to initialize", e);
            }
        }
        ...

        if (result) {
            ...
            final long initialDelay = UtilAll.computeNextMorningTimeMillis() - System.currentTimeMillis();
            final long period = 1000 * 60 * 60 * 24;
            this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    try {
                        BrokerController.this.getBrokerStats().record();
                    } catch (Throwable e) {
                        log.error("schedule record error.", e);
                    }
                }
            }, initialDelay, period, TimeUnit.MILLISECONDS);

			...
        }
        return result;
    }

record方法主要是在日志中输出以下信息:

yesterday put message total: xxx (其含义是昨天成功写入broker中的消息总条数)
yesterday get message total: xxx (其含义是昨天成功broker中获取消息的次数)

BrokerStats中包含的指标信息如下:

指标含义
msgPutTotalYesterdayMorning昨天成功写入broker的数据总条数
msgPutTotalTodayMorning今天成功写入broker的数据总条数
msgGetTotalYesterdayMorning昨天成功从broker获取消息的次数
msgGetTotalTodayMorning今天成功从broker获取消息的次数
public void record() {
        this.msgPutTotalYesterdayMorning = this.msgPutTotalTodayMorning;
        this.msgGetTotalYesterdayMorning = this.msgGetTotalTodayMorning;

        this.msgPutTotalTodayMorning =
            this.defaultMessageStore.getStoreStatsService().getPutMessageTimesTotal();
        this.msgGetTotalTodayMorning =
            this.defaultMessageStore.getStoreStatsService().getGetMessageTransferedMsgCount().get();

        log.info("yesterday put message total: {}", msgPutTotalTodayMorning - msgPutTotalYesterdayMorning);
        log.info("yesterday get message total: {}", msgGetTotalTodayMorning - msgGetTotalYesterdayMorning);
    }

四、RocketMQ-Exporter

在RocketMQ-Exporter中有5个定时任务来获取指标数据,定时任务的周期是每分钟一次并且是在每分钟的第15秒开始执行,定时任务的周期可以在其配置文件中进行修改。

task:
  count: 5 # num of scheduled-tasks
  collectTopicOffset:
    cron: 15 0/1 * * * ?
  collectConsumerOffset:
    cron: 15 0/1 * * * ?
  collectBrokerStatsTopic:
    cron: 15 0/1 * * * ?
  collectBrokerStats:
    cron: 15 0/1 * * * ?
  collectBrokerRuntimeStats:
    cron: 15 0/1 * * * ?

RocketMQ-Exporter中的指标及其含义具体如下:

指标名称含义
rocketmq_producer_offset记录了topic在clusterName→brokerName维度上的offset,这里的offset不是物理偏移量,而是将该topic在broker上各个messagequeue上最大的条数之和
rocketmq_topic_dlq_offset记录了死信队列在clusterName→brokerName维度上的offset,这里的offset不是物理偏移量,而是将该topic在broker上各个messagequeue上最大的条数之和
rocketmq_topic_retry_offset记录了重试队列在clusterName→brokerName维度上的offset,这里的offset不是物理偏移量,而是将该topic在broker上各个messagequeue上最大的条数之和
rocketmq_group_count记录了每个订阅关系中consumerGroup下的在线客户端数量
rocketmq_client_consume_fail_msg_count一小时内消费失败的消息条数
rocketmq_client_consume_fail_msg_tps该指标含义是消费失败TPS,statsKey形式为topic@group
rocketmq_client_consume_ok_msg_tpsconsumer在消费完消息后会对消费结果进行处理,在该处理过程中会统计consumer消费成功以及失败的消息条数,该指标含义是消费成功TPS,statsKey形式为topic@group
rocketmq_client_consume_rt该指标含义是consumer消费消息花费的时间,statsKey形式为topic@group
rocketmq_client_consumer_pull_rtconsumer在从broker端成功拉取到消息后会在consumer端统计本次拉取消息花费的时间(时间差是用拉取到消息的时间减去发送请求前的时间),statsKey形式为topic@group,该指标的含义是consumer拉取消息的响应时间
rocketmq_client_consumer_pull_tpsconsumer在从broker端成功拉取到消息后会在consumer端统计本次拉取的消息条数,statsKey形式为topic@group,该指标的含义是consumer拉取消息的TPS
rocketmq_group_retrydiff记录了group在该重试队列上brokerOffset和consumerOffset之间的差距
rocketmq_group_dlqdiff记录了group在该死信队列上brokerOffset和consumerOffset之间的差距
rocketmq_group_diff记录了group在除重试队列和死信队列外其他的topic上brokerOffset和consumerOffset之间的差距
rocketmq_consumer_offset记录了group在topic的每个broker上consumerOffset,注意这里是以brokerName为维度
rocketmq_group_get_latency_by_storetime记录了group订阅的某个topic在broker级别上的消费延迟时间
rocketmq_producer_tps每秒钟成功写入commitlog中的消息条数,维度是cluster→brokerName
rocketmq_producer_message_size每秒钟成功写入commitlog中的消息大小,维度是cluster→brokerName
rocketmq_consumer_tps每秒钟成功从broker获取的消息条数,维度是cluster→brokerName,group订阅的topic
rocketmq_consumer_message_size每秒钟成功从broker获取的消息大小,维度是cluster→brokerName,group订阅的topic
rocketmq_send_back_numsconsumer消费失败的消息成功写入SCHEDULE_TOPIC_XXXX中的消息条数,维度是cluster→brokerName
rocketmq_broker_tpsBROKER_PUT_NUMS在broker端是统计消息存储到broker端的条数,这里该指标数据是表示单位时间存储到broker端的消息条数
rocketmq_broker_qpsBROKER_GET_NUMS在broker端是统计从broker端成功获取消息的条数,这里该指标数据是表示单位时间内成功从broker端成功获取消息的条数
rocketmq_brokeruntime_msg_put_total_today_now以broker为维度,统计了消息写入commitlog的次数
rocketmq_brokeruntime_msg_gettotal_today_nowGetMessage方法中统计的,是在成功获取到数据后自增的,可以理解为从broker获取消息的次数
rocketmq_brokeruntime_msg_puttotal_todaymorning以broker为维度,统计了消息写入commitlog的次数
rocketmq_brokeruntime_msg_gettotal_todaymorningGetMessage方法中统计的,是在成功获取到数据后自增的,可以理解为从broker获取消息的次数
rocketmq_brokeruntime_msg_puttotal_yesterdaymorning以broker为维度,统计了消息写入commitlog的次数
rocketmq_brokeruntime_msg_gettotal_yesterdaymorningGetMessage方法中统计的,是在成功获取到数据后自增的,可以理解为从broker获取消息的次数
rocketmq_brokeruntime_send_threadpoolqueue_headwait_timemillssendMessageExecutor其队列中头部元素等待时间(计算方式是用现在的时间-队列头部元素创建时间)
brokerRuntimeQueryThreadPoolQueueHeadWaitTimeMillspullMessageExecutor其队列中头部元素等待时间(计算方式是用现在的时间-队列头部元素创建时间)
rocketmq_brokeruntime_pull_threadpoolqueue_headwait_timemillsqueryMessageExecutor其队列中头部元素等待时间(计算方式是用现在的时间-队列头部元素创建时间)
rocketmq_brokeruntime_query_threadpoolqueue_sizeQueryMessageProccessor队列中包含发送消息请求数
rocketmq_brokeruntime_pull_threadpoolqueue_sizePullMessageProccessor队列中包含拉取消息请求数
rocketmq_brokeruntime_send_threadpoolqueue_capacitySendMessageProccessor队列中最大能缓存的发消息请求数
rocketmq_brokeruntime_pull_threadpoolqueue_capacityPullMessageProccessor队列中最大能缓存的拉取消息请求数
rocketmq_brokeruntime_remain_howmanydata_toflushcommitlog中还有多少消息需要flush
rocketmq_brokeruntime_commitlog_minoffsetcommitlog中最小物理offset
rocketmq_brokeruntime_commitlog_maxoffsetcommitlog中最大物理offset
rocketmq_brokeruntime_dispatch_maxbuffer4.9.1版本中没有找到使用的地方
rocketmq_brokeruntime_consumequeue_disk_ratioconsumequeue的空间使用率
rocketmq_brokeruntime_commitlog_disk_ratiocommitlog的空间使用率
rocketmq_brokeruntime_pagecache_lock_time_millsRocketMQ在写消息时会加锁,这个是用来计算当前时间与上次加锁时的时间差值
rocketmq_brokeruntime_getmessage_entire_time_maxgetMessage(…)方法中会统计获取消息的花费时间,该指标记录获取消息花费时间的最大值
rocketmq_brokeruntime_putmessage_times_total该指标是对putMessageTopicTimesTotal中的的value进行求和,即表示成功追加到commitlog中的消息总条数,如果和为0,则该指标的值被赋值为1
rocketmq_brokeruntime_send_threadpool_queue_sizeSendMessageProccessor队列中包含发消息请求数
rocketmq_brokeruntime_start_accept_sendrequest_timebroker开始接收请求的时间,该字段的set方法没有被调用,初始值是0
rocketmq_brokeruntime_putmessage_entire_time_maxbroker端普通消息或者批量消息写入内存中花费的时间统计,花费的时间分13级别,同时会记录所有花费的时间中的最大值,该指标含义就是统计消息写入broker内存中花费的最大时间
rocketmq_brokeruntime_earliest_message_timestampRocketMQ中最早的一条消息的存储时间
rocketmq_brokeruntime_remain_transientstore_buffer_numbs保存异步刷新消息到磁盘之前存储消息的缓冲区大小
rocketmq_brokeruntime_query_threadpool_queue_capacityQueryMessageProccessor队列中最大能缓存的请求数
rocketmq_brokeruntime_put_message_average_size平均一次写入broker内存的消息大小
rocketmq_brokeruntime_put_message_size_total消息写入broker的总大小
rocketmq_brokeruntime_dispatch_behind_bytesRocketMQ会对每条消息构建对应的consumequeue和index,该指标表示剩余多少字节消息待创建consumequeue和index
rocketmq_brokeruntime_pmdt_0msbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[<=0ms]范围的消息条数
rocketmq_brokeruntime_pmdt_0to10msbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[0-10ms]范围的消息条数
rocketmq_brokeruntime_pmdt_10to50msbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[10-50ms]范围的消息条数
rocketmq_brokeruntime_pmdt_50to100msbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[50-100ms] 范围的消息条数
rocketmq_brokeruntime_pmdt_100to200msbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[100-200ms]范围的消息条数
rocketmq_brokeruntime_pmdt_200to500msbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[200-500ms] 范围的消息条数
rocketmq_brokeruntime_pmdt_500to1sbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[500ms-1s]范围的消息条数
rocketmq_brokeruntime_pmdt_1to2sbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[1-2s] 范围的消息条数
rocketmq_brokeruntime_pmdt_2to3sbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[2-3s]范围的消息条数
rocketmq_brokeruntime_pmdt_3to4sbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[3-4s] 范围的消息条数
rocketmq_brokeruntime_pmdt_4to5sbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[4-5s]范围的消息条数
rocketmq_brokeruntime_pmdt_5to10sbroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[5-10s] 范围的消息条数
rocketmq_brokeruntime_pmdt_10stomorebroker端普通消息或者批量消息写入内存中花费的时间统计,该指标统计的是花费时间在[10s-] 范围的消息条数
rocketmq_brokeruntime_commitlogdir_capacity_totalcommitlog目录空间总大小
rocketmq_brokeruntime_commitlogdir_capacity_freecommitlog目录还有多少空间可用
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值