怎么用这篇附录
这里只收三类命令:本书实际跑过的、排错时必须用的、容易记错的。
没有收录的:需要额外组件才能用的(Connect、MirrorMaker)、大队列版本才有的、以及所有带 --zookeeper 参数的——后者在 4.x 已经彻底不存在,看到任何资料还在写 --zookeeper,直接判定为过时。
路径约定:容器内 Kafka 工具在 /opt/kafka/bin/,下面统一用 $K 代替。
K=/opt/kafka/bin
# 容器内执行时用内部 listener
B_INNER=k1:9092,k2:9092,k3:9092
# 宿主机执行时用 EXTERNAL 端口(本机为 29091/29092/29093)
B_OUTER=127.0.0.1:29091,127.0.0.1:29092,127.0.0.1:29093
这两个变量的区别是本书最容易踩的坑,附录末尾单独讲。
第一组:集群
确认 broker 活没活、有几个、版本对不对:
docker exec k1 $K/kafka-broker-api-versions.sh --bootstrap-server $B_INNER
输出里每个 broker 一行,末尾的 -> (api-versions) 表示连通。如果某个节点显示 ERROR: DisconnectException,说明该节点的 listener 或端口映射有问题,不是 broker 挂了。
看 controller 是谁、raft 进度到哪:
docker exec k1 $K/kafka-metadata-quorum.sh --bootstrap-server k1:9092 describe --status
本机输出节选:
ClusterId: 5L6g3nShT-eMCtK--X86sw
LeaderId: 1
LeaderEpoch: 6
HighWatermark: 1225
MaxFollowerLag: 0
MaxFollowerLagTimeMs: 0
CurrentVoters: [{"id": 1, ...}, {"id": 2, ...}, {"id": 3, ...}]
CurrentObservers: []
LeaderId 是 controller leader,和分区 leader 是两回事(4.3 节)。CurrentVoters 是 raft 投票成员——三个节点都在里面,意味着停掉两个就失去多数派。
第二组:主题
建 topic,把该带的三件事一次带全:
docker exec k1 $K/kafka-topics.sh --bootstrap-server $B_INNER \
--create --topic demo \
--partitions 3 \
--replication-factor 3 \
--config min.insync.replicas=2 \
--config retention.ms=604800000
--replication-factor 不能超过 broker 数。单节点上建 RF=3 会直接报 InvalidReplicationFactorException——这本书不在单节点上「假装」三副本。
查:
docker exec k1 $K/kafka-topics.sh --bootstrap-server $B_INNER --describe --topic demo
4.x 的输出比旧版多两列:
Topic: demo Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1,2,3 Elr: LastKnownElr:
Elr 和 LastKnownElr 是 ELR 机制(4.3 节)的字段,没开对应配置时为空,不是版本问题。
改配置(不需要重启):
docker exec k1 $K/kafka-configs.sh --bootstrap-server $B_INNER \
--alter --entity-type topics --entity-name demo \
--add-config retention.ms=86400000
分区数只能加不能减:kafka-topics.sh --alter --topic demo --partitions 6。
删:
docker exec k1 $K/kafka-topics.sh --bootstrap-server $B_INNER --delete --topic demo
delete.topic.enable 在 4.x 默认是 true,不需要像 2.x 时代那样先改配置。
第三组:消费组
看位点、lag、成员:
docker exec k1 $K/kafka-consumer-groups.sh --bootstrap-server $B_INNER \
--describe --group my-group
五列的含义:
| 列 | 含义 | 异常时说明什么 |
|---|---|---|
CURRENT-OFFSET | 组已提交的位点 | 为空表示还没提交过 |
LOG-END-OFFSET | 分区当前 LEO | 和 CURRENT-OFFSET 的差就是 lag |
LAG | 未消费条数 | 持续增大 = 消费跟不上 |
CONSUMER-ID | 分配到的消费者 | 为空 = 该分区当前没被分配 |
HOST / CLIENT-ID | 消费者位置 | 用来定位是哪台机器 |
列出所有组:
docker exec k1 $K/kafka-consumer-groups.sh --bootstrap-server $B_INNER --list
重置位点——这是生产环境的高危操作,必须加 --execute 才会真正生效:
# 先干跑,看清会动哪些分区
docker exec k1 $K/kafka-consumer-groups.sh --bootstrap-server $B_INNER \
--group my-group --topic demo --reset-offsets --to-earliest --dry-run
# 确认后执行
docker exec k1 $K/kafka-consumer-groups.sh --bootstrap-server $B_INNER \
--group my-group --topic demo --reset-offsets --to-earliest --execute
前提是组里没有活跃消费者,否则报 GroupNotEmptyException。重置位点不删消息,只改这个组读到哪。
还有一个容易混淆的命令:查某个分区在某个时间点的位点。
docker exec k1 $K/kafka-get-offsets.sh --bootstrap-server $B_INNER \
--topic demo --time -1
--time -1 是最新(即 HW),-2 是最早,正数是毫秒时间戳。
第四组:日志与元数据
这是「看磁盘上的真相」的一组,全书用得很频繁。
看分区目录里有哪些文件:
docker exec k1 ls -l /tmp/kafka-logs/demo-0/
00000000000000000000.index
00000000000000000000.log
00000000000000000000.timeindex
leader-epoch-checkpoint
partition.metadata
.log 是数据,.index 是位移索引,.timeindex 是时间索引。文件名前缀是该段的 base offset。
解出一条条记录:
docker exec k1 $K/kafka-dump-log.sh \
--files /tmp/kafka-logs/demo-0/00000000000000000000.log \
--print-data-log
输出里每条记录占两行,上面一行以 baseOffset: 开头,下面一行以 | offset: 开头。数记录条数必须用 grep -c '^| offset:',用 grep -c 'offset:' 会数成两倍——本机就在这上面栽过一次,把 19 条数成 20 条,进而把「HW 和 LEO 差 3 条」算成了「差 4 条」。
看 HW 的落盘快照:
docker exec k1 cat /tmp/kafka-logs/replication-offset-checkpoint
0
1
demo 0 16
格式是「topic 分区 HW」。它由 replica.high.watermark.checkpoint.interval.ms(默认 5000)驱动刷盘,不是实时值。
解 cluster metadata 日志(5.1 节):
docker exec k1 $K/kafka-dump-log.sh \
--files /tmp/kraft-combined-logs/__cluster_metadata-0/00000000000000000000.log \
--cluster-metadata-decoder
注意 4.x 容器镜像里 metadata 日志的目录名可能不是 kafka-logs,先用 find / -name "__cluster_metadata-0" -type d 定位。
第五组:副本与选举
手动触发 preferred leader 回切:
docker exec k1 $K/kafka-leader-election.sh --bootstrap-server $B_INNER \
--election-type preferred --topic demo --partition 0
--election-type 只有两个取值:PREFERRED 和 UNCLEAN。没有「指定某个副本当 leader」这个选项。UNCLEAN 是可以手动触发不干净选举的,脚本里不要出现它。
查看副本分布是否均衡(需要 JSON 文件描述目标分配):
docker exec k1 $K/kafka-reassign-partitions.sh --bootstrap-server $B_INNER \
--topics-to-move-json-file topics.json --broker-list "1,2,3" --generate
比对两个副本的数据是否一致:
docker exec k1 $K/kafka-replica-verification.sh --bootstrap-server $B_INNER \
--topic-whitelist "demo"
它逐条比较 leader 和 follower 的消息内容,大 topic 上会跑很久,别在生产高峰用。
第六组:压测
生产者:
docker exec k1 $K/kafka-producer-perf-test.sh \
--topic demo \
--num-records 100000 \
--record-size 1024 \
--throughput -1 \
--producer-props bootstrap.servers=$B_INNER acks=all
--throughput -1 表示不限速,尽最大努力打。4.3.0 里 --producer-props 会打印一行弃用提示,改用 --command-property 即可,参数格式不变。
消费者:
docker exec k1 $K/kafka-consumer-perf-test.sh \
--bootstrap-server $B_INNER --topic demo --messages 100000
这两个工具的数字在本书语境下只能看相对差异,不能当基准。 本机 load 常年在十几到二十几之间,同一条命令两次运行可能差一倍。12.1 节有完整的口径说明。
端口与 listener:本机实战踩过的三个坑
这一节不在原计划里,是两个真实故障逼出来的。
坑一:宿主机端口被别的进程占着
本机第一次起三节点集群,k3 的 EXTERNAL 端口对外表现为「能连上、但发请求超时」。查下来:
$ lsof -nP -iTCP:19093 -sTCP:LISTEN
COMMAND PID USER FD TYPE
java 419 ... 71u IPv6 TCP *:19093 (LISTEN)
这个 19093 被一个与本项目无关的 Java 进程占着。Docker 的端口映射声明成功了(docker port k3 显示 19093 已映射),但实际绑定失败,流量进了另一个进程。
症状很有迷惑性:nc -z 测端口是 OPEN 的(因为确实有进程在听),但 Kafka 协议请求超时。而 k1/k2 的同类端口正常,所以看起来「像是 k3 这一个节点坏了」。
排查方法:用一段十几行的 Python 直接发一个 ApiVersions 请求,看有没有回包:
import socket, struct
s = socket.create_connection(('127.0.0.1', 19093), 3)
body = struct.pack('>hhi', 18, 0, 1) + struct.pack('>h', -1) # ApiVersions v0
s.sendall(struct.pack('>i', len(body)) + body)
s.settimeout(3)
print(len(s.recv(64))) # 有回包 = 真的是 Kafka;超时 = 被别的进程占着
端口全部 OPEN 不等于 Kafka 都在。 起集群后至少用它验一遍三个 EXTERNAL 端口。
坑二:advertised 端口和映射端口不一致
第二个坑是我自己写 compose 时留下的。k2 的配置长这样:
ports: ["19092:19092"]
environment:
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,EXTERNAL://0.0.0.0:19091 # 继承来的
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://k2:9092,EXTERNAL://127.0.0.1:19092
LISTENERS 是从 YAML 锚点继承的 k1 的值(监听 19091),ADVERTISED 单独覆盖成了 19092。结果:容器内监听 19091,宿主机把 19092 映射进来,两边对不上。
宿主机客户端的表现是:bootstrap 连 k1 能拿到元数据,元数据里写着 broker 2 在 127.0.0.1:19092,连过去被断开,最终报:
TimeoutException: Topic xxx not present in metadata after 30000 ms
这个报错极具误导性——它说「topic 不在元数据里」,实际 topic 好好的,是元数据里的地址连不上。
正确的写法是每个节点都显式覆盖 KAFKA_LISTENERS:
k2:
environment:
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093,EXTERNAL://0.0.0.0:19092
坑三:容器内用宿主机地址
想验证「三个 EXTERNAL listener 都通」,很容易顺手在容器里执行:
docker exec k1 kafka-broker-api-versions.sh \
--bootstrap-server 127.0.0.1:19091,127.0.0.1:19092,127.0.0.1:19093
这条命令是错的。 在 k1 容器里,127.0.0.1 是 k1 自己,19092 和 19093 指向的是一台没有监听这些端口的机器。输出会是两个 DisconnectException,让你以为 k2/k3 坏了。
容器内验证必须用容器名:
docker exec k1 kafka-broker-api-versions.sh --bootstrap-server k1:9092,k2:9092,k3:9092
宿主机验证才用 EXTERNAL 端口。「在哪台机器上执行」决定「用哪组地址」,这是这一整组坑的共同根源。
判断口诀
| 你在哪执行 | bootstrap 用 | 原因 |
|---|---|---|
| 容器 k1/k2/k3 里 | k1:9092,k2:9092,k3:9092 | 容器网络内,用内部 listener |
| 宿主机(Java 客户端、命令行) | 127.0.0.1:29091,29092,29093 | 走端口映射,用 EXTERNAL |
如果宿主机客户端报「topic 不在元数据里」,先别怀疑 topic,先检查元数据里返回的 broker 地址能不能连上。
一页速查表
| 想做什么 | 命令 |
|---|---|
| 看集群是否活着 | kafka-broker-api-versions.sh --bootstrap-server $B |
| 看 controller 是谁 | kafka-metadata-quorum.sh --bootstrap-server k1:9092 describe --status |
| 建 topic | kafka-topics.sh --bootstrap-server $B --create --topic T --partitions P --replication-factor R |
| 看 ISR | kafka-topics.sh --bootstrap-server $B --describe --topic T |
| 改 topic 配置 | kafka-configs.sh --bootstrap-server $B --alter --entity-type topics --entity-name T --add-config k=v |
| 加分区 | kafka-topics.sh --bootstrap-server $B --alter --topic T --partitions N |
| 看消费积压 | kafka-consumer-groups.sh --bootstrap-server $B --describe --group G |
| 重置位点(干跑) | kafka-consumer-groups.sh ... --reset-offsets --to-earliest --dry-run |
| 看最新位点(HW) | kafka-get-offsets.sh --bootstrap-server $B --topic T --time -1 |
| 看磁盘上的记录 | kafka-dump-log.sh --files <path>.log --print-data-log |
| 数记录条数 | 上面接 `grep -c ‘^ |
| 看 HW 快照 | cat /tmp/kafka-logs/replication-offset-checkpoint |
| 回切 leader | kafka-leader-election.sh ... --election-type preferred |
| 比对副本 | kafka-replica-verification.sh --bootstrap-server $B --topic-whitelist T |
| 压测 | kafka-producer-perf-test.sh / kafka-consumer-perf-test.sh |
附录 B 给出可以直接复制的集群与客户端模板。这两个附录都不含新概念,只在需要查参数时翻。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。