中 进阶
可观测性三件套#
一句话答案#
可观测性三件套:Metrics(发现问题)→ Tracing(定位链路)→ Logging(确认根因),三者协同才能在微服务环境下快速排障。
核心要点
全景概览#
| 维度 | 解决问题 | 典型工具 | 数据形态 |
|---|---|---|---|
| Metrics | ”系统是不是有问题?“ | Prometheus + Grafana | 时序数值(Counter/Gauge/Histogram) |
| Logging | ”具体报了什么错?“ | ELK(Elasticsearch + Logstash + Kibana) | 结构化文本 |
| Tracing | ”请求经过了哪些服务,哪里慢?“ | Jaeger / SkyWalking / Zipkin | 分布式调用链 |
1. Metrics —— 发现问题#
四种指标类型(Prometheus):
| 类型 | 用途 | 示例 |
|---|---|---|
| Counter | 只增不减的累加值 | 请求总数、错误总数 |
| Gauge | 可增可减的瞬时值 | 当前线程数、队列长度 |
| Histogram | 分布统计(分桶) | 请求延迟分布(P50/P99) |
| Summary | 客户端计算分位数 | 类似 Histogram,但在客户端算 |
核心监控指标(RED 方法):
- Rate:请求速率(QPS)
- Errors:错误率(5xx 比例)
- Duration:延迟分布(P50/P99/P999)
告警规则示例:
# 5 分钟内错误率 > 5% 触发告警
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
for: 2myaml2. Logging —— 确认根因#
结构化日志#
为什么用 JSON 而不是纯文本?
- 纯文本:
2026-05-25 10:30:00 ERROR OrderService - 创建订单失败: 库存不足 - 结构化:
{
"timestamp": "2026-05-25T10:30:00.123Z",
"level": "ERROR",
"service": "order-service",
"traceId": "abc123def456",
"spanId": "span789",
"class": "OrderService",
"method": "createOrder",
"userId": 12345,
"orderId": "ORD-20260525-001",
"message": "创建订单失败",
"reason": "库存不足",
"skuId": "SKU-100"
}json结构化日志能被 ES 索引,支持按任意字段搜索和聚合。
TraceID 透传#
请求进入网关 → 生成 TraceID(如 UUID)
→ 放入 HTTP Header(X-Trace-Id)
→ 服务 A 取出放入 MDC(SLF4J)
→ 服务 A 调用服务 B 时透传 Header
→ 所有服务的日志都带同一个 TraceID
→ 在 ELK 中按 TraceID 搜索 → 一次请求的全链路日志plaintextSpring Boot 实现: Spring Cloud Sleuth / Micrometer Tracing 自动注入 TraceID。
日志级别规范#
| 级别 | 用途 | 示例 |
|---|---|---|
| ERROR | 需要立即处理的错误 | 数据库连接失败、支付失败 |
| WARN | 异常但可自愈 | 重试成功、降级生效 |
| INFO | 关键业务节点 | 订单创建成功、用户登录 |
| DEBUG | 开发调试信息 | 方法入参出参(生产关闭) |
注意: 生产环境日志级别通常设 INFO,不要开 DEBUG——日志量会暴增。
ELK 架构#
应用日志 → Filebeat(采集) → Logstash(解析/过滤) → Elasticsearch(存储/索引) → Kibana(查询/可视化)plaintext3. Tracing —— 定位链路#
核心概念#
- Trace:一次完整请求的调用链,由一个 TraceID 标识
- Span:Trace 中的一个操作单元(如一次 RPC 调用、一次 DB 查询)
- Parent-Child:Span 之间的父子关系构成调用树
Trace: abc123
├── Span 1: API Gateway (50ms)
│ ├── Span 2: Order Service (30ms)
│ │ ├── Span 3: MySQL Query (5ms)
│ │ └── Span 4: Redis Get (2ms)
│ └── Span 5: Inventory Service (15ms)
│ └── Span 6: MySQL Update (8ms)plaintextJaeger vs SkyWalking#
| 维度 | Jaeger | SkyWalking |
|---|---|---|
| 侵入性 | 需要代码埋点(OpenTelemetry SDK) | Java Agent 自动埋点,零代码侵入 |
| 语言支持 | 多语言(Go/Java/Python) | 以 Java 为主,其他语言支持较弱 |
| 功能 | 专注 Tracing | Tracing + Metrics + Logging 一体化 |
| 适用 | 多语言微服务 | Java 技术栈 |
三者协同排障流程#
1. Grafana 告警:订单服务 P99 延迟从 200ms 飙升到 2s
↓
2. Jaeger/SkyWalking 查看慢 Trace:
发现 Span "Redis Get" 从 2ms 变成 800ms
↓
3. Kibana 搜索该 TraceID 的日志:
发现 Redis 连接池满,等待获取连接
↓
4. 根因:Redis 连接数配置过小 + 流量突增
↓
5. 修复:调大连接池 + 加连接池监控指标plaintext日志敏感信息脱敏#
生产日志中不能出现:
- 用户密码、Token
- 完整手机号、身份证号
- 信用卡号
脱敏方式: 日志框架层做正则替换,或用 Logback 的自定义 Converter。
面试回答(2分钟版)
可观测性三件套是Metrics、Tracing和Logging,三者协同才能在微服务环境下快速定位问题。Metrics回答”系统是不是有问题”,用Prometheus+Grafana监控RED指标(请求速率、错误率、延迟分布),发现P99飙高了就知道有问题。Tracing回答”问题在哪个环节”,通过Jaeger或SkyWalking查看分布式调用链,一个Trace由多个Span组成,能精确看到哪个服务、哪个SQL慢了。Logging回答”具体错了什么”,在ELK中按TraceID搜索,拿到那一次请求经过所有服务的完整日志,确认根因。实际排障流程就是这三步闭环:Grafana告警触发→查Trace定位慢Span→搜日志确认根因→修复→补监控。关键实践:日志必须结构化JSON并透传TraceID,否则微服务环境下日志散落各处根本没法串联。
追问与易错
追问方向:
- “Metrics 和 Tracing 有什么区别?”→ Metrics 是聚合数据(知道”慢了”),Tracing 是个体数据(知道”谁慢了”)
- “日志量太大怎么办?”→ 采样(只记录 10% 的 Trace)+ 日志分级 + 冷热分离
- “你项目中怎么做的监控?”→ 结合具体项目讲
易错点:
- ❌ 只有 Metrics 没有 Tracing——知道系统慢但不知道慢在哪
- ❌ 日志不带 TraceID——微服务环境下无法串联一次请求的完整日志
- ❌ 所有日志都打 INFO——要么日志太少看不出问题,要么太多找不到重点