TT Bigdata TT Bigdata
首页
  • 部署专题

    • 常规安装
    • 一键部署
  • 组件专题

    • 安装教程
    • 魔改分享
  • 版本专题

    • 更新说明
    • BUG临时处理
  • Ambari-Env

    • 环境准备
    • 开始使用
  • 组件编译

    • 专区—Ambari
    • 专区—Bigtop-官方组件
    • 专区—Bigtop-扩展组件
  • 报错解决

    • 专区—Ambari
    • 专区—Bigtop
  • 其他技巧

    • APT仓库增量更新
    • Maven镜像加速
    • Gradle镜像加速
    • Bower镜像加速
    • 虚拟环境思路
    • R环境安装+一键安装脚本
    • Ivy配置私有镜像仓库
    • Node.js 多版本共存方案
    • Ambari Web本地启动
    • Npm镜像加速
    • PostgreSQL快速安装
    • Temurin JDK 23快速安装
  • 成神之路

    • 专区—Ambari
    • 专区—Ambari-Metrics
    • 专区—Bigtop
  • 集成案例

    • Redis集成教学
    • Dolphin集成教学
    • Doris集成教学
    • 持续整理...
  • 核心代码

    • 各组件代码
    • 通用代码模板
  • 国产化&其他系统

    • Rocky系列
    • Ubuntu系列
  • Grafana监控方案

    • Ambari-Metrics插件
    • Infinity插件
  • 支持&共建

    • 蓝图愿景
    • 合作共建
登陆
GitHub (opens new window)

JaneTTR

数据酿造智慧,每一滴都是沉淀!
首页
  • 部署专题

    • 常规安装
    • 一键部署
  • 组件专题

    • 安装教程
    • 魔改分享
  • 版本专题

    • 更新说明
    • BUG临时处理
  • Ambari-Env

    • 环境准备
    • 开始使用
  • 组件编译

    • 专区—Ambari
    • 专区—Bigtop-官方组件
    • 专区—Bigtop-扩展组件
  • 报错解决

    • 专区—Ambari
    • 专区—Bigtop
  • 其他技巧

    • APT仓库增量更新
    • Maven镜像加速
    • Gradle镜像加速
    • Bower镜像加速
    • 虚拟环境思路
    • R环境安装+一键安装脚本
    • Ivy配置私有镜像仓库
    • Node.js 多版本共存方案
    • Ambari Web本地启动
    • Npm镜像加速
    • PostgreSQL快速安装
    • Temurin JDK 23快速安装
  • 成神之路

    • 专区—Ambari
    • 专区—Ambari-Metrics
    • 专区—Bigtop
  • 集成案例

    • Redis集成教学
    • Dolphin集成教学
    • Doris集成教学
    • 持续整理...
  • 核心代码

    • 各组件代码
    • 通用代码模板
  • 国产化&其他系统

    • Rocky系列
    • Ubuntu系列
  • Grafana监控方案

    • Ambari-Metrics插件
    • Infinity插件
  • 支持&共建

    • 蓝图愿景
    • 合作共建
登陆
GitHub (opens new window)
  • 试读&介绍

  • Ambari-Metrics解读【简写AMS】

    • 源码下载及环境初始化
    • 项目目录及模块解读
    • AMS-Collector剖析

    • AMS-Collector表结构实战

    • AMS-Collector-元数据-接口实战

    • AMS-Collector-指标查询-接口实战

      • [/metrics] — 监控数据接口查询方法
      • [/metrics] — 请求参数概括及详解索引
      • [/metrics] — Service 代码整体逻辑概览
      • [/metrics] — metricNames 生命周期
      • [/metrics] — seriesAggregateFunc生命周期
      • [/metrics] — getUuidsForGetMetricQuery精讲
        • 一、函数定位与目标
        • 二、入参回顾:metricFunctions.keySet()
        • 三、完整源码与分支判定
        • 四、通配符场景:扫描 + SQL 拼装 + 复合 UUID
          • 1、元数据分表扫描(基础 SQL)
          • 2、WHERE 条件:根据 IN / LIKE 动态拼装
          • 3、复合 UUID 拼装(20B)
        • 五、无通配符场景:直取路径与分支
        • 六、参数 → UUID 形态映射表
      • [/metrics] — applyTopNCondition精讲
      • [/metrics] — getAggregateMetricRecords精讲
      • [/metrics] — getMetricRecords精讲
      • [/metrics] — 临时指标精讲
    • AMS-Collector-普通指标写入-接口实战

    • AMS-Collector-聚合指标写入-接口实战

  • GOD-Ambari-Metrics
  • Ambari-Metrics解读【简写AMS】
  • AMS-Collector-指标查询-接口实战
JaneTTR
2025-09-15
目录

[/metrics] — getUuidsForGetMetricQuery精讲

# 一、函数定位与目标

List<byte[]> uuids = metricMetadataManager.getUuidsForGetMetricQuery(
  metricFunctions.keySet(),
  hostnames,
  applicationId,
  instanceId,
  transientMetricNames);
1
2
3
4
5
6

提示

本函数的职责是:把“请求参数”映射为“一组待查询的 UUID”。

  • 指标 UUID:16B
  • 主机 UUID:4B
  • 复合 UUID(指标+主机):20B(=16B + 4B) 是否走通配符扫描、是否拼接主机 UUID,完全由参数与通配符决定。

# 二、入参回顾:metricFunctions.keySet()

该参数是什么?在此文解读了。

[/metrics] — metricNames 生命周期

  • 输入示例
[
 "cpu.usage",
 "cpu.usage._avg",
 "cpu.usage._max",
 "rpc.rpcdetailed.numOpen._diff._sum"
]
1
2
3
4
5
6
  • 解析后的 metricsFunctions:

  • key = cpu.usage

    • value = [ (VALUE, NONE), (AVG, NONE), (MAX, NONE) ]
  • key = rpc.rpcdetailed.numOpen

    • value = [ (SUM, DIFF) ]

因此:

metricsFunctions.keySet() = ["cpu.usage", "rpc.rpcdetailed.numOpen"]
1

后续查询只围绕“干净的指标名”开展,函数/后缀不参与元数据匹配。

# 三、完整源码与分支判定

public List<byte[]> getUuidsForGetMetricQuery(Collection<String> metricNames,
                                              List<String> hostnames,
                                              String appId,
                                              String instanceId,
                                              List<String> transientMetricNames) {

  List<byte[]> uuids = new ArrayList<>();

  boolean metricNameHasWildcard = false;
  for (String metricName : metricNames) {
    if (hasWildCard(metricName)) {
      metricNameHasWildcard = true;
      break;
    }
  }

  boolean hostNameHasWildcard = false;
  if (CollectionUtils.isNotEmpty(hostnames)) {
    for (String hostname : hostnames) {
      if (hasWildCard(hostname)) {
        hostNameHasWildcard = true;
        break;
      }
    }
  }

  if (hasWildCard(instanceId) || hasWildCard(appId) || hostNameHasWildcard || metricNameHasWildcard) {
    try {
      List<TimelineMetricMetadata> metricMetadataFromStore =
        hBaseAccessor.scanMetricMetadataForWildCardRequest(metricNames, appId, instanceId);
      List<byte[]> hostUuidsFromStore =
        hBaseAccessor.scanHostMetadataForWildCardRequest(hostnames);

      for (TimelineMetricMetadata matchedEntry : metricMetadataFromStore) {
        if (matchedEntry.getUuid() != null) {
          if (CollectionUtils.isNotEmpty(hostnames)) {
            for (byte[] hostUuidEntry : hostUuidsFromStore) {
              uuids.add(ArrayUtils.addAll(matchedEntry.getUuid(), hostUuidEntry));
            }
          } else {
            uuids.add(matchedEntry.getUuid());
          }
        } else if (isTransientMetric(matchedEntry.getMetricName(), matchedEntry.getAppId())) {
          transientMetricNames.add(matchedEntry.getMetricName());
        }
      }
      return uuids;
    } catch (SQLException e) {
      LOG.error("Unable to query metadata table to check satisfying metric keys for wildcard request : " + e);
      return uuids;
    }
  } else {

    if (CollectionUtils.isNotEmpty(hostnames)) {
      if (CollectionUtils.isNotEmpty(metricNames)) {
        // Skip transient metrics
        for (String metricName : metricNames) {
          if (isTransientMetric(metricName, appId)) {
            transientMetricNames.add(metricName);
            continue;
          }
          TimelineMetric metric = new TimelineMetric();
          metric.setMetricName(metricName);
          metric.setAppId(appId);
          metric.setInstanceId(instanceId);
          for (String hostname : hostnames) {
            metric.setHostName(hostname);
            byte[] uuid = getUuid(metric, false);
            if (uuid != null) {
              uuids.add(uuid);
            }
          }
        }
      } else {
        for (String hostname : hostnames) {
          byte[] uuid = getUuidForHostname(hostname, false);
          if (uuid != null) {
            uuids.add(uuid);
          }
        }
      }
    } else {
      for (String metricName : metricNames) {
        if (isTransientMetric(metricName, appId)) {
          transientMetricNames.add(metricName);
          continue;
        }
        TimelineClusterMetric metric = new TimelineClusterMetric(metricName, appId, instanceId, -1l);
        byte[] uuid = getUuid(metric, false);
        if (uuid != null) {
          uuids.add(uuid);
        }
      }
    }
  }

  return uuids;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

判定规则(结论先行)

  • 任一参数含通配符 → 走元数据扫描(metric 与 host 分表扫描)→ 命中后拼接成 20B 或仅取 16B;

  • 无通配符 → 走直取:

    • 传 host 且传 metric → 直接算出 20B;
    • 仅传 host → 获取 4B host UUID;
    • 仅传 metric → 获取 16B cluster UUID;
  • transient 指标 → 不取 UUID,记录到 transientMetricNames。

# 四、通配符场景:扫描 + SQL 拼装 + 复合 UUID

我们以这个请求为例:

image-20250915140643493

放到 Apifox 中:

image-20250915141513514

关键逻辑调用:

image-20250915142058208

# 1、元数据分表扫描(基础 SQL)

  • 指标元数据(有 METRIC_NAME 的场景):
SELECT METRIC_NAME, APP_ID, INSTANCE_ID, UUID
FROM METRICS_METADATA_UUID
1
2
  • 主机元数据(只关心 HOSTNAME → UUID 的映射):
SELECT HOSTNAME, UUID
FROM HOSTED_APPS_METADATA_UUID
1
2

# 2、WHERE 条件:根据 IN / LIKE 动态拼装

image-20250915174236301

指标名条件(appendMetricNameClause)核心实现:

protected boolean appendMetricNameClause(StringBuilder sb) {
  boolean appendConjunction = false;
  List<String> metricsLike = new ArrayList<>();
  List<String> metricsIn = new ArrayList<>();

  if (getMetricNames() != null) {
    for (String name : getMetricNames()) {
      if (name.contains("%")) { metricsLike.add(name); }
      else { metricsIn.add(name); }
    }

    sb.append("(");

    // IN 子句
    if (CollectionUtils.isNotEmpty(metricsIn)) {
      sb.append("METRIC_NAME");
      if (metricNamesNotCondition) { sb.append(" NOT"); }
      sb.append(" IN (");
      for (int i = 0; i < metricsIn.size(); i++) {
        sb.append("?");
        if (i < metricsIn.size() - 1) { sb.append(", "); }
      }
      sb.append(")");
      appendConjunction = true;
    }

    // IN 与 LIKE 间的 OR/AND
    if (CollectionUtils.isNotEmpty(metricsIn) && CollectionUtils.isNotEmpty(metricsLike)) {
      sb.append(metricNamesNotCondition ? " AND " : " OR ");
    }

    // LIKE 子句(可多项 OR/AND 串联)
    if (CollectionUtils.isNotEmpty(metricsLike)) {
      for (int i = 0; i < metricsLike.size(); i++) {
        sb.append("METRIC_NAME");
        if (metricNamesNotCondition) { sb.append(" NOT"); }
        sb.append(" LIKE ?");
        if (i < metricsLike.size() - 1) {
          sb.append(metricNamesNotCondition ? " AND " : " OR ");
        }
      }
      appendConjunction = true;
    }

    if (appendConjunction) { sb.append(")"); }

    // 绑定参数顺序:先 IN,后 LIKE
    metricNames.clear();
    if (CollectionUtils.isNotEmpty(metricsIn)) { metricNames.addAll(metricsIn); }
    if (CollectionUtils.isNotEmpty(metricsLike)) { metricNames.addAll(metricsLike); }
  }
  return appendConjunction;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

等价 SQL 形态(仅示意):

SELECT METRIC_NAME, APP_ID, INSTANCE_ID, UUID
FROM METRICS_METADATA_UUID
WHERE (
  METRIC_NAME [NOT] IN (?,?,?)
  [AND|OR] METRIC_NAME [NOT] LIKE ?
  [AND|OR] METRIC_NAME [NOT] LIKE ?
)
1
2
3
4
5
6
7

实际查询结果示意:

image-20250915181534393

主机条件(appendHostnameClause)核心实现:

protected boolean appendHostnameClause(StringBuilder sb, boolean appendConjunction) {
  boolean hostnameContainsRegex = false;
  if (hostnames != null) {
    for (String hostname : hostnames) {
      if (hostname.contains("%")) { hostnameContainsRegex = true; break; }
    }
  }

  StringBuilder hostnamesCondition = new StringBuilder();
  if (hostnameContainsRegex) {
    hostnamesCondition.append(" (");
    for (String hostname : getHostnames()) {
      if (hostnamesCondition.length() > 2) { hostnamesCondition.append(" OR "); }
      hostnamesCondition.append("HOSTNAME LIKE ?");
    }
    hostnamesCondition.append(")");
    appendConjunction = append(sb, appendConjunction, getHostnames(), hostnamesCondition.toString());

  } else if (CollectionUtils.isNotEmpty(hostnames)) {
    for (String hostname : getHostnames()) {
      if (hostnamesCondition.length() > 0) { hostnamesCondition.append(" ,"); }
      else { hostnamesCondition.append(" HOSTNAME IN ("); }
      hostnamesCondition.append('?');
    }
    hostnamesCondition.append(')');
    appendConjunction = append(sb, appendConjunction, getHostnames(), hostnamesCondition.toString());

  } else {
    appendConjunction = append(sb, appendConjunction, getHostnames(), " HOSTNAME = ?");
  }
  return appendConjunction;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

等价 SQL 形态(仅示意):

SELECT HOSTNAME, UUID
FROM HOSTED_APPS_METADATA_UUID
WHERE (
  HOSTNAME [NOT] IN (?, ?, ?)
  [AND|OR] HOSTNAME [NOT] LIKE ?
  [AND|OR] HOSTNAME [NOT] LIKE ?
)
1
2
3
4
5
6
7

查询结果示意:

image-20250915181606880

# 3、复合 UUID 拼装(20B)

当同时命中“指标元数据(16B)”与“主机元数据(4B)”时,最终会将二者 addAll 拼接 为 20B:

image-20250915182207862

性能提醒

通配符越多,扫描范围越大。优先使用精确指标名与明确主机列表;必要时配合 limit/precision 控参,降低无效命中与元数据表压力。

# 五、无通配符场景:直取路径与分支

源码中的另一条主干逻辑如下(节选,已按注释归纳):

if (CollectionUtils.isNotEmpty(hostnames)) {
  if (CollectionUtils.isNotEmpty(metricNames)) {
    // metric + host → 计算出 20B
    for (String metricName : metricNames) {
      if (isTransientMetric(metricName, appId)) {
        transientMetricNames.add(metricName);
        continue;
      }
      TimelineMetric metric = new TimelineMetric();
      metric.setMetricName(metricName);
      metric.setAppId(appId);
      metric.setInstanceId(instanceId);
      for (String hostname : hostnames) {
        metric.setHostName(hostname);
        byte[] uuid = getUuid(metric, false);
        if (uuid != null) { uuids.add(uuid); }
      }
    }
  } else {
    // 只有 host → 取 4B host UUID
    for (String hostname : hostnames) {
      byte[] uuid = getUuidForHostname(hostname, false);
      if (uuid != null) { uuids.add(uuid); }
    }
  }
} else {
  // 只有 metric → 取 16B cluster UUID
  for (String metricName : metricNames) {
    if (isTransientMetric(metricName, appId)) {
      transientMetricNames.add(metricName);
      continue;
    }
    TimelineClusterMetric metric = new TimelineClusterMetric(metricName, appId, instanceId, -1L);
    byte[] uuid = getUuid(metric, false);
    if (uuid != null) { uuids.add(uuid); }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

直取的优势

  • 无额外扫描:不访问元数据表;
  • 定位清晰:参数 → UUID 一步到位;
  • 更稳定:规避 LIKE/OR 过多导致的执行计划不稳定。

# 六、参数 → UUID 形态映射表

场景 metricNames hostnames appId/instanceId 通配符 SQL 路径 返回 UUID 形态
A 有 有 任意 否 直取 20B(16B 指标 + 4B 主机)
B 有 无 任意 否 直取 16B(Cluster 级)
C 无 有 任意 否 直取 4B(仅主机)
D 有/无 有/无 任意 是 扫描 METRICS_METADATA_UUID / HOSTED_APPS_METADATA_UUID 命中后:20B 或 16B
E transient 任意 任意 任意 跳过 不返回 UUID,记录到 transientMetricNames

注:是否返回 20B 取决于是否传入/命中主机;仅指标时为 16B。

#Ambari#Ambari-Metrics#TimelineService#Controller#Metadata#Phoenix#HBase#UUID
[/metrics] — seriesAggregateFunc生命周期
[/metrics] — applyTopNCondition精讲

← [/metrics] — seriesAggregateFunc生命周期 [/metrics] — applyTopNCondition精讲→

最近更新
01
[/metrics/aggregated] — 聚合数据范围 检查点
09-19
02
[/metrics] — 反向分析接口参数 请求抓包
09-17
03
[/metrics] — 普通指标写入方法 POST
09-17
更多文章>
Theme by Vdoing | Copyright © 2017-2025 JaneTTR | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式