概述
前文《反模式与排查宝典》详细展示了当 Kafka 集群出现问题时,如何像一名技术侦探一样,从纷繁复杂的异常现象中抽丝剥茧,定位根因。然而,再高超的排查技巧,也比不上在问题萌芽阶段就将其捕获的“先知”能力。被动响应是运维的初级阶段,主动探查、提前预警才是构建高可用系统的核心思想。本文将从零开始,为你构建一套面向生产环境的 Kafka 可观测性体系。它将如同一位忠实的哨兵,通过 Prometheus 指标和 Grafana 仪表盘,在 UnderReplicatedPartitions 攀升、消费积压陡增、Controller 发生切换等风险信号出现的第一时间发出警报。
Kafka 的可观测性体系是运维者最敏锐的感官延伸。UnderReplicatedPartitions 的每一次增加,都是 ISR 收缩的无声警报,预示着分区的可用性正在受到威胁;records-lag-max 的持续攀升,标志着消费者正逐步掉队,业务数据的实时性将无法保证;而 ActiveControllerCount 的瞬时波动,可能触发集群元数据同步的短暂混乱。本文将系统地拆解 Broker、Producer、Consumer 及事务这四大维度的核心监控指标,深入对比 CLI 工具、JMX 指标和 Burrow 三种 Lag 监控方案的优劣与原理,并搭建一条从 kafka-exporter/Prometheus 采集、Grafana 可视化到 PrometheusRule 告警的完整监控链路。
核心要点
- 三大层面核心指标体系:深度解读 Broker(ISR、Controller、网络吞吐)、Producer(发送速率、错误率、重试率)和 Consumer(Lag、消费速率、心跳)的关键指标及其背后的架构含义。
- Lag 监控三剑客:横向对比 CLI 工具(即时快照)、JMX 指标(长期趋势)与 Burrow(智能状态机)的监控方案,深入剖析 Burrow 如何通过滑动窗口算法实现 Lag 的智能评估,减少误报。
- 完整监控链路搭建:基于
kafka-exporter→Prometheus→Grafana→PrometheusRule构建端到端的监控与告警体系,并提供完整的配置与 PromQL 示例。 - Spring 生态无缝整合:展示如何通过 Micrometer 和 Spring Boot Actuator,将 Kafka 客户端及 Kafka Streams 的指标优雅地暴露到监控系统中。
- 告警规则分级策略:设计从 P0(立即响应)到 P2(计划关注)的分级告警策略,并结合
for持续时间与absent检测,构建精准、低噪的告警体系。
文章组织架构图
flowchart TB
subgraph S1 ["1. Kafka 核心监控指标体系"]
A1["Broker 指标: ISR收缩, Controller状态, 网络吞吐"]
A2["Producer 指标: 发送速率, 错误率, 重试率, 延迟"]
A3["Consumer 指标: 消费Lag, 消费速率, 心跳"]
A4["事务指标: lastStableOffset空洞"]
end
subgraph S2 ["2. 消费 Lag 的三种监控方案"]
B1["CLI 工具: 即时快照"]
B2["JMX 指标: 长期趋势"]
B3["Burrow: 智能状态机评估"]
end
subgraph S3 ["3. Prometheus + Grafana 监控链路"]
C1["数据采集: kafka-exporter & JMX"]
C2["数据存储与查询: Prometheus & PromQL"]
C3["可视化: Grafana 层次化仪表盘"]
end
subgraph S4 ["4. 告警规则设计与分级策略"]
D1["P0/P1/P2 告警分级决策树"]
D2["关键 PrometheusRule 定义"]
D3["误报防范: for & absent"]
end
subgraph S5 ["5. Spring Kafka 与 Kafka Streams 监控整合"]
E1["Micrometer 指标注册"]
E2["Actuator 端点暴露"]
E3["Kafka Streams 特有指标"]
end
subgraph S6 ["6. 故障模拟与监控验证"]
F1["故障一: 消费积压告警"]
F2["故障二: ISR 收缩告警"]
F3["故障三: Burrow 智能判定对比"]
end
subgraph S7 ["7. 面试高频专题"]
G1["核心指标体系"]
G2["方案对比与原理"]
G3["监控链路设计"]
end
S1 --> S2 --> S3 --> S4 --> S5 --> S6 --> S7
S2 -.->|"方案选择影响"| S3
S3 -.->|"数据驱动"| S4
S5 -.->|"应用层指标整合"| S3
classDef topic fill:#f8f9fa,stroke:#333,stroke-width:2px,rx:5,color:#333;
class S1,S2,S3,S4,S5,S6,S7 topic;
1. Kafka 核心监控指标体系
一个健壮的监控体系始于对核心指标的深刻理解。指标不是孤立的数字,它们是系统内部运行机制的数字化投射。我们将指标分为 Broker、Producer、Consumer 和事务四个层面,并探讨它们如何反映前文所学的 ISR 机制、性能瓶颈和事务原理。
flowchart TB
subgraph Broker层
B1[UnderReplicatedPartitions: ISR收缩]
B2[ActiveControllerCount: Controller活性]
B3[OfflinePartitionsCount: 分区不可用]
B4[BytesInPerSec / BytesOutPerSec: 网络吞吐]
B5[TotalTimeMs: 请求处理延迟]
end
subgraph Producer层
P1[record-send-rate: 发送速率]
P2[record-error-rate: 错误率]
P3[record-retry-rate: 重试率]
P4[request-latency-avg: 请求延迟]
P5[outgoing-byte-rate: 字节速率]
end
subgraph Consumer层
C1[records-lag-max: 最大消费Lag]
C2[records-consumed-rate: 消费速率]
C3[records-lead: 消费超前]
C4[fetch-rate / fetch-latency-avg]
C5[heartbeat-rate: 心跳频率]
end
subgraph 事务层
T1[lastStableOffset: 稳定位点]
T2[LogEndOffset: 日志末尾]
T3[事务空洞大小: LSO与LEO差值]
end
Broker层 --- Producer层 --- Consumer层 --- 事务层
- 图表主旨概括:本图展示了 Kafka 核心监控指标体系的四个层级,分别为 Broker、Producer、Consumer 和事务层。清晰地揭示了各层面需重点关注的指标,它们共同构成了一个从基础设施到应用逻辑再到高级特性的全方位监控网络。
- 逐层/逐元素分解:
- Broker 层:聚焦于集群本身的健康度。
UnderReplicatedPartitions和OfflinePartitionsCount是关乎数据可用性与持久性的生命体征指标;ActiveControllerCount反映集群元数据管理的稳定性;BytesInPerSec/TotalTimeMs等则从吞吐和延迟角度衡量 Broker 的性能表现。 - Producer/Consumer 层:从客户端的视角,关注消息的生产与消费行为。速率、错误率和延迟是衡量客户端性能的黄金三角。特别地,
records-lag-max是消费延迟的关键度量。 - 事务层:深入到 Kafka 的事务特性,通过
lastStableOffset与LogEndOffset的差值来监控未完成的事务对消费者进度的阻塞情况,即“事务空洞”。
- Broker 层:聚焦于集群本身的健康度。
- 设计原理映射:这种分层设计遵循了可观测性的“自上而下”原则,也与第 5、7、14 篇的知识体系直接关联。Broker 指标是 ISR 机制(第 5 篇)和性能瓶颈(第 14 篇)的直接反馈。
records-lag-max是消费者组重平衡(第 5 篇)和消费策略是否高效的最终体现。事务指标是事务协调器内部状态(第 7 篇)的外化。 - 工程联系与关键结论:在 Grafana 仪表盘设计中,我们会复用这个分层思想,设计出从集群全局概览到具体客户端、再到事务细节的下钻式面板。关键结论:这套指标体系不是指标的堆砌,而是一个有机整体。例如,Broker 的
TotalTimeMs升高可能直接导致 Producer 的request-latency-avg增加和record-retry-rate上升,最终表现为 Consumer 的records-lag-max增长。一个出色的监控系统能够帮助运维人员在这些看似孤立的指标间建立起因果联系。
1.1 Broker 级别核心指标
Broker 是 Kafka 集群的基石,其稳定性直接决定了整个系统的可用性。以下指标是必须纳入监控的“生命体征”。
| 指标名称 | 含义 | 正常值 | 告警意义 |
|---|---|---|---|
UnderReplicatedPartitions | 处于 ISR 收缩状态的分区数量,即 Follower 未及时追上 Leader。 | 0 | 任何大于 0 的值都意味着数据冗余度下降,增加数据丢失风险,详见第 5 篇。 |
ActiveControllerCount | 集群中活跃 Controller 的数量。 | 1 | 不等于 1 表示 Controller 出现脑裂或失效,集群元数据管理将出现严重问题。 |
OfflinePartitionsCount | 无 Leader 的分区数量。 | 0 | 大于 0 表示有分区完全不可读写,属于 P0 级灾难性故障。 |
BytesInPerSec / BytesOutPerSec | Broker 的网络进出流量,单位 bytes/s。 | 视集群规模 | 反映集群的整体吞吐负载,可与性能调优(第 14 篇)的容量规划联动。 |
TotalTimeMs | 请求处理的总耗时(包括排队、处理、响应)。 | 毫秒级 | 急剧上升代表 Broker 出现性能瓶颈,可细分为 QueueTimeMs、LocalTimeMs 等进一步定位。 |
深入解读:
-
UnderReplicatedPartitions与 ISR 机制:在第 5 篇中我们了解到,ISR 是保障消息不丢失的核心机制。UnderReplicatedPartitions指标是 ISR 收缩的直接体现。非零值的出现,通常意味着 Follower Broker 的副本同步线程跟不上 Leader 的写入速度。这背后可能是 Follower 磁盘 I/O 高负载、网络带宽打满或发生了 GC 停顿。此指标的预警价值极高,它提供了一个宝贵的“时间窗口”,允许你在数据可用性真正降级(所有副本宕机)前介入处理。 -
ActiveControllerCount的唯一性:Controller 负责分区 Leader 选举、ISR 维护等关键任务。该指标必须恒为 1。其值的任何变化(哪怕是短暂变为0或2),都标志着发生了 Controller 重选或脑裂,日志中通常会伴随LeaderAndIsr请求的激增,可能导致短暂的服务中断。
1.2 Producer 级别核心指标
Producer 指标反映了数据源头写入系统的健康状况,对于评估上游业务的稳定性和延迟至关重要。
| 指标名称 | 含义 | 告警意义 |
|---|---|---|
record-send-rate | 每秒发送的消息条数。 | 反映生产吞吐量,可建立基线,用于容量规划。 |
record-error-rate | 每秒发送失败的消息条数。 | 任何持续的 >0 都需立即关注,是消息丢失或重试风暴的根源。 |
record-retry-rate | 每秒重试的消息条数。 | 单独升高而错误率不高,表示 Broker 有短暂压力(如网络抖动)且可恢复;若与错误率同步升高,则问题严重。 |
request-latency-avg | Producer 请求的平均延迟。 | 直接影响上游业务的响应时间,是 Broker 端 TotalTimeMs 的客户端体现。 |
outgoing-byte-rate | 每秒发送的字节数。 | 从字节层面衡量吞吐量,便于与网络带宽进行对比,识别是否打满网卡。 |
深度解读:
- 重试率 (
record-retry-rate) 的双面性:这是retries配置和delivery.timeout.ms配置协同作用的结果。一定程度的瞬时重试是正常的,它证明了系统的韧性与idempotence(幂等性,见第 7 篇)的价值。但持续高涨的重试率是 Broker 端过载或网络不稳定的确凿证据,必须排查。
1.3 Consumer 级别核心指标
Consumer 指标是衡量消息是否被及时处理的最终裁判,是业务方最直观的监控维度。
| 指标名称 | 含义 | 告警意义 |
|---|---|---|
records-lag-max | 消费者组在所有分区上的最大 Lag。 | 这是监控消费延迟的“金指标”。持续增长表示消费者已长时间未提交或处理速度严重滞后。 |
records-consumed-rate | 每秒消费的消息条数。 | 反映消费者的处理能力,应与 record-send-rate 对比。 |
records-lead | 消费者当前消费位点领先于活跃 Segment 末尾的消息数。 | 正常应为正数。若趋近于零或为负,说明消费者即将或已经追上了最新消息,常用于判断消费速度是否过快。 |
fetch-rate | 每秒拉取请求 (Fetch Request) 次数。 | 过高可能意味着 fetch 批次设置过小,导致网络往返次数过多。 |
heartbeat-rate | 每秒心跳次数。 | 绝对关键:若为 0,消费者已离线。Broker 在 session.timeout.ms 后触发再平衡。 |
1.4 事务相关指标
事务指标是为 Kafka 高级应用(如 exactly-once 语义、流处理)定制的精密探测器。
- 核心概念:
lastStableOffset(LSO) 与LogEndOffset(LEO)。- LEO(日志末端位移):下一条将要被写入的消息的位移。
- LSO(最后稳定位移):对于开启了事务的 Topic,消费者(非
read_committed模式除外)最多只能消费到 LSO 所标记的位置。LSO 之前的消息要么已提交,要么已中止。任何处于进行中(未完成)事务的消息都在 LSO 之后。
- 监控指标:“事务空洞”大小 =
LogEndOffset-lastStableOffset。 - 监控意义:这个差值代表了有多少数据已经被写入日志,但对消费者而言是“不可见”的。一个不断增大的“事务空洞”是一个严重信号,它意味着有大量未完成的事务在阻塞消费者的消费进度,可能导致业务延迟。这通常与第 7 篇中讨论的事务协调器问题或生产者僵死有关。
2. 消费 Lag 的三种监控方案
消费 Lag 是 Kafka 监控中复杂度最高的指标之一。它是指生产进度与消费进度之间的差距,是衡量系统实时性的终极指标。监控 Lag 并非只是获取一个数值那么简单,如何评估这个数值的严重性、如何避免瞬时波动造成的误报,才是构建智能告警的核心。
2.1 方案一:CLI 工具 —— 即时快照
这是最直接、最传统的方式,利用 Kafka 自带的命令行工具获取消费组的实时状态。
# 查询特定消费组的 Lag
bin/kafka-consumer-groups.sh \
--bootstrap-server localhost:9092 \
--describe \
--group my-consumer-group
该命令会输出如下所示的信息,其中 LAG 列是我们关注的核心。
GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID HOST CLIENT-ID
my-group my-topic 0 12345 12350 5 consumer-1-xxx /127.0.0.1 consumer-1
my-group my-topic 1 54321 54421 100 consumer-2-yyy /127.0.0.1 consumer-2
- 工作原理:直接向 GroupCoordinator 和各个 Partition Leader 发起请求,获取消费者提交的
CURRENT-OFFSET和分区的LOG-END-OFFSET,并计算差值。 - 优势:即开即用,无需额外部署,是临时排查和脚本化监控的利器。结果直观,是事实的权威来源。
- 劣势:无持久化,无历史趋势。你只能看到当前时刻的快照,无法回溯 Lag 过去一分钟或一小时的走势。告警逻辑简单粗暴,通常只能设置一个绝对阈值,比如
LAG > 10000。这种方式极易受消费行为瞬时波动的影响,产生大量误报。
2.2 方案二:JMX 指标 —— 长期趋势
Kafka 客户端(Consumer)通过 JMX 暴露了丰富的 MBean,可以被 Prometheus 等监控系统拉取并持久化。
- 核心指标:
kafka.consumer:type=consumer-fetch-manager-metrics,client-id=xxx中的records-lag-max。 - 数据采集:通过 JMX Exporter(如
jmx_exporter)作为 Java Agent 运行在 Consumer 进程中,或者利用kafka-exporter这类独立组件通过模拟客户端来获取 Lag 信息。Prometheus 则定期从这些 Exporter 的/metrics端点拉取数据。 - 优势:
- 长期趋势存储:Prometheus 可以存储长达数月的指标数据,让你能清晰地绘制 Lag 的长期趋势图。
- 强大的 PromQL 聚合查询:可以对 Lag 数据进行求导,计算其增长率。
- 劣势:告警依据依然多基于阈值。尽管我们可以设置“Lag 的增长率 > 阈值”,但这仍然是一个相对固化的规则,无法自适应消费模式的变化。例如,一个定时批量消费的任务,其 Lag 会在任务间隙自然升高,然后迅速降低,固定的告警规则极易在这种情况下产生误报。
2.3 方案三:Burrow 智能状态机 —— 动态评估
LinkedIn 开源的 Burrow 正是为了解决上述痛点而生的。它不再通过单一阈值来判断 Lag 的健康度,而是为每个消费组维护一个内部状态机,通过分析 Lag 的历史行为来动态评估其是否真的“卡住”了。
flowchart TD
Start["开始监控某消费组分区"] --> Init["初始化滑动窗口<br/>填充 Lag 数据点"]
Init --> WindowFull{"窗口是否已填满?"}
WindowFull -- "否" --> Continue["继续采集"]
WindowFull -- "是" --> Calc["滑动窗口计算<br/>1. 计算Lag增长率<br/>2. 判断消费状态"]
Calc --> State{"当前状态判定"}
subgraph StateMachine ["消费组状态机"]
OK["OK: Lag稳定或下降"]
WARNING["WARNING: Lag上升但消费未停止"]
ERROR["ERROR: Lag持续上升且消费停止"]
end
State -- "Lag <= Commit" --> OK
OK -- "消费速率=0,Lag持续上升" --> WARNING
WARNING -- "消费速率恢复,Lag下降" --> OK
WARNING -- "消费速率持续为0,Lag持续上升" --> ERROR
ERROR -- "消费恢复" --> OK
ERROR --> Alert["触发告警"]
classDef stateOk fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#333;
classDef stateWarning fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,color:#333;
classDef stateError fill:#ffebee,stroke:#c62828,stroke-width:2px,color:#333;
class OK stateOk;
class WARNING stateWarning;
class ERROR stateError;
- 图表主旨概括:本流程图描绘了 Burrow 通过维护一个滑动窗口来计算 Lag 变化,并驱动其内部消费组状态机在 OK、WARNING、ERROR 三个状态间迁移的完整过程。
- 逐层/逐元素分解:
- 初始化与滑动窗口:Burrow 为每个分区的 Lag 维护一个时间窗口,持续采集 Lag 数据点。只有当窗口存满数据,后续的计算才具有统计意义。
- 核心计算:Burrow 的核心算法是计算滑动窗口内 Lag 的线性加权移动平均,从而得出 Lag 是处于稳定、上升还是下降的趋势,并评估消费者的提交行为。它不是简单地比较 Lag 值,而是分析 Lag 的变化模式。
- 消费组状态机:这是 Burrow 最智能的部分。
- OK: Lag 保持稳定或在下降。即使 Lag 的绝对值非常大,只要消费进度在持续追赶,状态就是 OK。
- WARNING: 消费者仍在提交位移,但消费速度持续跟不上生产速度,导致 Lag 在上升。这表示消费者有“掉队”风险,但尚未彻底停止。
- ERROR: 消费者已经停止提交位移(或提交极慢),且 Lag 在持续增加。这表明消费者几乎肯定已经“卡住”了,需要立即介入。
- 设计原理映射:这个设计完美解决了传统阈值告警的两个核心缺陷:无法感知上下文和无法识别模式。Burrow 通过滑动窗口引入了时间维度,通过状态机引入了行为模式识别。它模仿了一个资深运维人员的判断逻辑:“我知道它 Lag 了,但它是在追赶还是在原地踏步?”
- 工程联系与关键结论:Burrow 不是一个替代 Prometheus 的监控系统,而是一个专门的补充组件。Burrow 擅长判定,生成的事件可以作为高精度的告警源;Prometheus 擅长度量和可视化,提供详尽的图表用于排障。关键结论:Burrow 通过消费组状态机实现了从“被动接收数字”到“主动评估行为”的跨越。它不会因为一个批量消费任务的周期性 Lag 高峰而报警,这是因为它识别出 Lag 会在短时间内被迅速消费掉,系统整体处于 OK 状态。这极大地减少了运维告警的噪声。
2.4 方案对比与选型
| 方案 | 核心指标 | 持久性 | 评估方式 | 告警精度 | 实现复杂度 | 适用场景 |
|---|---|---|---|---|---|---|
| CLI 工具 | 瞬时 Lag | 无 | 静态阈值 | 低(高误报) | 极低 | 临时排查、脚本巡檢 |
| JMX 指标 | records-lag-max | 支持 | 静态/动态阈值、趋势 | 中(可设增长趋势) | 中 | 长期趋势分析、容量规划、与Prometheus体系结合 |
| Burrow | 消费组状态 (OK/WARNING/ERROR) | 支持 (存于其内部) | 智能状态机 | 高(低误报) | 高 | 作为核心告警源,生成高精度事件,对误报要求极高的生产环境 |
实战选型建议:一个成熟的生产级监控体系,不会单选,而是组合使用。典型的组合是:JMX 指标(通过 Prometheus) + Burrow。JMX/Prometheus负责全面的指标采集与可视化,Burrow则专门负责消费Lag的智能告警,其状态变更事件可以推送至 Alertmanager,作为P1/P2级别告警的主要来源。
3. Prometheus + Grafana 监控链路搭建
本节将搭建一条从 Exporter 数据采集到可视化分析与告警的完整链路。我们的目标不是罗列配置,而是揭示每个组件如何协同工作,形成一套可观测性管道。
sequenceDiagram
participant Kafka as Kafka Cluster
participant KEP as kafka-exporter
participant JXP as jmx_exporter
participant Prom as Prometheus Server
participant Graf as Grafana
participant AM as Alertmanager
participant Dev as DevOps
Note over Kafka, JXP: 采集层
Kafka->>KEP: API请求(模拟客户端)<br/>获取消费组Lag
Kafka->>JXP: HTTP GET /metrics<br/>拉取Broker JMX指标
Note over KEP, Prom: 存储与计算层
Prom->>KEP: Pull, HTTP GET /metrics<br/>获取Lag指标
Prom->>JXP: Pull, HTTP GET /metrics<br/>获取Broker指标
Prom->>Prom: 存储时序数据 & <br/>执行PromQL规则评估
Note over Prom, AM: 可视化与告警层
Graf->>Prom: 查询PromQL, 获取数据
Prom-->>Graf: 返回时序数据,渲染仪表盘
Prom->>AM: 触发告警规则<br/>发送告警事件
AM->>Dev: 发送告警通知<br/>(邮件/钉钉/PagerDuty)
Dev->>Graf: 通过链接跳转<br/>查看仪表盘排查
- 图表主旨概括:本序列图描绘了从 Kafka 集群采集指标数据,经由 Prometheus 存储与计算,最终到 Grafana 可视化和 Alertmanager 告警通知的完整数据流。
- 逐层/逐元素分解:
- 采集层:我们采用两种 Exporter。
kafka-exporter(独立部署)专门用于采集 Consumer Group Lag。jmx_exporter(作为 Broker Java Agent 或独立进程)通过 JMX 拉取 Broker 内部的各项性能指标。它们共同构成了数据的源头。 - 存储与计算层:Prometheus Server 遵循 Pull 模型,定期从两个 Exporter 的
/metrics接口抓取数据并存入库中。同时,它内置的 Rules Engine 会持续评估我们定义的 PrometheusRule 文件,一旦满足条件,就将告警推送给 Alertmanager。 - 可视化与告警层:Grafana 作为展示前端,通过 PromQL 查询 Prometheus 中的数据,以图标形式渲染。Alertmanager 负责对告警进行分组、抑制、静默,并最终通过邮件、钉钉等方式通知到运维人员(DevOps)。DevOps 收到告警后,直接点击告警信息中的链接,即可跳转到 Grafana 对应的仪表盘开始排障。
- 采集层:我们采用两种 Exporter。
- 设计原理映射:这套架构完美体现了管道-过滤器模式。指标数据像水流一样,依次经过采集(过滤、转换)、存储、计算、展示和路由各个处理单元。每个单元职责单一,可替换、可扩展。这与第 14 篇性能调优中“分层调优”的思路异曲同工。
- 工程联系与关键结论:Pull 模型是整个架构的基石,它意味着监控系统(Prometheus)不需要被监控方(Kafka/Exporter)主动推送,降低了耦合度,也天然支持服务发现。关键结论:这种 Pull 模型架构使得监控系统与 Kafka 集群的解耦更加彻底。Prometheus 通过服务发现机制,可以动态地发现新加入的 Broker 或 Exporter,而无需重启,极大地提升了整个监控体系的可扩展性和弹性。
3.1 数据采集:kafka-exporter 与 jmx_exporter 配置
kafka-exporter 配置
kafka-exporter 是一个用 Go 编写的轻量级组件,它模拟一个 Consumer Client 连接到 Kafka 集群,定期计算所有消费组的 Lag。
关键启动参数:
./kafka_exporter \
--kafka.server=kafka-broker1:9092 \ # 只需连接任意一个Broker即可发现整个集群
--kafka.server=kafka-broker2:9092 \
--kafka.server=kafka-broker3:9092 \
--sasl.enabled=true \ # 开启SASL认证(生产环境必须)
--sasl.mechanism=SCRAM-SHA-256 \
--sasl.username=monitor_user \
--sasl.password=monitor_pass \
--web.listen-address=:9308 \ # Exporter自身暴露/metrics的端口
--kafka.version=3.4.0 # 建议指定版本,避免协议探测带来额外开销
jmx_exporter 采集 Broker 指标
jmx_exporter 作为 Java Agent 随 Broker 进程启动,暴露内部 JMX MBean。
在 Broker 启动脚本中加入 Java Agent 参数:
export KAFKA_OPTS="$KAFKA_OPTS -javaagent:/opt/jmx_exporter/jmx_prometheus_javaagent.jar=7071:/opt/jmx_exporter/kafka-broker.yml"
kafka-broker.yml 配置文件内容示例:
lowercaseOutputName: true
rules:
# 采集所有指标,然后在Prometheus侧进行筛选
- pattern: ".*"
更精细化的配置可以只暴露我们关心的指标,以减小 /metrics 接口的响应大小和 Prometheus 的存储压力。
3.2 Prometheus 抓取配置
将 Exporter 的地址配置到 Prometheus 的 prometheus.yml 中的 scrape_configs 部分。
scrape_configs:
- job_name: 'kafka-exporter'
scrape_interval: 30s # Lag变化相对较慢,采样间隔可以略长
metrics_path: '/metrics'
static_configs:
- targets:
- 'kafka-exporter:9308' # 指向kafka-exporter服务
- job_name: 'kafka-brokers'
scrape_interval: 15s # Broker指标变化快,需要更密集的采样
metrics_path: '/metrics'
static_configs:
- targets:
- 'broker-1:7071'
- 'broker-2:7071'
- 'broker-3:7071'
3.3 关键 PromQL 查询语句
PromQL 是将指标数据转化为洞察的法宝。以下是几个关键查询:
- 计算消费 Lag (针对
kafka-exporter指标)
# 名为 'my-group' 的消费组在各个分区上的 Lag
kafka_consumer_group_lag{group_id="my-group"}
- 计算 Lag 的增长率 (预测趋势)
# 过去5分钟内,Lag的每秒增长速率。正值表示Lag在增加。
rate(kafka_consumer_group_lag{group_id="my-group"}[5m])
- 计算 ISR 收缩比例
# 如果 ISR 成员数少于分区副本数,则存在收缩
(kafka_broker_replicas - kafka_broker_isr) > 0
- 计算请求延迟的 99 分位数
# 计算Broker处理Produce请求的P99延迟
histogram_quantile(0.99, rate(kafka_network_requestmetrics_totaltimems_bucket{request="Produce"}[5m]))
3.4 Grafana 仪表盘的层次化布局
一个好的仪表盘应该像一本好书,引导读者从概览到细节、从宏观到微观。
flowchart TD
L1[“第1层: 集群全局概览<br/>--------<br/>• 整体吞吐量<br/>• 活跃Controller<br/>• 在线/离线分区数<br/>• 全集群ISR收缩状态”]
L2[“第2层: 单Topic / 消费组下钻<br/>--------<br/>• Topic维度的流入/流出字节<br/>• 选定消费组的全局Lag<br/>• 组内消费者在线状态”]
L3[“第3层: 单分区 / 单消费者分析<br/>--------<br/>• 单分区的Lag趋势<br/>• 单消费者的请求延迟/速率<br/>• Broker分区的Segment详情”]
L1 -- “点击仪表盘/选择变量” --> L2
L2 -- “进一步钻取” --> L3
- 图表主旨概括:此图定义了基于“全局 → 组件 → 单元”思想的 Grafana 仪表盘层级结构,旨在实现从宏观监控到微观排障的无缝下钻。
- 逐层/逐元素分解:
- L1 全局概览(红色警报区):这是任何运维人员打开后第一眼看到的面板。它只展示全集群级别的、“生死攸关”的指标:Controller 是否唯一、有无离线分区、ISR 收缩情况、集群总吞吐等。这里任何异常图标都应引发最高级别的关注。
- L2 主题/消费组下钻:从 L1 发现问题(如 ISR 收缩),点击进入此层,定位到是哪个 Topic 的哪个分区出了问题。或在 L1 发现总 Lag 飙升,在此层定位是哪个消费组落后了。Grafana 的模板变量在这里至关重要,通过变量下拉框可以自由切换 Topic 和 Consumer Group。
- L3 单元分析:最细致的一层。当你锁定到具体某个问题的分区或消费者实例后,在这里观察它的详细指标曲线,如单分区的
Log-End-OffsetvsCurrent-Offset,单消费者的 GC 时间、CPU 使用率等底层指标,用于最终确定根因。
- 设计原理映射:这与金字塔式的故障排查理念完全一致,从概括性的症状(L1)出发,逐步收敛到具体的病因(L3)。它也符合用户体验设计原则,避免在一个页面上堆砌海量信息,降低了认知负载。
- 工程联系与关键结论:Grafana 的
dashboard links和panel links功能是实现这个下钻体验的关键。你可以在一个面板上创建链接,点击后跳转到另一个仪表盘,并传递当前选择的 Topic、Partition 等变量。关键结论:一个设计精良的层次化仪表盘,能将指标的 MTTD(平均发现时间)从数小时降低到分钟级,因为它直接引导用户走完了排障推理的前几步。
4. 告警规则设计与分级策略
如果说监控体系是哨兵,那么告警就是哨兵拉响的警报。告警规则的设计质量,直接关系到运维团队的睡眠质量。一个糟糕的告警会让团队患上“狼来了”综合征,最终忽略真正的灾难。
flowchart LR
Symptom1["现象: 分区无Leader"] --> Rule_P0{"指标: OfflinePartitionsCount > 0<br/>持续时间: for 1m"}
Symptom2["现象: Controller脑裂"] --> Rule_P0_2{"指标: ActiveControllerCount != 1"}
Symptom3["现象: ISR严重收缩"] --> Rule_P1{"指标: UnderReplicatedPartitions > 0<br/>且持续上升 for 5m"}
Symptom4["现象: 消费严重积压一"] --> Rule_P1_2{"指标: records-lag-max > P1阈值<br/>且 Burrow状态 == ERROR<br/>持续时间: for 2m"}
Symptom5["现象: Burrow状态WARNING"] --> Rule_P2{"指标: Burrow状态 == WARNING<br/>持续时间: for 10m"}
Symptom6["现象: 请求延迟缓慢上升"] --> Rule_P2_2{"指标: TotalTimeMs P99 > 基线200%<br/>持续时间: for 15m"}
Rule_P0 & Rule_P0_2 --> P0["P0告警: 立即响应<br/>通知: 电话 + 即时通讯"]
Rule_P1 & Rule_P1_2 --> P1["P1告警: 紧急处理<br/>通知: 即时通讯 + 邮件"]
Rule_P2 & Rule_P2_2 --> P2["P2告警: 计划关注<br/>通知: 邮件/工单"]
subgraph AntiJitter ["误报防范"]
For_Duration["for持续时间: 避免瞬时抖动"]
Absent_Check["absent检测: 发现指标缺失"]
end
For_Duration -- "应用于" --> Rule_P0 & Rule_P1 & Rule_P2
Absent_Check -- "辅助" --> Rule_P0
classDef p0 fill:#ffcdd2,stroke:#b71c1c,stroke-width:2px,color:#000;
classDef p1 fill:#ffe0b2,stroke:#e65100,stroke-width:2px,color:#000;
classDef p2 fill:#fff9c4,stroke:#f57f17,stroke-width:2px,color:#000;
class P0 p0;
class P1 p1;
class P2 p2;
- 图表主旨概括:这是一棵从系统现象到告警级别判定的决策树,展示了如何将不同严重程度的指标异常映射到 P0/P1/P2 三级告警,并引入了关键的误报防范机制。
- 逐层/逐元素分解:
- 现象与规则节点:树的起点是我们在监控中看到的现象。每个现象都通向一个或多个由 PromQL 定义的告警规则。例如,数据不可用的现象直接关联到
OfflinePartitionsCount > 0。 - 告警级别判定:根据规则预定义的严重性,告警被分入三个等级。
- P0 (灾难):影响数据可用性或一致性的核心故障,需要运维人员立即放下手中一切事务介入。
- P1 (危机):数据可用性降级(如 ISR 收缩)或严重性能问题(如消费严重积压),若不处理可能演变为 P0 故障。
- P2 (隐患):潜在风险预警或性能退化,需要在工作时间或下一个计划窗口内处理。
- 误报防范:告警规则必须结合
for持续时间和absent()函数。for确保问题持续存在才告警,过滤掉瞬时毛刺。absent()则用于检测指标本身是否消失,这常常意味着 Exporter 或 Broker 已经宕机。
- 现象与规则节点:树的起点是我们在监控中看到的现象。每个现象都通向一个或多个由 PromQL 定义的告警规则。例如,数据不可用的现象直接关联到
- 设计原理映射:这套分级策略遵循了风险管理的优先级矩阵。它强迫团队对每一个故障场景的后果和紧急性进行预判,并固化为可自动执行的规则。
- 工程联系与关键结论:告警规则的阈值不是凭空设定的,它们必须源于对系统基线的监控和学习。例如,P2 请求延迟的阈值“基线 200%”,需要你先用一段时间观察出业务高峰期下的正常延迟基线。关键结论:告警的分级和设计是一个持续迭代的过程,好的告警是“精而不在多”。每次误报或者漏报后,都应复盘并调整规则,让这个决策树进化得越来越精准。
4.1 关键 PrometheusRule 定义
以下是一些核心告警规则的 YAML 定义。
groups:
- name: kafka_critical_alerts
rules:
# P0 告警:分区不可用
- alert: KafkaPartitionOffline
expr: kafka_controller_kafkacontroller_offlinepartitionscount > 0
for: 1m # 持续1分钟,过滤瞬时抖动
labels:
severity: P0
annotations:
summary: “Kafka 分区不可用!”
description: “集群发现有 {{ $value }} 个分区无 Leader,数据读写已中断。”
# P0 告警:Controller 异常
- alert: KafkaControllerNotUnique
expr: sum(kafka_controller_kafkacontroller_activecontrollercount) != 1
for: 1m
labels:
severity: P0
annotations:
summary: “Kafka Controller 唯一性异常!”
description: “当前 Cluster 中活跃 Controller 数量为 {{ $value }},期望值为 1。”
# P1 告警:ISR 收缩 (动态阈值示例)
- alert: KafkaISRShrinking
expr: (kafka_server_replicamanager_underreplicatedpartitions) > 0
for: 5m
labels:
severity: P1
annotations:
summary: “Kafka 集群出现 ISR 收缩”
description: “当前有 {{ $value }} 个分区处于 under-replicated 状态,已持续5分钟。”
# P2 告警:指标缺失
- alert: KafkaExporterMissing
expr: absent(kafka_consumer_group_lag)
for: 5m
labels:
severity: P2
annotations:
summary: “kafka-exporter 指标缺失”
description: “在过去5分钟内,Prometheus 未能从 kafka-exporter 抓取到任何 lag 指标。请检查 exporter 状态。”
5. Spring Kafka 与 Kafka Streams 的监控整合
对于 Java 和 Spring 生态的开发者而言,监控不仅需要基础设施视角,更需要与应用视角无缝融合。Spring Boot 通过 Micrometer 和 Actuator 提供了这种可能性,让我们能够像管理业务指标一样管理 Kafka 客户端指标。
5.1 通过 Micrometer 自动注册 Kafka 客户端指标
Micrometer 是一套指标门面,而 Spring Kafka 在内部已经集成了它。从 Spring Kafka 2.3 版本开始,默认会自动将 KafkaProducer 和 KafkaConsumer 的关键指标绑定到 Spring 管理的 MeterRegistry 中。你几乎不需要任何额外的代码。
启用并配置指标绑定 (application.yml)
spring:
kafka:
producer:
# ... producer 的其他配置
client-id: my-producer-app
properties:
“[spring.kafka.metrics.export.enabled]”: true # 显式启用指标导出
consumer:
# ... consumer 的其他配置
client-id: my-consumer-app
properties:
“[spring.kafka.metrics.export.enabled]”: true
# ...
management:
endpoints:
web:
exposure:
include: “prometheus, health, metrics, info” # 暴露prometheus端点
metrics:
tags:
application: “my-kafka-service” # 为所有指标添加应用标签
完成上述配置后,你会发现在 /actuator/metrics 和 /actuator/prometheus 端点中,会自动出现以 spring_kafka 前缀的指标,例如 spring_kafka_consumer_records_consumed_total。
5.2 Kafka Streams 指标监控
Kafka Streams 应用的监控更为复杂,因为它内部涉及多线程处理、状态存储等。Spring Kafka Streams 同样提供了 Micrometer 集成。
@Configuration
public class KafkaStreamsConfig {
@Bean(“defaultKafkaStreamsConfig”)
public KafkaStreamsConfiguration defaultKafkaStreamsConfig() {
// ... StreamsBuilder 和属性配置
return new KafkaStreamsConfiguration(props);
}
// 关键:添加 KafkaStreamsMicrometerListener,自动将 Streams 指标注册到 MeterRegistry
@Bean
public KafkaStreamsMicrometerListener kafkaStreamsMicrometerListener(MeterRegistry meterRegistry) {
return new KafkaStreamsMicrometerListener(meterRegistry);
}
}
添加此 Bean 后,Kafka Streams 的指标(如 kafka.streams:type=stream-metrics 下的 process-rate, commit-rate, state-latency-avg 等)都会被注册到 Micrometer 中,并通过 Actuator 暴露。这些指标对于理解 Streams 应用的内部状态至关重要:
process-rate: 处理速率,反映拓扑的计算能力。commit-rate: 位移提交频率,过高可能带来 Broker 压力,过低可能增加故障恢复时间。state-cleanup-rate: 状态清理速率,与状态存储的大小压缩有关。
这使得我们可以将 Kafka Streams 应用也纳入到统一的 Grafana 仪表盘和告警体系中来,实现全链路的可观测性。
6. 故障模拟与监控验证
理论的价值需要实践来检验。本节我们将模拟三种典型的故障场景,并验证我们刚刚构建的监控与告警体系是否能有效运作。
6.1 故障一:消费积压告警验证
操作步骤:
- 启动生产者,持续以 1000 msg/s 的速度向 Topic
perf-test写入数据。 - 启动消费者组
lag-test-group进行消费,一切正常。 - 模拟故障:暂停消费者进程
kill -STOP <consumer_pid>5 分钟。 - 5 分钟后恢复消费者:
kill -CONT <consumer_pid>。
预期现象与验证:
-
验证 PromQL:在 Prometheus 控制台执行查询。
kafka_consumer_group_lag{group_id="lag-test-group", topic="perf-test"}图表应显示 Lag 值在消费者停止后线性急剧上升,在消费者恢复后迅速下降为零。
-
仪表盘变化:在 Grafana 的 “Consumer 概览面板” 中,
lag-test-group的 Lag 曲线会在暂停期间形成一个陡峭的上扬,随后是一个向下的消费追赶轨迹。 -
告警触发日志:
- Burrow 告警 (如果部署):在消费者暂停约 2-3 分钟后,你会在 Burrow 的日志和 Email 中看到状态从
OK变为ERROR。 - Prometheus 告警:如果配置了
records-lag-max > 100000的 P1 规则,并设置了for: 2m,那么在大约 2 分多钟后,Alertmanager 会收到KafkaConsumerLagHigh告警,并发送通知。
- Burrow 告警 (如果部署):在消费者暂停约 2-3 分钟后,你会在 Burrow 的日志和 Email 中看到状态从
-
结论:此实验完美验证了对消费者“僵死”故障的监控能力。尤为重要的是,即使我们设置了
for: 2m的门槛,Burrow 也可能更早地报出ERROR,因为它通过分析消费速率模式,可能在第 1 分钟就判定消费者“停止了消费行为”,而非等到 Lag 的绝对值达到阈值。
6.2 故障二:ISR 收缩告警验证
操作步骤:
- 创建 Topic
isr-test,replication.factor=2,min.insync.replicas=1。 - 确定一个 Follower 所在的 Broker,通过工具(如
tc)或模拟高负载,使其磁盘 I/O 严重延迟。# 在目标Broker上,为流入的复制流量增加10ms延迟和0.1%丢包 tc qdisc add dev eth0 root netem delay 10ms loss 0.1% - 持续向
isr-test写入高压数据。
预期现象与验证:
-
验证 PromQL:执行查询监控 ISR 收缩。
(kafka_server_replicamanager_underreplicatedpartitions) > 0在故障注入后,查询结果将从
0变为1或更大,代表该 Topic 的分区出现了 ISR 收缩。 -
仪表盘变化:在 Grafana 的 “Broker 概览面板” 中,
UnderReplicatedPartitions仪表会跳到非零值,通常以醒目的红色闪烁。 -
告警触发日志:若配置了
KafkaISRShrinking的 P1 规则,在持续 5 分钟后,你将收到告警通知,明确指出有分区处于 under-replicated 状态。 -
结论:
UnderReplicatedPartitions是 ISR 机制健康状况最直接的“体温计”。此故障模拟验证了通过监控 ISR 变化,可以比 Producer 感受到延迟或错误之前,更早地发现 Broker 的 IO 性能问题。这是一种典型的下游指标 (客户端) 和上游指标 (服务器端) 的联动分析案例。
6.3 故障三:Burrow 智能判定 vs. 简单阈值告警
这是最能体现 Burrow 价值的实验。
操作步骤:
- 创建一个消费者组
burrow-test-group,消费 Topicwave-topic。 - 生产者以正弦波模式生产消息:生产 5 分钟,然后停止 5 分钟,循环往复。这模拟了一种周期性的批量ETL任务。
- 设置一个简单的阈值告警:
records-lag-max > 50000。
现象对比与验证:
-
简单阈值告警:会反复触发!在生产停止的 5 分钟内,消费者追上了所有消息,Lag 为零。然后下一波生产开始,Lag 迅速爬升,每 5 分钟就会超过 50000 阈值,触发一次告警。运维人员会疯掉。
-
Burrow 判定:Burrow 观察到 Lag 的周期性升降,并识别出当 Lag 上升时,消费者仍在积极提交位移、消费速度不为零。因此,它会将消费组状态标记为
WARNING(表示存在滞后但仍在处理)或保持在OK状态。最重要的是,它不会触发ERROR,因为它判断消费者没有“卡住”。 -
结论:这个实验直击要害:基于阈值的告警系统在非稳态消费模式下会产生大量噪音,而 Burrow 的行为模式分析能透过现象看本质,区分“波动的 Lag”和“阻塞的消费者”。 这就是为什么在复杂业务场景下,Burrow 是不可或缺的高级智能组件。
7. 面试高频专题
Q1: UnderReplicatedPartitions 和 OfflinePartitionsCount 这两个 Broker 指标分别反映了什么问题?哪一个更严重?
- 一句话回答:
UnderReplicatedPartitions指部分同步副本收缩,导致数据冗余度下降;OfflinePartitionsCount指分区完全没有 Leader,导致数据完全不可读写,后者是绝对性的灾难,远比前者严重。 - 详细解释:
UnderReplicatedPartitions代表 ISR 集合中的 Follower 没有及时追上 Leader。集群仍在提供读写服务,但每个出问题的分区数据备份减少,若此时 Leader 宕机,可能面临数据丢失。OfflinePartitionsCount表示所有持有该分区副本的 Broker 都不可用,该分区直接停摆。任何生产或消费请求都会失败。 - 追问与回答:
- 追问 1: 遇到
UnderReplicatedPartitions > 0的告警,你的排查思路是什么?- 答:首先,我会在 Grafana 上查看受影响分区所在 Broker 的资源指标,特别是磁盘 I/O Util 和网络吞吐。然后查看 Broker 日志,寻找与 ISR 收缩或 Leader 选举相关的 WARN。接着,我会检查是否是集群滚动重启或网络抖动导致的短暂波动,关注其持续时间。
- 追问 2: 如果
OfflinePartitionsCount > 0且 Unclean Leader Election 没有开启,如何恢复?- 答:必须尽快修复宕机的 Broker。如果短时间内无法修复,可能需要考虑手动进行分区重分配,将离线分区的副本分配到健康的 Broker 上,或者作为最后手段,开启 Unclean Leader Election 以保可用性,但接受数据损失。
- 追问 3: 如何设计一个提前预防
OfflinePartitionsCount问题的告警?- 答:可以对
OfflinePartitionsCount的前置指标告警。例如,如果某个 Broker 的kafka_server_replicamanager_underreplicatedpartitions长时间只增不减,且磁盘SMART检查出现异常,就提前发出严重告警。
- 答:可以对
- 追问 1: 遇到
Q2: Burrow 的 Lag 评估算法与简单的阈值告警有何本质区别?为什么 Burrow 能减少误报?
- 一句话回答:简单阈值告警评估的是 Lag 的绝对值,而 Burrow 的评估算法评估的是 Lag 的变化趋势和速度,因此它能区分正常的消费波动和真正的消费停滞。
- 详细解释:简单的阈值告警是基于“状态(State)”的,例如“此刻 Lag 大于 10000”。Burrow 的算法是基于“模式(Pattern)”的,它维护滑动窗口,计算加权移动平均,判断的是“在过去一段时间内,Lag 是以一个怎样的加速度在增长?消费者还在提交位移吗?”。一个定时批量消费的消费者组(如每晚凌晨的报表作业),Lag 会周期性地升高然后归零,阈值告警就会频繁误报。而 Burrow 会识别出这个模式,并始终将其维持在 OK 状态,因为消费者的行为是健康的。
- 追问与回答:
- 追问 1: Burrow 的 WARNING 状态和 ERROR 状态的核心区别是什么?
- 答:核心区别在于消费者是否还在提交位移。WARNING 状态下,Burrow 检测到位移在持续提交,但 Lag 仍在增长,说明“掉队了但还在挣扎”。ERROR 状态下,Burrow 检测到位移提交停止或几乎停止,而 Lag 在飞涨,说明“彻底卡住了”。
- 追问 2: 如果一个消费者停顿了 <2 秒钟后又恢复,Burrow 会报错吗?
- 答:通常不会。Burrow 的评估是基于滑动窗口的,通常窗口长度在分钟级。几秒钟的短暂停顿会被这个滑动窗口平滑掉,被视为正常的执行抖动,不会触发状态变化。
- 追问 3: 在一个不使用 Burrow 的环境中,如何用 PromQL 近似实现类似的效果?
- 答:可以用
rate(kafka_consumer_group_lag[5m])计算 Lag 的增长率,并结合rate(consumer_fetch_manager_records_consumed_rate[5m])判断消费是否停滞。如果 Lag 增长率 > 0 且消费速率 == 0,持续一段时间,就可以近似判定为“类 ERROR”状态。但精度和鲁棒性不如 Burrow。
- 答:可以用
- 追问 1: Burrow 的 WARNING 状态和 ERROR 状态的核心区别是什么?
Q3: 如何通过 lastStableOffset 和 LogEndOffset 监控事务空洞?空洞变大意味着什么?
- 一句话回答:事务空洞大小 =
LogEndOffset-lastStableOffset,它代表被未完成事务阻塞、对消费者不可见的消息数量。空洞持续增大会导致 read_committed 消费者滞后,甚至数据处理中断。 - 详细解释:
lastStableOffset(LSO) 是事务性 Topic 中最后一个已提交(或中止)的位移。任何处于ONGOING状态的事务都会位于 LSO 之后。LogEndOffset(LEO) 是日志末尾。如果一个生产者开启了事务并写入了一批消息,但事务迟迟未提交(生产者僵死、网络分区等),LSO 就会停滞,而 LEO 随着其他非事务消息或新事务的写入继续前进,二者之间的“空洞”就会越来越大。 - 追问与回答:
- 追问 1: 发现事务空洞持续增大,你的第一反应是什么?我应该去查看哪个组件的日志?
- 答:我会首先怀疑有生产者处于僵死状态。我会去查看对应 Topic 的 Producer 日志,查找长时间未提交的事务
transactional.id。同时,我也会检查transaction-coordinator的日志,看是否有关于事务超时的记录。
- 答:我会首先怀疑有生产者处于僵死状态。我会去查看对应 Topic 的 Producer 日志,查找长时间未提交的事务
- 追问 2: 这对非事务消费者(read_uncommitted)有影响吗?
- 答:完全没影响。
read_uncommitted的消费者会忽略所有事务标记,直接消费到 LEO,包括那些属于未提交事务的消息。
- 答:完全没影响。
- 追问 3:
transaction.timeout.ms配置项与事务空洞有何关系?- 答:该配置项定义了非活跃事务的超时时间。如果生产者在此时间内既没有提交也没有中止,事务协调器将主动中止该事务,从而推动 LSO 前进,清除事务空洞。因此,一个健康的空洞大小应在大部分时间内趋近于零,偶尔的短暂增大也会因超时机制而消失。
- 追问 1: 发现事务空洞持续增大,你的第一反应是什么?我应该去查看哪个组件的日志?
Q4: Grafana 仪表盘的设计原则是什么?如何避免一个面板过于复杂?
- 一句话回答:核心原则是“分层、聚焦、统一”,通过从全局到局部的层次化下钻设计避免单一面板的信息过载,并通过单位、颜色和卡片布局的统一性降低认知成本。
- 详细解释:
- 分层:如正文所述,使用“概览 → 场景下钻 → 单实例分析”的层级结构。
- 聚焦:每个面板只回答一个核心问题。例如,“集群总吞吐量”和“ISR收缩详情”应放在不同面板或行,一个回答“忙不忙”,一个回答“稳不稳”。
- 统一:相同类型的面板使用相同的单位、颜色、横纵轴刻度范围。例如,所有延迟图都用 Y 轴显示毫秒,所有速率图都用
req/s。这能快速形成视觉习惯。
- 追问与回答:
- 追问 1: 新接手一个复杂的 Kafka 集群,你会如何着手为其设计第一版监控仪表盘?
- 答:我会遵循“黄金信号”法则。先搞定四大黄金信号:延迟、流量、错误、饱和度。对应到 Kafka 就是:请求延迟、网络吞吐、错误率/重试率、磁盘/网络/CPU饱和度。然后再加入 Kafka 特有的健康信号:ISR、Controller、Lag。确保第一版仪表盘覆盖这些基础信号即可。
- 追问 2: 你会在仪表盘上放置静态阈值线吗?有什么好处?
- 答:会的,尤其是表达容量或 SLA 的指标。例如,在 Broker 的网络流量图上画一条网卡带宽 80% 的黄色预警线(
bytes/sec)。运维者一看便知离容量瓶颈还有多远,而不需要心算,这就是可视化增强直觉感知的价值。
- 答:会的,尤其是表达容量或 SLA 的指标。例如,在 Broker 的网络流量图上画一条网卡带宽 80% 的黄色预警线(
- 追问 3: 如何处理因“消费者组重平衡”而产生的指标锯齿状波动,让图表更清晰?
- 答:可以在 PromQL 查询中使用
avg_over_time()或max_over_time()进行平滑处理。更重要的是,如果观察到这种锯齿波动发生,应创建一个专门的面板来监控重平衡速率 (rebalance-rate),将现象本身(Lag锯齿)和根因(重平衡)分开,使两个面板都更清晰。
- 答:可以在 PromQL 查询中使用
- 追问 1: 新接手一个复杂的 Kafka 集群,你会如何着手为其设计第一版监控仪表盘?
Q5: 描述一个完整的 Prometheus 告警的生命周期,从规则配置到通知恢复。
- 一句话回答:生命周期始于 Prometheus 规则评估触发告警,经过 Alertmanager 的分组/抑制/静默路由,最终通过集成通知到运维人员,排查后标记为解决,整个过程形成一个闭环。
- 详细解释:1. 触发: 假设
KafkaISRShrinking规则的expr返回 True 并持续超过for 5m,Prometheus 生成一个告警,状态为pending->firing。 2. 路由: Prometheus 将firing状态的告警推送到 Alertmanager。Alertmanager 根据路由树(route配置)将告警分发给对应的接收器。3. 处理: 在分发前,Alertmanager 可能会进行分组(将多个同类告警合并成一条消息)、抑制(如果已有更高级别告警,静默此条)、静默(运维人员可手动设置静默时间窗口)。4. 通知: Alertmanager 通过 Email/PagerDuty/Webhook 等接收器发送通知。5. 恢复与闭环: 当指标恢复正常,Prometheus 会发送一个resolved事件给 Alertmanager,后者通知运维人员“问题已自动恢复”。最终由人工在工单系统中关闭。 - 追问与回答:
- 追问 1: Alertmanager 的“分组”功能有何价值?请举例。
- 答:价值在于防止告警风暴。比如,一个核心交换机故障可能导致下游 50 个 Broker 同时出现 ISR 收缩。如果没有分组,你会收到 50 封邮件。有了分组,Alertmanager 可以将这 50 个告警聚合成一条消息:“多个 Broker 出现 ISR 收缩,受影响 Broker: [broker-1, broker-2, ...]”,避免了轰炸。
- 追问 2: 什么是告警的“死信”问题,如何通过
absent函数解决?- 答:死信是指整个监控链路中断,导致没有任何指标上报,因而也永远不会触发告警。例如,
kafka-exporter进程挂了,records-lag-max > threshold这个规则就永远不会被触发,因为根本没有数据。absent(kafka_consumer_group_lag)正是用来专门检测这类问题的,它不依赖于任何指标值,而是检测指标是否存在。
- 答:死信是指整个监控链路中断,导致没有任何指标上报,因而也永远不会触发告警。例如,
- 追问 3: 如何测试我写的一条告警规则是否有效?
- 答:最安全的方式是使用 Prometheus 的表达式浏览器,输入规则中的
expr,查看其历史输出。如果历史数据中出现过 >0 的情况,且持续时间满足了for条件,那么理论上该规则就会触发。更进一步,可以在测试环境中专门搭建独立的 Prometheus 实例并灌入重放的时序数据来精确验证。
- 答:最安全的方式是使用 Prometheus 的表达式浏览器,输入规则中的
- 追问 1: Alertmanager 的“分组”功能有何价值?请举例。
Q6: Spring Kafka 应用中,records-lag-max 指标飙升,但应用日志无异常,从监控视角如何排查?
- 一句话回答:结合 JVM 和操作系统指标向下排查,依次检查消费者应用本身的 GC 停顿、处理逻辑耗时、线程池利用率,以及下游依赖的性能,定位是消费者“懒了”还是“堵了”。
- 详细解释:Lag 是最终结果。应用日志无异常说明消费者线程没有报错,它可能只是在慢。要么是处理逻辑慢,要么是 JVM GC 导致的世界暂停(STW),要么是消费后写入的数据库/缓存等下游依赖变慢堵住了回压。
- 追问与回答:
- 追问 1: 如何利用 Micrometer 指标精准定位是哪个 Consumer 成员慢?
- 答:查看
kafka_consumer_fetch_manager_records_consumed_rate指标,并按client_id标签分组。如果某个成员的消费速率显著低于同组其他成员,它就是木桶的短板。再结合kafka_consumer_coordinator_rebalance_latency_avg,看它是否经常导致重平衡。
- 答:查看
- 追问 2: 消费者“假死”如何通过监控发现?
- 答:观察
heartbeat-rate。如果该指标掉零,消费者要么是真正的 JVM 进程挂起(长 GC),要么是网络分区,但 Coordinator 尚未将其踢出组。这是“假死”,此时它不拉取消息也不处理,Lag 急剧上升。
- 答:观察
- 追问 3: 如何区分是消费者处理慢还是简单消费能力不足?
- 答:查看
fetch-rate和fetch-latency-avg。如果fetch-rate很低且fetch-latency正常,但records-consumed-rate也很低,说明消费者在拉取端就不积极,可能是处理逻辑或下游调用阻塞了poll循环。如果fetch-rate很高但有效消费速率低,可能说明正在处理大量重复或无效的消息。
- 答:查看
- 追问 1: 如何利用 Micrometer 指标精准定位是哪个 Consumer 成员慢?
Q7: 如何对 Kafka Streams 应用进行有效的健康监控?
- 一句话回答:从线程活跃度、状态存储和拓扑处理这三个维度,监控线程死亡、状态存储大小变化和拓扑节点的处理速率。
- 详细解释:Kafka Streams 的健康远比普通 Consumer 复杂。需要关注:1. 线程模型:
stream-metrics下的alive-thread-count应恒等于配置的线程数。任何下降都意味着有处理线程意外退出。2. 状态存储:RocksDB 的大小、写入速率和压缩率。状态目录磁盘使用率是重要的饱和度指标。3. 拓扑处理:每个拓扑节点的process-rate、punctuate-rate等,用于识别处理瓶颈。 - 追问与回答:
- 追问 1: 如果
alive-thread-count下降了一半,会发生什么?- 答:分配给该实例的任务会重新平衡到其他线程或实例上,进行“修复再平衡”。如果线程是异常退出,可能导致数据重复处理(如果开启 at-least-once)或任务卡住。
- 追问 2: RocksDB 的
block-cache-hit-rate指标很重要吗?- 答:是的,它直接反映了状态查询的效率。如果该值过低,意味着大量查询穿透到磁盘,处理延迟会显著增加,从而导致
process-rate下降和消费 Lag 堆积。
- 答:是的,它直接反映了状态查询的效率。如果该值过低,意味着大量查询穿透到磁盘,处理延迟会显著增加,从而导致
- 追问 3: 如何优雅地将 Kafka Streams 的
state-cleanup-rate与告警结合?- 答:
state-cleanup-rate是状态清理速率。如果数据写入量很大但清理速率为零或极低,说明状态存储可能在无限增长,磁盘容量将面临饱和风险。可以设置一个 P2 告警:当 RocksDB 磁盘使用率超过 70% 且state-cleanup-rate持续低于某阈值时发出提醒。
- 答:
- 追问 1: 如果
Q8: 在设计大规模集群的监控方案时,你会如何解决 Prometheus 自身的高可用问题?
- 一句话回答:通过部署多个配置完全相同、独立的 Prometheus Server,并在上游使用 Thanos/Cortex 或 VictoriaMetrics 等聚合层来实现全局视图和高可用。
- 详细解释:单点 Prometheus 存在故障风险和存储与性能的垂直瓶颈。高可用方案通常不是说两个 Prometheus 之间做主从,而是部署两个(或更多)完全相同的、独立的实例,同时抓取所有目标,形成数据冗余。然后通过 Thanos/Cortex 等组件进行全局去重、长期存储和历史数据的聚合查询。
- 追问与回答:
- 追问 1: 为什么不用 Prometheus 自身联邦模式做 HA?
- 答:联邦模式主要用于跨数据中心或多级网络的分层架构,将下级 Prometheus 的聚合指标抓取到上级。它不适合做故障转移的 HA,因为主备切换时存在数据断点,且配置复杂。更现代的做法是同样的数据多写多存。
- 追问 2:
remote_write和remote_read在这个架构中扮演什么角色?- 答:
remote_write用于将本地 Prometheus 的时序数据实时发送到远程、高可用的长期存储后端(如 Cortex, VictoriaMetrics, M3DB)。remote_read则允许 Prometheus 或 Grafana 在查询时,透明地从本地短期存储和远程长期存储之中拉取历史数据。
- 答:
- 追问 3: 如果不使用任何额外组件,仅用 Prometheus + Grafana,如何实现最简单的“准高可用”?
- 答:可以部署两个完全一样的 Prometheus Server。在 Grafana 中配置两个 DataSource,主数据源指向 Prometheus-1,备用指向 Prometheus-2。但这需要人工切换,或使用一些简单的代理层来做健康检查和切换。无法自动实现无缝切换,只能算是灾备。
- 追问 1: 为什么不用 Prometheus 自身联邦模式做 HA?
Q9: Kafka 监控中,哪些指标可以反映网络层面的问题?
- 一句话回答:Broker 侧的
NetworkProcessorAvgIdlePercent、request-latency和incoming/outgoing-byte-rate结合客户端侧的fetch-latency-avg和record-retry-rate是诊断网络问题的关键组合。 - 详细解释:
NetworkProcessorAvgIdlePercent表示网络处理线程空闲百分比,持续低于 0.2 代表网络层繁忙。如果incoming/outgoing-byte-rate接近网卡极限,则网络可能是瓶颈。若 Broker 侧request-latency很高,但本地处理和 IO 延迟均低,那延迟就是花在了网络传输上。 - 追问与回答:
- 追问 1: Producer 的
record-retry-rate升高一定是 Broker 端网络问题吗?- 答:不一定。但也可能与 Broker 端的请求队列有关。如果有队列(
NetworkProcessorAvgIdlePercent低),请求无法及时处理,Producer 等待request.timeout.ms后就会重试。也可能是纯网络丢包。
- 答:不一定。但也可能与 Broker 端的请求队列有关。如果有队列(
- 追问 2: 如何监控 Broker 之间的内部复制网络?
- 答:核心指标是 Broker 的
FollowerReplicationBytesInPerSec和LeaderReplicationBytesOutPerSec。结合一个监控面板,显示 Follower 的复制延迟ReplicationBytesInPerSec通常反映它的网络带宽占用。如果它打满,一定会出现 ISR 收缩。
- 答:核心指标是 Broker 的
- 追问 1: Producer 的
Q10: 当Kafka集群数据量非常大,导致Prometheus存储压力大时,你的优化思路是什么?
- 一句话回答:从优化采样频率、过滤无用指标、聚合降采样( Recording Rules )和使用更高效的远程存储这几个方面入手。
- 详细解释:1. 降低抓取频率: 对变化慢的指标(如Lag,ISR),将
scrape_interval从15s调整为30s或1m。2. Relabel过滤: 在prometheus.yml的metric_relabel_configs中,丢弃那些你不关心的、高基数的指标(如某些永远不会产生告警的内部指标)。3. Recording Rules: 这是最根本的解决大数据量查询问题的方法。将耗时的聚合查询(如rate(...[5m]))预计算并存储成一个新的指标,仪表盘和告警直接查询这个新的小数据量指标。4. 远程存储: 将数据remote_write到 VictoriaMetrics 或 Thanos 中,它们有更好的压缩算法和水平扩展能力。 - 追问与回答:
- 追问 1: 什么是“高基数”指标,它对 Prometheus 有什么危害?
- 答:指-label 的取值非常多的指标,例如直接暴露
user_id作为标签的指标。高基数会急剧增加内存占用和索引大小,导致插入和查询都变得极其缓慢,是 Prometheus 的大忌。必须通过 relabel 丢弃。
- 答:指-label 的取值非常多的指标,例如直接暴露
- 追问 2: 能否举例说明一个适合用 Recording Rule 优化的场景?
- 答:监控 Lag 增长率的时候,
rate(kafka_consumer_group_lag[5m])这个查询需要遍历原始指标 5 分钟的所有采样点来计算。一个 Recording Rule 可以每分钟预先计算好这个值,存为kafka_consumer_group_lag:rate5m。后续告警和面板直接查这个新指标,查询成本降低几十倍。
- 答:监控 Lag 增长率的时候,
- 追问 1: 什么是“高基数”指标,它对 Prometheus 有什么危害?
Q11: 简述如何将Micrometer中自定义的 Kafka 业务处理耗时指标添加到监控中。
- 一句话回答:使用
@Timed注解或在代码中注入MeterRegistry,创建一个Timer,在消息处理的开始和结束处记录时间,Micrometer 就会自动暴露该指标。 - 详细解释:最优雅的方式是在你的
@KafkaListener方法上添加@Timed(value = “kafka_message_process_duration”, extraTags = {“topic”, “#message.topic”})。你需要一个TimedAspectBean 来让这个注解生效。这样,每次消息处理的耗时就会被记录到名为kafka_message_process_duration_seconds的 Timer 中,并带有 Topic 作为标签,通过/actuator/prometheus暴露。 - 追问与回答:
- 追问 1: 用
@Timed和手动meterRegistry.timer(...).record(),有何选择上的考量?- 答:
@Timed方便、声明式,适合场景简单、想监控整个监听器方法的情况。手动方式更精细,你可以只监控方法中特定代码块的耗时,或者记录处理成功/失败的不同耗时,灵活性更高。
- 答:
- 追问 2: 这个自定义指标能用于 Prometheus 告警吗?
- 答:当然。它在 Prometheus 中就是一个标准的指标。你可以写一条规则
rate(kafka_message_process_duration_seconds_sum[5m]) / rate(kafka_message_process_duration_seconds_count[5m]) > 1,来监控消息处理的平均耗时,当超过 1 秒时告警。
- 答:当然。它在 Prometheus 中就是一个标准的指标。你可以写一条规则
- 追问 1: 用
Q12: (系统设计题) 一个全球部署的金融交易系统,使用 Kafka 作为核心消息总线,需要在北京、伦敦、纽约三地部署集群并相互同步(MirrorMaker 2)。请设计一个多集群统一的监控告警架构。
- 一句话回答:核心思想是“联邦采集,集中可视化与告警”。在每个数据中心部署独立的 Prometheus + Exporter 集群,通过
remote_write将关键聚合指标或所有数据写入到一个全局的、高可用的监控中心(如 Thanos),实现统一的可视化和跨集群告警。 - 详细解释:
- 各数据中心(DC)采集: 北京、伦敦、纽约每个 DC 内部署一个或多个高可用的 Prometheus 实例,专门负责采集本 DC 内 Kafka 集群、Exporter、MirrorMaker 2 的指标。这是“联邦”的叶子节点。
- 全局监控中心: 在全球选一个节点(如总部所在 DC),部署一个 Thanos/Cortex 集群作为全局数据汇聚点。所有 DC 的 Prometheus 通过
remote_write将数据发送至此。 - 统一可视化: 部署一个或多个全局的 Grafana,其数据源指向 Thanos Query 前端。Thanos Query 能自动去重来自多个数据中心的相同数据,并透明地查询。
- 分层告警:
- 本地告警: 各 DC 的 Prometheus 负责评估本 DC 内的紧急规则(如本地 Broker 宕机、MM2 延迟),并推送到本地 Alertmanager 直接通知本地运维团队。
- 全局告警: 全球 Thanos Ruler 负责评估跨 DC 的业务级告警,如“全球交易总 Lag 超过阈值”、“任一集群的 Controller 异常”等,并推送到一个全局 Alertmanager 通知全球运维指挥中心。
- 关键指标: 必须包含 MirrorMaker 2 的
replication-latency-ms,它体现了跨集群数据同步的延迟,是衡量全球数据一致性的核心。
- 追问与回答:
- 追问 1: 这种跨广域网的场景,Prometheus 的
remote_write需要关注什么配置?- 答:需要重点关注
remote_write的队列配置,如capacity、max_shards、min_backoff和max_backoff。为了防止远端临时不可用导致数据丢失,必须配置足够大的本地队列容量。同时,要考虑广域网带宽成本,可以通过 Recording Rules 聚合并提前丢弃不必要的指标,只将聚合后的核心指标写入全局。
- 答:需要重点关注
- 追问 2: 如果 MirrorMaker 2 出现高延迟,你如何通过这个架构快速定位是哪个集群到哪个集群的链路问题?
- 答:我会在全局 Grafana 中打开一个名为“MM2 全球拓扑视图”的面板,这个面板会按
source_cluster和target_cluster展示所有 MM2 实例的replication-latency-ms热力图。一眼就能看出比如“伦敦 -> 纽约”的所有实例延迟都飙高,从而锁定是伦敦到纽约的跨域网络问题,而非 MM2 进程本身的问题。
- 答:我会在全局 Grafana 中打开一个名为“MM2 全球拓扑视图”的面板,这个面板会按
- 追问 3: 这个设计如何满足金融行业对审计和高可用的严苛要求?
- 答:首先,
remote_write必须启用 TLS 和 mTLS 对传输数据进行加密和认证。其次,全局 Thanos/Cortex 存储本身需要多副本部署,数据采用纠删码或多副本存储。最后,整个监控系统的配置、仪表盘、告警规则全部用 GitOps 管理,任何变更都有迹可循,可审计。
- 答:首先,
- 追问 1: 这种跨广域网的场景,Prometheus 的
文末速查表
| 分类 | 关键指标 | 含义 | 正常期望 | P0/P1/P2 场景 |
|---|---|---|---|---|
| Broker | UnderReplicatedPartitions | ISR 收缩的分区数 | 0 | > 0 持续 5m (P1) |
| Broker | ActiveControllerCount | 活跃 Controller 数 | 1 | != 1 (P0) |
| Broker | OfflinePartitionsCount | 无 Leader 的分区数 | 0 | > 0 (P0) |
| Broker | TotalTimeMs | 请求总延迟 | < 10ms | P99 > 基线 2 倍 (P2) |
| Producer | record-error-rate | 发送失败速率 | 0 | > 0 持续 (P1) |
| Producer | record-retry-rate | 重试速率 | 低 | 与错误率同步升高 (P1) |
| Consumer | records-lag-max | 组内最大 Lag | 低 | 持续增长且消费速率为 0 (P1) |
| Consumer | heartbeat-rate | 心跳速率 | > 0 | 掉零 (P0 消费者假死) |
| 事务 | LEO - LSO | 事务空洞大小 | 趋近于 0 | 持续增大 (P1) |
延伸阅读
- 《Kafka: The Definitive Guide》 2nd Edition, Chapter 12: Monitoring Kafka.
- Kafka Official Documentation - Monitoring
- Prometheus Official Documentation
- Burrow GitHub Repository