Micrometer 不支持直接注册对象列表,尤其当 ID 等字段构成高基数标签时,会引发 JVM 内存暴涨和 Prometheus 存储/查询性能崩溃,应改用低基数聚合指标或外部导出方案。
micrometer 不支持直接将 list
在 Micrometer + Prometheus 架构中,MeterRegistry 的职责是收集可聚合、低基数、语义明确的观测信号(如计数、直方图、Gauge 值),而非序列化业务实体。您期望的如下形式:
my_orders{app="my-api", id="my_id_1", country="DK", status="ACTIVE"}
看似直观,实则危险:若每小时新增 10,000 个订单,id 标签将生成 10,000 个独立时间序列,单日即达 24 万+ —— 这严重违反 Prometheus 最佳实践 和 高基数警示指南。
✅ 推荐替代方案:
-
按业务维度聚合(首选)
使用 TaggedGauge 或 Counter 统计关键维度组合,例如:@Scheduled(fixedRate = 300_000) // 每5分钟刷新 public void reportOrderStats(MeterRegistry registry) { Map<immutablelist>, Long> counts = orderRepository.countByCountryAndStatus(); counts.forEach((tags, count) -> Counter.builder("orders.by_country_status") .tag("country", tags.get(0)) .tag("status", tags.get(1)) .register(registry) .increment(count) ); }</immutablelist>输出效果(安全、可聚合):
orders_by_country_status{app="my-api",country="DK",status="ACTIVE"} 127 orders_by_country_status{app="my-api",country="DK",status="ARCHIVED"} 42 orders_by_country_status{app="my-api",country="DE",status="ACTIVE"} 89 -
使用 Micrometer 的 TimeGauge 表达“当前活跃数”
若需 Grafana 展示实时订单快照(非逐条),可维护一个线程安全计数器:private final AtomicInteger activeOrderCount = new AtomicInteger(0); @PostConstruct public void initActiveCount() { TimeGauge.builder("orders.active.count", activeOrderCount, AtomicInteger::get) .description("Current number of ACTIVE orders") .register(registry); } @Scheduled(fixedRate = 60_000) public void refreshActiveCount() { activeOrderCount.set(orderRepository.countByStatus(Status.ACTIVE)); } -
完全规避 Prometheus:导出为 OpenMetrics 文本端点(进阶)
若必须保留每条订单元数据(如调试、审计),可实现自定义 @RestController 返回标准 OpenMetrics 格式(不经过 Micrometer),并配置 Prometheus static_configs 抓取:@GetMapping(value = "/actuator/orders-metrics", produces = TEXT_PLAIN_VALUE) public String exportOrdersAsMetrics() { return orderRepository.findAllActive().stream() .map(o -> String.format( "my_orders{app=\"my-api\",id=\"%s\",country=\"%s\",status=\"%s\"} 1", escape(o.getId()), escape(o.getCountry().name()), escape(o.getStatus().name()) )) .collect(Collectors.joining("\n")) + "\n"; }⚠️ 注意:此方式绕过 Micrometer 生命周期管理,需自行控制抓取频率、错误处理与 Cardinality 风险,并仅限低频、小规模场景。
? 关键总结:
- ❌ 禁止将唯一 ID、UUID、邮箱、手机号等作为 Prometheus 标签;
- ✅ 优先聚合(count/groupBy)、降维(按国家/状态/日期分区);
- ✅ 若需明细,交由 Grafana 关联数据库(如 PostgreSQL 数据源)或专用日志/事件系统(Loki、Elasticsearch);
- ✅ 所有自定义指标务必添加 description 和业务上下文标签(如 app, env, instance)。
Micrometer 是观测管道的“仪表”,不是数据仓库——让对的工具做对的事,才能构建稳定、可扩展的可观测体系。










