这是《Kafka 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 学 Kafka 最忌讳只看不练。本章从下载安装包开始,把单机 KRaft 集群跑起来,再扩展成一台机器上的三节点“伪分布式”集群,为后续所有实验打底。
3.1 软件准备
| 软件 | 版本要求 | 说明 |
|---|---|---|
| JDK | 17+ | Kafka 4.x 要求 Java 17 及以上,推荐 17 或 21 LTS |
| Kafka | 4.x | 官网下载二进制包,选 Scala 2.13 构建 |
| 操作系统 | Linux/macOS/Windows | 学习推荐 WSL2 或 Linux;Windows 原生用 .bat 脚本 |
| 内存 | 4GB+ 空闲 | 三节点伪集群建议 8GB |
验证 JDK:
java -version
下载并解压(以 4.0.0 为例,请按需替换版本号):
tar -xzf kafka_2.13-4.0.0.tgz
cd kafka_2.13-4.0.0
解压后的目录结构:
bin/ 可执行脚本(.sh)
config/ 配置文件
libs/ 依赖 jar
logs/ 运行日志(server.log 等)
site-docs/ 文档
3.2 Kafka 4.x 只有一种模式:KRaft
Kafka 3.x 之前依赖 ZooKeeper 管理元数据(Broker 注册、Controller 选举、topic 配置等)。这带来两个问题:
- 运维两套分布式系统,故障域更复杂;
- 元数据变更要先写 ZooKeeper 再通知 Broker,规模大时收敛慢。
KRaft(Kafka Raft)把元数据放进 Kafka 自己的 Raft 日志(__cluster_metadata)里,由 Controller 仲裁,Broker 作为学习者同步元数据。Kafka 4.0 起彻底移除 ZooKeeper。
所以本书从第一天就用 KRaft,这也是新集群的正确起点。
3.3 单节点 KRaft 启动
三步走:生成集群 ID -> 格式化存储目录 -> 启动服务。
# 1. 生成集群 ID
KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
# 2. 用默认配置格式化存储目录
bin/kafka-storage.sh format \
--standalone \
--config config/server.properties \
--cluster-id $KAFKA_CLUSTER_ID
# 3. 启动
bin/kafka-server-start.sh config/server.properties
--standalone 会自动生成单节点控制器仲裁配置,非常适合本地学习。
验证进程是否就绪:
bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092 | head -n 5
能看到 Broker 返回 API 版本列表,说明启动成功。
Windows 用户对应命令:
bin\windows\kafka-storage.bat random-uuid
bin\windows\kafka-storage.bat format --standalone --config config\server.properties --cluster-id <上一步的ID>
bin\windows\kafka-server-start.bat config\server.properties
3.4 server.properties 关键配置解读
打开 config/server.properties,重点理解以下几行:
# 节点 ID,集群内唯一
node.id=1
# 控制器仲裁者列表(多节点集群必配)
# controller.quorum.voters=1@localhost:9093,2@localhost:9094,3@localhost:9095
# 监听地址:名称://主机:端口
listeners=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093
# 对外公布的地址,客户端用它连接
advertised.listeners=PLAINTEXT://localhost:9092
# 哪些监听器用于控制器通信
controller.listener.names=CONTROLLER
# 监听器安全协议映射
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_SSL:SASL_SSL,SASL_PLAINTEXT:SASL_PLAINTEXT
# 数据目录,多个目录用逗号分隔(可做多磁盘条带化)
log.dirs=/tmp/kafka-logs
# 自动创建 topic:学习期开,生产环境建议关
auto.create.topics.enable=true
三个易错点:
listeners是进程实际绑定的地址;advertised.listeners是返回给客户端的地址。生产环境必须填 Broker 可被客户端访问的主机名/IP,写成localhost会导致远程客户端连不上;- 修改
log.dirs后必须重新执行format(或清空目录),否则元数据不匹配会启动失败; - 端口冲突是新手最常见的启动报错,多个节点共机时端口必须全部错开。
3.5 三节点伪分布式集群
在同一台机器上模拟生产集群,理解节点交互非常有效。规划如下:
| 节点 | node.id | broker 端口 | controller 端口 | 数据目录 |
|---|---|---|---|---|
| broker-1 | 1 | 19092 | 19093 | /tmp/kraft-1 |
| broker-2 | 2 | 29092 | 29093 | /tmp/kraft-2 |
| broker-3 | 3 | 39092 | 39093 | /tmp/kraft-3 |
创建三个配置文件 config/server-1.properties、server-2.properties、server-3.properties,内容以节点 1 为例:
process.roles=broker,controller
node.id=1
controller.quorum.voters=1@localhost:19093,2@localhost:29093,3@localhost:39093
listeners=PLAINTEXT://:19092,CONTROLLER://:19093
advertised.listeners=PLAINTEXT://localhost:19092
controller.listener.names=CONTROLLER
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
log.dirs=/tmp/kraft-1
num.partitions=3
default.replication.factor=3
min.insync.replicas=2
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=2
节点 2、3 只需替换 node.id、两个端口和 log.dirs。
格式化并依次启动:
KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
for i in 1 2 3; do
bin/kafka-storage.sh format \
--config config/server-$i.properties \
--cluster-id $KAFKA_CLUSTER_ID \
--ignore-formatted
done
bin/kafka-server-start.sh -daemon config/server-1.properties
bin/kafka-server-start.sh -daemon config/server-2.properties
bin/kafka-server-start.sh -daemon config/server-3.properties
验证三台都活着:
bin/kafka-metadata-quorum.sh \
--bootstrap-server localhost:19092 describe --status
bin/kafka-broker-api-versions.sh --bootstrap-server localhost:19092 \
| grep -E '^localhost'
describe --status 能看到 LeaderId 和三个 voter,说明 Raft 仲裁工作正常。
3.6 用 Docker Compose 一键起环境
如果不想手动管理目录和端口,可以用官方镜像快速起单节点:
services:
kafka:
image: apache/kafka:4.0.0
container_name: kafka
ports:
- "9092:9092"
environment:
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
启动:docker compose up -d。进入容器执行命令:docker exec -it kafka /opt/kafka/bin/kafka-topics.sh --list --bootstrap-server localhost:9092。
3.7 停止与日志
优雅停止:
bin/kafka-server-stop.sh
排查启动问题时优先看:
logs/server.log:主日志,包含启动失败原因;logs/controller.log:KRaft 控制器日志;logs/kafkaServer.out:JVM 层输出,OOM、端口占用常在这里。
3.8 常见启动故障速查
| 症状 | 常见原因 | 处理 |
|---|---|---|
启动秒退,日志提示 InconsistentClusterIdException |
目录已被其他集群格式化 | 清空 log.dirs 后重新 format |
Address already in use |
端口冲突 | 换端口或杀掉占用进程 |
| 客户端连不上,Broker 日志正常 | advertised.listeners 返回了错误地址 |
改成客户端可达的主机名/IP |
| 集群hang在选举 | controller.quorum.voters 配错 |
检查每个节点 ID@host:port |
| JMX 端口冲突 | 多节点共机未设 JMX_PORT | 每个节点指定不同 JMX_PORT |
本章小结
- Kafka 4.x 只有 KRaft 模式,元数据存在
__cluster_metadataRaft 日志中; - 单机启动三步:
random-uuid->format->server-start; listeners是绑定地址,advertised.listeners是客户端连接地址,两者必须区分;- 伪分布式集群能真实复现副本同步、Leader 选举与容错切换,是后续原理实验的基础;
- 遇到问题先看
server.log与controller.log。
思考题
- 为什么 Kafka 一定要引入
advertised.listeners,只用listeners不行吗? - 伪集群中 kill 掉某个 Broker,哪些 topic 分区会不可用?什么情况下仍然可用?
- 把
log.dirs配成多个目录有什么好处?Kafka 如何在多个目录间分配分区?