Skip to content

可观测性体系 ​

Java 架构师 · 可观测与工程化

可观测性(Observability)指"通过系统外部输出推断内部状态的能力"——区别于监控(Monitoring,只回答"已知问题"),可观测性要能回答未知问题(为什么突然慢了)。它由三大支柱构成:指标(Metrics)、日志(Logs)、链路追踪(Traces)。它是微服务排障、容量评估与 SLO 保障的基础。本章讲清三者关系、Prometheus 拉模型与 PromQL、Grafana 可视化、SkyWalking 字节码插桩链路,以及 ELK 日志链路。

一、三大支柱:不是三选一,是互补 ​

  • Metrics(时序数值):QPS、延迟、错误率、饱和度。适合告警与趋势。维度(label)可切片:http_requests_total{app="order",status="500"}。
  • Logs(离散事件):结构化文本(JSON)。适合查原因。量大、成本高,需采样/分级。
  • Traces(调用链):一次请求跨服务的完整 span 树。适合定位瓶颈与故障点(哪个服务慢、哪个 SQL 慢)。

三者关系:Trace 定位"哪一步慢" → Metrics 看"该服务整体趋势" → Logs 看"那一步具体报错"。单一支柱都不够——只有指标不知道原因,只有日志找不到影响面,只有链路看不到长期趋势。OpenTelemetry 正把它们用同一个 TraceId 打通。

二、黄金信号与 SLO ​

Google SRE 的四大黄金信号:延迟(Latency)、流量(Traffic)、错误(Errors)、饱和度(Saturation)。RED(请求/错误/时延)是服务视角,USE(利用率/饱和度/错误)是资源视角。

SLO(Service Level Objective):如"下单接口 99.9% 请求 < 300ms"。错误预算(Error Budget) = 1 - SLO 的可容忍失败量。预算消耗过快 → 冻结发布(先稳定再上新)。可观测不是"为好看",而是"在 SLO 被打破前行动"。

三、Prometheus:拉模型(Pull)与 PromQL ​

Prometheus 采用拉模型:Server 主动按 scrape_interval 从各实例的 /metrics 端点拉取指标。与之相对的是 Pushgateway(短任务/批处理主动推)。

为什么拉模型?

  • 中心化发现:Prometheus 知道该拉谁(基于服务发现),实例无需知道监控地址。
  • 易调试:直接 curl /metrics 就能看数据。
  • 自愈判断:拉不到 = 实例挂了(天然健康检查)。
  • 缺点:网络需 Server 能访问实例(跨网络/短生命周期任务需 Pushgateway 中转)。

Micrometer 暴露指标(Java 标准):

java
@Bean
MeterRegistryCustomizer<MeterRegistry> cfg() {
    return r -> r.config().commonTags("app", "order-service"); // 统一标签
}
// 业务埋点
Counter.builder("order.create.total").register(registry).increment();
Timer.builder("order.latency").register(registry).record(duration);

PromQL(核心查询语言):

promql
# 近5分钟 QPS
rate(http_requests_total[5m])
# 错误率
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
# P99 延迟(histogram)
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# 按实例饱和度高负载
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.2

Grafana 负责把这些查询变成看板(Dashboard),并把阈值告警接入 Alertmanager(分级:page/warning,避免告警疲劳)。

四、指标采集栈 ​

五、SkyWalking:字节码插桩的无侵入链路 ​

SkyWalking 用 Java Agent(字节码增强,-javaagent) 在类加载时织入埋点,无需改业务代码即可采集:HTTP/gRPC/Dubbo/DB/JMQ 的 span,并自动绘制服务拓扑图与调用链。原理是 Instrumentation + 字节码改写(ASM/ByteBuddy),在方法入口/出口插入 span 的开始/结束,通过 TraceId 串联跨线程/跨进程。

为什么选字节码插桩而非手动埋点:

  • 无侵入:业务零改动,接入成本低,覆盖全(连第三方 SDK 内部也可见)。
  • 一致性强:统一协议,不会有"某服务忘了埋"的断链。
  • 代价:少量性能开销(通常 < 5%)、需理解插件机制排查断链。

链路排查示例:用户投诉"下单慢" → 在 SkyWalking 查该 TraceId → 看到 Gateway(2ms) → Order(80ms) → Inventory(5ms) → DB(70ms),一眼定位是 DB 慢,再看该 span 的日志定位慢 SQL。

六、ELK:日志检索链路 ​

ELK = Elasticsearch(存储/检索)+ Logstash/Filebeat(采集/解析)+ Kibana(可视化)。结构化日志(JSON,带 traceId、level、app)才能高效检索。关键:所有服务日志带统一 TraceId,跨服务排障时能按 TraceId 串起完整链路。

json
{"ts":"2026-09-14T10:00:00Z","level":"ERROR","app":"order","traceId":"a1b2c3","msg":"deduct failed","orderId":"1001"}

七、OpenTelemetry:三支柱统一标准 ​

OTel 提供厂商无关的 SDK/协议,一套埋点同时产出 Metrics/Traces/Logs,后端可接 Prometheus/SkyWalking/Jaeger/ELK。避免被单一 APM 厂商锁定,是当下事实标准。

可观测性的常见陷阱

  • 埋点过多拖慢业务:高频 Counter 未采样,影响性能;只埋关键路径,或用直方图聚合。
  • 日志打印敏感信息:明文密码、手机号、证件号入日志 → 违反合规(见安全章节),必须脱敏。
  • 只监控不告警:看板漂亮但无阈值告警,故障靠用户反馈才发现。告警要分级。
  • 链路采样缺失:全量 Trace 成本高,需按"错误/慢请求优先采样",正常请求按比例采样。
  • 指标爆炸(高基数):label 用 userId 这种无限基数 → Prometheus 内存爆。label 只放有限枚举维度。

八、最佳实践 ​

  • 统一接入 OpenTelemetry,指标/链路/日志用同一 TraceId 打通。
  • 先定 SLO 再定告警,错误预算驱动发布节奏(预算烧完冻结发布)。
  • 结构化日志 + 统一 TraceId,跨服务串联排障;日志分级、敏感字段脱敏。
  • 看板分层:业务大盘(GMV/下单量)→ 服务细节(P99/错误率)→ 资源水位(CPU/连接)。
  • 告警分级(page/warning),避免告警疲劳导致真故障被忽略。

本节小结

可观测性三支柱互补:Metrics 看趋势/告警(Prometheus 拉模型 + PromQL 切片聚合)、Traces 定位单请求瓶颈(SkyWalking 字节码插桩无侵入串联)、Logs 查具体原因(ELK 结构化 + 统一 TraceId)。核心是"为行动而观测"——用 SLO 与错误预算把模糊的"稳不稳"变成可度量、可告警的工程指标,告警分级避免疲劳。OpenTelemetry 正统一三支柱、避免厂商锁定。落地关键:统一 TraceId 打通、结构化日志脱敏、先 SLO 后告警、采样控成本。架构师要让分布式系统的"黑盒"变"白盒",在 SLO 被打破前行动。

📚 官方文档与参考资料 ​

✏️ 练习与自测 ​

  1. 设计题:为一个下单接口定义 SLO 与错误预算,并写出触发"page 级告警"与"冻结发布"的两条规则(含 PromQL)。
  2. 排查题:用户投诉"下单慢",用 Trace + Metrics + Logs 如何逐步定位是网关、订单服务还是数据库?画出排查链路与每步要看的关键指标。
  3. 设计题:设计日志规范:哪些字段必带(traceId/level/app)、哪些信息禁止打印(密码/PII),说明理由;并给出在网关/服务层统一注入 TraceId 的方案。
  4. 辨析题:Prometheus 为什么用拉模型而非推模型?短生命周期任务(如 CronJob)如何纳入 Prometheus 监控?高基数 label(如 userId)会带来什么灾难?