战Solon StateMachine 实现状态机使用示例详解F
Kafka 官方只提供二进制压缩包,解压即用,既不属于系统自带,也不走 make install,所以社区习惯放到 /opt(专门放大型第三方独立软件),避免日后系统升级或包管理器误碰。问题概述:start-kafka.sh 依旧是 “瞬间结束” 的脚本——它把 Zookeeper 和 3 个 Kafka 丢到后台就退出了,systemd(Type=simple)发现主进程退出,于是立即执行 Ex
3.3.2 为什么更合适
3.3.2 是 3.3 线的最后一个补丁版,修掉 20 个 bug(含潜在数据丢失/稳定性问题)。
Scala 2.13 比 2.12 体积小一点点,官方也写 “建议使用 2.13”。
以后真要扩容到多机,同一版本直接复制配置即可,避免混版本。
二、CentOS 7 单机跑“伪分布式 3 副本”思路
用同一台机器开 3 个 Kafka 进程,每个进程一个 broker.id 和独立端口/目录,Zookeeper 仍用同一实例。
只能验证功能/调试用,真正高可用必须 ≥3 台物理机。
三、10 分钟落地步骤
前置依赖
sudo yum install -y java-11-openjdk-devel wget
java -version # 确认 11+
目录规划
sudo mkdir -p /opt/kafka-cluster
cd /opt/kafka-cluster
下载并解压 3.3.2
wget https://archive.apache.org/dist/kafka/3.3.2/kafka_2.13-3.3.2.tgz
tar -xzf kafka_2.13-3.3.2.tgz
mv kafka_2.13-3.3.2 kafka
# 建 3 份配置
cp -r kafka kafka-1 kafka-2 kafka-3
起 Zookeeper(单节点)
# 用自带脚本即可
nohup kafka/bin/zookeeper-server-start.sh kafka/config/zookeeper.properties > zk.log 2>&1 &
为 3 个 broker 准备配置文件
把下面三段分别写进 kafka-1/config/server.properties、kafka-2/config/server.properties、kafka-3/config/server.properties,只改差异项:
配置项 broker 1 broker 2 broker 3
broker.id 1 2 3
listeners PLAINTEXT://:9092 PLAINTEXT://:9093 PLAINTEXT://:9094
log.dirs /tmp/kafka-logs-1 /tmp/kafka-logs-2 /tmp/kafka-logs-3
zookeeper.connect localhost:2181 localhost:2181 localhost:2181
其余保持默认即可。
启动 3 个 broker
nohup kafka-1/bin/kafka-server-start.sh kafka-1/config/server.properties > k1.log 2>&1 &
nohup kafka-2/bin/kafka-server-start.sh kafka-2/config/server.properties > k2.log 2>&1 &
nohup kafka-3/bin/kafka-server-start.sh kafka-3/config/server.properties > k3.log 2>&1 &
验证 3 副本 topic
# 建 topic,3 副本 3 分区
kafka/bin/kafka-topics.sh --create --topic test-ha \
--bootstrap-server localhost:9092 \
--partitions 3 --replication-factor 3
# 查看分区分布
kafka/bin/kafka-topics.sh --describe --topic test-ha --bootstrap-server localhost:9092
应当看到每个分区都有 Leader, Replicas: 1,2,3, Isr: 1,2,3。
简单生产/消费测试
# 生产
kafka/bin/kafka-console-producer.sh --topic test-ha --bootstrap-server localhost:9092
# 消费
kafka/bin/kafka-console-consumer.sh --topic test-ha --bootstrap-server localhost:9092 --from-beginning
四、单机“三副本”的局限
高可用:? 一台机器挂了,三个进程一起死。
功能验证:? 可以跑通多副本、Leader 切换、ISR 列表变化。
后续扩容:把配置里的 listeners 改成各自主机名/IP,log.dirs 换成独立磁盘即可。
一句话总结
下载 kafka_2.13-3.3.2.tgz,单机起 3 个 broker 就能跑 3 副本测试;真正高可用还需再加两台机器。
注:/usr/local 是给“本地手工编译”或“包管理器之外的系统级软件”留的;Kafka 官方只提供二进制压缩包,解压即用,既不属于系统自带,也不走 make install,所以社区习惯放到 /opt(专门放大型第三方独立软件),避免日后系统升级或包管理器误碰。
安装和部署
下面给你一份面向 CentOS 7、虚拟机单机的“一行不落”复制粘贴教程。
目标目录:/opt/kafka3.3.2
源包路径:/root/kafka_2.13-3.3.2.tgz
一、提前确认
java -version # 需 11 以上
#输出
[root@localhost redis6.2.14]# java -version
java version "17.0.15" 2025-04-15 LTS
Java(TM) SE Runtime Environment (build 17.0.15+9-LTS-241)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.15+9-LTS-241, mixed mode, sharing)
[root@localhost redis6.2.14]#
二、解压并更名
sudo mkdir -p /opt/kafka3.3.2
sudo tar -xzf /root/kafka_2.13-3.3.2.tgz -C /opt/kafka3.3.2 --strip-components=1
解压后目录结构应为:
/opt/kafka3.3.2/bin
/opt/kafka3.3.2/config
…
三、建数据 / 日志目录
sudo mkdir -p /opt/kafka3.3.2/data/zookeeper
sudo mkdir -p /opt/kafka3.3.2/data/kafka-logs-1 \
/opt/kafka3.3.2/data/kafka-logs-2 \
/opt/kafka3.3.2/data/kafka-logs-3
sudo chown -R $USER:$USER /opt/kafka3.3.2
四、Zookeeper 单节点配置
cp /opt/kafka3.3.2/config/zookeeper.properties /opt/kafka3.3.2/config/zookeeper.properties.bak
编辑 /opt/kafka3.3.2/config/zookeeper.properties 只改两项(其余默认):
dataDir=/opt/kafka3.3.2/data/zookeeper
clientPort=2181
五、准备 3 份 broker 配置
for i in 1 2 3; do
cp /opt/kafka3.3.2/config/server.properties /opt/kafka3.3.2/config/server-$i.properties
done
分别编辑 3 个文件,只改以下 4 行(操作在之后代码块):
文件 broker.id listeners log.dirs advertised.listeners
server-1.properties 1 PLAINTEXT://:9092 /opt/kafka3.3.2/data/kafka-logs-1 PLAINTEXT://localhost:9092
server-2.properties 2 PLAINTEXT://:9093 /opt/kafka3.3.2/data/kafka-logs-2 PLAINTEXT://localhost:9093
server-3.properties 3 PLAINTEXT://:9094 /opt/kafka3.3.2/data/kafka-logs-3 PLAINTEXT://localhost:9094
其余保持默认(尤其 zookeeper.connect=localhost:2181)。
不需要手动打开图形编辑器。
用一条 sed 或 echo 命令就能一次改好,下面给出复制即用的三行脚本,把 4 个关键配置一次性写入对应文件:
下面给出一次可直接复制/粘贴的 修正版脚本,并逐条指出与旧脚本的区别。
可直接执行的脚本(已修复换行/引号问题)
# 1 号 broker
sed -ri \
-e 's/^broker.id=.*/broker.id=1/' \
-e 's|^listeners[[:space:]]*=.*|listeners=PLAINTEXT://:9092|' \
-e 's|^log.dirs[[:space:]]*=.*|log.dirs=/opt/kafka3.3.2/data/kafka-logs-1|' \
-e 's|^#?advertised.listeners[[:space:]]*=.*|advertised.listeners=PLAINTEXT://localhost:9092|' \
/opt/kafka3.3.2/config/server-1.properties
# 2 号 broker
sed -ri \
-e 's/^broker.id=.*/broker.id=2/' \
-e 's|^listeners[[:space:]]*=.*|listeners=PLAINTEXT://:9093|' \
-e 's|^log.dirs[[:space:]]*=.*|log.dirs=/opt/kafka3.3.2/data/kafka-logs-2|' \
-e 's|^#?advertised.listeners[[:space:]]*=.*|advertised.listeners=PLAINTEXT://localhost:9093|' \
/opt/kafka3.3.2/config/server-2.properties
# 3 号 broker
sed -ri \
-e 's/^broker.id=.*/broker.id=3/' \
-e 's|^listeners[[:space:]]*=.*|listeners=PLAINTEXT://:9094|' \
-e 's|^log.dirs[[:space:]]*=.*|log.dirs=/opt/kafka3.3.2/data/kafka-logs-3|' \
-e 's|^#?advertised.listeners[[:space:]]*=.*|advertised.listeners=PLAINTEXT://localhost:9094|' \
/opt/kafka3.3.2/config/server-3.properties
与旧脚本的区别逐条说明(注意之前的报错,因为出现转义字符的问题,上面代码已经修改过。)
序号 旧问题 修正做法 说明
1 单引号里跨行反斜杠续行 直接拆成 多段 -e 表达式 避免 sed 报 “未终止的正则”
2 分隔符 / 与路径 /opt/... 冲突 统一用 ` ` 作分隔符
3 正则 ^listeners=.* 可能匹配不到空格 写成 ^listeners[[:space:]]*= 兼容 key=value 或 key = value
4 只处理 #advertised.listeners 的注释行 改成 #? 同时匹配已取消注释或仍注释的行
5 无 -r 扩展正则 加 -r(GNU sed) 让 ?、`
把上面脚本一次性粘进终端即可,无需再手工编辑。
执行完后,三份配置就全部改好,无需再打开任何编辑器。
六、创建并启动脚本(一次性复制即可用)
? 下面给你一条命令一条命令的「复制-粘贴」版完整步骤,连脚本内容都帮你一次性生成,不用手写,跟着敲即可。
① 一键生成启动脚本(包含全部内容)
cat > ~/start-kafka.sh <<'EOF' #这一步直接是创建脚本,就是把下面的代码保存成脚本'~/start-kafka.sh'
#!/bin/bash
# 启动 Zookeeper
nohup /opt/kafka3.3.2/bin/zookeeper-server-start.sh \
-daemon /opt/kafka3.3.2/config/zookeeper.properties \
> /opt/kafka3.3.2/zk.log 2>&1 &
sleep 5 # 等 Zookeeper 完全起来
# 启动 3 个 broker
for i in 1 2 3; do
nohup /opt/kafka3.3.2/bin/kafka-server-start.sh \
-daemon /opt/kafka3.3.2/config/server-$i.properties \
> /opt/kafka3.3.2/broker-$i.log 2>&1 &
done
echo "Zookeeper + 3 brokers started. Logs under /opt/kafka3.3.2/"
EOF
② 赋可执行权限
chmod +x ~/start-kafka.sh
③ 运行脚本
~/start-kafka.sh
#运行结果
Zookeeper + 3 brokers started. Logs under /opt/kafka3.3.2/#
④ 验证是否都起来了
jps | egrep 'QuorumPeerMain|Kafka'
应看到 1 个 QuorumPeerMain(Zookeeper)+ 3 个 Kafka 进程。
#运行结果(有偏差)
27940 Kafka
26891 QuorumPeerMain
有偏差:你只看到 1 个 Kafka 进程,说明 3 个 broker 并没有全部成功启动(后面有我的解决和修改过程可以直接跳过,你也可以在这里逐步排查,可能问题不一样)
你只看到 1 个 Kafka 进程,说明 3 个 broker 并没有全部成功启动。
按下面 3 步排查并一次性修复:
立即查日志定位原因 ,然后这里我的是空目录
# 看看每个 broker 日志末尾 20 行
tail -n 20 /opt/kafka3.3.2/broker-*.log
常见报错:端口被占用、目录权限不足、log.dirs 不存在、JMX 端口冲突等。
一键补齐缺失目录 & 权限
mkdir -p /opt/kafka3.3.2/data/{kafka-logs-1,kafka-logs-2,kafka-logs-3}
chown -R $(whoami):$(whoami) /opt/kafka3.3.2
先停后启(干净重启)
# 如果还有残存进程
pkill -f 'kafka\.Kafka' # 杀 Kafka
pkill -f QuorumPeerMain # 杀 Zookeeper
# 重新启动
~/start-kafka.sh
# 再验证
sleep 5
jps | egrep 'QuorumPeerMain|Kafka'
正常应看到 1 个 QuorumPeerMain + 3 个 Kafka。
如果日志里出现 “Address already in use”
说明 9092/9093/9094 端口被系统占用,改脚本里的端口即可;
如果日志里出现 “No space left on device” 或 权限 denied,就回到第 2 步继续修正目录/权限。
下面是我的解决过程梳理和总结
【一句话总结错误原因】
“9092 被旧 broker 死死占着,导致 2、3 号 broker 起不来;把旧进程杀掉、把 3 台 broker 端口一次性改成 9191/9192/9193,再清掉 .lock 文件,就彻底 OK。”
1、完整报错链路(按时间线)
时间 现象 根因 日志关键行
T0 jps 只有 1 个 Kafka 2、3 号没起来 —
T1 broker-2.log 报错 端口 9092 被占用 Socket server failed to bind to 0.0.0.0:9092: 地址已在使用
T2 ls -l /opt/kafka3.3.2/data 目录缺 log.dirs 没提前建 —
T3 .lock 已存在 旧进程没停干净 Failed to acquire lock on file .lock
2、修复步骤(复制即用)
杀干净所有残留进程
pkill -9 -f 'kafka\.Kafka'
pkill -f QuorumPeerMain
一次性建目录 + 删 .lock
mkdir -p /opt/kafka3.3.2/data/{kafka-logs-1,kafka-logs-2,kafka-logs-3}
chown -R $(whoami):$(whoami) /opt/kafka3.3.2/data
for i in 1 2 3; do rm -f /opt/kafka3.3.2/data/kafka-logs-$i/.lock; done
把 3 份配置端口全部改成 9191/9192/9193
for i in 1 2 3; do
port=$((9190+i))
cfg="/opt/kafka3.3.2/config/server-$i.properties"
sed -ri \
-e "s#^listeners[[:space:]]*=.*#listeners=PLAINTEXT://:${port}#" \
-e "s#^advertised.listeners[[:space:]]*=.*#advertised.listeners=PLAINTEXT://localhost:${port}#" \
-e "s#^broker.id[[:space:]]*=.*#broker.id=$i#" \
"$cfg"
done
启动并验证
~/start-kafka.sh
sleep 5 && jps | egrep 'QuorumPeerMain|Kafka'
实际输出结果,终于成功:
[root@localhost ~]# ~/start-kafka.sh
Zookeeper + 3 brokers started. Logs under /opt/kafka3.3.2/
[root@localhost ~]# sleep 5 && jps | egrep 'QuorumPeerMain|Kafka'
36151 Kafka
36152 Kafka
36186 Kafka
26891 QuorumPeerMain
30317 Kafka
[root@localhost ~]#
[root@localhost ~]# ss -lntup | grep :9092
tcp LISTEN 0 50 [::]:9092 [::]:* users:(("java",pid=30317,fd=125))
[root@localhost ~]# # 会看到 PID 30317
[root@localhost ~]# kill -9 30317
[root@localhost ~]# sleep 2 && jps | egrep 'QuorumPeerMain|Kafka'
26891 QuorumPeerMain
[root@localhost ~]# for p in 9191 9192 9193; do echo "== $p =="; ss -lntup | grep ":$p"; done
== 9191 ==
== 9192 ==
== 9193 ==
确认 9092 已释放
ss -lntup | grep :9092 # 应无结果
3、后续不再踩坑的 3 条建议
永远先杀干净旧进程再启动新配置。
端口批量错开(如 9191/9192/9193),避免手动改漏。
日志为空或报错时立即 tail -n 30,定位关键字只用 10 秒。
七、 一键关闭(可选备用脚本)
如果想一次性关闭,再做一个 ~/stop-kafka.sh:
cat > ~/stop-kafka.sh <<'EOF'
#!/bin/bash
# 依次关闭 3 个 broker
for i in 1 2 3; do
/opt/kafka3.3.2/bin/kafka-server-stop.sh /opt/kafka3.3.2/config/server-$i.properties
done
# 关闭 Zookeeper
/opt/kafka3.3.2/bin/zookeeper-server-stop.sh /opt/kafka3.3.2/config/zookeeper.properties
echo "All stopped."
EOF
#赋权
chmod +x ~/stop-kafka.sh
全部完成,直接复制即可。
? 验证
# 建 topic(3 分区 3 副本)
/opt/kafka3.3.2/bin/kafka-topics.sh --create --topic test-ha \
--bootstrap-server localhost:9092 --partitions 3 --replication-factor 3
# 查看
/opt/kafka3.3.2/bin/kafka-topics.sh --describe --topic test-ha --bootstrap-server localhost:9092
看到每个分区 Replicas: 1,2,3 且 Isr: 1,2,3 即成功。
八、开机自启(可选)
下面给出 CentOS 7 systemd 一键方案,复制即可用。
完成后执行 systemctl enable --now kafka 就能开机自启 + 立即启动。
① 创建 systemd 服务文件
sudo tee /etc/systemd/system/kafka.service >/dev/null <<'EOF'
[Unit]
Description=Kafka 3.3.2 Cluster (3 brokers)
After=network.target
[Service]
Type=forking
User=root
ExecStart=/root/start-kafka.sh
ExecStop=/root/stop-kafka.sh
RemainAfterExit=yes
TimeoutStartSec=60
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target
EOF
② 创建对应的 stop 脚本(如果还没有)
sudo tee /root/stop-kafka.sh >/dev/null <<'EOF'
#!/bin/bash
# 依次停 3 个 broker
for i in 1 2 3; do
/opt/kafka3.3.2/bin/kafka-server-stop.sh
done
# 停 Zookeeper
/opt/kafka3.3.2/bin/zookeeper-server-stop.sh
EOF
sudo chmod +x /root/stop-kafka.sh
③ 重载并设为开机自启
sudo systemctl daemon-reload
sudo systemctl enable --now kafka
④ 验证
#一般检查到这一步就可以,出现active就成功,不用之后的重启
systemctl status kafka
⑤慎重验证
# 重启后自动拉起,该命令用于重启整个Linux服务器就是整个机器!!慎用!!!
sudo reboot
# 登录后检查,jps用来列出所有java进程和主类名,后面用来确定Zookeeper和Kafka boker是否正常执行
jps | egrep 'QuorumPeerMain|Kafka'
看到 1 个 QuorumPeerMain + 3 个 Kafka 即成功。
这里我报错:
systemctl status kafka 显示 active (exited),说明 systemd 认为 start-kafka.sh 已经执行完成并退出,但 它不负责守护进程。
而 jps 里 看不到任何 Zookeeper/Kafka 进程 → 3 个 broker 并没有真正跑起来。
根本原因
start-kafka.sh 里用了 nohup … & 把进程放到后台,systemd 默认 Type=forking 需要主进程长期存活,而你的脚本瞬间结束,systemd 就认为服务已经“成功退出”。
问题概述:start-kafka.sh 依旧是 “瞬间结束” 的脚本——它把 Zookeeper 和 3 个 Kafka 丢到后台就退出了,systemd(Type=simple)发现主进程退出,于是立即执行 ExecStop,然后整服务标记为 failed。
解决思路:让 systemd 直接托管 Zookeeper + 3 个 Kafka,而不是托管一个“启动脚本”。最简做法是把它们拆成 4 个独立 service(1 Zookeeper + 3 Kafka),相互依赖即可。下面给出 “一条龙” 方案,复制即用。
之后又所有服务都报 status=127 —— 典型“找不到可执行文件或脚本”错误。
90% 是 路径写错 或 没给执行权限。原因是:1.JDK 不在 PATH(systemd 环境变量 ≠ 登录 shell)2.配置里缺 JAVA_HOME。
解决办法为下面的⑦,然后成功
快速修复:
下面是一份“问题-定位-修复”的完整复盘,完全保留你运行过的每一个代码块,方便你后续直接复制使用或给团队做文档。
? 从 第一次只看到 1 个 Kafka 开始,直到 真正 4 个进程常驻并成功开机自启。
① 第一次现象:只有 1 个 Kafka
[root@localhost ~]# jps | egrep 'QuorumPeerMain|Kafka'
27940 Kafka
26891 QuorumPeerMain
② 立即查看 3 份日志尾
[root@localhost ~]# tail -n 20 /opt/kafka3.3.2/broker-*.log
==> /opt/kafka3.3.2/broker-1.log <==
==> /opt/kafka3.3.2/broker-2.log <==
==> /opt/kafka3.3.2/broker-3.log <==
日志为空 → 说明根本没成功启动。
③ 前台启动 2 号 broker,现场报错
# 先停干净
pkill -f 'kafka\.Kafka'
pkill -f QuorumPeerMain
# 前台跑 2 号
/opt/kafka3.3.2/bin/kafka-server-start.sh /opt/kafka3.3.2/config/server-2.properties
输出核心报错:
Socket server failed to bind to 0.0.0.0:9092: 地址已在使用.
④ 确认 9092 被谁占用
[root@localhost ~]# ss -lntup|grep 9092
tcp LISTEN 0 50 [::]:9092 [::]:* users:(("java",pid=27940,fd=125))
⑤杀掉旧进程 & 清理锁文件
# 杀掉所有残留
pkill -9 -f 'kafka\.Kafka'
# 清理 .lock
for i in 1 2 3; do
rm -f /opt/kafka3.3.2/data/kafka-logs-$i/.lock
done
⑥ 把 3 份配置端口改成不冲突的 9191/9192/9193
for i in 1 2 3; do
port=$((9190+i))
cfg="/opt/kafka3.3.2/config/server-$i.properties"
sed -ri \
-e "s#^listeners[[:space:]]*=.*#listeners=PLAINTEXT://:${port}#" \
-e "s#^advertised.listeners[[:space:]]*=.*#advertised.listeners=PLAINTEXT://localhost:${port}#" \
-e "s#^broker.id[[:space:]]*=.*#broker.id=$i#" \
"$cfg"
done
⑦ 写死 JAVA_HOME 到所有 systemd 单元
# 查出 JDK 路径
[root@localhost ~]# readlink -f $(which java)
/usr/local/jdk17/bin/java
# 写入环境变量
for f in /etc/systemd/system/{zookeeper,kafka-{1..3}}.service; do
sudo sed -i '/\[Service\]/a Environment=JAVA_HOME=/usr/local/jdk17' "$f"
done
⑧ 一键创建 4 个独立 systemd 单元(复制即用)
8.1 Zookeeper
sudo tee /etc/systemd/system/zookeeper.service >/dev/null <<'EOF'
[Unit]
Description=Zookeeper for Kafka
After=network.target
[Service]
Type=simple
User=root
ExecStart=/opt/kafka3.3.2/bin/zookeeper-server-start.sh /opt/kafka3.3.2/config/zookeeper.properties
ExecStop=/opt/kafka3.3.2/bin/zookeeper-server-stop.sh
Environment=JAVA_HOME=/usr/local/jdk17
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
8.2 3 个 Kafka broker
for i in 1 2 3; do
port=$((9190+i))
sudo tee /etc/systemd/system/kafka-${i}.service >/dev/null <
[Unit]
Description=Kafka Broker ${i}
After=zookeeper.service
Requires=zookeeper.service
[Service]
Type=simple
User=root
ExecStart=/opt/kafka3.3.2/bin/kafka-server-start.sh /opt/kafka3.3.2/config/server-${i}.properties
ExecStop=/opt/kafka3.3.2/bin/kafka-server-stop.sh
Environment=JAVA_HOME=/usr/local/jdk17
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
done
⑨ 重载 & 自启
sudo systemctl daemon-reload
sudo systemctl enable --now zookeeper
for i in 1 2 3; do
sudo systemctl enable --now kafka-${i}
done
⑩ 最终验证
更多推荐
所有评论(0)