介绍
Rocketmq-exporter
是用于监控 RocketMQ broker 端和客户端所有相关指标的系统,通过 mqAdmin
从 broker 端获取指标值后封装成 87 个 cache。
警告
过去版本曾是 87 个 concurrentHashMap,由于 Map 不会删除过期指标,所以一旦有 label 变动就会生成一个新的指标,旧的无用指标无法自动删除,久而久之造成内存溢出。而使用 Cache 结构可可以实现过期删除,且过期时间可配置。
Rocketmq-expoter
获取监控指标的流程如下图所示,Expoter 通过 MQAdminExt 向 MQ 集群请求数据,请求到的数据通过 MetricService 规范化成 Prometheus 需要的格式,然后通过 /metics 接口暴露给 Promethus。
Metric 结构
Metric
类位于 org.apache.rocketmq.expoter.model.metrics
包下,实质上是一些实体类,每个实体类代表一类指标, 总共 14 个 Metric 类。这些类作为 87 个 Cache 的 key, 用不同的 label 值进行区分。
实体类中包含了 LABEL 的三个维度:BROKER、CONSUMER、PRODUCER
-
broker 相关 metric 类有: BrokerRuntimeMetric、BrokerMetric、DLQTopicOffsetMetric、TopicPutNumMetric
-
消费者相关类有: ConsumerRuntimeConsumeFailedMsgsMetric 、ConsumerRuntimeConsumeFailedTPSMetric 、ConsumerRuntimeConsumeOKTPSMetric、ConsumerRuntimeConsumeRTMetric、ConsumerRuntimePullRTMetric、ConsumerRuntimePullTPSMetric、ConsumerCountMetric、ConsumerMetric、ConsumerTopicDiffMetric
-
生产者相关 metric 类有: ProducerMetric
Prometheus 拉取 metrics 的过程
RocketMQ-exporter
项目和 Prometheus
相当于服务器和客户端的关系,RocketMQ-exporter 项目引入了 Prometheus 的 client 包,该包中规定了需要获取的信息的类型即项目中的 MetricFamilySamples 类,Prometheus 向 expoter 请求 metrics,expoter 将信息封装成相应的类型之后返回给 Prometheus。
rocketmq-expoter 项目启动后,会获取 rocketmq 的各项 metrics 收集到 mfs 对象中,当浏览器或 Prometheus 访问相应的接口时,会通过 service 将 mfs 对象中的 samples 生成 Prometheus 所支持的格式化数据。主要包含以下步骤:
浏览器通过访问 ip:5557/metrics,会调用 RMQMetricsController 类下的 metrics 方法,其中 ip 为 rocketmq-expoter 项目运行的主机 ip
private void metrics(HttpServletResponse response) throws IOException {
StringWriter writer = new StringWriter();
metricsService.metrics(writer);
response.setHeader("Content-Type", "text/plain; version=0.0.4; charset=utf-8");
response.getOutputStream().print(writer.toString());
}
通过新建 StringWriter 对象用于收集 metrics 指标,调用 MetricsService 类中的方法 metrics 将 expoter 中提取到的指标收集到 writer 对象中,最后将收集到的指标输出到网页上。
收集到的指标格式为:
<metric name>{<label name>=<label value>, ...} <metric value>
如:
rocketmq_group_diff{group="rmq_group_test_20220114",topic="fusion_console_tst",countOfOnlineConsumers="0",msgModel="1",} 23.0
MetricCollectTask 类中的 5 个定时任务
MetricCollectTask 类中有 5 个定时任务,分别为 collectTopicOffset、collectConsumerOffset、collectBrokerStatsTopic、collectBrokerStats 和 collectBrokerRuntimeStats。用于收集消费位点信息以及 Broker 状态信息等。其 cron 表达式为:cron: 15 0/1 * * * ?,表示每分钟会收集一次。其核心功能是通过 mqAdminExt 对象从集群中获取 broker 中的信息,然后将其添加到对应的 87 个监控指标中,以 collectTopicOffset 为例:
- 首先初始化TopicList对象,通过mqAdminExt.fetchAllTopicList()方法获取到集群的所有topic信息。
TopicList topicList = null;
try { topicList = mqAdminExt.fetchAllTopicList();
} catch (Exception ex) {
log.error(String.format("collectTopicOffset-exception comes getting topic list from namesrv, address is %s",
JSON.toJSONString(mqAdminExt.getNameServerAddressList())));
return;
}
- 将 topic 加入到 topicSet 中,循环遍历每一个 topic,通过 mqAdminExt.examineTopicStats(topic)函数来检查 topic 状态。
Set < String > topicSet = topicList != null ? topicList.getTopicList() : null;
for (String topic: topicSet) {
TopicStatsTable topicStats = null;
try {
topicStats = mqAdminExt.examineTopicStats(topic);
} catch (Exception ex) {
log.error(String.format("collectTopicOffset-getting topic(%s) stats error. the namesrv address is %s",
topic,
JSON.toJSONString(mqAdminExt.getNameServerAddressList())));
continue;}
- 初始化 topic 状态 set,用于用于按 broker 划分的 topic 信息位点的 hash 表 brokerOffsetMap,以及一个用于按 broker 名字为 key 的用于存储更新时间戳的 hash 表 brokerUpdateTimestampMap。
Set<Map.Entry<MessageQueue, TopicOffset>> topicStatusEntries = topicStats.getOffsetTable().entrySet();
HashMap<String, Long> brokerOffsetMap = new HashMap<>();
HashMap<String, Long> brokerUpdateTimestampMap = new HashMap<>();
for (Map.Entry<MessageQueue, TopicOffset> topicStatusEntry : topicStatusEntries) {
MessageQueue q = topicStatusEntry.getKey();
TopicOffset offset = topicStatusEntry.getValue();
if (brokerOffsetMap.containsKey(q.getBrokerName())) {
brokerOffsetMap.put(q.getBrokerName(), brokerOffsetMap.get(q.getBrokerName()) + offset.getMaxOffset());
} else {
brokerOffsetMap.put(q.getBrokerName(), offset.getMaxOffset());
}
if (brokerUpdateTimestampMap.containsKey(q.getBrokerName())) {
if (offset.getLastUpdateTimestamp() > brokerUpdateTimestampMap.get(q.getBrokerName())) {
brokerUpdateTimestampMap.put(q.getBrokerName(), offset.getLastUpdateTimestamp());
}
} else {
brokerUpdateTimestampMap.put(q.getBrokerName(),
offset.getLastUpdateTimestamp());
}
}
- 最后通过遍历 brokerOffsetMap 中的每一项,通过调用 metricsService 获取到 metricCollector 对象,调用 RMQMetricsCollector 类中的 addTopicOffsetMetric 方法,将相应的值添加到 RMQMetricsCollector 类中 87 个指标对应的其中一个指标的 cache 中。
Set<Map.Entry<String, Long>> brokerOffsetEntries = brokerOffsetMap.entrySet();
for (Map.Entry<String, Long> brokerOffsetEntry : brokerOffsetEntries) {
metricsService.getCollector().addTopicOffsetMetric(clusterName, brokerOffsetEntry.getKey(), topic,
brokerUpdateTimestampMap.get(brokerOffsetEntry.getKey()), brokerOffsetEntry.getValue());
}
}
log.info("topic offset collection task finished...." + (System.currentTimeMillis() - start));
}
Rocketmq-exporter 收集指标流程图
快速开始
配置 application.yml
application.yml
中重要的配置主要有:
-
server.port 设置 promethus 监听 rocketmq-exporter 的端口, 默认为 5557
-
rocketmq.config.webTelemetryPath 配置 promethus 获取指标的路径,默认为 /metrics ,使用默认值即可.
-
rocketmq.config.enableACL 如果 RocketMQ 集群开启了 ACL 验证,需要配置为 true, 并在 accessKey 和 secretKey 中配置相应的 ak, sk.
-
rocketmq.config.outOfTimeSeconds 用于配置存储指标和相应的值的过期时间,若超过该时间,cache 中的 key 对应的节点没有发生写更改,则会进行删除.一般配置为 60s 即可(根据 promethus 获取指标的时间间隔进行合理配置,只要保证过期时间大于等于 promethus 收集指标的时间间隔即可)
-
task..cron 配置 exporter 从 broker 拉取指标的定时任务的时间间隔,默认值为"15 0/1 * * ?" 每分钟的 15s 拉取一次指标.
启动 exporter 项目
按照 promethus 官网配置启动
配置 promethus 的 static_config: -targets 为 exporter 的启动 IP 和端口,如: localhost:5557
访问 promethus 页面
本地启动默认为: localhost:9090 ,则可对收集到的指标值进行查看,如下图所示:
提示
为了达到更好的可视化效果,观察指标值变化趋势, promethus 搭配 grafana 效果更佳哦!
可观测性指标
可观测性指标主要包括两个大类: 服务端指标和客户端指标, 服务端指标由服务端直接生成, 客户端指标在客户端产生, 由服务端通过 rpc 请求客户端获取到. 客户端指标又可细分为生产端指标和消费端指标.所有 87 个可观测性指标及其主要含义如下: