Spring Boot: 可观测性 Observability

最后更新:2026-08-26

可观测性是生产运维的眼睛——指标看趋势、日志找原因、链路追踪定位根因,三支柱缺一不可。

1. 你将学到


2. 一个 SRE 工程师的真实故事

(1) 痛点:线上问题黑盒排查

OrderFlow 线上报错,Bob 只能看应用日志大海捞针:哪个接口慢?哪个服务出了问题?请求经过了哪些微服务?一概不知。Alice 和 Bob 经常花 4 小时排查一个线上问题,其中 3 小时在"猜"问题在哪。

(2) Observability 三支柱的解法

100%
graph LR
    A["Observability<br/>Three Pillars"] --> B["Metrics<br/>What happened?<br/>Prometheus"]
    A --> C["Logs<br/>Why happened?<br/>ELK / Loki"]
    A --> D["Traces<br/>Where happened?<br/>Jaeger / Zipkin"]

指标发现问题,日志分析原因,链路追踪定位根因。

(3) 收益

Bob 建立 Observability 体系后,Grafana 看板实时展示 P99 延迟和错误率,Prometheus 告警自动通知,OpenTelemetry 链路追踪 30 秒定位问题根因。平均故障排查时间从 4 小时降到 15 分钟。


3. Micrometer 指标收集

(1) 四种指标类型

类型 含义 只增不减 典型场景
Counter 计数器(只增) 请求总数、订单创建数
Gauge 当前值(可增可减) 当前连接数、队列长度
Timer 耗时分布 请求响应时间
DistributionSummary 分布统计 请求体大小分布

▶ 示例: 自定义业务指标

JAVA
@Service
public class OrderMetrics {

    private final Counter orderCreatedCounter;
    private final Counter orderCancelledCounter;
    private final Timer orderCreationTimer;
    private final Gauge pendingOrdersGauge;

    public OrderMetrics(MeterRegistry registry, OrderRepository orderRepo) {
        this.orderCreatedCounter = Counter.builder("orderflow.orders.created")
            .description("Total orders created")
            .tag("service", "orderflow")
            .register(registry);

        this.orderCancelledCounter = Counter.builder("orderflow.orders.cancelled")
            .description("Total orders cancelled")
            .register(registry);

        this.orderCreationTimer = Timer.builder("orderflow.orders.creation.duration")
            .description("Order creation duration")
            .publishPercentiles(0.5, 0.95, 0.99)
            .publishPercentileHistogram()
            .register(registry);

        this.pendingOrdersGauge = Gauge.builder("orderflow.orders.pending",
                orderRepo, repo -> repo.countByStatus("PENDING"))
            .description("Current pending orders count")
            .register(registry);
    }

    public void recordOrderCreated() {
        orderCreatedCounter.increment();
    }

    public Timer.Sample startCreationTimer() {
        return Timer.start(orderCreationTimer);
    }

    public void recordCreationComplete(Timer.Sample sample) {
        sample.stop(orderCreationTimer);
    }
}

输出:

TEXT 📖 仅展示
// 执行成功

▶ 示例: 在 Service 中使用指标

JAVA
@Service
public class OrderServiceImpl implements OrderService {

    private final OrderMetrics metrics;

    @Override
    @Transactional
    public Order createOrder(CreateOrderRequest request) {
        Timer.Sample sample = metrics.startCreationTimer();
        try {
            Order order = doCreateOrder(request);
            metrics.recordOrderCreated();
            return order;
        } finally {
            metrics.recordCreationComplete(sample);
        }
    }
}

输出:

TEXT 📖 仅展示
// 执行成功

4. Prometheus 集成

▶ 示例: Prometheus 依赖与配置

XML
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

输出:

TEXT 📖 仅展示
// 执行成功
YAML
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,prometheus,metrics
  metrics:
    tags:
      application: ${spring.application.name}
    export:
      prometheus:
        enabled: true

▶ 示例: Prometheus 抓取配置

YAML
# prometheus.yml
scrape_configs:
  - job_name: 'orderflow'
    metrics_path: '/actuator/prometheus'
    scrape_interval: 15s
    static_configs:
      - targets: ['orderflow:8080']

输出:

TEXT 📖 仅展示
Monitoring config loaded
Prometheus targets: 3 active
Grafana dashboard: ready

▶ 示例: PromQL 查询与告警

YAML
# PromQL queries
# P99 latency for order API
histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{uri=~"/api/v1/orders.*"}[5m])) by (le, uri))

# Error rate (5xx responses)
sum(rate(http_server_requests_seconds_total{status=~"5.."}[5m]))
/
sum(rate(http_server_requests_seconds_total[5m]))

# Orders per minute
rate(orderflow_orders_created_total[1m]) * 60

输出:

TEXT 📖 仅展示
Configuration applied successfully
YAML
# alert_rules.yml
groups:
- name: orderflow
  rules:
  - alert: HighErrorRate
    expr: |
      sum(rate(http_server_requests_seconds_total{status=~"5.."}[5m]))
      / sum(rate(http_server_requests_seconds_total[5m])) > 0.001
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: "OrderFlow error rate exceeds 0.1%"

  - alert: HighP99Latency
    expr: |
      histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le)) > 0.1
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "OrderFlow P99 latency exceeds 100ms"

5. Grafana Dashboard

(1) 关键看板指标

面板 指标 PromQL
请求速率 QPS sum(rate(http_server_requests_seconds_total[5m]))
P50/P95/P99 延迟 响应时间分布 histogram_quantile(0.99, ...)
错误率 5xx 比例 rate(...{status=~"5.."})/rate(...)
JVM 堆内存 内存使用 jvm_memory_used_bytes{area="heap"}
GC 暂停 GC 耗时 rate(jvm_gc_pause_seconds_sum[5m])
HikariCP 活跃连接 连接池使用 hikaricp_connections_active
订单创建速率 业务指标 rate(orderflow_orders_created_total[1m])

▶ 示例: Grafana Dashboard JSON 片段

JSON
{
  "dashboard": {
    "title": "OrderFlow Observability",
    "panels": [
      {
        "title": "Request Rate (QPS)",
        "type": "timeseries",
        "targets": [{
          "expr": "sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\"}[5m]))"
        }]
      },
      {
        "title": "P99 Latency",
        "type": "timeseries",
        "targets": [{
          "expr": "histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{application=\"orderflow-service\"}[5m])) by (le))"
        }]
      },
      {
        "title": "Error Rate",
        "type": "gauge",
        "targets": [{
          "expr": "sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\",status=~\"5..\"}[5m])) / sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\"}[5m]))"
        }],
        "fieldConfig": {
          "defaults": {
            "thresholds": {
              "steps": [
                {"value": 0, "color": "green"},
                {"value": 0.001, "color": "red"}
              ]
            }
          }
        }
      }
    ]
  }
}

输出:

JSON
{
  "dashboard": {
    "title": "OrderFlow Observability",
    "panels": [
      {
        "title": "Request Rate (QPS)",
        "type": "timeseries",
        "targets": [
          {
            "expr": "sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\"}[5m]))"
          }
        ]
      },
      {
        "title": "P99 Latency",
        "type": "timeseries",
        "targets": [
          {
            "expr": "histogram_quantile(0.99, sum(rate(http_server_request

6. OpenTelemetry 链路追踪

(1) 分布式追踪概念

100%
graph LR
    A["Client"] --> B["API Gateway<br/>Trace: abc123<br/>Span 1"]
    B --> C["Order Service<br/>Span 2<br/>parent: Span 1"]
    C --> D["Product Service<br/>Span 3<br/>parent: Span 2"]
    C --> E["Database<br/>Span 4<br/>parent: Span 2"]
概念 含义
Trace 一次请求的完整链路
Span 链路中的一个操作
Context Span 间传递的追踪上下文
SpanId 当前 Span 唯一标识
TraceId 整个 Trace 唯一标识

▶ 示例: OpenTelemetry 依赖

XML
<dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>

输出:

TEXT 📖 仅展示
// 执行成功
YAML
# application.yml
otel:
  exporter:
    otlp:
      endpoint: http://otel-collector:4317
  resource:
    attributes:
      service.name: orderflow-service
  traces:
    exporter: otlp

▶ 示例: 自定义 Span

JAVA
@Service
public class OrderService {

    private final Tracer tracer;

    public OrderService(Tracer tracer) {
        this.tracer = tracer;
    }

    public Order createOrder(CreateOrderRequest request) {
        Span span = tracer.spanBuilder("create-order")
            .setAttribute("product.id", request.productId())
            .setAttribute("quantity", request.quantity())
            .startSpan();

        try (Scope scope = span.makeCurrent()) {
            Order order = doCreateOrder(request);
            span.setAttribute("order.id", order.getId());
            return order;
        } catch (Exception e) {
            span.recordException(e);
            span.setStatus(StatusCode.ERROR, e.getMessage());
            throw e;
        } finally {
            span.end();
        }
    }
}

输出:

TEXT 📖 仅展示
// 执行成功

7. 综合示例:OrderFlow SLO 看板

YAML
# docker-compose.observability.yml
version: "3.9"
services:
  prometheus:
    image: prom/prometheus:latest
    ports: ["9090:9090"]
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - ./alert_rules.yml:/etc/prometheus/alert_rules.yml

  grafana:
    image: grafana/grafana:latest
    ports: ["3000:3000"]
    environment:
      GF_SECURITY_ADMIN_PASSWORD: admin
    volumes:
      - grafana-data:/var/lib/grafana

  otel-collector:
    image: otel/opentelemetry-collector:latest
    ports: ["4317:4317", "4318:4318"]
    volumes:
      - ./otel-collector-config.yml:/etc/otelcol/config.yaml

  jaeger:
    image: jaegertracing/all-in-one:latest
    ports: ["16686:16686"]

  app:
    build: .
    ports: ["8080:8080"]
    environment:
      OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
      OTEL_SERVICE_NAME: orderflow-service
      MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: health,prometheus,metrics
SLO 指标 目标 告警阈值 PromQL
P99 延迟 < 100ms > 100ms 持续 5 分钟 histogram_quantile(0.99, ...)
错误率 < 0.1% > 0.1% 持续 5 分钟 rate(5xx)/rate(all)
可用性 > 99.9% < 99.9% 持续 5 分钟 1 - rate(5xx)/rate(all)
订单吞吐 > 1000/min < 500/min 持续 10 分钟 rate(orders_created)[1m]*60

❓ 常见问题

Q Metrics 和 Logs 有什么区别?
A Metrics 是聚合数值(计数器、直方图),适合监控趋势和告警。Logs 是离散事件记录,适合分析具体原因。发现问题看 Metrics,分析原因看 Logs。
Q Prometheus 和 Grafana 的分工是什么?
A Prometheus 负责数据采集和存储(时序数据库)+ 告警规则。Grafana 负责可视化展示(Dashboard)。两者配合使用,Prometheus 是数据源,Grafana 是展示层。
Q OpenTelemetry 和 Jaeger 有什么关系?
A OpenTelemetry 是"采集"标准(SDK + API),Jaeger 是"存储和展示"后端。应用用 OTel SDK 采集 Trace 数据,发送到 Jaeger 存储和查询。
Q publishPercentiles 和 histogram_quantile 有什么区别?
A publishPercentiles 在应用端计算分位数(节省 Prometheus 存储,但多实例不精确)。histogram_quantile 在 Prometheus 端计算(支持多实例聚合,更精确)。生产环境推荐 Prometheus 端计算。
Q SLO 和 SLA 有什么区别?
A SLO(Service Level Objective)是内部目标,如 P99 < 100ms。SLA(Service Level Agreement)是对客户的承诺,附带违约赔偿。SLO 是 SLA 的基础。
Q 如何选择日志后端?
A ELK(Elasticsearch + Logstash + Kibana)功能强大但资源消耗高;Loki(Grafana 生态)轻量但查询能力有限。中小项目推荐 Loki,大型项目推荐 ELK。

📖 小节


📝 作业

  1. 基础题(难度⭐):为 OrderFlow 配置 Micrometer + Prometheus,暴露 /actuator/prometheus 端点,编写 3 个自定义业务指标(订单创建计数、下单耗时、待处理订单数)。

  2. 进阶题(难度⭐⭐):配置 Prometheus 抓取 + Grafana Dashboard,展示 HTTP QPS、P99 延迟、错误率、JVM 堆内存。编写 2 条 Prometheus 告警规则(高错误率、高延迟)。

  3. 挑战题(难度⭐⭐⭐):集成 OpenTelemetry + Jaeger,实现分布式链路追踪,在 OrderService 中添加自定义 Span,追踪完整的下单链路(Controller → Service → Repository),在 Jaeger UI 中查看 Trace 详情。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏