第 18 章 · Prometheus、Grafana 与 SLO 告警
本章目标:在 svc-spring-demo 全服务暴露 Prometheus 指标;搭建 Grafana 多服务大盘与 Alertmanager 告警;定义 SLI/SLO 与错误预算;将 ch09 Tracing 与 Metrics、Logs 通过 traceId 关联;对接 ch17 压测基线做回归对比;建立微服务可观测性 Runbook。
学时建议:6~7 小时(含 3 小时 Grafana + 告警联调)
前置:spring-cloud-web ch01~ch17;ch09 Micrometer Tracing;spring-boot-web ch18 Actuator/Prometheus 概念;ch12 毕业项目。
18.1 从 Tracing 到完整可观测性
ch09 解决了「慢在哪一跳」;生产还需回答「整体是否健康」「何时该扩容/回滚」。
svc-spring-demo 可观测性栈(教学虚构)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Prometheus │ │ Grafana │ │ Alertmanager│
│ 抓取指标 │──►│ 大盘+告警 │──►│ 钉钉/邮件 │
└──────▲──────┘ └─────────────┘ └─────────────┘
│ /actuator/prometheus(各服务 + Gateway)
┌──────┴──────────────────────────────────────────┐
│ gateway │ bff │ order │ product │ user │ ... │
└─────────────────────────────────────────────────┘
│ traceId 关联
┌──────▼──────┐ ┌─────────────┐
│ Zipkin │ │ Loki/ELK │
│ Span 树 │ │ 结构化日志 │
└─────────────┘ └─────────────┘
| 支柱 | ch09 已做 | 本章补充 |
|---|---|---|
| Traces | Zipkin 全链路 | 与 metrics 联动排障 |
| Metrics | Actuator 基础 | Prometheus 抓取 + SLO |
| Logs | MDC traceId | 日志标签与告警关联 |
18.2 各服务 Prometheus 配置
父工程 svc-common 或各服务统一:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
# Nacos 共享配置 observability.yml
management:
server:
port: 9090 # Actuator 独立管理端口,与业务端口 808x 隔离;下方 Prometheus 抓取 :9090 即来源于此
endpoints:
web:
exposure:
include: health,info,prometheus,metrics
endpoint:
health:
probes:
enabled: true # K8s liveness/readiness(ch19)
metrics:
tags:
application: ${spring.application.name}
env: staging
prometheus:
metrics:
export:
enabled: true
Gateway 注意:Spring Cloud Gateway 基于 WebFlux,指标名为 spring_cloud_gateway_requests_seconds 等;BFF/业务服务为 http_server_requests_seconds。
| 服务 | 关键指标 |
|---|---|
| gateway-svc | 网关 QPS、4xx/5xx、路由耗时 |
| order-svc | 下单 Counter、Feign 客户端耗时 |
| product-svc | 缓存命中率(自定义 Gauge) |
| 全服务 | jvm_memory_used_bytes、hikaricp_connections_active |
18.3 自定义业务指标(微服务)
@Component
@RequiredArgsConstructor
public class OrderMetrics {
private final MeterRegistry registry;
@PostConstruct
void init() {
Counter.builder("svc.orders.created")
.tag("service", "order-svc")
.register(registry);
}
public void recordOrderCreated(String tenantId) {
registry.counter("svc.orders.created",
"tenant", tenantId).increment();
}
}
Feign 指标(Resilience4j 开启后):
resilience4j_circuitbreaker_state— 熔断器开/关resilience4j_circuitbreaker_calls_seconds— 受保护调用耗时
BFF 聚合耗时:
@Timed(value = "bff.order.detail", percentiles = {0.5, 0.95, 0.99})
public OrderDetailVO getOrderDetail(Long orderId) { ... }
18.4 Prometheus 抓取多服务
deploy/observability/prometheus.yml:
global:
scrape_interval: 15s
scrape_configs:
- job_name: gateway
metrics_path: /actuator/prometheus
static_configs:
- targets: ['gateway-svc:9090']
labels: { service: gateway-svc }
- job_name: business-services
metrics_path: /actuator/prometheus
static_configs:
- targets:
- 'order-svc:9090'
- 'product-svc:9090'
- 'bff-svc:9090'
- 'user-svc:9090'
端口说明:抓取目标里的:9090是各业务容器的 management 端口(18.2 已配置management.server.port: 9090);下方 compose 中 Prometheus 自身 UI 也映射宿主机 9090,二者在不同容器/进程中,不冲突。
K8s 环境(ch19):改用 kubernetes_sd_configs + Pod 注解 prometheus.io/scrape: "true"。
docker-compose.observability.yml:
services:
prometheus:
image: prom/prometheus:v2.48.0
ports: ["9090:9090"]
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:10.2.0
ports: ["3000:3000"]
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-changeme}
alertmanager:
image: prom/alertmanager:v0.26.0
ports: ["9093:9093"]
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
虚构域名与密码;严禁提交真实 Webhook、SMTP 凭证。
18.5 Grafana 大盘设计
建议为 svc-spring-demo 建三层大盘:
| 大盘 | 面板 |
|---|---|
| Overview | 全服务 QPS、全局 5xx 率、P95 延迟 |
| Gateway | 路由级 QPS、429 限流次数 |
| order-svc | 下单 TPS、Feign 下游耗时、DB 连接池 |
PromQL 示例:
# 全局 5xx 率(5 分钟)
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
/
sum(rate(http_server_requests_seconds_count[5m]))
# order-svc P95
histogram_quantile(0.95,
sum(rate(http_server_requests_seconds_bucket{application="order-svc"}[5m])) by (le))
# Gateway 限流(自定义 Counter gateway.ratelimit.rejected)
sum(rate(gateway_ratelimit_rejected_total[5m]))
导入社区面板:JVM (Micrometer)、Spring Boot 2.1 Statistics(指标名兼容 Boot 3)。
与 ch17 联动:压测前后对比同一面板的 P95 曲线,回归超过 20% 触发审查。