三节点Kafka(3.9.0-支持jdk8的最高稳定版本)集群部署
一.背景介绍
本次搭建Kafka服务架构为批流一体数仓架构,整体的流程图如下:

1.项目背景与架构综述 (Project Background & Architecture)
1.1 建设目标
为了满足业务对数据鲜活度日益增长的需求,本项目旨在构建基于 Flink + Paimon 的新一代**流批一体(Stream-Batch Unified)**实时数据仓库。项目核心目标是打通从业务数据库(MySQL)到数据湖(Paimon)的实时同步链路,实现 ODS 层的亚秒级入湖与 DWD 层的实时清洗转换,同时利用 Paimon 的特性统一流式读写与批量查询能力。
1.2 数据链路架构
整体数据流转架构设计如下(参考架构流程图图):
数据源层 (Source):采用 MySQL 5.7.44 搭建一主两从(Master-Slave)的高可用集群,确保业务稳定性。
数据采集层 (Ingestion):部署 Maxwell 1.44.0 监听 Slave1 节点的 Binlog,将变更数据实时解析为 JSON 流。
消息缓冲层 (Buffer):构建 3节点 Kafka 3.9.0 集群(Topic: ods_mysql),作为 CDC 数据的核心缓冲区,承担削峰填谷与高吞吐消息分发的任务。
计算与存储层 (Compute & Storage):
使用 Flink 1.19.3 作为计算引擎,通过 Flink SQL 定义流式任务。
底层存储采用 Apache Paimon(1.1.1),构建无分区的 ODS 表与 DWD 表,实现数据的分层管理与实时更新。
2. 技术选型与环境制约说明 (Technical Selection & Constraints)
2.1 核心环境制约:CDH 6.3.2 与 JDK 8
本项目的计算资源调度强依赖于现有的 CDH 6.3.2 (Cloudera Distribution Hadoop) 大数据平台底座。由于 CDH 6.3.2 生态系统(包括 HDFS、YARN、MapReduce)与 JDK 1.8 (Java 8) 存在深度绑定关系,这对上层组件的选型产生了以下决定性影响:
Flink on YARN 的兼容性:Flink 1.19.3 任务必须提交至 CDH 6.3.2 的 YARN 集群运行。为了保障 Flink Client、YARN Container 以及 Hadoop Classpath 之间的类加载兼容性,避免 UnsupportedClassVersionError 或运行时环境冲突,整个计算链路的运行环境必须严格锁定在 JDK 8。
Kafka 版本选择逻辑:尽管 Kafka 社区在新版本中逐步转向 Java 11+,但为了与 CDH 6.3.2 的 JDK 8 环境保持一致,确保 Flink Kafka Connector 在 YARN 容器中运行的稳定性,我们选用了 Kafka 3.9.0。该版本是在支持 JDK 8 运行环境前提下的高版本稳定发行版,既能提供较新的功能特性与性能优化,又能完美适配基于 JDK 8 的存量生产环境。
3. 组件版本清单 (Component Specifications)
为确保集群搭建的兼容性,各组件具体版本规划如下:
| 组件名称 | 版本号 | 角色/部署说明 | 备注 |
|---|---|---|---|
| JDK | 1.8.0_412 | 基础运行环境,全节点统一 | CDH 6.3.2 官方最高支持版本 |
| Kafka | 3.9.0 | 3 节点集群(Broker ×3) | 支持 JDK 8 的最高稳定版,Scala 2.12 官方二进制包 |
| MySQL | 5.7.44 | 1 Master + 2 Slave 主从架构 | 与 CDH 6.x 认证版本一致,Row-Based Binlog |
| Maxwell | 1.44.0 | 单节点 CDC 采集,连接 Slave1 | 末版默认 JDK 8 编译,向下兼容 Kafka 3.9 |
| Flink | 1.19.3 | On YARN 模式,流 & 批一体 | 官方提供 JDK 8 二进制包,内置 kafka-client 3.9 |
| Paimon | 1.1.1 | 数据湖存储,集成 Flink 1.19 | 支持 CDC 高效写入,与 Flink 1.19 官方兼容 |
| Hadoop | CDH 6.3.2 | 资源调度(YARN)+ 存储(HDFS) | 整套体系已含 ZooKeeper、HDFS、YARN,无需额外部署 |
4.Kafka三节点部署(3.9版本)
4.1 JDK 8 安装
#下载jdk8
sudo yum install -y java-1.8.0-openjdk-devel
修改环境变量
vi ~/.bashrc
添加以下:
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk
export PATH=$JAVA_HOME/bin:$PATH
#刷新环境变量
source ~/.bashrc
#查看有没有成功
java -version
如图所示结果则证明安装成功:

4.2 kafka三节点部署(xx.xx.xx.xx1,xx.xx.xx.xx2,xx.xx.xx.xx3)
4.2.1下载压缩包并创建日志存放文件
#下载 & 解压 3.9.0(三台同时)
wget -c https://archive.apache.org/dist/kafka/3.9.0/kafka_2.13-3.9.0.tgz
tar -zxvf kafka_2.13-3.9.0.tgz
#创建日志存放文件
mkdir -p /home/bigdata/kafka-data
4.2.2修改配置文件
#进入目录
cd /home/bigdata/kafka_2.13-3.9.0/config/kraft/
复制默认配置文件,备份(所有节点):
#节点一
cp config/kraft/server.properties config/kraft/server1.properties
#节点二
cp config/kraft/server.properties config/kraft/server2.properties
#节点三
cp config/kraft/server.properties config/kraft/server3.properties
修改配置文件节点一(xx.xx.xx.xx1):
修改server1.properties文件:
process.roles=broker,controller
node.id=1
controller.quorum.voters=1@xx.xx.xx.xx1:9093,2@xx.xx.xx.xx2:9093,3@xx.xx.xx.xx3:9093
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
inter.broker.listener.name=PLAINTEXT
advertised.listeners=PLAINTEXT://xx.xx.xx.xx1:9092,CONTROLLER://xx.xx.xx.xx1:9093
controller.listener.names=CONTROLLER
log.dirs=/home/bigdata/kafka-data
num.partitions=3
off-sets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=2
修改配置文件节点一(xx.xx.xx.xx2):
修改server2.properties文件:
bash
process.roles=broker,controller
node.id=2
controller.quorum.voters=1@xx.xx.xx.xx1:9093,2@xx.xx.xx.xx2:9093,3@xx.xx.xx.xx3:9093
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
inter.broker.listener.name=PLAINTEXT
advertised.listeners=PLAINTEXT://xx.xx.xx.xx2:9092,CONTROLLER://xx.xx.xx.xx2:9093
controller.listener.names=CONTROLLER
log.dirs=/home/bigdata/kafka-data
num.partitions=3
off-sets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=2
修改配置文件节点一(xx.xx.xx.xx3):
修改server3.properties文件:
bash
process.roles=broker,controller
node.id=2
controller.quorum.voters=1@xx.xx.xx.xx1:9093,2@xx.xx.xx.xx2:9093,3@xx.xx.xx.xx3:9093
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
inter.broker.listener.name=PLAINTEXT
advertised.listeners=PLAINTEXT://xx.xx.xx.xx3:9092,CONTROLLER://xx.xx.xx.xx3:9093
controller.listener.names=CONTROLLER
log.dirs=/home/bigdata/kafka-data
num.partitions=3
off-sets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=2
参数解释:
| 参数 | 示例值 | 含义/作用 | 备注 |
|---|---|---|---|
| process.roles | broker,controller | 该节点同时承担「代理」与「控制器」角色 | 3 节点集群推荐全设为 broker,controller,形成合并模式 |
| node.id | 1 | 集群内唯一数字 ID,替代原来的 broker.id | 每台分别写 1/2/3,与 voters 列表对应 |
| controller.quorum.voters | 1@ip1:9093,2@ip2:9093,3@ip3:9093 | 构成控制仲裁的节点与端口 | 必须列出全部 3 个;格式 id@host:port |
| listeners | PLAINTEXT://:9092,CONTROLLER://:9093 | 本机监听的所有接口与端口 | 0.0.0.0 表示绑定任意地址;生产可改为内网 IP |
| inter.broker.listener.name | PLAINTEXT | Broker 之间通信使用的监听器名字 | 必须与 listeners 里某一项名字一致 |
| advertised.listeners | PLAINTEXT://ip1:9092,CONTROLLER://ip1:9093 | 对外公布的地址,供客户端和其他节点连接 | 写客户端能解析的 IP 或主机名 |
| controller.listener.names | CONTROLLER | 控制器仲裁使用的监听器名字 | 只能选一个,与 voters 里的端口对应 |
| log.dirs | /home/bigdata/kafka-data | 存储段文件(.log/.index/.timeindex)的目录 | 多块盘可逗号分隔;需保证读写性能 |
| num.partitions | 3 | 自动创建 Topic 时的默认分区数 | 后期可通过 kafka-topics.sh 动态扩容 |
| offsets.topic.replication.factor | 3 | __consumer_offsets 副本数 | 必须 ≤ 存活 broker 数,建议 =3 |
| transaction.state.log.replication.factor | 3 | __transaction_state 副本数 | 使用 EOS/Flink 精确一次时必须 =3 |
| transaction.state.log.min.isr | 2 | 事务 Topic 最小可用副本 | 设为 replication.factor-1,兼顾高可用与一致性 |
4.2.3格式化存储 (仅需生成一次 UUID)
三台机器必须使用同一个 Cluster ID 进行初始化。
在主节点(xx.xx.xx.xx1)上生成 UUID:
cd /home/bigdata/kafka_2.13-3.9.0
bin/kafka-storage.sh random-uuid
结果如下:

在节点 1 (xx.xx.xx.xx1) 上格式化:
bin/kafka-storage.sh format -t "A9LsS6bwQ1Spwmxdnk6vFg" -c config/kraft/server1.properties
在节点 2 (xx.xx.xx.xx2) 上格式化 (使用同一个 UUID):
bin/kafka-storage.sh format -t "A9LsS6bwQ1Spwmxdnk6vFg" -c config/kraft/server2.properties
在节点 3 (xx.xx.xx.xx3) 上格式化 (使用同一个 UUID):
bin/kafka-storage.sh format -t "A9LsS6bwQ1Spwmxdnk6vFg" -c config/kraft/server3.properties
4.2.3启动三节点Kafka,并进行测试
启动命令:
cd /home/bigdata/kafka_2.13-3.9.0
然后在节点1上执行:
bin/kafka-server-start.sh -daemon config/kraft/server1.properties
然后在节点2上执行:
bin/kafka-server-start.sh -daemon config/kraft/server2.properties
然后在节点3上执行:
bin/kafka-server-start.sh -daemon config/kraft/server3.properties
然后查看集群状态:
bin/kafka-metadata-quorum.sh --bootstrap-server xx.xx.xx.xx1:9092 describe --status

创建topic:
bin/kafka-topics.sh --create \
--bootstrap-server xx.xx.xx.xx1:9092 \
--topic test-topic \
--partitions 3 \
--replication-factor 3

然后验证分区分布情况(关键)
创建后,我们需要看下这个 Topic 的分区是否均匀分布在了三台机器上。
bin/kafka-topics.sh --describe \
--bootstrap-server xx.xx.xx.xx1:9092 \
--topic test-topic

模拟发送消息(生产者)
启动控制台生产者,向集群发送几条消息。
bin/kafka-console-producer.sh \
--bootstrap-server xx.xx.xx.xx1:9092 \
--topic test-topic
出现 > 符号后,随意输入几行文字,按回车发送:
hello word

模拟接收消息(消费者)
启动控制台消费者,看看能否读到刚才发的消息。
bin/kafka-console-consumer.sh \
--bootstrap-server xx.xx.xx.xx1:9092 \
--topic test-topic \
--from-beginning

4.3总流程测试代码
from kafka import KafkaProducer, KafkaConsumer, KafkaAdminClient
from kafka.admin import NewTopic
from kafka.errors import KafkaError, TopicAlreadyExistsError
import json
import time
import random
import threading
# 配置信息
BOOTSTRAP_SERVERS = ['xx.xx.xx.xx1:9092', 'xx.xx.xx.xx2:9092', 'xx.xx.xx.xx3:9092']
TEST_TOPIC = 'performance_test'
TEST_GROUP = 'test_consumer_group'
def create_test_topic():
"""创建测试主题(如果不存在)"""
admin = KafkaAdminClient(bootstrap_servers=BOOTSTRAP_SERVERS)
try:
topic = NewTopic(
name=TEST_TOPIC,
num_partitions=6,
replication_factor=3,
topic_configs={'retention.ms': '172800000'} # 保留2天
)
admin.create_topics([topic])
print(f"主题 {TEST_TOPIC} 创建成功")
except TopicAlreadyExistsError:
print(f"主题 {TEST_TOPIC} 已存在")
finally:
admin.close()
def producer_performance_test():
"""生产者性能测试"""
producer = KafkaProducer(
bootstrap_servers=BOOTSTRAP_SERVERS,
value_serializer=lambda v: json.dumps(v).encode('utf-8'),
acks='all',
compression_type='gzip',
retries=5
)
start_time = time.time()
msg_count = 0
for i in range(1000):
payload = {
"id": i,
"timestamp": time.time(),
"metric": random.uniform(0, 100),
"source": f"server-{random.randint(1, 10)}",
"is_alert": random.random() > 0.9
}
future = producer.send(
TEST_TOPIC,
value=payload,
key=str(i % 10).encode() # 按key分区
)
# 异步处理发送结果
future.add_callback(
lambda metadata: print(f"发送成功: 分区={metadata.partition} 偏移量={metadata.offset}")
).add_errback(
lambda exc: print(f"发送失败: {exc}")
)
msg_count += 1
# 每100条刷新一次
if i % 100 == 0:
producer.flush()
producer.flush()
elapsed = time.time() - start_time
print(f"\n生产者测试完成: 发送 {msg_count} 条消息, 耗时 {elapsed:.2f} 秒, "
f"吞吐量 {msg_count / elapsed:.2f} msg/s")
def consumer_group_test():
"""消费者组测试"""
def start_consumer(consumer_id):
consumer = KafkaConsumer(
TEST_TOPIC,
bootstrap_servers=BOOTSTRAP_SERVERS,
group_id=TEST_GROUP,
auto_offset_reset='earliest',
enable_auto_commit=False,
value_deserializer=lambda x: json.loads(x.decode('utf-8')),
consumer_timeout_ms=10000
)
print(f"消费者 {consumer_id} 启动...")
try:
for message in consumer:
print(f"消费者 {consumer_id} [分区 {message.partition}]: "
f"偏移量={message.offset} 值={message.value}")
# 模拟处理时间
time.sleep(random.uniform(0.01, 0.1))
consumer.commit()
finally:
consumer.close()
print(f"消费者 {consumer_id} 关闭")
# 启动3个消费者线程
threads = []
for i in range(3):
t = threading.Thread(target=start_consumer, args=(i,))
t.start()
threads.append(t)
time.sleep(1) # 错开启动时间
for t in threads:
t.join()
def transaction_test():
"""事务消息测试(精确一次语义)"""
try:
producer = KafkaProducer(
bootstrap_servers=BOOTSTRAP_SERVERS,
value_serializer=lambda v: json.dumps(v).encode('utf-8'),
transactional_id='test_transaction',
# 添加安全协议配置(如果集群需要)
# security_protocol='SASL_PLAINTEXT',
# sasl_mechanism='PLAIN',
# sasl_plain_username='admin',
# sasl_plain_password='admin-secret',
# 增加超时时间
request_timeout_ms=15000,
retries=5
)
# 初始化事务 - 关键步骤!
producer.init_transactions()
print("事务初始化成功")
try:
print("开始事务...")
producer.begin_transaction()
# 发送业务消息
for i in range(5):
msg = {"tx_id": i, "data": f"transaction_{i}"}
producer.send(TEST_TOPIC, value=msg)
print(f"事务消息发送: {msg}")
# 提交事务
print("提交事务...")
producer.commit_transaction()
print("事务提交成功")
except KafkaError as e:
print(f"事务操作失败: {e}")
# 尝试中止事务
try:
producer.abort_transaction()
print("事务已中止")
except Exception as abort_err:
print(f"中止事务失败: {abort_err}")
finally:
producer.close()
except Exception as init_err:
print(f"事务初始化失败: {init_err}")
def get_topic_metadata():
"""获取主题元数据"""
admin = KafkaAdminClient(bootstrap_servers=BOOTSTRAP_SERVERS)
# 获取所有主题
topics = admin.list_topics()
print("\n集群主题列表:")
for topic in topics:
print(f" - {topic}")
# 获取特定主题详情
if TEST_TOPIC in topics:
partitions = admin.describe_topics([TEST_TOPIC])
print(f"\n主题 {TEST_TOPIC} 详情:")
for p in partitions[0]['partitions']:
print(f"分区 {p['partition']}: 领导者={p['leader']} 副本={p['replicas']} ISR={p['isr']}")
admin.close()
def monitor_consumer_group():
"""监控消费者组"""
admin = KafkaAdminClient(bootstrap_servers=BOOTSTRAP_SERVERS)
# 获取消费者组列表
groups = admin.list_consumer_groups()
print("\n消费者组列表:")
for group in groups:
print(f" - {group[0]} ({group[1]})")
# 获取特定消费者组详情
if any(TEST_GROUP in g for g in groups):
group_details = admin.describe_consumer_groups([TEST_GROUP])
print(f"\n消费者组 {TEST_GROUP} 详情:")
for member in group_details[0].members:
print(f"成员 {member.member_id}:")
print(f" 客户端ID: {member.client_id}")
print(f" 主机: {member.client_host}")
print(f" 分配分区: {member.assignment}")
admin.close()
if __name__ == "__main__":
print("=" * 50)
print("开始 Kafka 综合测试")
print("=" * 50)
create_test_topic()
print("\n[阶段1] 生产者性能测试")
producer_performance_test()
print("\n[阶段2] 消费者组测试")
consumer_group_test()
print("\n[阶段3] 事务消息测试")
transaction_test()
print("\n[阶段4] 元数据检查")
get_topic_metadata()
print("\n[阶段5] 消费者组监控")
monitor_consumer_group()
print("\n测试完成!")
5.kafka全流程测试脚本介绍
(1)脚本名称
kafka_full_test.py
(2) 运行环境
- 执行路径: D:\pycharm\PyCharm 2024.2.4\pythonProject1\kafka\kafka_full_test.py
- 解释器:Python 3.8(位于 D:\pycharm\PyCharm 2024.2.4\python3.8.venv\Scripts\python.exe )
- 测试对象:Kafka 集群(主题 performance_test )
(3)核心功能
该脚本用于对 Kafka 集群的核心功能与性能进行全面测试,涵盖 5 个关键阶段:
- 生产者性能测试:验证消息发送效率与分区分发能力;
- 消费者组测试:验证多消费者协同消费、分区分配与消息解析能力;
- 事务消息测试:验证事务消息的初始化、发送与提交功能;
- 元数据检查:验证集群主题、分区及副本配置的正确性;
- 消费者组监控:查看集群中活跃的消费者组信息。
(4)测试结果总结
- 阶段 1:生产者性能测试
-
测试内容:向主题 performance_test 发送消息,验证生产者吞吐量与分区分发逻辑。
-
关键结果:
- 共发送 1000 条消息,耗时 0.22 秒,吞吐量达4569.43 msg/s;(第一次测试为耗时 2.06 秒,吞吐量达485.81 msg/s)

- 消息均匀分发至 6 个分区(分区 0-5),各分区偏移量连续递增(例如分区 3 偏移量从 302 增至 601,分区 5 从 200 增至 399),说明分区负载均衡正常。
- 共发送 1000 条消息,耗时 0.22 秒,吞吐量达4569.43 msg/s;(第一次测试为耗时 2.06 秒,吞吐量达485.81 msg/s)
- 阶段 2:消费者组测试
-
测试内容:启动 3 个消费者组成消费组,验证分区分配、消息消费完整性与内容解析能力。
-
关键结果:

-
消费者分区分配:消费者 0 负责分区 5、4;消费者 1 负责分区 3、2;消费者 2 负责分区 0、1,分配逻辑合理;
-
消息消费完整:所有发送的消息均被正确消费,包含事务消息(如 {‘tx_data’: ‘transaction_0’} )和业务消息(含 id 、 timestamp 、 metric 、 source 、 is_alert 等字段);
-
事务消息:如
{‘tx_data’: ‘transaction_0’}
- tx_data :事务数据字段,存储事务相关的标识信息(如事务 ID)。
-
业务消息:如
{‘id’: 1, ‘timestamp’: 1752463717.3170567, ‘metric’: 75.68237032259279, ‘source’: ‘server-10’, ‘is_alert’: False}
- id :消息唯一标识,用于区分不同消息。
- timestamp :消息生成的时间戳(Unix 时间戳,精确到毫秒),记录消息产生的时间。
- metric :指标值,通常为数值型数据(如服务器性能指标)。
- source :消息来源,如 “server-10” 表示消息来自编号为 10 的服务器。
- is_alert :告警标识(布尔值), True 表示该消息触发告警, False 表示正常消息。
-
-
消费状态正常:消费者均成功启动并关闭,无消息丢失或重复消费。
-
- 阶段 3:事务消息测试
-
测试内容:验证 Kafka 事务消息的初始化、发送与提交机制。
-
关键结果:

- 事务初始化成功,发送 5 条事务消息( tx_id 0-4,数据为 transaction_0 至 transaction_4 );
- tx_id :事务唯一标识,用于关联同一事务中的多条消息。
- data :事务具体数据内容。
- 事务提交成功,无异常报错,说明事务功能正常。
- 阶段 4:元数据检查
-
测试内容:验证 Kafka 集群主题、分区及副本配置的正确性。
-
关键结果:

-
集群主题列表:包含 test-topic1 、 performance_test 、 __consumer_offsets 等 10 个主题,符合预期;
-
主题
performance_test
详情:
- 共 6 个分区(0-5);
- 每个分区均配置 3 个副本,领导者(Leader)分布在不同节点,ISR(同步副本)包含所有副本(如分区 5:领导者 = 1,副本 =[1,2,3],ISR=[1,2,3]),集群副本同步正常。
-
- 阶段 5:消费者组监控
-
测试内容:查看集群中活跃的消费者组信息。
-
关键结果:

- 消费者组列表:包含 test_consumer_group 和 test-group ,均为 consumer 类型,与测试配置一致。
2251

被折叠的 条评论
为什么被折叠?



