可观测性三支柱
系统出问题,最怕的是「不知道哪里出了什么问题」。可观测性(Observability)就是回答这个问题的一套工程实践,通常讲三个支柱:Metrics(指标)、Logs(日志)、Traces(链路追踪)。指标回答「系统现在什么状态」,日志回答「发生了什么」,链路回答「一次请求经历了什么」。
三者各有分工:指标适合做告警和趋势分析,比如 CPU 使用率、请求延迟、错误率;日志适合查细节,比如某次异常堆栈;链路适合定位慢请求的瓶颈。生产环境三个都要有,但切入点往往从指标开始,因为指标最轻、最容易规模化。
Micrometer 就是 Java 世界做指标这一支柱的标准化库。它像一个适配层:应用只跟 Micrometer 打交道,至于指标最后送到 Prometheus、InfluxDB 还是 Datadog,通过不同的 Registry 实现切换,代码不用改。
Spring Boot 从 2.x 开始内置 Micrometer,配好依赖和端点就能暴露指标,几乎零成本接入。这也是它成为事实标准的原因:不绑架监控后端,插拔式集成。
可观测性不是监控工具的堆砌,而是「系统状态可被回答」的能力。
- MeterRegistry指标注册与聚合入口。
- Counter只增不减的计数器。
- Timer耗时与频率统计。
- Prometheus标准指标后端。
Micrometer 核心概念
Micrometer 的核心对象是 MeterRegistry。所有指标(Meter)都从它创建。指标又分几种类型:Counter(计数器)、Gauge(瞬时值)、Timer(计时)、DistributionSummary(分布统计)。每个指标可以带多个 Tag(标签),Tag 用来做维度切分。
import io.micrometer.core.instrument.*;
MeterRegistry registry = new SimpleMeterRegistry();
// 计数器:只增不减
Counter orders = Counter.builder("shop.orders")
.tag("channel", "app")
.register(registry);
orders.increment();
// 计时器:统计耗时
Timer timer = Timer.builder("shop.checkout")
.tag("pay", "wechat")
.register(registry);
timer.record(() -> doCheckout());
Tag 是 Micrometer 的灵魂。同样是订单数,带上 channel=app、channel=web 就能分别看各渠道。但 Tag 不能滥用,取值太多会让指标基数爆炸,一般建议标签取值控制在几十以内。
接入 Prometheus
Spring Boot 项目接入 Prometheus 只需加一个依赖,启动后 /actuator/prometheus 端点就自动暴露指标。
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
非 Spring 项目手动建一个 PrometheusMeterRegistry,把指标注册进去,再用 SimpleMeterRegistry 的输出格式推给 Prometheus 抓取即可。Prometheus 用 pull 模型,定期来端点抓数据,因此端点要稳定可访问。
PrometheusMeterRegistry registry = new PrometheusMeterRegistry(
PrometheusConfig.DEFAULT);
registry.scrape(); // 返回 Prometheus 文本格式的指标
采集链路通常是:应用暴露指标端点,Prometheus 定时抓取,Grafana 连接 Prometheus 画图。三件套在 K8s 环境里几乎成了标配。
常用指标类型
四类 Meter 覆盖大部分场景:Counter 统计次数(订单数、错误数),Gauge 记录瞬时值(当前线程数、队列长度),Timer 统计耗时分布(接口 P99 延迟),DistributionSummary 统计值分布(响应体大小)。
// Gauge:瞬时值
AtomicInteger queueSize = new AtomicInteger(0);
Gauge.builder("task.queue.size", queueSize,
AtomicInteger::get).register(registry);
// Summary:分布统计
DistributionSummary summary = DistributionSummary
.builder("http.response.bytes")
.register(registry);
summary.record(1024);
// Timer 带百分位
Timer httpTimer = Timer.builder("http.server.requests")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
P99 这类百分位指标对排查「尾部延迟」特别有用:平均延迟正常,但 P99 飙高,说明少数请求拖慢了整体体验。这在监控面板上一定要有。
业务埋点实践
技术指标(内存、GC、线程池)由框架自动采集,业务指标要自己埋。埋点要回答业务问题:转化率、下单成功率、支付时长、库存不足次数。
@Service
public class OrderService {
private final MeterRegistry registry;
public void createOrder(OrderDto dto) {
try {
doCreate(dto);
Counter.builder("biz.order.created")
.tag("channel", dto.getChannel())
.register(registry).increment();
} catch (Exception e) {
Counter.builder("biz.order.failed")
.register(registry).increment();
throw e;
}
}
}
埋点纪律:指标名统一前缀(biz.、http.、db.),Tag 只放低基数维度,计数器语义保持「只增」,重试逻辑别把同一事件计两次。埋点不是越多越好,先埋「业务关键路径」,再逐步补。
Grafana 与告警
| 环节 | 要点 |
|---|---|
| 数据源 | Grafana 配置 Prometheus 数据源 |
| 看板 | 导入官方 JVM / Spring Boot 看板模板 |
| 告警 | 基于 PromQL 规则触发,如错误率大于 1% |
| 通知 | 接钉钉 / 邮件 / 企业微信 webhook |
# Prometheus 告警规则示例
groups:
- name: shop.rules
rules:
- alert: HighErrorRate
expr: sum(rate(http.server.requests_total{status=~"5.."}[5m]))
/ sum(rate(http.server.requests_total[5m])) > 0.01
for: 5m
labels:
severity: warning
告警要能落地:触发、通知、认领、恢复,闭环才算数。
监控实战清单
- 先接 Prometheus + Grafana,再看板后告警。
- 技术指标自动采,业务指标手动埋。
- 指标名统一命名空间,Tag 控制基数。
- P99 延迟、错误率、饱和度三件必看。
- 告警规则可解释、可认领、不炸群。
监控的目标是提前发现问题,而不是事后解释问题。