《Java 消息中间件·Kafka 内核与客户端》附录 A Kafka 命令速查

把全书用到的命令按「集群、主题、消费组、日志、副本、压测」六组整理成一页,每条都标注了它在哪一节被实际用过。另附一节端口与 listener 的实战排查——包括本机遇到的两个坑:宿主机端口被别的进程占用、以及 advertised 端口和映射端口不一致导致客户端连维。

怎么用这篇附录

这里只收三类命令:本书实际跑过的、排错时必须用的、容易记错的。

没有收录的:需要额外组件才能用的(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
建 topickafka-topics.sh --bootstrap-server $B --create --topic T --partitions P --replication-factor R
看 ISRkafka-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
回切 leaderkafka-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 给出可以直接复制的集群与客户端模板。这两个附录都不含新概念,只在需要查参数时翻。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《Java 消息中间件·事件驱动架构》附录 B 架构决策记录模板
  2. 《Java 消息中间件·事件驱动架构》附录 A 术语表
  3. 《Java 消息中间件·事件驱动架构》15.3 全书验收