"你的服务挂了你怎么知道?"——企业级可观测性三支柱:Metrics + Logging + Tracing,一个都不能少

1. 可观测性全景图

1.1 三大支柱

可观测性(Observability)是现代分布式系统中比监控更高维度的概念。监控告诉你"系统挂了",可观测性让你能自己回答"为什么挂了"。业界公认的三大支柱:

支柱英文典型工具核心回答的问题数据特点
指标MetricsPrometheus + Grafana"系统现在什么状态?"数字型,周期性采集,存储成本低
日志LoggingELK Stack (ES+Logstash+Kibana)"具体发生了什么?"文本型,信息最丰富,存储成本高
链路追踪TracingSkyWalking / 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都使用同一时间基准以秒级精度对齐
标签/LabelsPrometheus 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} + 时间戳 + 值每一条指标就是一个带标签的时序数据流
PromQLPrometheus的查询语言相当于"指标界的SQL",支持聚合/运算/预测
Exporter暴露指标的适配器把第三方系统(MySQL/Redis/JVM)的指标转成Prometheus格式
TSDB时序数据库,默认本地存储15天按时间分块存储,压缩率高,但一般不长期保存
Service Discovery自动发现监控目标配合K8s,Pod增删自动发现,不需要手动配

Pull vs Push 模型对比:

特性Pull (Prometheus)Push (传统Zabbix/OpenTSDB)
网络要求监控端能连到TargetTarget能连到监控端
自动发现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/TPSrate(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 Dump

2.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_countYoung GC / Full GC频率
GC耗时jvm_gc_pause_seconds_sumGC暂停总时间,重点关注Full GC
线程状态jvm_threads_states_threadsRUNNABLE/BLOCKED/WAITING 线程数
类加载jvm_classes_loaded_classes已加载类数量,排查类加载泄漏
文件描述符process_files_open_files文件句柄数,排查句柄泄漏
CPUsystem_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
QPSrate(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)
按服务聚合QPSsum 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日志框架选型

维度LogbackLog4j2说明
性能优秀更优秀(异步Logger吞吐量高10倍)Log4j2使用无锁数据结构,高并发下更优
异步AsyncAppenderAsync 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"查询所有错误日志
按TraceIdtraceId: "a1b2c3d4"一次请求的完整日志链路
按服务名service.name: "order-service"查看某个服务的所有日志
按关键字"NullPointerException"全文搜索关键词
按业务IDorderId: "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请求
SpanTrace中的每个调用单元相当于每个方法调用或RPC调用
TraceId全局唯一的Trace标识相当于请求的身份证号
SpanIdSpan的唯一标识相当于每个步骤的编号
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.jar

Step 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 对比

维度SkyWalkingPinpointJaegerZipkin
开源组织ApacheNaver (韩国)CNCF (Uber)Apache (Twitter)
接入方式javaagentjavaagentSDK/AgentSDK/Agent
代码侵入无无有(需引入SDK)有(需引入SDK)
性能损耗低 (<10%)中 (10-20%)低低
拓扑图自动生成自动生成需配置需配置
告警引擎内置内置无无
存储ES/H2/MySQL/TiDBHBaseES/Cassandra/KafkaES/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: false

5.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 可以做到"一次请求的每一毫秒都了解的去向",
不需要再靠"重启试试"或者"加日志再观察"这种老办法了。