SpringCloud---可观测性与监控:Actuator / Micrometer / Prometheus / Grafana 深度解析

基于:Spring Boot 3.5.x(示例兼容 4.x,配 Micrometer 1.15)/ Prometheus 3.x / Grafana 13.2

「应用内埋点(Actuator + Micrometer)→ 采集存储(Prometheus)→ 可视化告警(Grafana)」主线,拆解每个组件的设计思想与核心机制,剖析一条指标从代码埋点到 Grafana 面板的全链路,最后一章重点讲解 Grafana 的使用实战(数据源、面板、变量、PromQL 配方、告警体系)。


目录

  1. 可观测性问题域与整体架构
    • [1.1 三支柱与组件职责](#1.1 三支柱与组件职责)
    • [1.2 指标数据链路全景](#1.2 指标数据链路全景)
    • [1.3 组件定位表](#1.3 组件定位表)
  2. 组件核心机制
    • [2.1 Actuator:应用状态的统一暴露层](#2.1 Actuator:应用状态的统一暴露层)
    • [2.2 Micrometer:指标门面与三大指标类型 ★★★](#2.2 Micrometer:指标门面与三大指标类型 ★★★)
    • [2.3 Prometheus:拉模型与 PromQL ★★★](#2.3 Prometheus:拉模型与 PromQL ★★★)
  3. [主线流程剖析 ★★★](#主线流程剖析 ★★★)
    • [3.1 全链路五步](#3.1 全链路五步)
    • [3.2 时序与数据延迟](#3.2 时序与数据延迟)
  4. 核心类关系图
  5. [Grafana 使用详解 ★★★](#Grafana 使用详解 ★★★)
    • [5.1 安装与启动](#5.1 安装与启动)
    • [5.2 接入 Prometheus 数据源](#5.2 接入 Prometheus 数据源)
    • [5.3 创建第一个面板](#5.3 创建第一个面板)
    • [5.4 可视化类型与面板配置](#5.4 可视化类型与面板配置)
    • [5.5 模板变量 Variables](#5.5 模板变量 Variables)
    • [5.6 JVM / Spring 指标面板配方](#5.6 JVM / Spring 指标面板配方)
    • [5.7 告警体系:规则 → 触点 → 通知策略](#5.7 告警体系:规则 → 触点 → 通知策略)
    • [5.8 组织、权限与 Provisioning](#5.8 组织、权限与 Provisioning)
    • [5.9 Grafana 常见问题](#5.9 Grafana 常见问题)
  6. 扩展点与常见问题

1. 可观测性问题域与整体架构

1.1 三支柱与组件职责

微服务化后,一次请求可能跨十几个服务,"出问题在哪一环"成了核心难题。可观测性(Observability)通过三个支柱回答三类问题:

支柱 回答的问题 典型技术栈 本文覆盖
日志 Logs 发生了什么(详情) ELK / Loki / 云厂商日志 ❌ 不展开
指标 Metrics 现在是否异常(聚合数字) Actuator + Micrometer + Prometheus + Grafana ✅ 本文主线
追踪 Traces 请求路径与耗时分布 Micrometer Tracing + Zipkin / Tempo / SkyWalking 提及(见 6 章)

指标的价值:低成本、可聚合、可告警。单条日志动辄 KB 级且无法长期存储,指标一行样本十几字节,可存数年、可做阈值告警------所以监控体系(而非排障体系)总是从指标起步。

1.2 指标数据链路全景

#mermaid-svg-3MZk4Nt6bbWEPm5E{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3MZk4Nt6bbWEPm5E .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3MZk4Nt6bbWEPm5E .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3MZk4Nt6bbWEPm5E .error-icon{fill:#552222;}#mermaid-svg-3MZk4Nt6bbWEPm5E .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3MZk4Nt6bbWEPm5E .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3MZk4Nt6bbWEPm5E .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3MZk4Nt6bbWEPm5E .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3MZk4Nt6bbWEPm5E .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3MZk4Nt6bbWEPm5E .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3MZk4Nt6bbWEPm5E .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3MZk4Nt6bbWEPm5E .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3MZk4Nt6bbWEPm5E .marker.cross{stroke:#333333;}#mermaid-svg-3MZk4Nt6bbWEPm5E svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3MZk4Nt6bbWEPm5E p{margin:0;}#mermaid-svg-3MZk4Nt6bbWEPm5E .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3MZk4Nt6bbWEPm5E .cluster-label text{fill:#333;}#mermaid-svg-3MZk4Nt6bbWEPm5E .cluster-label span{color:#333;}#mermaid-svg-3MZk4Nt6bbWEPm5E .cluster-label span p{background-color:transparent;}#mermaid-svg-3MZk4Nt6bbWEPm5E .label text,#mermaid-svg-3MZk4Nt6bbWEPm5E span{fill:#333;color:#333;}#mermaid-svg-3MZk4Nt6bbWEPm5E .node rect,#mermaid-svg-3MZk4Nt6bbWEPm5E .node circle,#mermaid-svg-3MZk4Nt6bbWEPm5E .node ellipse,#mermaid-svg-3MZk4Nt6bbWEPm5E .node polygon,#mermaid-svg-3MZk4Nt6bbWEPm5E .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3MZk4Nt6bbWEPm5E .rough-node .label text,#mermaid-svg-3MZk4Nt6bbWEPm5E .node .label text,#mermaid-svg-3MZk4Nt6bbWEPm5E .image-shape .label,#mermaid-svg-3MZk4Nt6bbWEPm5E .icon-shape .label{text-anchor:middle;}#mermaid-svg-3MZk4Nt6bbWEPm5E .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3MZk4Nt6bbWEPm5E .rough-node .label,#mermaid-svg-3MZk4Nt6bbWEPm5E .node .label,#mermaid-svg-3MZk4Nt6bbWEPm5E .image-shape .label,#mermaid-svg-3MZk4Nt6bbWEPm5E .icon-shape .label{text-align:center;}#mermaid-svg-3MZk4Nt6bbWEPm5E .node.clickable{cursor:pointer;}#mermaid-svg-3MZk4Nt6bbWEPm5E .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3MZk4Nt6bbWEPm5E .arrowheadPath{fill:#333333;}#mermaid-svg-3MZk4Nt6bbWEPm5E .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3MZk4Nt6bbWEPm5E .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3MZk4Nt6bbWEPm5E .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3MZk4Nt6bbWEPm5E .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3MZk4Nt6bbWEPm5E .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3MZk4Nt6bbWEPm5E .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3MZk4Nt6bbWEPm5E .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3MZk4Nt6bbWEPm5E .cluster text{fill:#333;}#mermaid-svg-3MZk4Nt6bbWEPm5E .cluster span{color:#333;}#mermaid-svg-3MZk4Nt6bbWEPm5E div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3MZk4Nt6bbWEPm5E .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3MZk4Nt6bbWEPm5E rect.text{fill:none;stroke-width:0;}#mermaid-svg-3MZk4Nt6bbWEPm5E .icon-shape,#mermaid-svg-3MZk4Nt6bbWEPm5E .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3MZk4Nt6bbWEPm5E .icon-shape p,#mermaid-svg-3MZk4Nt6bbWEPm5E .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3MZk4Nt6bbWEPm5E .icon-shape .label rect,#mermaid-svg-3MZk4Nt6bbWEPm5E .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3MZk4Nt6bbWEPm5E .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3MZk4Nt6bbWEPm5E .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3MZk4Nt6bbWEPm5E :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 可视化层
采集存储层
应用层(每个微服务)
HTTP GET,每 15s
触发告警规则
PromQL 查询
业务代码埋点

Counter / Timer / Gauge
Actuator 端点

/actuator/prometheus
Prometheus Server

定时 scrape 拉取
TSDB 时序库
Alertmanager

告警路由/去重/静默
Grafana

Dashboard / Panel
告警通知

邮件/钉钉/飞书/Webhook

关键认知 :这条链路是拉模型------Prometheus 主动去每个应用"拉"指标,应用无需知道监控系统在哪。Grafana 不存数据,它只是 Prometheus 的"查询前端 + 渲染器"。

1.3 组件定位表

组件 角色 一句话职责
Spring Boot Actuator 暴露层 把应用内部状态(健康、指标、日志级别......)统一暴露成 HTTP/JMX 端点
Micrometer 指标门面 统一指标 API,屏蔽 Prometheus/Influx/OTLP 等后端差异("指标的 SLF4J")
Prometheus 采集 + 存储 + 查询 定时拉取指标存 TSDB,提供 PromQL 查询与告警规则评估
Grafana 可视化 + 告警 多数据源 Dashboard,把 PromQL 结果渲染成图表并触发告警

2. 组件核心机制

2.1 Actuator:应用状态的统一暴露层

Actuator 是 Spring Boot 的"内窥镜",所有能力以**端点(Endpoint)**形式暴露,默认前缀 /actuator

核心端点清单

端点 用途 端点 用途
health 健康检查(探活/就绪) metrics 指标名清单
info 应用自定义信息 prometheus Prometheus 抓取格式
beans Bean 装配报告 loggers 运行时改日志级别
conditions 自动配置条件报告 threaddump 线程转储
configprops 配置属性绑定报告 heapdump 堆转储下载
env 环境变量(可脱敏) scheduledtasks 定时任务清单

启用与暴露配置(默认只暴露 health,必须显式打开):

yaml 复制代码
management:
  endpoints:
    web:
      base-path: /actuator
      exposure:
        include: health,info,metrics,prometheus,loggers   # 按需暴露
        exclude: env,beans                                # 敏感端点显式排除
  endpoint:
    health:
      show-details: always                                # 健康详情(含组件级状态)
      probes:
        enabled: true                                     # k8s 探针需要(/health/liveness /health/readiness)
  metrics:
    tags:
      application: ${spring.application.name}             # 所有指标自动追加 application 标签

安全建议(生产必做):把 Actuator 挪到独立管理端口,不对外暴露:

yaml 复制代码
management:
  server:
    port: 8081          # 业务端口 8080 不暴露监控端点
    address: 127.0.0.1  # 仅本机/Prometheus 可达

自定义端点(把业务状态纳入监控体系):

java 复制代码
@Component
@Endpoint(id = "orderStats")
public class OrderStatsEndpoint {

    private final AtomicLong totalOrders = new AtomicLong();

    // GET /actuator/orderStats
    @ReadOperation
    public Map<String, Object> stats() {
        return Map.of(
            "totalOrders", totalOrders.get(),
            "desc", "订单统计"
        );
    }

    // POST /actuator/orderStats(可选写操作)
    @WriteOperation
    public void reset() {
        totalOrders.set(0);
    }
}

2.2 Micrometer:指标门面与三大指标类型 ★★★

设计思想:门面模式 。如同 SLF4J 之于日志,Micrometer 定义统一 API(MeterRegistry),后端实现可插拔:

类比 日志 指标
门面 API SLF4J Micrometer
后端实现 Logback / Log4j2 PrometheusMeterRegistry / InfluxMeterRegistry / OTLP Registry
切换成本 换一个依赖 换一个 Registry

三大指标类型(含五小类):

类型 语义 典型场景
Counter 只增不减的计数 订单数、错误数、请求数
Gauge 瞬时值(回调函数求值) 队列长度、连接池活跃数、CPU 使用率
Timer 时长 + 计数 + 分位数直方图 接口耗时、DB 查询耗时
DistributionSummary 值分布(非时间) 请求体大小、响应字节数
LongTaskTimer 进行中任务时长 定时任务、批量任务

业务埋点示例MeterRegistry 直接注入即可用):

java 复制代码
@Component
public class OrderMetrics {

    private final Counter orderCounter;
    private final Timer orderLatency;
    private final AtomicInteger pendingOrders = new AtomicInteger();

    public OrderMetrics(MeterRegistry registry) {
        // ① Counter:订单总数,channel 为维度标签
        orderCounter = Counter.builder("orders.total")
                .description("订单总数")
                .tag("channel", "app")
                .register(registry);

        // ② Timer:下单耗时,同时发布 P50/P95/P99 直方图分位
        orderLatency = Timer.builder("order.latency")
                .publishPercentiles(0.5, 0.95, 0.99)
                .publishPercentileHistogram()      // 供 Prometheus histogram_quantile 计算
                .register(registry);

        // ③ Gauge:待处理订单数(回调式,每次 scrape 实时求值)
        Gauge.builder("orders.pending", pendingOrders, AtomicInteger::get)
                .register(registry);
    }

    public void recordOrder(String channel, long costMillis) {
        pendingOrders.incrementAndGet();
        try {
            orderCounter.increment();
            orderLatency.record(costMillis, TimeUnit.MILLISECONDS);
        } finally {
            pendingOrders.decrementAndGet();
        }
    }
}

注解方式@Timed,需注册 TimedAspect 切面):

java 复制代码
@Configuration
public class MetricsConfig {
    @Bean
    public TimedAspect timedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);   // @Timed 生效的前提
    }
}

// 使用:方法级计时,extraTags 追加维度
@Timed(value = "order.create", histogram = true, extraTags = {"biz", "order"})
public Order create(Order order) { ... }

命名约定与标签

  • 名称用 . 分层(orders.totalhttp.server.requests),Prometheus 导出时 ._
  • **Tag(标签)**是 Prometheus 多维模型的基石:orders_total{channel="app"}{channel="wx"} 是两条独立时间序列
  • ⚠️ 基数(Cardinality)红线 :Tag 取值必须是有限小集合 。把 userIdorderId 当 Tag 会造成时间序列爆炸,直接拖垮 Prometheus 内存

Prometheus 导出格式/actuator/prometheus 的文本内容,Boot 4.x 为 OpenMetrics 格式):

复制代码
# HELP orders_total 订单总数
# TYPE orders_total counter
orders_total{application="order-service",channel="app",} 42.0

# HELP order_latency_seconds
# TYPE order_latency_seconds histogram
order_latency_seconds_bucket{application="order-service",le="0.005",} 3.0
order_latency_seconds_bucket{application="order-service",le="0.01",}  8.0
order_latency_seconds_bucket{application="order-service",le="+Inf",}  42.0
order_latency_seconds_count{application="order-service",} 42.0
order_latency_seconds_sum{application="order-service",} 21.36

关键认知 :Micrometer 的 Timer 导出为 Prometheus histogram_bucket/_count/_sum 三组序列),这是后面 P99 面板能算出来的数据基础------le 桶是预先配置的,桶粒度决定分位数精度。

2.3 Prometheus:拉模型与 PromQL ★★★

架构:拉(Pull)模型 。Prometheus Server 按 scrape_interval 周期主动拉取每个 target 的指标端点,写本地 TSDB。对比推模型(Push):应用无感知、天然带健康探测(拉不到即 target down)、适合 k8s 服务发现。

数据模型 :一条样本 = 指标名{标签集} 值 时间戳,如:

复制代码
http_server_requests_seconds_count{application="order-service",method="GET",status="200"} 1023 @1720000000

PromQL 核心语法速查

语法 含义 示例
直接查询 即时向量 jvm_memory_used_bytes{area="heap"}
rate(v[5m]) 5 分钟窗口平均每秒速率(仅限 counter rate(http_server_requests_seconds_count[5m])
irate(v[5m]) 瞬时速率(只看最后两点,毛刺敏感) 适合长周期监控快速变化
increase(v[1h]) 窗口内增量 increase(orders_total[1h]) 每小时订单数
sum by (label) 按标签聚合求和 sum by (application) (rate(...[5m]))
histogram_quantile(φ, ...) 从 histogram 桶推算分位数 histogram_quantile(0.99, sum by (le) (rate(..._bucket[5m])))
topk(k, v) 取前 k topk(5, rate(jvm_gc_pause_seconds_count[5m]))
offset 时间平移对比 rate(...[5m]) offset 1h 与一小时前对比
比较/布尔 阈值过滤 rate(...) > 10

抓取配置prometheus.yml):

yaml 复制代码
global:
  scrape_interval: 15s          # 全局抓取周期
  evaluation_interval: 15s      # 告警规则评估周期

scrape_configs:
  # 静态配置:开发/小规模
  - job_name: 'spring-boot'
    metrics_path: '/actuator/prometheus'
    scrape_interval: 10s
    static_configs:
      - targets:
          - 'order-service:8081'
          - 'stock-service:8081'

  # 服务发现:生产推荐(Consul/Nacos/k8s 自动发现新实例)
  - job_name: 'spring-boot-k8s'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: "true"
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)

告警规则alerts.yml,由 --alertmanager.url 关联):

yaml 复制代码
groups:
  - name: spring-boot-alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
          /
          sum(rate(http_server_requests_seconds_count[5m])) > 0.05
        for: 2m                      # 持续 2 分钟才触发,防抖动
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.application }} 错误率超过 5%"
          value: "当前错误率 {{ $value | humanizePercentage }}"

一句话概括:Prometheus 的完整闭环 = scrape 采集 → TSDB 存储 → PromQL 查询 → 规则评估 → Alertmanager 去重/分组/路由。


3. 主线流程剖析 ★★★

3.1 全链路五步

一个 http.server.requests 指标从代码到 Grafana 面板,经历五站:
#mermaid-svg-mEI9LxwilwaV0Hp7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mEI9LxwilwaV0Hp7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mEI9LxwilwaV0Hp7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mEI9LxwilwaV0Hp7 .error-icon{fill:#552222;}#mermaid-svg-mEI9LxwilwaV0Hp7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mEI9LxwilwaV0Hp7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mEI9LxwilwaV0Hp7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mEI9LxwilwaV0Hp7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mEI9LxwilwaV0Hp7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mEI9LxwilwaV0Hp7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mEI9LxwilwaV0Hp7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mEI9LxwilwaV0Hp7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mEI9LxwilwaV0Hp7 .marker.cross{stroke:#333333;}#mermaid-svg-mEI9LxwilwaV0Hp7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mEI9LxwilwaV0Hp7 p{margin:0;}#mermaid-svg-mEI9LxwilwaV0Hp7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mEI9LxwilwaV0Hp7 .cluster-label text{fill:#333;}#mermaid-svg-mEI9LxwilwaV0Hp7 .cluster-label span{color:#333;}#mermaid-svg-mEI9LxwilwaV0Hp7 .cluster-label span p{background-color:transparent;}#mermaid-svg-mEI9LxwilwaV0Hp7 .label text,#mermaid-svg-mEI9LxwilwaV0Hp7 span{fill:#333;color:#333;}#mermaid-svg-mEI9LxwilwaV0Hp7 .node rect,#mermaid-svg-mEI9LxwilwaV0Hp7 .node circle,#mermaid-svg-mEI9LxwilwaV0Hp7 .node ellipse,#mermaid-svg-mEI9LxwilwaV0Hp7 .node polygon,#mermaid-svg-mEI9LxwilwaV0Hp7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mEI9LxwilwaV0Hp7 .rough-node .label text,#mermaid-svg-mEI9LxwilwaV0Hp7 .node .label text,#mermaid-svg-mEI9LxwilwaV0Hp7 .image-shape .label,#mermaid-svg-mEI9LxwilwaV0Hp7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-mEI9LxwilwaV0Hp7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mEI9LxwilwaV0Hp7 .rough-node .label,#mermaid-svg-mEI9LxwilwaV0Hp7 .node .label,#mermaid-svg-mEI9LxwilwaV0Hp7 .image-shape .label,#mermaid-svg-mEI9LxwilwaV0Hp7 .icon-shape .label{text-align:center;}#mermaid-svg-mEI9LxwilwaV0Hp7 .node.clickable{cursor:pointer;}#mermaid-svg-mEI9LxwilwaV0Hp7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mEI9LxwilwaV0Hp7 .arrowheadPath{fill:#333333;}#mermaid-svg-mEI9LxwilwaV0Hp7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mEI9LxwilwaV0Hp7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mEI9LxwilwaV0Hp7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mEI9LxwilwaV0Hp7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mEI9LxwilwaV0Hp7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mEI9LxwilwaV0Hp7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mEI9LxwilwaV0Hp7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mEI9LxwilwaV0Hp7 .cluster text{fill:#333;}#mermaid-svg-mEI9LxwilwaV0Hp7 .cluster span{color:#333;}#mermaid-svg-mEI9LxwilwaV0Hp7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mEI9LxwilwaV0Hp7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mEI9LxwilwaV0Hp7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-mEI9LxwilwaV0Hp7 .icon-shape,#mermaid-svg-mEI9LxwilwaV0Hp7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mEI9LxwilwaV0Hp7 .icon-shape p,#mermaid-svg-mEI9LxwilwaV0Hp7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mEI9LxwilwaV0Hp7 .icon-shape .label rect,#mermaid-svg-mEI9LxwilwaV0Hp7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mEI9LxwilwaV0Hp7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mEI9LxwilwaV0Hp7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mEI9LxwilwaV0Hp7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ① 埋点与注册

业务代码 + Spring 自动绑定

(JVM/GC/连接池/HTTP 指标)
② Actuator 暴露

PrometheusScrapeEndpoint

输出 Prometheus 文本格式
③ Prometheus 抓取

scrape → TSDB 存储

(scrape_interval 周期)
④ Grafana 查询

PromQL → Prometheus HTTP API

(/api/v1/query_range)
⑤ 渲染与告警

Panel 图表 / Alert Rule 评估

→ 通知渠道

步骤 关键点 涉及组件
① 埋点与注册 Spring Boot 自动配置通过 MeterBinder 批量绑定 JVM/GC/Logback/数据源/HTTP 指标,业务代码只需 MeterRegistry 埋点 Micrometer
② 暴露 PrometheusMeterRegistry 把内存指标快照序列化为文本,/actuator/prometheus 每次访问实时生成 Actuator
③ 抓取 Server 对每个 target 发 GET /actuator/prometheus,解析后写 TSDB;拉取失败标记 target down Prometheus
④ 查询 Grafana 把面板表达式翻译成 PromQL,走 /api/v1/query_range?start&end&step 批量取回 Prometheus + Grafana
⑤ 渲染 结果转 DataFrame(长表)交给 Panel 渲染;告警规则由 Prometheus(或 Grafana 托管)周期评估 Grafana

3.2 时序与数据延迟

延迟来源 量级
指标到注册表 即时(业务调用时更新)
暴露延迟 0(端点实时生成快照)
抓取延迟 ≤ scrape_interval(默认 15s)
聚合窗口 [5m] 等窗口本身引入的平滑延迟
告警延迟 for: 2m 持续判定 + evaluation_interval

关键认知 :监控数据天然是滞后的(约 scrape_interval + 聚合窗口)。排查"现在这一刻"的问题要靠日志与追踪;指标回答的是"最近 5 分钟趋势如何"。面板上显示的时间戳是抓取时刻而非事件时刻,告警设计也要接受这 1~3 分钟延迟。


4. 核心类关系图

#mermaid-svg-3WLldYJaOJcwa1kO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3WLldYJaOJcwa1kO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3WLldYJaOJcwa1kO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3WLldYJaOJcwa1kO .error-icon{fill:#552222;}#mermaid-svg-3WLldYJaOJcwa1kO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3WLldYJaOJcwa1kO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3WLldYJaOJcwa1kO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3WLldYJaOJcwa1kO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3WLldYJaOJcwa1kO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3WLldYJaOJcwa1kO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3WLldYJaOJcwa1kO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3WLldYJaOJcwa1kO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3WLldYJaOJcwa1kO .marker.cross{stroke:#333333;}#mermaid-svg-3WLldYJaOJcwa1kO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3WLldYJaOJcwa1kO p{margin:0;}#mermaid-svg-3WLldYJaOJcwa1kO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3WLldYJaOJcwa1kO .cluster-label text{fill:#333;}#mermaid-svg-3WLldYJaOJcwa1kO .cluster-label span{color:#333;}#mermaid-svg-3WLldYJaOJcwa1kO .cluster-label span p{background-color:transparent;}#mermaid-svg-3WLldYJaOJcwa1kO .label text,#mermaid-svg-3WLldYJaOJcwa1kO span{fill:#333;color:#333;}#mermaid-svg-3WLldYJaOJcwa1kO .node rect,#mermaid-svg-3WLldYJaOJcwa1kO .node circle,#mermaid-svg-3WLldYJaOJcwa1kO .node ellipse,#mermaid-svg-3WLldYJaOJcwa1kO .node polygon,#mermaid-svg-3WLldYJaOJcwa1kO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3WLldYJaOJcwa1kO .rough-node .label text,#mermaid-svg-3WLldYJaOJcwa1kO .node .label text,#mermaid-svg-3WLldYJaOJcwa1kO .image-shape .label,#mermaid-svg-3WLldYJaOJcwa1kO .icon-shape .label{text-anchor:middle;}#mermaid-svg-3WLldYJaOJcwa1kO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3WLldYJaOJcwa1kO .rough-node .label,#mermaid-svg-3WLldYJaOJcwa1kO .node .label,#mermaid-svg-3WLldYJaOJcwa1kO .image-shape .label,#mermaid-svg-3WLldYJaOJcwa1kO .icon-shape .label{text-align:center;}#mermaid-svg-3WLldYJaOJcwa1kO .node.clickable{cursor:pointer;}#mermaid-svg-3WLldYJaOJcwa1kO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3WLldYJaOJcwa1kO .arrowheadPath{fill:#333333;}#mermaid-svg-3WLldYJaOJcwa1kO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3WLldYJaOJcwa1kO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3WLldYJaOJcwa1kO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3WLldYJaOJcwa1kO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3WLldYJaOJcwa1kO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3WLldYJaOJcwa1kO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3WLldYJaOJcwa1kO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3WLldYJaOJcwa1kO .cluster text{fill:#333;}#mermaid-svg-3WLldYJaOJcwa1kO .cluster span{color:#333;}#mermaid-svg-3WLldYJaOJcwa1kO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3WLldYJaOJcwa1kO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3WLldYJaOJcwa1kO rect.text{fill:none;stroke-width:0;}#mermaid-svg-3WLldYJaOJcwa1kO .icon-shape,#mermaid-svg-3WLldYJaOJcwa1kO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3WLldYJaOJcwa1kO .icon-shape p,#mermaid-svg-3WLldYJaOJcwa1kO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3WLldYJaOJcwa1kO .icon-shape .label rect,#mermaid-svg-3WLldYJaOJcwa1kO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3WLldYJaOJcwa1kO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3WLldYJaOJcwa1kO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3WLldYJaOJcwa1kO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 可视化侧(Grafana)
采集存储侧(Prometheus Server)
应用侧(spring-boot-actuator + micrometer-core)
HTTP GET /actuator/prometheus
/api/v1/query_range
MeterRegistry(接口)
PrometheusMeterRegistry
MeterBinder 自动绑定

Jvm*Metrics / LogbackMetrics

DataSourcePoolMetrics / TomcatMetrics
PrometheusScrapeEndpoint

/actuator/prometheus
CollectorRegistry

(prometheus client 原生注册表)
ScrapeManager

按 job 周期并发拉取
TSDB 时序存储
PromQL Engine
RuleManager

告警规则评估
Prometheus DataSource
Query Runner

PromQL → DataFrame
Panel 渲染引擎
Alert Rule 调度器

关键类 职责一句话
MeterRegistry 指标注册门面:查找/创建/删除 Meter
PrometheusMeterRegistry 门面的 Prometheus 实现,与原生 CollectorRegistry 桥接
MeterBinder 绑定接口:bindTo(registry),自动配置批量注册基础设施指标
PrometheusScrapeEndpoint Actuator 端点,把注册表内容输出为 Prometheus 文本格式
ScrapeManager Prometheus 抓取调度器:并发拉取、超时控制、target 健康状态
PromQL Engine 查询引擎:解析 → 执行 → 返回矩阵
Query Runner Grafana 查询执行器:PromQL 请求、响应解析为 DataFrame

5. Grafana 使用详解 ★★★

Grafana 是最终把指标变成图表与告警 的地方。它不存储业务数据,核心模型:DataSource(数据源)→ Dashboard(面板集合)→ Panel(单个图表)→ Alert(告警规则)。本章按从零到生产可用的顺序展开。

5.1 安装与启动

Docker 一条命令起步(默认端口 3000,初始账号 admin / admin,首次登录强制改密):

bash 复制代码
docker run -d --name grafana -p 3000:3000 \
  -v grafana-data:/var/lib/grafana \
  grafana/grafana:13.2.0

# 验证:http://localhost:3000
# 生产环境建议同时挂载配置目录:
# -v grafana-config:/etc/grafana/provisioning

5.2 接入 Prometheus 数据源

UI 方式Connections → Data sources → Add data source → Prometheus,只需填两项:

配置项 说明
Prometheus server URL http://prometheus:9090 Server 地址(Grafana 容器视角,同 docker 网络用容器名)
Name Prometheus 数据源标识,面板查询引用

填完点 Save & test ,出现 Successfully queried the Prometheus API 即连通。

Provisioning 方式(生产推荐,配置即代码,免 UI 手工操作):

yaml 复制代码
# /etc/grafana/provisioning/datasources/prometheus.yaml
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy              # 由 Grafana 服务端代理请求(浏览器不直连 Prometheus)
    url: http://prometheus:9090
    isDefault: true
    jsonData:
      timeInterval: 15s        # 面板最小步长,建议与 scrape_interval 一致

5.3 创建第一个面板

从零创建一个 QPS 面板(Grafana 13 的流程):

  1. Dashboards → New → New dashboard → Add visualization(或现有面板 Edit
  2. 数据源选 Prometheus
  3. 查询编辑两种模式:
    • Query builder(构建器) :Metric 下拉选 http_server_requests_seconds_count,Label filters 加 application=order-service
    • Code(代码):直接输入 PromQL,生产多用此模式:
promql 复制代码
sum(rate(http_server_requests_seconds_count{application="order-service"}[5m]))
  1. 右侧 Panel options:Title 填「QPS」,Legend 选 {``{instance}}
  2. 右上角点 Run queries 预览,图形出来即成功
  3. Apply 保存面板 → 保存 Dashboard(Dashboards → New → Save,命名 Spring Boot 概览,存入 Folder)

5.4 可视化类型与面板配置

选型一句话:看趋势用 Time series,看单值用 Stat,看占比用 Pie,看明细用 Table

可视化 适用场景 关键配置
Time series 默认折线图:QPS、CPU、内存趋势 Legend 位置/格式、轴单位、Thresholds(超阈值变色)
Stat 大数字:当前错误数、实例数 Value type、颜色阈值、Graph mode(加迷你趋势线)
Gauge 仪表盘:内存使用率、线程池占用率 Min/Max、阈值分档(绿/黄/红)
Bar gauge 横向条形:各实例 CPU 对比 Orientation、展示模式
Table 明细列表:top N 接口耗时 排序、分页
Pie chart 占比:错误分布、流量来源 Labels、Tooltip
Histogram 直方图桶分布:耗时分布 桶宽、颜色
Text 静态说明/Markdown 支持变量插值

常用面板级设置(右侧栏):

  • Legend{``{application}} - {``{instance}} 自定义图例;Sort by 按值排序找热点实例
  • Standard options → Unitpercent (0-100)s (seconds)bytes,让数值可读
  • ThresholdsAdd threshold 设红黄线,配 Thresholds mode: absolute,超阈值图形变色
  • Graph styles → Gradient/Point:填充渐变、点状显示,区分多序列

5.5 模板变量 Variables

变量让一个 Dashboard 服务所有应用 ,而非每应用一份。配置位置:Dashboard settings → Variables → Add variable

变量类型 用法 示例
Query 用数据源查询动态生成选项(最常用) label_values(http_server_requests_seconds_count, application)
Custom 手工逗号分隔固定列表 prod,staging
Constant 固定值(隐藏变量) data_source_name=Prometheus
Interval 时间步长选项 1m,5m,15m
Text box 自由输入 按用户输入过滤
yaml 复制代码
# Query 变量的完整配置(JSON Model 简化)
- name: application
  label: 应用
  type: query
  datasource: Prometheus
  query: label_values(http_server_requests_seconds_count, application)
  multi: true            # 多选
  includeAll: true       # 提供 "All" 选项
  refresh: 1             # 1=切换 dashboard 时刷新

使用:面板 PromQL 中写 $application,多选时 Grafana 自动生成正则 (order-service|stock-service)

promql 复制代码
sum(rate(http_server_requests_seconds_count{application=~"$application"}[5m]))

✅ 三个高频变量模板(照抄即用):

promql 复制代码
# 应用列表
label_values(http_server_requests_seconds_count, application)
# 实例列表(依赖 application 变量联动)
label_values(http_server_requests_seconds_count{application="$application"}, instance)
# 接口列表
label_values(http_server_requests_seconds_count{application="$application"}, uri)

5.6 JVM / Spring 指标面板配方

微服务监控必备面板 (复制 PromQL 即可出图,$application 为 5.5 节变量):

面板 PromQL 类型/单位
QPS sum(rate(http_server_requests_seconds_count{application=~"$application"}[5m])) Time series, short
P99 响应时间 histogram_quantile(0.99, sum by (le) (rate(http_server_requests_seconds_bucket{application=~"$application"}[5m]))) Time series, s
错误率 sum(rate(http_server_requests_seconds_count{application=~"$application",status=~"5.."}[5m])) / sum(rate(http_server_requests_seconds_count{application=~"$application"}[5m])) Time series, percent
JVM 堆内存 sum by (application) (jvm_memory_used_bytes{application=~"$application",area="heap"}) Time series, bytes
GC 耗时 rate(jvm_gc_pause_seconds_sum{application=~"$application"}[5m]) Time series, s
GC 次数 rate(jvm_gc_pause_seconds_count{application=~"$application"}[5m]) Time series
线程数 jvm_threads_live_threads{application=~"$application"} Time series
CPU 使用率 system_cpu_usage{application=~"$application"}(宿主)/ process_cpu_usage(进程) Time series, percent
连接池活跃数 hikaricp_connections_active{application=~"$application"} Time series
连接池等待 hikaricp_connections_pending{application=~"$application"} Stat,告警重点
Tomcat 忙碌线程 tomcat_threads_busy_threads{application=~"$application"} Time series
日志 ERROR 速率 sum(rate(logback_events_total{application=~"$application",level="error"}[5m])) Time series
启动时间 process_uptime_seconds{application=~"$application"} Stat, s

进阶技巧

promql 复制代码
# 按接口聚合的 P99 排行(Table 面板 + Instant 查询)
topk(10, histogram_quantile(0.99,
  sum by (le, uri) (rate(http_server_requests_seconds_bucket[5m]))))

# 环比对比(今天 vs 昨天同一时刻,双序列对比观察)
sum(rate(http_server_requests_seconds_count[5m])) offset 1d

# 灰度发布前后对比(instance 维度拆分)
sum by (instance) (rate(http_server_requests_seconds_count{application="order-service"}[5m]))

5.7 告警体系:规则 → 触点 → 通知策略

Grafana 告警三层模型:Alert Rule(什么算异常)→ Contact Point(发给谁/怎么发)→ Notification Policy(谁该收到什么)

① 创建告警规则Alerting → Alert rules → New alert rule

配置区 填写
Rule name 订单错误率过高
Query(表达式) 数据源 Prometheus,PromQL 同上"错误率"
Reduce / Threshold 输入 A,Last()Is above 0.05(Grafana 10+ 用 Reduce/Threshold 管道)
Set evaluation behavior Evaluate every 1m,for 2m
Labels severity=warningteam=order(供通知策略路由)
Folder / Group 规则归属目录与评估分组

② 配置联系人(Contact point)Alerting → Contact points → Add。以通用 Webhook 为例(钉钉/飞书机器人都走 webhook):

Integration Webhook
URL https://oapi.dingtalk.com/robot/send?access_token=xxx
Message 模板(Go template 语法)
复制代码
【{{ .CommonLabels.severity }}】{{ .CommonLabels.alertname }}
应用:{{ index .CommonLabels "application" }}
详情:{{ range .Alerts }}{{ .Annotations.summary }},当前值 {{ .Annotations.value }}{{ end }}

③ 通知策略Alerting → Notification policies → Default policy。默认策略接兜底触点;再按 label 细分路由(例如 severity=critical 单独发电话/短信,team=order 只发订单组群)。

④ 静默(Silences)Alerting → Silences,发版/维护窗口按标签静默 1 小时,避免告警轰炸。

关键认知:Grafana 告警与 Prometheus 自带告警(2.3 节)二选一即可。团队若已用 Alertmanager 体系,规则放 Prometheus 更内聚;否则 Grafana 托管告警(UI 友好、可带图)是更低门槛的选择。

5.8 组织、权限与 Provisioning

概念 说明
Organization 独立命名空间,数据源/Dashboard/用户完全隔离(多租户)
Team / User Role Viewer (只看)/ Editor (改面板)/ Admin(全管理)三级
Folder 权限 Dashboard 按文件夹授权,Viewer 只读指定业务域
匿名访问 auth.anonymous 开启后生成只读分享链接(大屏/汇报常用)
Provisioning 数据源与 Dashboard 均可用 yaml 代码化管理(5.2 节示例),配合 Git 入库

Grafana 13 新特性(升级参考):Suggested dashboards(按数据源推荐现成 Dashboard,带 Prometheus 兼容性评分)、Dynamic dashboards(变量驱动自适应布局)、Two-way Git Sync(Dashboard 双向 Git 同步,含恢复与审计)。

5.9 Grafana 常见问题

Q1:面板显示 No data,排查顺序?

按链路反查五步:① 数据源 Save & test 是否连通;② Prometheus Status → Targets 该 job 是否 up(App 是否存活、路径 management.endpoints.web.exposure 是否包含 prometheus);③ PromQL 在 Prometheus 自带 Graph 页能否出数据(排除 Grafana 问题);④ 指标名是否写错(Prometheus 中 ._、大小写敏感);⑤ 面板时间范围是否覆盖(Last 5 minutesLast 6 hours)。

Q2:面板数据比实际慢几分钟?

正常。延迟 = scrape_interval + PromQL 窗口平滑,见 3.2 节。不能接受就把 scrape_interval 调到 5s(代价:存储与抓取负载翻倍)。

Q3:变量下拉是空的?

检查 Query 变量表达式中的指标在数据源中是否存在;label_values() 的第一个参数必须是存在的指标名;变量依赖链(instance 依赖 application)要注意层级顺序。

Q4:告警规则评估正常但不发通知?

依次查:规则状态是否 Firing(而非 Pending/Normal)→ 规则是否挂了 Contact point → Notification policy 是否匹配该 label 路由 → Contact point 测试按钮发送是否成功(webhook 网络连通性、机器人 token 有效性)。

Q5:多实例时如何只看一个实例?

面板查询用 instance="192.168.1.10:8081" 过滤,或建立 instance 模板变量(5.5 节模板)在顶部切换。


6. 扩展点与常见问题

6.1 扩展点

扩展方式 示例
应用层 Micrometer SPI:自定义 MeterRegistry/MeterBinder 接入 Influx/OTLP/自研监控后端
应用层 Actuator 自定义端点、HealthIndicator 业务状态端点(2.1 节)、依赖组件健康
采集层 Prometheus exporters node_exporter(主机)、mysqld_exporter(MySQL)、kube-state-metrics(k8s)
采集层 服务发现 relabel k8s/Consul/Nacos 自动发现,标签重写
展示层 Grafana 插件生态 170+ 数据源、120+ 可视化面板
演进方向 OpenTelemetry(OTLP) Boot 4.x 深度集成 OTLP,指标/追踪/日志三支柱统一协议

6.2 常见问题

Q1:指标基数爆炸导致 Prometheus 内存暴涨?

Tag 只放低基数 维度(application/instance/status/uri 顶层)。排查:Prometheus → Status → TSDB Status → Top 10 label names with high cardinality,找到高基数标签后去掉或聚合(uri 含参数时用 uri 而非完整 URL)。

Q2:@Timed 注解加了没数据?

① 是否注册了 TimedAspect(2.2 节)------不加切面注解不生效;② 被代理方法是否 public 且经 Spring 代理调用(同类内部调用失效,与 @Transactional 同理);③ management.endpoints.web.exposure.include 是否含 prometheus。

Q3:P99 数值与实际感受不符?

Histogram 分位数精度取决于桶布局 。Micrometer 默认桶适合毫秒~秒级;若接口是微秒级或分布极宽,用 @Timed(histogram = true) 或自定义 publishPercentileHistogram 桶(DistributionStatisticConfig),Grafana 侧同步用 histogram_quantile 而非均值。

Q4:Prometheus 磁盘增长过快?

--storage.tsdb.retention.time=30d 设保留期;② 降低 scrape_interval;③ 检查高基数标签(Q1);④ 长期存储用 remote_write 转存(VictoriaMetrics/Thanos),本地只留热数据。

Q5:生产环境如何安全暴露 Actuator?

management.server.port 独立端口 + 内网访问(2.1 节);配合 Spring Security 对 /actuator/** 鉴权;敏感端点(env/heapdump/threaddump)不暴露。

Q6:如何把指标和调用链追踪关联起来?

Micrometer Tracing(原 Sleuth 后继)生成 traceId/spanId 时,可配置 exemplars:Prometheus 样本携带 exemplar 指向对应 trace,Grafana 面板开启 Exemplars 后,从 P99 毛刺一键跳转 Tempo/Zipkin 查看具体慢请求链路。


参考资料

相关推荐
qq_452396232 小时前
第四篇:《Prometheus 深度实战:指标设计、Exporter 开发与服务发现》
服务发现·prometheus
LlmCraft|大模型工程实践2 小时前
08. Docker Compose 一键编排:多容器应用的指挥家
java·spring cloud·eureka
橘子编程4 小时前
Java邮件发送全攻略:从入门到实战
java·开发语言·spring boot·spring·spring cloud·maven
刘某的Cloud4 小时前
k8s部署prometheus架构规则
linux·运维·kubernetes·prometheus·监控
苏渡苇5 小时前
HandlerInterceptor 和 WebFilter:两套 HTTP 请求拦截
spring boot·spring·http·spring cloud
随遇而安zx6 小时前
SpringCloud---安全(Security / OAuth2 / JWT)设计思想与深度解析
安全·spring cloud
LlmCraft|大模型工程实践6 小时前
12. Docker 日志管理与监控:ELK 日志收集 + Prometheus 性能监控
elk·docker·prometheus
骇客野人6 小时前
SpringBoot+SpringCloud高并发系统设计、搭建与落地实施方案
java·spring boot·spring cloud
随遇而安zx8 小时前
SpringCloud---Gateway vs Netflix Zuul 网关对比深度解析
spring·spring cloud·gateway