三节点Kafka(3.9.0-支持jdk8的最高稳定版本)集群部署

三节点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)

为确保集群搭建的兼容性,各组件具体版本规划如下:

组件名称版本号角色/部署说明备注
JDK1.8.0_412基础运行环境,全节点统一CDH 6.3.2 官方最高支持版本
Kafka3.9.03 节点集群(Broker ×3)支持 JDK 8 的最高稳定版,Scala 2.12 官方二进制包
MySQL5.7.441 Master + 2 Slave 主从架构与 CDH 6.x 认证版本一致,Row-Based Binlog
Maxwell1.44.0单节点 CDC 采集,连接 Slave1末版默认 JDK 8 编译,向下兼容 Kafka 3.9
Flink1.19.3On YARN 模式,流 & 批一体官方提供 JDK 8 二进制包,内置 kafka-client 3.9
Paimon1.1.1数据湖存储,集成 Flink 1.19支持 CDC 高效写入,与 Flink 1.19 官方兼容
HadoopCDH 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.rolesbroker,controller该节点同时承担「代理」与「控制器」角色3 节点集群推荐全设为 broker,controller,形成合并模式
node.id1集群内唯一数字 ID,替代原来的 broker.id每台分别写 1/2/3,与 voters 列表对应
controller.quorum.voters1@ip1:9093,2@ip2:9093,3@ip3:9093构成控制仲裁的节点与端口必须列出全部 3 个;格式 id@host:port
listenersPLAINTEXT://:9092,CONTROLLER://:9093本机监听的所有接口与端口0.0.0.0 表示绑定任意地址;生产可改为内网 IP
inter.broker.listener.namePLAINTEXTBroker 之间通信使用的监听器名字必须与 listeners 里某一项名字一致
advertised.listenersPLAINTEXT://ip1:9092,CONTROLLER://ip1:9093对外公布的地址,供客户端和其他节点连接写客户端能解析的 IP 或主机名
controller.listener.namesCONTROLLER控制器仲裁使用的监听器名字只能选一个,与 voters 里的端口对应
log.dirs/home/bigdata/kafka-data存储段文件(.log/.index/.timeindex)的目录多块盘可逗号分隔;需保证读写性能
num.partitions3自动创建 Topic 时的默认分区数后期可通过 kafka-topics.sh 动态扩容
offsets.topic.replication.factor3__consumer_offsets 副本数必须 ≤ 存活 broker 数,建议 =3
transaction.state.log.replication.factor3__transaction_state 副本数使用 EOS/Flink 精确一次时必须 =3
transaction.state.log.min.isr2事务 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. 阶段 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),说明分区负载均衡正常。
  1. 阶段 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 表示正常消息。
    • 消费状态正常:消费者均成功启动并关闭,无消息丢失或重复消费。

  1. 阶段 3:事务消息测试
  • 测试内容:验证 Kafka 事务消息的初始化、发送与提交机制。

  • 关键结果:
    在这里插入图片描述

    • 事务初始化成功,发送 5 条事务消息( tx_id 0-4,数据为 transaction_0 至 transaction_4 );
    • tx_id :事务唯一标识,用于关联同一事务中的多条消息。
    • data :事务具体数据内容。
    • 事务提交成功,无异常报错,说明事务功能正常。
  1. 阶段 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]),集群副本同步正常。
  1. 阶段 5:消费者组监控
  • 测试内容:查看集群中活跃的消费者组信息。

  • 关键结果:

    在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值