"你的服务挂了你怎么知道?"——企业级可观测性三支柱:Metrics + Logging + Tracing,一个都不能少
1. 可观测性全景图
1.1 三大支柱
可观测性(Observability)是现代分布式系统中比监控更高维度的概念。监控告诉你"系统挂了",可观测性让你能自己回答"为什么挂了"。业界公认的三大支柱:
| 支柱 | 英文 | 典型工具 | 核心回答的问题 | 数据特点 |
|---|---|---|---|---|
| 指标 | Metrics | Prometheus + Grafana | "系统现在什么状态?" | 数字型,周期性采集,存储成本低 |
| 日志 | Logging | ELK Stack (ES+Logstash+Kibana) | "具体发生了什么?" | 文本型,信息最丰富,存储成本高 |
| 链路追踪 | Tracing | SkyWalking / Jaeger / Zipkin | "请求经过了哪些服务?慢在哪?" | 请求级,关联Metrics和Logging |
三支柱的关系:Metrics 告诉你"CPU飙高到90%" → 你查 Tracing 发现"某接口耗时从50ms涨到5s" → 再看 Logging 发现"数据库连接池报错"。三者联动才能快速定位根因。
1.2 企业可观测性架构图
┌─────────────────────────────────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 用户服务 │ │ 订单服务 │ │ 支付服务 │ │ 库存服务 │ │ 网关服务 │ │
│ │ (User) │ │ (Order) │ │ (Pay) │ │ (Stock) │ │ (Gateway)│ │
│ │ JVM │ │ JVM │ │ JVM │ │ JVM │ │ JVM │ │
│ │ Micrometer│ │ Micrometer│ │ Micrometer│ │ Micrometer│ │ Micrometer│ │
│ │ Logback │ │ Logback │ │ Logback │ │ Logback │ │ Logback │ │
│ │ SkyWalking│ │ SkyWalking│ │ SkyWalking│ │ SkyWalking│ │ SkyWalking│ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼────────────┼────────────┼────────────┼────────────┼─────────────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 采集层 (Collector) │
│ │
│ 指标采集 日志采集 链路采集 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Prometheus │ │ Filebeat │ │ SkyWalking Agent │ │
│ │ (Pull Scrape)│ │ (tail -f) │ │ (javaagent) │ │
│ │ /metrics │ │ → Kafka/ES │ │ gRPC上报 │ │
│ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │
└─────────┼──────────────────────┼────────────────────────┼────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 存储层 (Storage) │
│ │
│ ┌──────────────┐ ┌──────────────────┐ ┌────────────────────┐ │
│ │ Prometheus │ │ Elasticsearch │ │ SkyWalking │ │
│ │ TSDB (本地) │ │ (分布式存储) │ │ Storage (ES/H2) │ │
│ │ 默认15天保留 │ │ 冷热分层,长保留 │ │ 拓扑数据+调用链 │ │
│ └──────┬───────┘ └────────┬─────────┘ └─────────┬──────────┘ │
└─────────┼───────────────────┼──────────────────────┼─────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 可视化层 (Visualization) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Grafana │ │ Kibana │ │ SkyWalking UI │ │
│ │ (指标看板) │ │ (日志分析) │ │ (拓扑图+调用链) │ │
│ │ JVM大盘/ │ │ 按TraceId │ │ 找慢调用/瓶颈 │ │
│ │ 业务大盘/接口 │ │ 搜关键字 │ │ 异常分布 │ │
│ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │
└─────────┼──────────────────────┼────────────────────────┼────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 告警层 (Alerting) │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ Alertmanager │ │
│ │ 分组 (Grouping) → 抑制 (Inhibition) → 静默 (Silence) │ │
│ └──────────────────────┬───────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 钉钉机器人 │ │ 企业微信 │ │ 飞书 │ │ PagerDuty │ │
│ │ Markdown │ │ Bot消息 │ │ 卡片消息 │ │ 电话告警 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘核心要点:可观测性不是多套系统的堆砌,而是数据联动。面试中如果能讲清楚"Metrics发现问题 → Tracing定位范围 → Logging找出根因"的闭环,就能体现出架构思维而非工具罗列。
1.3 三支柱的数据关联方式
| 关联手段 | 实现方式 | 示例 |
|---|---|---|
| TraceId | 日志中打印TraceId,Kibana直接搜索 | log.info("orderId={}, traceId={}", orderId, traceId) |
| 统一时间戳 | Metrics/Tracing/Logging都使用同一时间基准 | 以秒级精度对齐 |
| 标签/Labels | Prometheus label中包含service/instance维度和Tracing一致 | service="order-service" |
| 自定义业务ID | 业务维度关联(订单号/用户ID) | 从日志搜orderId → 查到TraceId → Tracing分析链路 |
2. Metrics — Prometheus + Grafana
2.1 Prometheus 核心概念
Prometheus 是云原生生态的事实标准监控系统,CNCF毕业项目。其核心设计理念:
| 概念 | 说明 | 类比 |
|---|---|---|
| Pull模型 | Prometheus Server主动从Target拉取指标 | 有点像"轮询体检",定期去各服务采数据 |
| 时间序列 | metric_name{label=value} + 时间戳 + 值 | 每一条指标就是一个带标签的时序数据流 |
| PromQL | Prometheus的查询语言 | 相当于"指标界的SQL",支持聚合/运算/预测 |
| Exporter | 暴露指标的适配器 | 把第三方系统(MySQL/Redis/JVM)的指标转成Prometheus格式 |
| TSDB | 时序数据库,默认本地存储15天 | 按时间分块存储,压缩率高,但一般不长期保存 |
| Service Discovery | 自动发现监控目标 | 配合K8s,Pod增删自动发现,不需要手动配 |
Pull vs Push 模型对比:
| 特性 | Pull (Prometheus) | Push (传统Zabbix/OpenTSDB) |
|---|---|---|
| 网络要求 | 监控端能连到Target | Target能连到监控端 |
| 自动发现 | K8s天然支持 | 需要注册中心或手动配置 |
| 故障感知 | Target挂了立即知道(拉不着) | 难以区分"没数据"还是"Target挂了" |
| 负载控制 | 服务端控制拉取频率 | 客户端可能突发推送 |
| 防火墙友好 | 需要打通网络 | Target主动上报更灵活 |
面试注意:Prometheus Pull模型是高频考点,要能说出两个核心优势:(1) Prometheus Server 控制节奏,不会对Target造成突发压力;(2) 天然知道Target是否存活,拉取失败直接告警。
2.2 Java应用如何暴露指标
Java应用通过 Micrometer(指标门面) + Spring Boot Actuator(端点暴露) 接入Prometheus。
架构层级:
┌─────────────────────────────────────────────────────────────────────┐
│ Java Application │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Micrometer (指标门面) │ │
│ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────────────┐│ │
│ │ │Counter│ │Gauge │ │Timer │ │Summary│ │Distribution ││ │
│ │ │(计数) │ │(瞬时值)│ │(耗时) │ │(分布) │ │ Sla ││ │
│ │ └──────┘ └──────┘ └──────┘ └──────┘ └──────────────┘│ │
│ └───────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼────────────────────────────────────┐ │
│ │ Spring Boot Actuator │ │
│ │ /actuator/prometheus → Prometheus格式指标输出 │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│ /actuator/prometheus (Pull)
▼
┌──────────────┐
│ Prometheus │
│ scrape: │
│ 30s interval│
└──────────────┘依赖配置 (pom.xml):
<!-- Micrometer 核心 + Prometheus 注册表 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<!-- Spring Boot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>application.yml 配置:
management:
endpoints:
web:
exposure:
include: health,info,prometheus,metrics
metrics:
tags:
application: ${spring.application.name}
export:
prometheus:
enabled: true
endpoint:
prometheus:
enabled: true自定义业务指标示例:
@Component
public class OrderMetrics {
private final Counter orderCreateCounter;
private final Timer orderCreateTimer;
private final Gauge orderPendingGauge;
public OrderMetrics(MeterRegistry registry) {
// 1. 计数器:统计订单创建量
this.orderCreateCounter = Counter.builder("order.create.total")
.tag("status", "success")
.description("Total number of created orders")
.register(registry);
// 2. 计时器:统计订单创建耗时
this.orderCreateTimer = Timer.builder("order.create.duration")
.tag("module", "order")
.publishPercentiles(0.5, 0.75, 0.90, 0.99)
.sla(Duration.ofMillis(50), Duration.ofMillis(100),
Duration.ofMillis(200), Duration.ofMillis(500))
.register(registry);
// 3. 仪表:当前待处理订单数量
this.orderPendingGauge = Gauge.builder("order.pending.count", this,
OrderMetrics::getPendingOrderCount)
.tag("status", "pending")
.description("Current pending order count")
.register(registry);
}
public void recordOrderCreation(long durationMs) {
orderCreateCounter.increment();
orderCreateTimer.record(Duration.ofMillis(durationMs));
}
private long getPendingOrderCount() {
// 实际从数据库或缓存查询
return 0L;
}
}Micrometer设计思想:类似于SLF4J之于日志,Micrometer是指标界的门面(Facade)。接口统一,底层实现可切换(Prometheus/Graphite/Datadog/New Relic)。面试时提到这个类比,能体现你的架构抽象思维。
2.3 四大黄金信号 (Four Golden Signals)
Google SRE圣经《Site Reliability Engineering》提出的四大指标,是监控体系设计的核心框架:
| 黄金信号 | 英文 | 说明 | Prometheus示例 |
|---|---|---|---|
| 延迟 | Latency | 请求处理耗时,区分成功和失败 | histogram_quantile(0.99, rate(http_request_duration_ms_bucket[5m])) |
| 流量 | Traffic | 系统负载,QPS/RPS/TPS | rate(http_requests_total[1m]) |
| 错误 | Errors | 请求失败率,HTTP 5xx/业务异常 | rate(http_requests_total{status=~"5.."}[1m]) / rate(http_requests_total[1m]) |
| 饱和度 | Saturation | 资源使用程度,离极限还有多远 | jvm_memory_used_bytes / jvm_memory_max_bytes |
四大信号联动分析示例:
场景:用户反馈"下单很慢"
1 延迟 (Latency)
→ 看Grafana:下单接口P99延迟从200ms飙到3s ✓ 问题确认
2 流量 (Traffic)
→ 看QPS曲线:峰值比平时高1.5倍,但不算异常 ✗ 不是流量突增
3 错误 (Errors)
→ 看错误率:接口错误率没升高,都是成功但慢 ✗ 不是代码抛异常
4 饱和度 (Saturation)
→ 看数据库连接池:active连接数接近max,等待时间长 ✓ 根因找到
→ 看GC:Full GC频繁,老年代暴涨 → 再深入分析GC日志/Memory Dump2.4 JVM指标监控
Spring Boot Actuator + Micrometer 自动暴露以下JVM指标:
| 指标类别 | 指标名 (prefix: jvm_) | 监控意义 |
|---|---|---|
| 堆内存 | jvm_memory_used_bytes{area="heap"} | 堆内存使用量,主要关注老年代增长趋势 |
| 非堆内存 | jvm_memory_used_bytes{area="nonheap"} | Metaspace/CodeCache使用量 |
| GC次数 | jvm_gc_pause_seconds_count | Young GC / Full GC频率 |
| GC耗时 | jvm_gc_pause_seconds_sum | GC暂停总时间,重点关注Full GC |
| 线程状态 | jvm_threads_states_threads | RUNNABLE/BLOCKED/WAITING 线程数 |
| 类加载 | jvm_classes_loaded_classes | 已加载类数量,排查类加载泄漏 |
| 文件描述符 | process_files_open_files | 文件句柄数,排查句柄泄漏 |
| CPU | system_cpu_usage / process_cpu_usage | 系统/进程CPU使用率 |
JVM监控核心关注点(告警阈值建议):
指标 告警阈值(严重) 说明
─────────────────────────────────────────────────────────────────
jvm_memory_used_bytes{area="heap"} >85% max 堆内存即将触发Full GC
jvm_gc_pause_seconds_sum >1000ms/次 Full GC过长,应用几乎不可用
jvm_threads_states_threads{state="blocked"} >20 线程争用严重,需排查锁
jvm_classes_loaded_classes 持续上涨 类加载泄漏(常见于动态代理/反射场景)2.5 业务指标监控
技术指标是基础,但业务指标才是老板关心的。作为后端开发,也需要关注业务层指标:
@RestController
@Slf4j
public class OrderController {
private final OrderMetrics orderMetrics;
@PostMapping("/order/create")
public Result createOrder(@RequestBody OrderCreateRequest request) {
long start = System.currentTimeMillis();
try {
// 业务逻辑...
orderService.create(request);
orderMetrics.recordOrderCreation(System.currentTimeMillis() - start);
return Result.success();
} catch (Exception e) {
// 记录失败指标
orderCreateCounter.tag("status", "fail").increment();
log.error("订单创建失败, orderId={}", request.getOrderId(), e);
throw e;
}
}
}常用业务指标设计:
| 业务域 | 指标示例 | 说明 |
|---|---|---|
| 订单 | order.create.total{status} | 订单创建量(成功/失败) |
| 支付 | payment.success.rate | 支付成功率 = 成功数/总支付数 |
| 退款 | refund.total.amount | 退款金额总量 |
| 用户 | user.register.total | 用户注册量 |
| 营销 | coupon.used.total{coupon_type} | 优惠券使用量,按类型分 |
| 风控 | risk.intercept.total{reason} | 风控拦截量,按原因分 |
| 转化 | order.conversion.funnel | 浏览→加购→下单→支付 转化漏斗 |
2.6 Grafana大盘设计
Grafana 是可观测性领域的"数据可视化瑞士军刀"。企业级Grafana大盘通常按职责分层:
大盘层级体系:
Grafana 组织
│
├── 运维侧大盘 (SRE/运维团队)
│ ├── 基础设施大盘 (CPU/内存/网络/磁盘)
│ ├── K8s集群大盘 (Pod/Node/Namespace)
│ └── 告警事件大盘 (告警数量/响应时间)
│
├── 服务侧大盘 (后端开发团队)
│ ├── JVM大盘 (所有Java服务的JVM概览)
│ ├── 接口大盘 (QPS/RT/Error Rate by endpoint)
│ ├── 中间件大盘 (Redis/MySQL/RocketMQ)
│ └── 依赖大盘 (外部API/下游服务调用)
│
└── 业务侧大盘 (产品/业务团队)
├── 核心业务大盘 (日活/订单量/GMV/支付成功率)
├── 转化漏斗大盘 (用户从浏览到下单各环节转化率)
└── 实时大盘 (近5分钟/1小时实时数据)典型Grafana看板行布局设计:
┌─────────────────────────────────────────────────────────────────────────┐
│ Row 1: 全局概览 (Global Overview) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ QPS (实时) │ │ RT (P99/P95) │ │ Error Rate │ │ 告警状态 │ │
│ │ Stat Panel │ │ Stat Panel │ │ Stat Panel │ │ Stat Panel │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────────────────┤
│ Row 2: JVM详情 (JVM Details) │
│ ┌──────────────────────────────────┐ ┌──────────────────────────────┐ │
│ │ 堆内存趋势 (Area Chart) │ │ GC次数+耗时 (Bar + Line) │ │
│ │ 分Eden/Survivor/Old展示 │ │ Young GC / Full GC │ │
│ └──────────────────────────────────┘ └──────────────────────────────┘ │
│ ┌──────────────────────────────────┐ ┌──────────────────────────────┐ │
│ │ 线程状态 (Stacked Bar) │ │ 类加载曲线 (Line chart) │ │
│ │ RUNNABLE/BLOCKED/WAITING │ │ 已加载/未加载 │ │
│ └──────────────────────────────────┘ └──────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────┤
│ Row 3: 业务指标 (Business Metrics) │
│ ┌──────────────────────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ 接口耗时分布 (Heatmap) │ │ 接口TOP10 │ │ 业务QPS (Table) │ │
│ │ 每个接口的RT随时间变化 │ │ 最慢接口排名 │ │ 各接口独立展示 │ │
│ └──────────────────────────┘ └──────────────┘ └────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘面试加分项:如果被问到"你设计过监控看板吗",回答框架是"分层设计"——第一排行全局概览(4个核心数字),第二行看JVM(堆/GC/线程),第三行看业务(接口RT/QPS)。这体现了从整体到细节的监控思路。
2.7 常用PromQL查询示例
| 查询目的 | PromQL |
|---|---|
| QPS | rate(http_requests_total[1m]) |
| P99延迟 | histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) |
| 错误率 | sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100 |
| CPU使用率 | 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[1m])) * 100) |
| 堆内存使用率 | jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} * 100 |
| Full GC频率 | rate(jvm_gc_pause_seconds_count{action="end of major GC"}[15m]) |
| 活跃线程数 | jvm_threads_states_threads{state="runnable"} |
| 磁盘剩余百分比 | (node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 |
| 近5分钟QPS同比昨日 | rate(http_requests_total[5m]) / rate(http_requests_total[5m] offset 1d) |
| 按服务聚合QPS | sum by(service)(rate(http_requests_total[1m])) |
PromQL进阶用法示例:
# 计算各服务的错误率,只显示错误率>1%的服务
sum by(service) (rate(http_requests_total{status=~"5.."}[5m]))
/
sum by(service) (rate(http_requests_total[5m]))
> 0.01
# 预测4小时后磁盘是否打满(线性回归预测)
predict_linear(node_filesystem_avail_bytes{device="/dev/sda1"}[6h], 4*3600) < 0
# 计算两个时间序列的差值(用于灰度发布对比)
rate(http_requests_total{version="v1"}[5m]) - rate(http_requests_total{version="v2"}[5m])3. Logging — ELK Stack
3.1 日志采集架构
ELK(Elasticsearch + Logstash + Kibana)是企业日志管理的主流方案。现代架构通常会引入 Filebeat 和 Kafka 做缓冲:
┌────────────────────────────────────────────────────────────────────────┐
│ 应用服务器(多台) │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Java App │ │ Java App │ │ Java App │ │
│ │ 日志→/app/logs/ │ │ 日志→/app/logs/ │ │ 日志→/app/logs/ │ │
│ │ Logback写入 │ │ Logback写入 │ │ Logback写入 │ │
│ └────────▲────────┘ └────────▲────────┘ └────────▲────────┘ │
│ │ │ │ │
│ ┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐ │
│ │ Filebeat │ │ Filebeat │ │ Filebeat │ │
│ │ (DaemonSet) │ │ (DaemonSet) │ │ (DaemonSet) │ │
│ │ tail -f 日志文件 │ │ tail -f 日志文件 │ │ tail -f 日志文件 │ │
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
└───────────┼────────────────────┼────────────────────┼─────────────────┘
│ │ │
└──────────┬────────┴────────┬───────────┘
│ │
┌───────▼───────┐ ┌──────▼───────┐
│ Kafka │ │ Logstash │
│ (削峰填谷) │ │ (消费解析) │
│ 可选,高流量用 │ │ Grok/Date │
└───────────────┘ └──────┬───────┘
│
┌─────────▼─────────┐
│ Elasticsearch │
│ (分布式存储+索引) │
│ 7.x / 8.x 版本 │
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ Kibana │
│ (搜索/分析/可视化) │
│ Dashboard/Canvas │
└───────────────────┘各组件职责对比:
| 组件 | 职责 | 特点 | 面试点 |
|---|---|---|---|
| Filebeat | 轻量级日志采集器 | Go编写,资源消耗极小(<10MB内存) | 为什么要用Filebeat而不是Logstash直接采集 |
| Logstash | 日志解析/转换/过滤 | 插件丰富(Grok/Date/Mutate) | Grok正则解析日志格式 |
| Kafka | 消息缓冲/削峰填谷 | 解决ES写入压力,解耦采集和处理 | 高流量场景下的必选项 |
| Elasticsearch | 全文搜索+聚合分析 | 倒排索引、分片机制、冷热分层 | 索引设计/查询优化 |
| Kibana | 可视化/搜索/告警 | Discover/Lens/Dashboard | 常用查询语法 |
3.2 Java日志框架选型
| 维度 | Logback | Log4j2 | 说明 |
|---|---|---|---|
| 性能 | 优秀 | 更优秀(异步Logger吞吐量高10倍) | Log4j2使用无锁数据结构,高并发下更优 |
| 异步 | AsyncAppender | Async Logger(基于LMAX Disruptor) | Log4j2的异步是无锁的,性能更好 |
| 自动重载 | 支持(scan="true") | 支持 | 生产环境不能改配置重启进程 |
| 配合SLF4J | 默认实现 | 需要桥接包 | Spring Boot默认用Logback |
| Spring Boot默认 | 是 | 否(需排除Logback引入) | 大部分项目直接跟Spring Boot走 |
| 无垃圾(GC-free) | 部分 | 完全支持 | Log4j2在"无GC"模式下不产生临时对象 |
生产级logback-spring.xml配置示例:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<springProperty scope="context" name="LOG_PATH" source="logging.file.path" defaultValue="/app/logs"/>
<!-- 控制台输出(开发环境) -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{traceId}] - %msg%n</pattern>
</encoder>
</appender>
<!-- 文件滚动输出(生产环境) -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APP_NAME}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{traceId}] - %msg%n</pattern>
</encoder>
</appender>
<!-- 异步Appender(提高吞吐量) -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>8192</queueSize>
<discardingThreshold>0</discardingThreshold>
<neverBlock>true</neverBlock>
<appender-ref ref="FILE"/>
</appender>
<!-- 错误日志单独输出 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APP_NAME}-error.log</file>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${APP_NAME}-error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{traceId}] - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</configuration>3.3 日志规范
日志格式标准:
时间戳 线程 级别 Logger TraceId 消息内容
2026-07-28 14:30:01.123 [http-nio-8080-exec-1] INFO c.e.o.service.OrderService [traceId=a1b2c3d4] - 订单创建成功 orderId=10001 userId=500日志级别使用规范:
| 级别 | 使用场景 | 示例 | 告警? |
|---|---|---|---|
| ERROR | 系统级异常,需要运维介入 | 数据库连接失败、空指针、远程调用超时 | P0/P1 |
| WARN | 业务异常,不需要立即处理但需关注 | 请求参数校验失败、降级触发、限流触发 | P3 |
| INFO | 关键业务节点记录 | 订单创建、支付完成、用户注册 | 不告警 |
| DEBUG | 开发调试用,生产默认关闭 | SQL参数、循环内部、条件分支 | 不告警 |
| TRACE | 更细粒度的调试日志 | 方法入口/出口、耗时打点 | 不告警 |
日志中必须包含的关键信息:
// 好的日志 ✓
log.info("订单创建成功 orderId={}, userId={}, amount={}, traceId={}",
order.getId(), order.getUserId(), order.getAmount(), traceId);
// 差的日志 ✗(没有业务上下文,无法定位)
log.info("创建订单成功");
// 异常日志 ✓
log.error("订单支付回调处理失败 orderId={}, payStatus={}, errorCode={}",
orderId, payStatus, errorCode, exception);
// 异常日志 ✗(没有打印异常堆栈)
log.error("下单失败:" + e.getMessage());日志规范黄金法则:每行日志都应该能回答 Who (TraceId/UserId)、What (业务动作)、When (时间戳)、Where (类/方法)、Why (上下文数据)。这样搜索日志时,一行就能还原出问题全貌。
3.4 分布式日志追踪 (MDC + TraceId透传)
在微服务架构中,一次请求跨多个服务,必须通过 TraceId 把所有服务的日志串起来:
TraceId透传原理:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Gateway │ │ Order │ │ User │ │ Pay │
│ 生成TraceId─────▶│ 透传 ──────▶│ 透传 ──────▶│ 透传 │
│ ID: a1b2 │ │ ID: a1b2 │ │ ID: a1b2 │ │ ID: a1b2 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
▼ ▼ ▼ ▼
日志(a1b2) 日志(a1b2) 日志(a1b2) 日志(a1b2)Spring Boot实现TraceId透传:
// 1. 网关/入口处生成TraceId (Filter)
@Component
@Slf4j
public class TraceIdFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String traceId = exchange.getRequest().getHeaders()
.getFirst("X-Trace-Id");
if (StringUtils.isBlank(traceId)) {
traceId = UUID.randomUUID().toString().replace("-", "");
}
MDC.put("traceId", traceId);
// 透传到下游微服务
exchange.getRequest().mutate()
.header("X-Trace-Id", traceId);
return chain.filter(exchange).then(Mono.fromRunnable(MDC::clear));
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
// 2. 微服务端接收到TraceId (Feign拦截器)
@Component
public class TraceIdInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
String traceId = MDC.get("traceId");
if (StringUtils.isNotBlank(traceId)) {
template.header("X-Trace-Id", traceId);
}
}
}
// 3. 微服务入口接收TraceId (Spring MVC Interceptor)
@Component
public class TraceIdHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) {
String traceId = request.getHeader("X-Trace-Id");
if (StringUtils.isBlank(traceId)) {
traceId = UUID.randomUUID().toString().replace("-", "");
}
MDC.put("traceId", traceId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
MDC.clear();
}
}3.5 Kibana常用查询
查询语法:
| 查询需求 | Kibana查询语句 | 说明 |
|---|---|---|
| 按时间范围 | @timestamp >= "2026-07-28T00:00:00" | 时间选择器或DSL语法 |
| 按级别 | log.level: "ERROR" | 查询所有错误日志 |
| 按TraceId | traceId: "a1b2c3d4" | 一次请求的完整日志链路 |
| 按服务名 | service.name: "order-service" | 查看某个服务的所有日志 |
| 按关键字 | "NullPointerException" | 全文搜索关键词 |
| 按业务ID | orderId: "10001" | 按订单号搜索 |
| 排除关键字 | NOT "health check" | 排除健康检查的干扰日志 |
| 组合查询 | log.level: "ERROR" AND service.name: "pay-service" | 多条件过滤 |
| 模糊匹配 | message: "timeout*" | 通配符搜索 |
| 正则匹配 | message: /order_\d{5}/ | 正则表达式搜索 |
Kibana发现页面的常用操作:
1. 先选时间范围(近15分钟/近1小时/自定义)
2. 搜索:service.name: "order-service" AND log.level: "ERROR"
3. 左侧可展开字段看分布:
- log.level → 看ERROR占比
- service.name → 看哪个服务错误最多
- host.name → 看是否有某个节点异常
4. 点开任意一行日志,看JSON格式的完整内容
5. 点击"View surrounding documents"看上下文日志Kibana 8.x的新特性(面试亮点):
- ES|QL:Elasticsearch的查询语言新标准,类似SQL的查询方式
- Alerting:内置告警规则,不再需要额外配置ElastAlert
- SLO (Service Level Objectives):内置SLO追踪,黄金信号友好
4. Tracing — SkyWalking
4.1 分布式链路追踪原理
分布式链路追踪的核心数据模型源于Google的 Dapper论文(2010年),定义了三个核心概念:
| 概念 | 说明 | 代码类比 |
|---|---|---|
| Trace | 一次请求完整的调用链路 | 相当于一次完整的HTTP请求 |
| Span | Trace中的每个调用单元 | 相当于每个方法调用或RPC调用 |
| TraceId | 全局唯一的Trace标识 | 相当于请求的身份证号 |
| SpanId | Span的唯一标识 | 相当于每个步骤的编号 |
| ParentSpanId | 父Span ID,构建父子关系 | 相当于栈里的调用关系 |
| Baggage | 跨Span传递的自定义数据 | 相当于ThreadLocal |
链路追踪的调用链模型:
Trace: t1 (一次下单请求)
│
├── Span A: Gateway [0ms~50ms] (入口)
│ └── Span B: /order/create [5ms~45ms] (HTTP调用)
│ ├── Span C: 校验Token [6ms~10ms] (本地调用)
│ ├── Span D: 查询库存-HTTP [11ms~30ms] (RPC)
│ │ └── Span E: 库存服务处理 [12ms~28ms]
│ ├── Span F: 扣减Redis [31ms~35ms] (缓存)
│ └── Span G: 写数据库 [36ms~42ms] (SQL)
│
总耗时: 50ms (Span A)
瓶颈分析: Span D 占用了 20ms (40%的耗时来源于库存服务)面试话术:"分布式链路追踪的本质就是给每个请求分配一个 全局唯一的TraceId,在请求经过的每个服务/每个方法调用时创建Span,通过ParentSpanId构建调用树。最终把一棵分布式请求的调用树还原出来,找到哪个节点最慢。"
4.2 SkyWalking架构
SkyWalking 是Apache基金会顶级项目,中国开源界的明星APM产品:
┌──────────────────────────────────────────────────────────────┐
│ 被监控应用 │
│ │
│ Java App (javaagent方式接入) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ SkyWalking Agent (探针) │ │
│ │ - 字节码增强拦截方法调用 │ │
│ │ - 自动采集: HTTP/gRPC/DB/Redis/MQ │ │
│ │ - 上报: gRPC协议 → OAP Server │ │
│ └──────────────┬───────────────────────────────────────┘ │
└─────────────────┼────────────────────────────────────────────┘
│ gRPC (9.0默认端口: 11800)
│ HTTP (9.0可选端口: 12800)
▼
┌──────────────────────────────────────────────────────────────┐
│ OAP Server (Observability Analysis Platform) │
│ │
│ 核心模块: │
│ - Trace持久化与索引 │
│ - 指标聚合 (分钟级/小时级) │
│ - 拓扑图构建 (根据调用关系自动发现) │
│ - 告警规则引擎 │
│ - 采样策略 (默认: 3秒内请求>500次时采样率50%) │
└──────────────┬──────────────────────────────┬─────────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────────────┐
│ 存储后端 │ │ SkyWalking UI (Web) │
│ │ │ │
│ 默认: H2 (演示用) │ │ - 仪表盘 (Dashboard) │
│ 推荐: Elasticsearch │ │ - 拓扑图 (Topology) │
│ 也可: MySQL/PostgreSQL│ │ - 调用链 (Trace) │
│ TiDB (大规模集群) │ │ - 告警 (Alarm) │
│ │ │ - 日志 (Log) 8.x+ │
└───────────────────────┘ └──────────────────────────────┘SkyWalking vs 传统监控的优势对比:
| 特性 | SkyWalking | 传统Pinpoint/Zipkin |
|---|---|---|
| 接入方式 | javaagent,零代码侵入 | javaagent,零代码侵入 |
| 性能损耗 | 一般 <10% | Pinpoint损耗稍大 |
| 存储 | ES/H2/MySQL/TiDB多种可选 | Jaeger依赖Cassandra/ES |
| 功能 | 链路+指标+拓扑+告警+日志 | 各有侧重 |
| 社区活跃度 | Apache顶级项目,中文社区活跃 | Pinpoint韩国社区维护为主 |
| 扩展性 | 支持gRPC/HTTP自定义上报 | 需要自己写Adapter |
4.3 Java应用接入SkyWalking
Step 1: 下载Agent
# 从 SkyWalking 官网下载最新 Agent
wget https://dlcdn.apache.org/skywalking/java-agent/9.1.0/apache-skywalking-java-agent-9.1.0.tgz
tar -zxvf apache-skywalking-java-agent-9.1.0.tgz -C /opt/skywalking/Step 2: JVM参数启动
# 方式一:java命令直接启动
java -javaagent:/opt/skywalking/agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=order-service \
-Dskywalking.collector.backend_service=oap-server:11800 \
-jar order-service.jar
# 方式二:Docker启动
docker run -d \
-e SW_AGENT_NAME=order-service \
-e SW_AGENT_COLLECTOR_BACKEND_SERVICES=oap-server:11800 \
-v /opt/skywalking/agent:/agent \
--name order-service \
myapp/order-service:latest \
java -javaagent:/agent/skywalking-agent.jar -jar app.jarStep 3: K8s环境 (Sidecar模式)
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
metadata:
annotations:
sidecar.skywalking.io/inject: "true" # 自动注入agent
spec:
containers:
- name: app
image: myapp/order-service:latest
env:
- name: SW_AGENT_NAME
value: "order-service"
- name: SW_AGENT_COLLECTOR_BACKEND_SERVICES
value: "skywalking-oap:11800"agent/config/agent.config 常用配置:
# 服务名称(在SkyWalking UI中显示)
agent.service_name=${SW_AGENT_NAME:my-service}
# OAP Server地址(gRPC端口)
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:127.0.0.1:11800}
# 采样率: 0-1, 1=100%, 0.5=50%
agent.sample_n_per_3_secs=${SW_AGENT_SAMPLE:500}
# 忽略指定路径的追踪(健康检查等无用链路)
agent.ignore_suffix=${SW_AGENT_IGNORE_SUFFIX:.jpg,.jpeg,.js,.css,.png,.gif,.ico,.svg}
# 日志级别
logging.level=${SW_AGENT_LOGGING_LEVEL:INFO}4.4 调用链分析实战
场景一:排查"支付接口突然变慢"
SkyWalking UI操作流程:
1. 打开 Dashboard → 选 "pay-service"
2. 在 端点列表 中找 "POST /pay/create"
3. 看响应时间曲线 → 发现从50ms涨到2s ✓
4. 点击 最慢的trace → 看调用树 →
发现主要耗时在调用 "user-service.getUserInfo" (占1500ms)
5. 结论: 用户服务接口延迟导致支付变慢,而不是支付服务本身的问题场景二:排查"新发布版本出错"
1. 打开 Dashboard → 拓扑图
2. 看到 pay-service 和 user-service 之间的线变红了 ✗
3. 点击红线 → "查看调用详情"
4. 发现 error rate 从0.1%涨到5%
5. 看具体的 error trace →
发现 user-service 新版本抛了NullPointerException
6. 结论: 新上线的user-service版本有bug,回滚即可SkyWalking UI中需要关注的几个关键视图:
| 视图 | 作用 | 面试中可以怎么描述 |
|---|---|---|
| 拓扑图 | 自动画出服务间调用关系,红色连线=异常 | "拓扑图一眼就能看出哪个服务有问题,不需要去查调用链" |
| 调用链 | 每次请求的完整Span列表 | "调用链可以看到每个Span的耗时,瓶颈一清二楚" |
| 仪表盘 | 各服务/端点的QPS/RT/P99/Error Rate | "仪表盘是日常巡检的首页,我每天上班先看一眼" |
| 慢查询 | 自动采集的数据库慢SQL | "自动采集MySQL慢查询,比DBA手动查慢查询日志方便" |
| 告警 | 配置的告警规则和触发记录 | "集成了告警引擎,熔断或超时会自动通知" |
4.5 SkyWalking vs Pinpoint vs Jaeger vs Zipkin 对比
| 维度 | SkyWalking | Pinpoint | Jaeger | Zipkin |
|---|---|---|---|---|
| 开源组织 | Apache | Naver (韩国) | CNCF (Uber) | Apache (Twitter) |
| 接入方式 | javaagent | javaagent | SDK/Agent | SDK/Agent |
| 代码侵入 | 无 | 无 | 有(需引入SDK) | 有(需引入SDK) |
| 性能损耗 | 低 (<10%) | 中 (10-20%) | 低 | 低 |
| 拓扑图 | 自动生成 | 自动生成 | 需配置 | 需配置 |
| 告警引擎 | 内置 | 内置 | 无 | 无 |
| 存储 | ES/H2/MySQL/TiDB | HBase | ES/Cassandra/Kafka | ES/Cassandra/Kafka |
| UI体验 | ★★★★★ | ★★★★ | ★★★ | ★★★ |
| 中文社区 | 活跃 | 较少 | 一般 | 一般 |
| 学习成本 | 低(中文文档多) | 中 | 中 | 低 |
| 面试热度 |
面试选型:被问到"你们为什么选SkyWalking"时,标准回答:1) 无代码侵入的javaagent方式接入;2) 开箱即用的拓扑图能快速定位问题;3) 全链路Metrics + Tracing覆盖全面;4) Apache顶级项目,社区活跃中文文档多。如果被反问"那为什么不用Zipkin",可以说"Zipkin需要业务代码手动埋点SkyWalking的agent更轻量且自动采集更全面"。
5. 告警体系
5.1 告警分级
告警不是"满了就告",而是要根据业务影响程度分级处理:
| 级别 | 名称 | 定义 | 响应时间 | 示例 |
|---|---|---|---|---|
| P0 | 致命 | 核心功能完全不可用,影响全部用户 | 立即响应(<5分钟) | 支付接口500、首页502、数据库不可用 |
| P1 | 严重 | 核心功能受损,影响部分用户 | <15分钟 | 订单超时率>10%、大量消息堆积 |
| P2 | 警告 | 非核心功能异常,或核心功能有风险趋势 | <1小时 | 某个非核心接口P99超阈值、磁盘使用>80% |
| P3 | 提示 | 不影响用户,但需要关注的趋势 | 下一个工作日 | 慢SQL增多、错误日志量增长趋势 |
告警分级响应流程:
P0/P1 告警
│
├─ 自动通知 → 值班人员立即响应
│ ├─ 钉钉/企微直接@所有人 + 电话通知PagerDuty
│ └─ 5分钟未确认 → 升级通知到技术Leader
│
├─ 应急响应 → 立即定位
│ ├─ 看Grafana → 确认影响范围和严重程度
│ ├─ 查SkyWalking → 找慢调用/异常分布
│ └─ 搜Kibana → 看具体错误日志
│
└─ 止损动作 → 先恢复再查根因
├─ 回滚新版本 / 切备用节点
├─ 降级非核心功能
└─ 修改配置 / 扩容 / 重启
P2/P3 告警
│
├─ 记录到Jira/工单 → 排期处理
├─ 日常跟进 → 值班人员次日确认
└─ 长期优化 → 纳入技术债务管理5.2 Prometheus AlertManager告警规则配置
告警规则示例 (prometheus-rules.yml):
groups:
- name: jvm_alerts
interval: 30s
rules:
- alert: JVMHeapUsageHigh
expr: |
(jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}) > 0.85
for: 5m
labels:
severity: critical
team: backend
annotations:
summary: "{{ $labels.instance }} 堆内存使用率 > 85%"
description: |
服务: {{ $labels.service }}
实例: {{ $labels.instance }}
当前使用率: {{ $value | humanizePercentage }}
最近5min持续超过85%,建议立即排查
- alert: FullGcFrequent
expr: |
rate(jvm_gc_pause_seconds_count{action="end of major GC"}[15m]) > 2
for: 10m
labels:
severity: critical
team: backend
annotations:
summary: "{{ $labels.instance }} Full GC 频繁 (>2次/15分钟)"
- name: business_alerts
interval: 30s
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))
> 0.01
for: 3m
labels:
severity: critical
team: backend
annotations:
summary: "服务 {{ $labels.service }} 错误率 > 1%"
description: "当前错误率: {{ $value | humanizePercentage }}"
- alert: HighLatency
expr: |
histogram_quantile(0.99,
rate(http_request_duration_seconds_bucket[5m])
) > 3
for: 5m
labels:
severity: warning
team: backend
annotations:
summary: "{{ $labels.service }} P99延迟 > 3秒"
- name: infra_alerts
interval: 30s
rules:
- alert: DiskSpaceLow
expr: |
(node_filesystem_avail_bytes / node_filesystem_size_bytes) < 0.1
and node_filesystem_mount_point =~ "/|/data"
for: 5m
labels:
severity: critical
team: ops
annotations:
summary: "{{ $labels.instance }} 磁盘空间 < 10%"
- alert: InstanceDown
expr: up == 0
for: 1m
labels:
severity: critical
team: ops
annotations:
summary: "{{ $labels.instance }} 已离线超过1分钟"AlertManager 路由配置 (alertmanager.yml):
route:
receiver: 'default'
group_by: ['alertname', 'service']
group_wait: 30s # 同组告警等待30s后发送
group_interval: 5m # 同组告警发送间隔
repeat_interval: 4h # 已解决告警4小时后重新通知
routes:
- receiver: 'critical-pager'
match:
severity: critical
repeat_interval: 1h
- receiver: 'warning-notification'
match:
severity: warning
repeat_interval: 12h
receivers:
- name: 'critical-pager'
webhook_configs:
- url: 'http://dingtalk-webhook:8060/alert/critical'
send_resolved: true
- url: 'http://webhook-server:8080/send-message?type=wechat'
send_resolved: true
- name: 'warning-notification'
webhook_configs:
- url: 'http://webhook-server:8080/send-message?type=dingtalk'
send_resolved: false5.3 告警通知渠道
| 渠道 | 优点 | 缺点 | 适用级别 |
|---|---|---|---|
| 钉钉机器人 | Webhook配置简单,支持Markdown | 容易被群消息淹没,不支持电话 | P2/P3 |
| 企业微信Bot | 与OA打通,支持@指定人 | 消息格式限制较多 | P2/P3 |
| 飞书机器人 | 卡片消息丰富,支持交互式按钮 | 用户量相对少 | P1/P2 |
| PagerDuty | 专业告警平台,支持电话/短信/App推送 | 付费 | P0/P1 |
| 短信 | 必达率高 | 成本高,信息量少 | P0 |
| 电话 | 最高优先级,无法忽视 | 打扰大,成本高 | P0(核心服务不可用) |
钉钉告警消息示例:
{
"msgtype": "markdown",
"markdown": {
"title": "【P0】生产环境告警 - 支付服务不可用",
"text": "## 生产环境告警\n\n" +
"---\n" +
"**告警级别**:P0 (致命)\n\n" +
"**告警名称**:HighErrorRate\n\n" +
"**服务**:pay-service\n\n" +
"**当前错误率**:25.3%\n\n" +
"**触发时间**:2026-07-28 14:30:00\n\n" +
"**持续时间**:3分钟\n\n" +
"**Dashboard**:[点击查看](http://grafana.company.com/d/pay)\n\n" +
"**Trace**:[查看调用链](http://skywalking.company.com/trace)\n\n" +
"---\n" +
"**当前值班**:张三 (@张三)\n\n" +
"**升级联系人**:李四 (138xxxxxxx)\n\n" +
"> 请立即响应!若5分钟内未确认,将自动升级到技术Leader。"
}
}5.4 告警降噪
告警疲劳(Alert Fatigue)是运维中的大问题——告警太多反而没人看。三大降噪策略:
| 策略 | 说明 | 配置方式 |
|---|---|---|
| 聚合 (Grouping) | 同类告警合并为一条,避免告警风暴 | AlertManager group_by: ['alertname', 'service'] |
| 抑制 (Inhibition) | 高级别告警自动抑制低级别告警 | 如:节点挂了 → 抑制该节点上的所有服务告警 |
| 静默 (Silence) | 已知问题/维护窗口期临时屏蔽告警 | AlertManager Silence 界面维护 |
聚合示例: 某服务30个Pod同时因"磁盘空间>90%"告警,聚合后变一条:
告警:disk-service 磁盘空间 > 90%
受影响实例: 30个
首次触发: 14:30
最新触发: 14:31
聚合策略: 按 alertname + service 分组,每5分钟发送一次抑制规则配置:
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['service', 'instance']
# 如果节点挂了(node_down),抑制该节点上的所有应用告警
- source_match:
alertname: 'InstanceDown'
target_match_re:
alertname: '.*'
equal: ['instance']5.5 告警值班轮转制度
告警机制只有在有人看时才有效。企业常见的值班制度:
┌────────────────────────────────────────────────────────────┐
│ 值班轮转表 (示例) │
├──────────┬──────────┬──────────┬──────────┬────────────────┤
│ 时间 │ 主值班 │ 备值班 │ 技术Leader│ 备注 │
├──────────┼──────────┼──────────┼──────────┼────────────────┤
│ 第1周 │ 张三 │ 李四 │ 王五 │ │
│ 第2周 │ 李四 │ 赵六 │ 王五 │ 张三休假 │
│ 第3周 │ 赵六 │ 张三 │ 王五 │ │
│ ... │ ... │ ... │ ... │ │
├──────────┴──────────┴──────────┴──────────┴────────────────┤
│ 值班职责: │
│ 1. P0/P1告警:立即响应,5分钟内确认,15分钟内定位并止损 │
│ 2. 告警周报:每周一上午出上周告警统计和趋势分析 │
│ 3. 告警复盘:P0/P1事件48小时内出事故复盘报告 │
└────────────────────────────────────────────────────────────┘告警响应SLA最佳实践:
P0: 确认 < 5min → 定位 < 15min → 恢复 < 30min → 复盘 < 48h
P1: 确认 < 15min → 定位 < 30min → 恢复 < 2h → 复盘 < 1周
P2: 确认 < 1h → 排入迭代 → 修复 < 2周
P3: 排入Backlog → 随版本修复核心要点:告警体系的终极目标是"少而精、快而准"。告警太多是噪音(没人看),告警太少是风险(漏了关键)。好的告警规则应该满足:(1) 可响应——收到告警你能做点什么 (2) 可联动——从告警消息能直接跳转到Grafana/Trace/日志 (3) 可行动——每条告警都有明确的SLA。
6. 面试高频追问
6.1 "如何监控一个接口的性能?"
回答框架(从工具到方法到指标):
✓ 优秀回答思路:
我会从三个层面来回答:
第一层:工具层面
- 暴露指标:Spring Boot Actuator + Micrometer 自动采集http request指标
- 链路追踪:SkyWalking Agent 自动采集每个接口的调用链
- 日志:每个接口的关键日志(入参、出参、耗时)
第二层:关键指标
- QPS(rate):接口每秒处理的请求数
- RT 分布(P50/P90/P99/P999):看整体的耗时分布
- Error Rate:5xx/业务异常比例
- 耗时分布热力图:以1分钟为粒度看每段时间的RT变化
第三层:告警与大盘
- Grafana 接口大盘:按QPS/RT/Error Rate展示Top接口
- 告警规则:P99 > 3s 持续5分钟告警,错误率 > 1% 告警
- 与Tracing联动:Grafana看板中嵌入SkyWalking的慢Trace链接
举个实际例子,我们之前有个订单查询接口从50ms涨到2s,
通过Grafana发现QPS没变 → 通过Tracing发现耗时在DB层 →
查SkyWalking慢SQL发现有个未加索引的查询 → 加索引解决。6.2 "凌晨收到告警怎么办?"
这是一个考察应急响应流程的问题。回答要体现出:冷静、有序、有章法。
✓ 优秀回答思路:
第一步:确认(5分钟内)
- 先看告警级别:P0/P1立刻处理,P2/P3确认后视情况
- 看Grafana大盘:确认是不是误报(如部署期间的毛刺)
- 看影响范围:是全部用户还是部分?是全部接口还是某个接口?
第二步:止损(15分钟内)- 先恢复再排查根因
- 如果和发布有关 → 立即回滚
- 如果是资源问题 → 扩容/重启/切流
- 如果是下游依赖挂了 → 降级/熔断
第三步:排查(止损后)
- 查日志(Kibana搜TraceId)
- 查链路(SkyWalking找慢调用)
- 查指标(看变化拐点)
第四步:复盘(48小时内)
- 为什么会发生?
- 为什么没有提前发现?
- 如何避免再发生?
举个实际例子,我遇到过凌晨3点收到"订单超时率>10%"的告警,
确认是P0后,看Grafana发现是Redis集群有个节点挂了,
导致大量Redis操作超时。止损方案是:先切流量到备用Redis集群,
白天再排查挂掉节点的原因(最后发现是内存不够被OOM Killer杀了)。6.3 "怎么排查一个请求慢在哪里?"
这是一个典型的微服务排障问题,核心思路是用Tracing逐层定位:
✓ 优秀回答思路:
我会用 SkyWalking 做分布式链路追踪来定位:
Step 1: 找到这条慢请求的 Trace
- 从用户处获取 TraceId(可以在响应头或日志中找到)
- 或者在 SkyWalking 的 Trace 页根据时间区间搜索
Step 2: 分析调用链
- 看 Span 列表,按耗时排序
- 找到最耗时的那个 Span(一般是瓶颈所在)
Step 3: 逐层深入定位
场景举例:下单接口从200ms慢到3s
- 发现最耗时 Span:order-service → user-service 的 Feign 调用(占2.5s)
- 进入 user-service 的 Span:发现最慢的是查询数据库的 Span
- 查看该 Span 的 Tags:发现执行的 SQL 是 `SELECT * FROM user WHERE id = ?`
- 确认是慢查询 → 分析有没有索引 → 用 EXPLAIN 确认
Step 4: 数据辅助
- 看当前时刻的 JVM 指标:有没有 Full GC?有没有线程 BLOCKED?
- 看数据库连接池:有没有打满?等待时间长吗?
- 看对应时间点的日志:有没有异常报错?
通过 Tracing 可以做到"一次请求的每一毫秒都了解的去向",
不需要再靠"重启试试"或者"加日志再观察"这种老办法了。