假如你是一名以python语言作为开发语言的程序员,
你想使用kafka这个功能强大的消息中间件来处理一些业务逻辑,你自己编写业务处理逻辑代码,比如生产环节,消费环节等.
因为你是使用python语言的, 所以你可能会用到confluent-kafka-python(基于C库librdkafka的高性能客户端), 或者是kafka-python(纯Python实现的客户端,该库包的安装方式是 pip install kafka-python, 而不是pip install kafka),
但是不管你用的是哪一个库包, 它们都仅仅是一个客户端库,它本身不包含Kafka服务端的实现逻辑。
也就是说, 如果你仅仅使用kafka-python这个客户端, 你并不能把消息发送到kafka中, 因为你并没有建立客户端和服务端之间的链接, 没有地方来承接这个消息.
所以, 你还是需要有一个Kafka服务端, 并且启动该服务, 来接收生产者发送的消息并传递给消费者。
也就是说, 你需要在本地搭建kafka环境, 并且把kafka服务启动起来.
我只有一台电脑,但是我想搭建一个kafka集群,所以我在一台机器上创建几个broker, 和正规的集群(有多台机器,每个机器是一个broker)不同,所以叫做伪集群.
在单台机器上搭建Kafka集群(称为伪集群)是完全可行的.
在Mac上使用KRaft模式(即无ZooKeeper)搭建Kafka 4.0.0伪集群,能获得更简单的架构和更快的启动速度。核心步骤是:准备环境 -> 下载安装 -> 生成集群UUID -> 为每个节点配置角色 -> 格式化存储 -> 依次启动 -> 验证与操作。
以下是详细步骤:
1/准备环境与安装Kafka 4.0.0
(1)确保Java 8+:在终端输入 java -version 确认。如需安装,可运行 brew install openjdk@17。
(2)下载Kafka 4.0.0:
* 前往 [Apache Kafka官网下载页](https://kafka.apache.org/downloads)。
* 下载 **`kafka_2.13-4.0.0.tgz`**(`2.13`是Scala版本,不影响使用)。
* 解压并移动到方便操作的目录,例如:
```bash
tar -xzf ~/Downloads/kafka_2.13-4.0.0.tgz
mv kafka_2.13-4.0.0 ~/kafka-kraft-cluster
cd ~/kafka-kraft-cluster
```
* 添加到环境变量(.zshrc文件)
```bash
export KAFKA_HOME="XXX"
export PATH="$KAFKA_HOME/bin:$PATH"
```
(3)还有一种方式下载kafka
wget archive.apache.org/dist/kafka/…
这种方式有一个问题:就是耗时太长(建议使用上面官网下载方式)
2/配置节点
(1)node1.properties配置
# ==================== 基础配置 ====================
node.id=1
process.roles=broker,controller
# ==================== 监听器配置 ====================
listeners=PLAINTEXT://localhost:19092,CONTROLLER://localhost:9093
advertised.listeners=PLAINTEXT://localhost:19092
inter.broker.listener.name=PLAINTEXT
# ==================== 控制器配置 ====================
controller.quorum.voters=1@localhost:9093,2@localhost:9094,3@localhost:9095
controller.listener.names=CONTROLLER
# ==================== KRaft配置 ====================
controller.quorum.election.timeout.ms=1000
controller.quorum.fetch.timeout.ms=1000
controller.quorum.request.timeout.ms=2000
# ==================== 存储配置 ====================
log.dirs=/path/to/your/kafka-cluster/node1/logs
metadata.log.dir=/path/to/your/kafka-cluster/node1/kraft
# ==================== 日志配置 ====================
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
# ==================== 服务器配置 ====================
num.io.threads=8
num.network.threads=3
num.replica.fetchers=2
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
# ==================== 日志保留策略 ====================
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
# ==================== ZooKeeper迁移配置 ====================
# 因为是纯KRaft模式,不需要以下配置
# zookeeper.connect=
# zookeeper.connection.timeout.ms=
(2)node2.properties配置
# ==================== 基础配置 ====================
node.id=2
process.roles=broker,controller
# ==================== 监听器配置 ====================
listeners=PLAINTEXT://localhost:19093,CONTROLLER://localhost:9094
advertised.listeners=PLAINTEXT://localhost:19093
inter.broker.listener.name=PLAINTEXT
# ==================== 控制器配置 ====================
controller.quorum.voters=1@localhost:9093,2@localhost:9094,3@localhost:9095
controller.listener.names=CONTROLLER
# ==================== 存储配置 ====================
log.dirs=/path/to/your/kafka-cluster/node2/logs
metadata.log.dir=/path/to/your/kafka-cluster/node2/kraft
# 其他配置与节点1相同...
(3)node3.properties配置
# ==================== 基础配置 ====================
node.id=3
process.roles=broker,controller
# ==================== 监听器配置 ====================
listeners=PLAINTEXT://localhost:19094,CONTROLLER://localhost:9095
advertised.listeners=PLAINTEXT://localhost:19094
inter.broker.listener.name=PLAINTEXT
# ==================== 控制器配置 ====================
controller.quorum.voters=1@localhost:9093,2@localhost:9094,3@localhost:9095
controller.listener.names=CONTROLLER
# ==================== 存储配置 ====================
log.dirs=/path/to/your/kafka-cluster/node3/logs
metadata.log.dir=/path/to/your/kafka-cluster/node3/kraft
# 其他配置与节点1相同...
3/格式化存储目录
(1)生成集群ID
重要:首次启动前,必须为每个节点格式化其数据目录。
./bin/kafka-storage.sh random-uuid
该命令会输出一个UUID(如 `W5oG0H4oTjO-b2SuNq5Dzg`),**请记下它**,后续步骤会用到。
(2)为所有节点格式化存储(假设集群ID为 W5oG0H4oTjO-b2SuNq5Dzg)
# 格式化node1的存储目录
./bin/kafka-storage.sh format -t W5oG0H4oTjO-b2SuNq5Dzg -c config/node1.properties
# 格式化node2的存储目录
./bin/kafka-storage.sh format -t W5oG0H4oTjO-b2SuNq5Dzg -c config/node2.properties
# 格式化node3的存储目录
./bin/kafka-storage.sh format -t W5oG0H4oTjO-b2SuNq5Dzg -c config/node3.properties
4/分步启动集群
按顺序启动节点(请打开多个终端标签页):
-
终端1 - 启动node1 (Controller):
./bin/kafka-server-start.sh config/node1.properties等待其日志输出“Ready to serve as the controller”,表示控制器已就绪。
-
终端2 - 启动node2 (Broker):
./bin/kafka-server-start.sh config/node2.properties -
终端3 - 启动node3 (Broker):
./bin/kafka-server-start.sh config/node3.properties
5/一次性启动/关闭集群
(1)启动集群sh脚本:
#!/bin/zsh
# 生成集群ID(首次运行时使用)
CLUSTER_ID=$(./bin/kafka-storage.sh random-uuid)
echo "集群ID: $CLUSTER_ID"
# 格式化存储目录(首次运行需要)
echo "格式化存储目录..."
./bin/kafka-storage.sh format -t $CLUSTER_ID -c config/node1.properties
./bin/kafka-storage.sh format -t $CLUSTER_ID -c config/node2.properties
./bin/kafka-storage.sh format -t $CLUSTER_ID -c config/node3.properties
# 启动集群(在三个不同的终端中)
echo "启动节点1..."
./bin/kafka-server-start.sh config/node1.properties &
echo "等待5秒..."
sleep 5
echo "启动节点2..."
./bin/kafka-server-start.sh config/node2.properties &
echo "等待5秒..."
sleep 5
echo "启动节点3..."
./bin/kafka-server-start.sh config/node3.properties &
echo "集群启动完成!"
(2)关闭集群sh脚本:
#!/bin/bash
echo "Stopping Kafka KRaft Cluster..."
# 优雅停止
pkill -f 'kafka-server-start.sh'
# 强制停止(如果优雅停止无效)
# kill -9 $(ps aux | grep kraft-server.properties | grep -v grep | awk '{print $2}')
echo "Cluster stopped."
6/总结关键点
-
✅ 每个节点必须有唯一的端口组合(这里指的是listeners参数中的PLAINTEXT和CONTROLLER)
-
✅
controller.quorum.voters必须完全相同 -
✅ 数据目录必须不同
-
✅ 节点ID必须唯一
-
❌ 不要复用端口,一台机器上的端口不能重复绑定
-
controller.quorum.voters:必须包含所有controller节点, 如果不是controller,则不能添加
-
process.roles:指定节点角色(broker, controller或两者)
-
node.id:每个节点必须有唯一ID
-
listeners:需要配置两个监听器,一个用于broker通信,一个用于controller通信
-
集群ID:所有节点必须使用相同的集群ID
7/故障排除和优化
(1)端口冲突
# 检查端口占用
lsof -i :19092,19093,19094,9093,9094,9095
# 如果端口被占用,修改配置文件中的端口号
(2)启动超时
# 增加选举超时时间
controller.quorum.election.timeout.ms=3000
controller.quorum.fetch.timeout.ms=3000
(3)性能优化建议
# JVM调整(在启动脚本中设置)
export KAFKA_HEAP_OPTS="-Xms2g -Xmx2g"
export KAFKA_JVM_PERFORMANCE_OPTS="-server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -Djava.awt.headless=true"
# 文件描述符限制(Mac)
sudo launchctl limit maxfiles 65536 65536
ulimit -n 65536
8/关键参数解释
-
process.roles:
broker,controller:节点同时承担两种角色- 纯controller节点:
controller - 纯broker节点:
broker
-
controller.quorum.voters:
- 格式:
node_id@host:port - 所有controller节点必须在此列出
- 端口用于controller间通信
- 格式:
-
listeners配置:
PLAINTEXT:客户端连接端口CONTROLLER:KRaft控制器间通信端口
-
存储路径:
log.dirs:消息数据存储metadata.log.dir:KRaft元数据存储(与数据分离)