各个节点的 listeners 配置必须不同,因为它们是绑定到不同的端口上的。
一、单机伪集群配置原则
在一台电脑上部署 Kafka 伪集群时:
❌ 错误做法(端口冲突):
properties
# 节点1
listeners=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093
# 节点2
listeners=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093 # ❌ 端口冲突!
✅ 正确做法(不同端口):
properties
# 节点1
listeners=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093
# 节点2
listeners=PLAINTEXT://localhost:9095,CONTROLLER://localhost:9096 # ✅ 不同端口
# 节点3
listeners=PLAINTEXT://localhost:9098,CONTROLLER://localhost:9099 # ✅ 不同端口
二、完整单机伪集群配置示例(3节点)
端口规划表:
| 节点 | 客户端端口 | 控制器端口 | 日志目录 |
|---|---|---|---|
| Node1 | 9092 | 9093 | /tmp/kafka-logs-1 |
| Node2 | 9095 | 9096 | /tmp/kafka-logs-2 |
| Node3 | 9098 | 9099 | /tmp/kafka-logs-3 |
配置文件:
config/kafka1.properties:
properties
# 基本配置
process.roles=broker,controller
node.id=1
# 监听器配置 - 端口 9092/9093
listeners=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093
advertised.listeners=PLAINTEXT://localhost:9092
# 数据目录
log.dirs=/tmp/kafka-logs-1
# KRaft 配置
controller.listener.names=CONTROLLER
controller.quorum.voters=1@localhost:9093,2@localhost:9096,3@localhost:9099
# 其他配置
num.network.threads=3
num.io.threads=8
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
num.partitions=1
num.recovery.threads.per.data.dir=1
config/kafka2.properties:
properties
process.roles=broker,controller
node.id=2
# 监听器配置 - 端口 9095/9096(与节点1不同!)
listeners=PLAINTEXT://localhost:9095,CONTROLLER://localhost:9096
advertised.listeners=PLAINTEXT://localhost:9095
log.dirs=/tmp/kafka-logs-2
controller.listener.names=CONTROLLER
controller.quorum.voters=1@localhost:9093,2@localhost:9096,3@localhost:9099
config/kafka3.properties:
properties
process.roles=broker,controller
node.id=3
# 监听器配置 - 端口 9098/9099(与其他节点不同!)
listeners=PLAINTEXT://localhost:9098,CONTROLLER://localhost:9099
advertised.listeners=PLAINTEXT://localhost:9098
log.dirs=/tmp/kafka-logs-3
controller.listener.names=CONTROLLER
controller.quorum.voters=1@localhost:9093,2@localhost:9096,3@localhost:9099
三、必须相同的配置 vs 必须不同的配置
必须相同的配置(集群一致性):
properties
# 所有节点的这些配置必须完全一致
controller.quorum.voters=1@localhost:9093,2@localhost:9096,3@localhost:9099
controller.listener.names=CONTROLLER
inter.broker.listener.name=PLAINTEXT
必须不同的配置(资源隔离):
properties
# 每个节点必须不同
node.id=1 # 唯一节点ID
listeners=... # 不同端口
log.dirs=/tmp/kafka-logs-1 # 不同数据目录
四、启动脚本示例
start-kafka-cluster.sh:
bash
#!/bin/bash
# 生成集群ID(首次运行时使用)
CLUSTER_ID=$(./bin/kafka-storage.sh random-uuid)
echo "集群ID: $CLUSTER_ID"
# 格式化存储目录(首次运行需要)
echo "格式化存储目录..."
./bin/kafka-storage.sh format -t $CLUSTER_ID -c config/kafka1.properties
./bin/kafka-storage.sh format -t $CLUSTER_ID -c config/kafka2.properties
./bin/kafka-storage.sh format -t $CLUSTER_ID -c config/kafka3.properties
# 启动集群(在三个不同的终端中)
echo "启动节点1..."
./bin/kafka-server-start.sh config/kafka1.properties &
echo "等待5秒..."
sleep 5
echo "启动节点2..."
./bin/kafka-server-start.sh config/kafka2.properties &
echo "等待5秒..."
sleep 5
echo "启动节点3..."
./bin/kafka-storage.sh format -t $CLUSTER_ID -c config/kafka3.properties
echo "集群启动完成!"
五、验证集群状态
检查端口占用:
bash
# 查看所有Kafka相关端口
netstat -an | grep -E "9092|9093|9095|9096|9098|9099"
# 或使用 lsof
lsof -i :9092,9093,9095,9096,9098,9099
测试集群功能:
bash
# 使用任意一个节点的客户端端口
BOOTSTRAP_SERVER="localhost:9092"
# 创建主题
./bin/kafka-topics.sh --bootstrap-server $BOOTSTRAP_SERVER \
--create --topic test-topic \
--partitions 3 --replication-factor 3
# 查看主题详情
./bin/kafka-topics.sh --bootstrap-server $BOOTSTRAP_SERVER \
--describe --topic test-topic
# 查看集群元数据
./bin/kafka-metadata-quorum.sh --bootstrap-server $BOOTSTRAP_SERVER \
describe --status
六、使用 Docker Compose 的单机伪集群配置
如果你更喜欢用 Docker,这里是一个 docker-compose.yml:
yaml
version: '3'
services:
kafka1:
image: apache/kafka:4.0.1
container_name: kafka1
ports:
- "9092:9092"
- "9093:9093"
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_CONTROLLER_QUORUM_VOTERS: "1@kafka1:9093,2@kafka2:9093,3@kafka3:9093"
KAFKA_LOG_DIRS: "/tmp/kafka-logs-1"
volumes:
- ./kafka-data-1:/tmp/kafka-logs-1
kafka2:
image: apache/kafka:4.0.1
container_name: kafka2
ports:
- "9095:9092"
- "9096:9093"
environment:
KAFKA_NODE_ID: 2
KAFKA_PROCESS_ROLES: "broker,controller"
KAFKA_LISTENERS: "PLAINTEXT://:9092,CONTROLLER://:9093"
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://localhost:9095"
KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER"
KAFKA_CONTROLLER_QUORUM_VOTERS: "1@kafka1:9093,2@kafka2:9093,3@kafka3:9093"
KAFKA_LOG_DIRS: "/tmp/kafka-logs-2"
volumes:
- ./kafka-data-2:/tmp/kafka-logs-2
kafka3:
image: apache/kafka:4.0.1
container_name: kafka3
ports:
- "9098:9092"
- "9099:9093"
environment:
KAFKA_NODE_ID: 3
KAFKA_PROCESS_ROLES: "broker,controller"
KAFKA_LISTENERS: "PLAINTEXT://:9092,CONTROLLER://:9093"
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://localhost:9098"
KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER"
KAFKA_CONTROLLER_QUORUM_VOTERS: "1@kafka1:9093,2@kafka2:9093,3@kafka3:9093"
KAFKA_LOG_DIRS: "/tmp/kafka-logs-3"
volumes:
- ./kafka-data-3:/tmp/kafka-logs-3
七、常见问题解决
问题1:端口已占用
bash
# 解决方案:修改端口
# 节点1:9092,9093 → 修改为 19092,19093
# 节点2:9095,9096 → 修改为 19095,19096
# 节点3:9098,9099 → 修改为 19098,19099
问题2:启动顺序问题
bash
# KRaft 模式需要至少一个控制器先启动
# 解决方案:逐个启动,间隔几秒
./bin/kafka-server-start.sh config/kafka1.properties
# 等待10秒
./bin/kafka-server-start.sh config/kafka2.properties
# 等待10秒
./bin/kafka-server-start.sh config/kafka3.properties
问题3:数据目录冲突
bash
# 解决方案:使用不同的目录
log.dirs=/tmp/kafka-cluster/node1
log.dirs=/tmp/kafka-cluster/node2
log.dirs=/tmp/kafka-cluster/node3
八、简化的配置方法(使用脚本自动生成)
generate-configs.sh:
bash
#!/bin/bash
BASE_PORT=9092
CONTROLLER_BASE_PORT=9093
NODES=3
for i in $(seq 1 $NODES); do
CLIENT_PORT=$((BASE_PORT + (i-1)*3))
CONTROLLER_PORT=$((CONTROLLER_BASE_PORT + (i-1)*3))
cat > config/kafka$i.properties << EOF
# 自动生成的配置 - 节点$i
process.roles=broker,controller
node.id=$i
listeners=PLAINTEXT://localhost:$CLIENT_PORT,CONTROLLER://localhost:$CONTROLLER_PORT
advertised.listeners=PLAINTEXT://localhost:$CLIENT_PORT
log.dirs=/tmp/kafka-logs-$i
controller.listener.names=CONTROLLER
controller.quorum.voters=1@localhost:9093,2@localhost:9096,3@localhost:9099
num.partitions=3
default.replication.factor=3
min.insync.replicas=2
EOF
echo "生成 config/kafka$i.properties: client_port=$CLIENT_PORT, controller_port=$CONTROLLER_PORT"
done
总结关键点:
- ✅ 每个节点必须有唯一的端口组合
- ✅
controller.quorum.voters必须完全相同 - ✅ 数据目录必须不同
- ✅ 节点ID必须唯一
- ❌ 不要复用端口,一台机器上的端口不能重复绑定