作者:陈承
引言
在数字化转型的浪潮中,应用性能监控(APM)已经成为保障系统稳定运行的重要基石。然而,传统的 APM 系统往往只能提供系统层面的性能数据,而无法深入业务核心。阿里云应用实时监控服务(ARMS)推出的自定义指标采集功能,正是为了打破这一局限,让监控真正成为业务增长的助推器。
01 为什么需要自定义指标采集?
1.1 传统 APM 系统的监控盲区
传统的 APM 系统通常关注以下系统层面的指标:
- CPU 使用率、内存占用
- 请求响应时间、吞吐量
- 数据库查询性能
- 接口调用成功率
这些指标往往是站在解决性能、错慢的角度设计的,很难直接反应业务功能的运行情况,但在实际业务场景中存在一定的监控盲区,比如下面几个场景:
场景一:电商大促
在双十一等大促活动中,系统的 CPU、内存指标可能完全正常,但如果订单转化率突然下降、支付成功率异常,这些业务层面的问题往往无法通过系统指标及时发现。
场景二:商城系统运营
对于商城系统而言,真正关键的业务指标包括:
- 实时订单数量与订单金额
- 商品库存水位
- 用户购物车转化率
- 优惠券使用率
- 退款率
这些业务指标直接反映了业务健康度和运营效率,但传统 APM 系统无法采集。
场景三:金融风控系统
金融系统需要实时监控:
- 交易笔数与金额
- 风险拦截率
- 异常交易占比
- 资金流转速度
这些指标对于业务决策至关重要,却游离于传统监控体系之外。
1.2 自定义指标的价值
引入自定义指标采集功能,能够带来以下核心价值:
✅ 业务可观测性:将业务指标与系统指标统一监控,形成完整的可观测性体系
✅ 快速问题定位:当业务异常时,可以快速关联系统指标,精准定位问题根因
✅ 数据驱动决策:实时的业务指标为运营和产品决策提供数据支撑
✅ 全链路追踪:业务指标与调用链结合,实现端到端的业务流程监控
02 Java 语言常见的指标定义框架对比
在 Java 生态系统中,有多个成熟的指标采集框架可供选择。了解它们的特点,有助于选择最适合的技术方案。
2.1 Micrometer
简介:Micrometer 是 Spring 生态的指标门面(Facade),类似于 SLF4J 之于日志。
核心特性:
- 提供统一的 API,支持多种监控系统后端(Prometheus、InfluxDB、Datadog等)
- 与 Spring Boot 深度集成
- 支持维度化指标(Tags/Labels)
代码示例:
@Autowired MeterRegistry registry; public void processOrder(Order order) { Counter.builder("orders.processed") .tag("status", order.getStatus()) .tag("channel", order.getChannel()) .register(registry) .increment(); }
优点:
- ✅ 多后端支持,一套代码适配多种监控系统
- ✅ Spring Boot 自动配置,开箱即用
- ✅ 支持维度化指标,查询灵活
- ✅ 社区活跃,持续更新
缺点:
- ❌ 强依赖 Spring 生态
- ❌ 不支持分布式追踪和日志
- ❌ 配置较为复杂
- ❌ 缺乏统一的可观测性标准
适用场景:Spring Boot 微服务应用。
2.2 Prometheus Client
简介:Prometheus Client 是 Prometheus 官方提供的 Java 客户端库,直接对接 Prometheus 生态,是 K8s 生态中众多组件暴露指标的首选方案。
核心特性:
- 原生集成:与 Prometheus 监控系统无缝对接
- Pull 模式:Prometheus 主动拉取指标,应用无需主动推送
- 强大的查询:支持 PromQL 强大的查询和聚合能力
- 丰富的生态:Grafana 可视化、AlertManager 告警
代码示例:
import io.prometheus.client.Counter; import io.prometheus.client.Gauge; import io.prometheus.client.Histogram; public class OrderMetrics { // 定义Counter:订单总数 private static final Counter orderCounter = Counter.build() .name("orders_total") .help("Total number of orders") .labelNames("status", "channel") // 定义标签 .register(); // 定义Gauge:当前处理中的订单数 private static final Gauge processingOrders = Gauge.build() .name("orders_processing") .help("Number of orders currently processing") .register(); // 定义Histogram:订单金额分布 private static final Histogram orderAmount = Histogram.build() .name("order_amount") .help("Order amount distribution") .buckets(50, 100, 200, 500, 1000, 5000) // 自定义分桶 .register(); public void processOrder(Order order) { // 订单数+1,带标签 orderCounter.labels(order.getStatus(), order.getChannel()).inc(); // 记录订单金额 orderAmount.observe(order.getAmount()); // 处理中订单+1 processingOrders.inc(); try { // 处理订单逻辑... } finally { // 处理完成,计数-1 processingOrders.dec(); } } }
Maven 依赖:
<dependency> <groupId>io.prometheus</groupId> <artifactId>simpleclient</artifactId> <version>0.16.0</version> </dependency> <!-- 用于暴露HTTP端点 --> <dependency> <groupId>io.prometheus</groupId> <artifactId>simpleclient_servlet</artifactId> <version>0.16.0</version> </dependency>
暴露指标端点(Spring Boot):
@Configuration public class PrometheusConfig { @Bean public ServletRegistrationBean<MetricsServlet> metricsServlet() { return new ServletRegistrationBean<>( new MetricsServlet(), "/metrics" ); } }
访问 http://localhost:8080/metrics\ 即可查看 Prometheus 格式的指标数据。
优点:
- ✅ Prometheus 生态原生支持,集成最佳
- ✅ Pull 模式,应用侧更简单,无需关心指标推送
- ✅ PromQL 查询功能强大,支持复杂的聚合和计算
- ✅ 与 Grafana 等可视化工具无缝对接
- ✅ 标签(Label)机制灵活,支持多维度查询
- ✅ 轻量级,性能开销小
缺点:
- ❌ 仅支持指标采集,不支持分布式追踪和日志
- ❌ Pull 模式在某些网络环境下部署复杂(需要暴露端口)
- ❌ 与非 Prometheus 监控系统集成需要额外适配
- ❌ 数据持久化依赖 Prometheus Server,客户端不存储历史数据
- ❌ 缺乏自动埋点能力,需要手动定义所有指标
适用场景:
- 已使用 Prometheus 监控体系的团队
- Kubernetes 环境的云原生应用
- 需要强大查询能力的监控场景
- 开源方案优先的项目
Prometheus vs 其他框架的独特优势:
1. Pull 模式的优势:
- 应用无需配置数据推送地址,降低耦合
- Prometheus 可以检测应用健康状态(抓取失败=应用异常)
- 便于服务发现和动态监控
2. PromQL 的强大:
# 计算订单增长率 rate(orders_total[5m]) # 按渠道分组统计 sum by(channel) (orders_total) # P99响应时间 histogram_quantile(0.99, order_amount_bucket)
3. 云原生标准:
- Kubernetes 原生支持 Prometheus 格式
- 大量开源组件提供/metrics 端点
- 监控即代码,配置版本化管理
2.3 OpenTelemetry
简介:OpenTelemetry(简称OTel)是 CNCF 的可观测性标准,整合了 OpenTracing 和 OpenCensus 两大项目。
核心特性:
- 三位一体:统一支持 Traces(追踪)、Metrics(指标)、Logs(日志)
- 厂商中立:标准化的数据模型和协议
- 自动埋点:通过 Java Agent 自动采集框架指标
- 灵活扩展:丰富的插件生态
代码示例:
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get(); Meter meter = openTelemetry.getMeter("order-service"); LongCounter orderCounter = meter.counterBuilder("orders.total") .setUnit("1") .setDescription("Total number of orders") .build(); orderCounter.add(1, Attributes.of( AttributeKey.stringKey("status"), "success", AttributeKey.stringKey("payment_method"), "alipay" ));
优点:
- ✅ 云原生标准,广泛支持
- ✅ 统一的可观测性体系(Traces + Metrics + Logs)
- ✅ 自动埋点,零代码侵入采集框架指标
- ✅ 丰富的上下文信息,支持指标与链路关联
- ✅ 社区活跃,各大云厂商支持
缺点:
- ❌ 学习曲线相对陡峭
- ❌ 需要额外的 Collector 部署
- ❌ 部分功能仍在演进中
- ❌ 配置相对复杂
适用场景:云原生微服务、分布式系统、需要统一可观测性的场景。
2.4 框架对比总结
特性 |
Micrometer |
Prometheus Client |
OpenTelemetry |
标准化程度 |
⭐⭐⭐ |
⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
多后端支持 |
✅ |
❌ (仅Prometheus) |
✅ |
分布式追踪 |
✅ |
❌ |
✅ |
自动埋点 |
部分支持 |
❌ |
✅ |
Spring集成 |
原生支持 |
需手动 |
需配置 |
学习成本 |
⭐⭐ |
⭐⭐ |
⭐⭐⭐ |
云原生支持 |
⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
社区活跃度 |
⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
查询能力 |
⭐⭐⭐ |
⭐⭐⭐⭐⭐ (PromQL) |
⭐⭐⭐⭐ |
数据模型 |
Push |
Pull |
Push/Pull |
可视化生态 |
丰富 |
优秀 (Grafana) |
丰富 |
选型建议:
- Spring Boot 应用 → Micrometer
- Prometheus 体系 → Prometheus Client
- 云原生/分布式系统 → OpenTelemetry(推荐)
- 已有 Grafana 大盘 → Prometheus Client 或 Micrometer
深度对比:Prometheus Client vs OpenTelemetry
对于云原生应用,Prometheus Client 和 OpenTelemetry 是最常见的选择,它们的核心区别:
维度 |
Prometheus Client |
OpenTelemetry |
核心定位 |
专注指标采集 |
完整可观测性方案 |
数据类型 |
仅Metrics |
Traces + Metrics + Logs |
数据传输 |
Pull模式(/metrics端点) |
Push模式(OTLP协议) |
后端绑定 |
绑定Prometheus |
支持多种后端 |
指标关联 |
通过标签 |
原生支持Trace关联 |
学习曲线 |
平缓 |
较陡 |
适用场景 |
K8s + Prometheus标准栈 |
多云/混合云/需要链路追踪 |
常见方案:
1. 纯 Prometheus 栈:Prometheus Client + Prometheus + Grafana
2. 混合方案:OpenTelemetry 采集 + Prometheus 格式导出 + Grafana
03 ARMS 自定义指标采集最佳实践
通过上面的对比可知,不同的指标定义框架均有其优缺点,ARMS 当前支持和 OpenTelemetry 深度集成,相比开源方案,极大的简化用户通过 OpenTelemetry SDK 技术栈定义指标、采集指标、配置大盘和报警的门槛,当然后续我们也有计划支持 micrometer 和 prometheus 指标的快捷采集。下面通过一个完整的电商秒杀场景,演示如何使用 ARMS 实现自定义指标采集。
3.1 场景介绍
假设我们要监控一个秒杀系统,需要实时追踪以下关键指标:
- 秒杀成功次数:按成功/失败分类统计
- 当前库存水位:实时库存数量
- 秒杀成功率:用于告警和大盘展示
3.2 第一步:添加依赖
在项目的 pom.xml中添加 OpenTelemetry 依赖:
<dependencies> <!-- OpenTelemetry API --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> </dependency> <!-- OpenTelemetry SDK (可选,用于本地测试) --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk</artifactId> </dependency> </dependencies> <!-- 统一版本管理 --> <dependencyManagement> <dependencies> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-bom</artifactId> <version>1.32.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
说明:
- ARMS Java Agent 会自动初始化 OpenTelemetry 实例
- 应用代码只需要依赖 opentelemetry-api
即可 - 无需配置 Exporter,数据自动上报到 ARMS
3.3 第二步:定义自定义指标
创建秒杀服务类,定义业务指标:
import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.LongCounter; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.ObservableLongGauge; import org.springframework.stereotype.Service; import javax.annotation.PreDestroy; import java.util.concurrent.atomic.AtomicInteger; @Service public class SeckillService { // 库存计数器(线程安全) private final AtomicInteger stock = new AtomicInteger(0); // 秒杀次数计数器 private final LongCounter seckillCounter; // 库存水位仪表盘 private final ObservableLongGauge stockGauge; // 指标维度Key private static final AttributeKey<String> RESULT_KEY = AttributeKey.stringKey("result"); private static final AttributeKey<String> PRODUCT_KEY = AttributeKey.stringKey("product_id"); public SeckillService() { // 获取ARMS Java Agent初始化的OpenTelemetry实例 OpenTelemetry openTelemetry = GlobalOpenTelemetry.get(); // 创建Meter,命名空间为"seckill" Meter meter = openTelemetry.getMeter("seckill"); // 定义Counter:记录秒杀请求次数(累计值) seckillCounter = meter.counterBuilder("product_seckill_count") .setUnit("1") .setDescription("秒杀请求次数,按成功/失败分类统计") .build(); // 定义Gauge:记录当前库存(瞬时值) stockGauge = meter.gaugeBuilder("product_current_stock") .ofLongs() .setDescription("当前商品库存数量") .buildWithCallback(measurement -> { // 每次采集时回调,上报当前库存 measurement.record(stock.get()); }); } /** * 初始化库存 */ public void initStock(int count) { stock.set(count); } /** * 秒杀商品 */ public String seckill(String productId, String userId) { int currentStock = stock.get(); // 库存不足,秒杀失败 if (currentStock <= 0) { // 记录失败次数 seckillCounter.add(1, Attributes.of( RESULT_KEY, "failed", PRODUCT_KEY, productId )); return "抢购失败,商品已售罄"; } // 尝试扣减库存(CAS操作保证线程安全) if (stock.decrementAndGet() >= 0) { // 秒杀成功 seckillCounter.add(1, Attributes.of( RESULT_KEY, "success", PRODUCT_KEY, productId )); return "恭喜!抢购成功,剩余库存:" + stock.get(); } else { // 并发情况下库存不足,回滚 stock.incrementAndGet(); seckillCounter.add(1, Attributes.of( RESULT_KEY, "failed", PRODUCT_KEY, productId )); return "抢购失败,商品已售罄"; } } /** * 销毁资源 */ @PreDestroy public void destroy() { // 关闭Gauge,停止采集 stockGauge.close(); } }
代码要点解析:
- Meter 命名:getMeter("seckill") 中的“seckill”是命名空间,后续需要在 ARMS 控制台配置
- Counter vs Gauge:
- Counter 用于累计值(只增不减),如秒杀请求总数
- Gauge 用于瞬时值(可增可减),如当前库存
- 维度设计:通过 Attributes 添加维度,可以按 result(成功/失败)、product_id(商品 ID)进行多维度分析
- 线程安全:使用 AtomicInteger保证高并发场景下的数据准确性
3.4 第三步:在 ARMS 控制台配置
1. 登录 ARMS 控制台,进入应用监控 > 应用设置 > 自定义配置
2. 开启自定义指标采集:在应用配置页面的探针采集配置模块,配置需要采集的指标
3. 配置说明:
- meters 参数填写第二步中定义的 Meter 名称(seckill)
- 支持配置多个 Meter,用逗号分隔:seckill,order,payment
3.5 第四步:查看指标数据
1. 进入 ARMS 控制台的 Prometheus 监控实例列表页面[1],并在顶部菜单栏中选择应用接入的地域。下方列表中实例类型为 Prometheus for 应用监控的实例即为当前地域所有 ARMS 应用的 APM 指标以及自定义指标的存储实例。如下图所示。
2. 单击该示例右侧共享版进入 Grafana 页面,然后单击 Explore,选择数据源为上一步对应的 Prometheus 实例名称。
3. 您可以通过 PromQL 简单查询在代码中定义的指标,如下图所示,也可以在 Grafana 中自定义展示大盘。
3.6 第五步:配置告警规则
进入 ARMS 控制台的 Prometheus 告警规则页面[2],并在顶部菜单栏中选择应用接入的地域。点击创建报警规则即可,如下图所示。
告警:库存预警
更多关于告警规则的内容参见创建 Prometheus 告警规则[3]。
3.7 最佳实践建议
✅ 指标命名规范
<namespace>_<metric_name> 例如: - order_created_count // 订单创建数 - payment_success_rate // 支付成功率 - user_login_duration // 登录耗时
✅ 维度设计原则
- 维度基数不宜过大(避免“维度爆炸”)
- 优先使用枚举类型维度(如 status: success/failed)
- 避免使用高基数维度(如 userId、orderId)
反例:
// ❌ 错误:userId基数过大 counter.add(1, Attributes.of( AttributeKey.stringKey("user_id"), userId ));
正例:
// ✅ 正确:使用枚举类型 counter.add(1, Attributes.of( AttributeKey.stringKey("user_type"), "vip" ));
✅ 性能优化
- 预先创建指标对象,避免频繁创建
- 使用批量记录 API 减少开销
- Gauge 回调函数保持轻量级
✅ 指标类型选择
场景 |
指标类型 |
示例 |
累计计数 |
Counter |
订单总数、请求总数 |
瞬时值 |
Gauge |
当前在线用户数、队列长度 |
分布统计 |
Histogram |
订单金额分布、响应时间分布 |
04 ARMS 自定义指标的核心优势
4.1 无缝集成,零成本接入
- ✅ 自动注入:使用 ARMS Java Agent,无需手动配置 OpenTelemetry
- ✅ 无侵入采集:框架指标自动采集,业务指标按需定义
- ✅ 统一上报:指标自动上报到 ARMS,无需部署 Collector
4.2 指标与链路关联
ARMS 的核心优势在于将自定义指标与分布式链路打通:
请求链路: 前端 -> 网关 -> 订单服务 -> 支付服务 ↓ 自定义指标:订单创建成功 ↓ 追踪:该订单的完整调用链
价值:当订单指标异常时,可以一键跳转到具体的调用链,快速定位问题。
4.3 丰富的可视化能力
- 📊 多维度聚合查询
- 📈 趋势对比分析
- 🎯 自定义大盘
- 🔔 灵活的告警规则
4.4 企业级特性
- 🔒 数据安全隔离
- 📦 长期数据存储
- ⚡ 高性能查询
- 🌐 跨地域部署
05 总结与展望
自定义指标采集功能是 APM 系统从“监控”走向“可观测”的关键一步。阿里云 ARMS 通过与 OpenTelemetry 标准深度集成,为用户提供了:
✨ 标准化:拥抱云原生标准,避免厂商锁定
✨ 简单化:一行配置,即开即用
✨ 可视化:指标、链路、日志三位一体
✨ 智能化:AI 异常检测,根因分析
应用场景:
- 电商系统:订单、支付、库存监控
- 金融系统:交易量、风控指标
- 游戏系统:在线人数、充值金额
- IoT 系统:设备在线率、消息量
未来展望:
ARMS 将继续深化自定义指标能力,支持更多框架和更多指标类型的自定义指标采集:
- 框架上支持 micrometer、prometheus 框架
- 指标类型上支持分位数、直方图
立即体验 ARMS 自定义指标采集功能,让监控真正服务于业务增长!
参考文档:
- ARMS 自定义指标采集官方文档
https://help.aliyun.com/zh/arms/application-monitoring/use-cases/customize-metrics-by-using-the-opentelemetry-java-sdk - OpenTelemetry 官方网站
https://opentelemetry.io/ - ARMS 产品主页
https://www.aliyun.com/product/arms
相关链接:
[1] Prometheus 监控实例列表页面
https://arms.console.aliyun.com/#/prom/cn-hangzhou
[2] Prometheus 告警规则页面
https://arms.console.aliyun.com/#/prom/alert/cn-hangzhou
[3] 创建 Prometheus 告警规则
https://help.aliyun.com/zh/arms/prometheus-monitoring/create-alert-rules-for-prometheus-instances
本文由阿里云 ARMS 团队出品