1. 为什么现在还有人要装 Kafka 2.5.0
先把话说在前头:Kafka 2.5.0 不是一个新版本,它发布于 2020 年,属于 2.x 系列里比较靠中间的一个版本。放到今天,3.x 甚至 4.x 都已经铺开了,KRaft 模式也在逐步取代 ZooKeeper。那为什么还有人在装 2.5.0?我接触到的真实场景大概有这么几类:一是老项目锁定版本,生产环境跑的就是 2.5.0,本地要搭一套一模一样的来复现问题;二是某些大数据组件生态对 Kafka 客户端版本有硬性依赖,升上去反而会踩兼容性的坑;三是教学和实验环境,2.5.0 的架构足够经典,ZooKeeper 和 Kafka 的关系清晰,适合拿来理解消息队列的底层机制。
所以这篇内容不是教你"追新",而是教你"把 2.5.0 稳稳当当地装起来、跑起来、能验证"。我会从环境准备、ZooKeeper 与 Kafka 的关系、单机与伪集群两种装法、参数配置、启动验证,一直到常见报错排查,完整走一遍。适合谁看?刚接触 Kafka 的运维和开发、需要复现老版本问题的工程师、以及想搞懂 Kafka 到底依赖什么才能跑起来的学习者。哪怕你之前只听过 Kafka 这个名字,跟着走也能装出一套能用的环境。
需要提前说明的是,Kafka 2.5.0 依赖 ZooKeeper 来管理集群元数据,这一点和后来的 KRaft 模式完全不同。你得先接受"要装两个东西"这个前提,后面所有步骤都是围绕这个前提展开的。理解了这一点,很多配置项你就不用死记硬背了。
2. 安装前的整体设计与思路拆解
2.1 为什么 2.5.0 必须搭配 ZooKeeper
Kafka 2.5.0 的架构里,ZooKeeper 承担的是"集群大脑"的角色。它不存消息,消息全在 Kafka 自己的日志文件里,但集群里有哪些 Broker、哪个 Broker 是 Controller、Topic 有多少分区、分区的 Leader 是谁、ISR 列表是什么,这些元数据统统放在 ZooKeeper 里。你可以把 ZooKeeper 理解成一个"通讯录加选举委员会":新 Broker 上线要到它这里登记,Controller 挂了要它来主持重新选举,消费者组的偏移量在早期版本里也存它这儿。
这就决定了安装顺序:先起 ZooKeeper,再起 Kafka。如果 ZooKeeper 没起来,Kafka 启动时会一直报连接超时,日志里刷Connection refused或者Session expired。我见过不少人卡在这一步,以为是 Kafka 配置错了,其实是 ZooKeeper 根本没跑。
那为什么 2.5.0 不自己管元数据?因为 KRaft 模式在 2.5.0 时期还处于早期实验阶段,根本没到能上生产的程度。真正把 ZooKeeper 拿掉是 3.3 之后的事。所以装 2.5.0,你就老老实实把 ZooKeeper 一起装了,别想着绕过。
2.2 单机、伪集群、真集群怎么选
装之前先想清楚你要什么形态,这直接决定配置怎么写。
| 形态 | Broker 数量 | ZooKeeper | 适用场景 | 配置复杂度 |
|---|---|---|---|---|
| 单机模式 | 1 | 1 | 本地开发、功能验证 | 低 |
| 伪集群 | 3 | 1 或 3 | 学习副本机制、模拟故障 | 中 |
| 真集群 | 3+ | 3+ | 生产环境 | 高 |
单机模式最简单,一个 ZooKeeper 加一个 Broker,改几个配置就能跑。伪集群是在一台机器上起多个 Broker 进程,用不同端口区分,主要用来观察分区副本、Leader 选举这些行为。真集群涉及跨机器部署,还要考虑网络、磁盘、机架感知,那是另一个话题了。
我个人的建议是:第一次装,先跑通单机模式,把流程走顺;等单机能稳定收发消息了,再折腾伪集群。一上来就搞三节点,配置一多,出问题你都不知道是哪个环节的锅。
2.3 版本与依赖的取舍逻辑
Kafka 2.5.0 对运行环境有几个硬性要求,这些不是随便定的:
- JDK 8 或 JDK 11。2.5.0 官方推荐 JDK 8,JDK 11 也支持。别用 JDK 17 及以上,2.5.0 里有些反射相关的代码在新版 JDK 上会因为模块化限制直接报错,比如
InaccessibleObjectException。这是很多人踩的坑,装完启动就崩,一看日志是 JDK 版本太高。 - Scala 2.12 或 2.13。Kafka 是 Scala 写的,2.5.0 提供了两个编译版本,下载包名里带
2.12或2.13。选哪个?看你生态里其他组件用什么,一般选 2.12 兼容性更广。 - ZooKeeper 3.5.x 或 3.4.x。2.5.0 自带的启动脚本可以拉起一个内置的 ZooKeeper,但生产上一般用独立部署的。学习环境用自带的就够。
这里有个细节:Kafka 安装包里其实自带了一个 ZooKeeper,在libs目录下能看到zookeeper-3.5.7.jar之类的文件,bin目录里也有zookeeper-server-start.sh。所以严格来说你不需要单独下载 ZooKeeper,直接用 Kafka 自带的就行。这也是为什么很多人说"装 Kafka 顺便把 ZooKeeper 也装了"。
3. 核心细节解析与实操要点
3.1 环境准备:JDK 和目录规划
第一步永远是环境。先确认 JDK:
java -version输出应该是1.8.0_xxx或者11.0.x。如果是 17 或更高,先装一个 JDK 8,用update-alternatives或者直接改JAVA_HOME切过去。
目录规划这块,我建议养成习惯,别把东西乱丢。我常用的布局是这样:
/opt/kafka/ # Kafka 安装目录 /opt/kafka/data/ # 消息日志目录,单独放 /opt/zookeeper/ # ZooKeeper 数据目录为什么把数据目录单独拎出来?因为 Kafka 的消息日志会疯狂增长,如果和安装目录混在一起,哪天磁盘满了你清理起来很麻烦,而且升级 Kafka 时容易误删数据。把log.dirs指向独立目录,升级时只换安装目录,数据原地不动,这是运维的基本素养。
注意:Kafka 的
log.dirs里存的不是"日志"(log4j 那种),而是消息数据本身。别被名字骗了,这个目录会非常大,务必挂在大磁盘上。
3.2 下载与解压的正确姿势
2.5.0 的包在官方归档里能找到,文件名类似kafka_2.12-2.5.0.tgz。下载完先校验一下大小,正常在 60MB 上下。解压:
tar -zxvf kafka_2.12-2.5.0.tgz -C /opt/ mv /opt/kafka_2.12-2.5.0 /opt/kafka解压完看一眼目录结构,心里有个数:
ls /opt/kafka # bin config libs logs site-docsbin:所有启动脚本,包括 Kafka 和 ZooKeeper 的config:配置文件,server.properties和zookeeper.properties都在这libs:所有 jar 包,包括依赖的 ZooKeeperlogs:Kafka 自身的运行日志(log4j 输出),不是消息数据
这里要区分清楚:logs目录是 Kafka 进程的运行日志,log.dirs配置指向的才是消息数据。两个"log"含义完全不同,新手最容易搞混。
3.3 ZooKeeper 配置的关键参数
ZooKeeper 的配置文件是config/zookeeper.properties。单机模式下,核心就几个参数:
dataDir=/opt/zookeeper/data clientPort=2181 maxClientCnxns=0 admin.enableServer=false逐个说:
dataDir:ZooKeeper 存快照和事务日志的地方。必须手动创建这个目录,ZooKeeper 不会自动建,目录不存在直接启动失败。clientPort=2181:客户端连接端口,Kafka 就是通过这个端口连上来的。默认 2181,如果被占用要改,改了之后 Kafka 那边也要同步改。maxClientCnxns=0:单个客户端的最大连接数,0 表示不限制。学习环境设 0 省事,生产上要限制防止连接泄漏。admin.enableServer=false:关掉 ZooKeeper 自带的 admin server。这个 admin server 默认占 8080 端口,经常和别的服务冲突,关掉能省不少麻烦。
实操心得:
dataDir目录一定要先mkdir -p建好,并且确保运行 Kafka 的用户对这个目录有读写权限。我见过太多"启动就退出"的案例,最后发现是权限问题或者目录不存在。
3.4 Kafka 核心配置逐项拆解
Kafka 的配置文件是config/server.properties。单机模式下,需要改的其实不多,但每一项都得理解:
broker.id=0 listeners=PLAINTEXT://:9092 advertised.listeners=PLAINTEXT://你的机器IP:9092 log.dirs=/opt/kafka/data zookeeper.connect=localhost:2181 num.partitions=1 default.replication.factor=1 offsets.topic.replication.factor=1 transaction.state.log.replication.factor=1 transaction.state.log.min.isr=1broker.id:每个 Broker 的唯一标识,集群里不能重复。单机就 0。listeners:Broker 监听的地址。PLAINTEXT://:9092表示在所有网卡上监听 9092 端口,不加密。advertised.listeners:这个最容易出错。它是 Broker 告诉客户端"你应该用这个地址来连我"的地址。如果只写localhost,那么远程客户端拿到这个地址后连不上。生产环境必须写真实的、客户端能访问到的 IP 或域名。log.dirs:消息数据目录,前面说过,单独放。zookeeper.connect:ZooKeeper 地址,多个用逗号分隔,如zk1:2181,zk2:2181,zk3:2181。num.partitions:新建 Topic 的默认分区数。单机设 1 就行。default.replication.factor:默认副本数。单机只能设 1,设 2 会因为凑不齐副本导致创建 Topic 失败。- 后面三个
replication.factor相关的:Kafka 内部 Topic(存偏移量、事务状态)的副本配置。单机环境必须都设成 1,否则内部 Topic 创建不出来,消费者根本没法用。
注意:单机环境下,凡是
replication.factor相关的参数,统统设成 1。这是新手最容易忽略的地方,设成大于 1 的值,Kafka 启动不报错,但一创建 Topic 就卡住或者报Replication factor: 2 larger than available brokers: 1。
4. 实操过程与核心环节实现
4.1 启动 ZooKeeper 并验证
配置改完,先起 ZooKeeper。前台启动方便看日志:
cd /opt/kafka bin/zookeeper-server-start.sh config/zookeeper.properties看到日志里出现binding to port 0.0.0.0/0.0.0.0:2181就说明起来了。别关这个终端,另开一个窗口验证:
echo ruok | nc localhost 2181 # 返回 imok 表示正常ruok是 ZooKeeper 的四字命令,返回imok就代表服务健康。如果nc没装,用telnet localhost 2181也行。这一步很关键,ZooKeeper 没确认好就去起 Kafka,后面出问题你会怀疑人生。
生产环境一般用后台启动:
bin/zookeeper-server-start.sh -daemon config/zookeeper.properties-daemon会让它后台跑,日志输出到logs/zookeeper.out。
4.2 启动 Kafka Broker
ZooKeeper 确认没问题后,起 Kafka:
bin/kafka-server-start.sh config/server.properties启动日志里会有一堆信息,重点看这几行:
[KafkaServer id=0] started:出现这行才算真正启动成功Registered broker 0 at path /brokers/ids/0:说明 Broker 已经在 ZooKeeper 注册了- 如果一直刷
Opening socket connection to server localhost/127.0.0.1:2181然后失败,说明 ZooKeeper 没连上
后台启动同理:
bin/kafka-server-start.sh -daemon config/server.properties启动后验证进程:
jps # 应该能看到 QuorumPeerMain(ZooKeeper)和 Kafka(Kafka Broker)jps是 JDK 自带的工具,能列出所有 Java 进程。看到这两个就说明两个服务都活着。
4.3 创建 Topic 并验证收发消息
环境起来了,得验证它真能干活。先创建一个 Topic:
bin/kafka-topics.sh --create \ --bootstrap-server localhost:9092 \ --replication-factor 1 \ --partitions 3 \ --topic test-topic注意 2.5.0 里--bootstrap-server和--zookeeper两种参数都支持。--bootstrap-server是走 Kafka 协议,--zookeeper是直接读 ZooKeeper。新版本推荐用--bootstrap-server,因为--zookeeper在后续版本被废弃了。这里用--bootstrap-server更贴近未来。
查看 Topic 列表和详情:
bin/kafka-topics.sh --list --bootstrap-server localhost:9092 bin/kafka-topics.sh --describe --bootstrap-server localhost:9092 --topic test-topicdescribe的输出会显示每个分区的 Leader、Replicas、Isr。单机环境下,三个分区的 Leader 都是 0,Replicas 和 Isr 都是0。
接下来开一个生产者:
bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic test-topic终端会进入等待输入状态,敲几行字回车:
hello kafka second message third message另开一个终端开消费者:
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test-topic --from-beginning--from-beginning表示从头消费,不加这个参数只会消费启动之后新产生的消息。你应该能看到刚才输入的三行字被打印出来。到这一步,一套能用的 Kafka 2.5.0 就算装好了。
4.4 伪集群模式的配置差异
如果你想观察副本机制,可以在一台机器上起三个 Broker。核心是复制三份server.properties,改这几个地方:
| 配置项 | broker-0 | broker-1 | broker-2 |
|---|---|---|---|
| broker.id | 0 | 1 | 2 |
| listeners | :9092 | :9093 | :9094 |
| log.dirs | /opt/kafka/data-0 | /opt/kafka/data-1 | /opt/kafka/data-2 |
| advertised.listeners | IP:9092 | IP:9093 | IP:9094 |
三个 Broker 连同一个 ZooKeeper。启动时分别指定不同配置文件:
bin/kafka-server-start.sh -daemon config/server-0.properties bin/kafka-server-start.sh -daemon config/server-1.properties bin/kafka-server-start.sh -daemon config/server-2.properties然后创建 Topic 时把副本数设成 3:
bin/kafka-topics.sh --create \ --bootstrap-server localhost:9092 \ --replication-factor 3 \ --partitions 3 \ --topic cluster-topic再用describe看,每个分区的 Replicas 应该是0,1,2这样的组合,Leader 分散在不同 Broker 上。这时候你可以手动 kill 掉一个 Broker,再describe,会看到 Leader 发生了切换,Isr 列表里少了一个。这就是副本机制在起作用,也是伪集群最大的价值——让你亲眼看到故障转移。
实操心得:伪集群启动三个 Broker 很吃内存,每个默认要 1GB 堆。如果机器内存紧张,改
bin/kafka-server-start.sh里的KAFKA_HEAP_OPTS,把-Xmx1G调成-Xmx512M。三个加起来 1.5GB,一般开发机扛得住。
5. 常见问题与排查技巧实录
装 Kafka 2.5.0 的过程里,报错基本集中在几个地方。我把踩过的坑整理成一张速查表,遇到问题先对号入座。
| 现象 | 可能原因 | 排查方向 | 解决方式 |
|---|---|---|---|
| Kafka 启动即退出 | JDK 版本过高 | 看日志有无InaccessibleObjectException | 换 JDK 8 或 11 |
| 一直刷连接 ZooKeeper 失败 | ZooKeeper 没起或端口不对 | echo ruok | nc localhost 2181 | 先起 ZooKeeper,核对端口 |
| 创建 Topic 报副本数过大 | 单机设了 replication.factor>1 | 看default.replication.factor | 改成 1 |
| 消费者收不到消息 | 没加--from-beginning | 确认消费起始位置 | 加参数或先发新消息 |
| 远程客户端连不上 | advertised.listeners 是 localhost | 检查该配置 | 改成真实 IP |
| 启动报 dataDir 不存在 | ZooKeeper 数据目录没建 | 看 zookeeper.properties | mkdir -p建目录 |
| 端口被占用 | 9092 或 2181 被别的进程占了 | netstat -tlnp | grep 9092 | 改端口或杀进程 |
5.1 JDK 版本导致的启动崩溃
这个坑我踩过不止一次。现象是执行kafka-server-start.sh后,进程起来几秒就没了,日志里一堆java.lang.reflect.InaccessibleObjectException。原因是 JDK 9 之后引入了模块系统,对反射访问做了限制,而 Kafka 2.5.0 里有些代码用了反射去访问 JDK 内部类。
解决办法就一个:换 JDK 8。别想着加--add-opens参数去绕过,2.5.0 涉及的反射点比较多,一个个加参数不现实,而且容易漏。直接切 JDK 8 最省心。切换方式:
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 export PATH=$JAVA_HOME/bin:$PATH确认java -version输出是 1.8 再启动。
5.2 ZooKeeper 连接超时的排查链路
Kafka 启动卡在连 ZooKeeper 上,排查要按顺序来:
- 先确认 ZooKeeper 进程在不在:
jps | grep QuorumPeerMain - 再确认端口通不通:
nc -zv localhost 2181 - 然后确认
server.properties里的zookeeper.connect地址对不对 - 最后看 ZooKeeper 日志有没有报错
这四步走下来,基本能定位。最常见的是 ZooKeeper 根本没起来,或者dataDir目录不存在导致 ZooKeeper 自己启动失败。ZooKeeper 启动失败时,日志在logs/zookeeper.out,里面会写清楚原因。
5.3 内部 Topic 创建失败的隐蔽问题
这个坑比较隐蔽。Kafka 启动看起来正常,但消费者一启动就报错,或者__consumer_offsets这个内部 Topic 一直创建不出来。根源是offsets.topic.replication.factor设成了大于 Broker 数量的值。
Kafka 内部有三个关键 Topic:__consumer_offsets(存消费者偏移量)、__transaction_state(存事务状态)。它们的副本数由offsets.topic.replication.factor和transaction.state.log.replication.factor控制。单机环境如果这俩设成 2 或 3,Kafka 会一直尝试创建但永远凑不齐副本,导致消费者无法提交偏移量。
解决方式就是前面配置里说的,单机统统设 1。这个问题的恶心之处在于它不报明显错误,只是"消费者不工作",新手很难联想到是内部 Topic 的问题。
5.4 磁盘写满与日志清理
Kafka 的消息数据默认保留 7 天(log.retention.hours=168),过期自动删。但如果你测试时疯狂发消息,7 天没到磁盘就满了。这时候要手动清理或者调整保留策略。
调整保留时间:
log.retention.hours=24 log.retention.bytes=1073741824log.retention.bytes限制单个分区最大占用,超过就删旧数据。测试环境建议设小一点,比如 1GB。
手动删除某个 Topic 的数据:
bin/kafka-topics.sh --delete --bootstrap-server localhost:9092 --topic test-topic注意 2.5.0 里删除 Topic 需要delete.topic.enable=true(默认就是 true)。删除是异步的,执行完不会立刻消失,等一会儿再--list就看不到了。
实操心得:测试环境我习惯把
log.retention.hours设成 1,log.retention.bytes设成 512MB。这样即使忘了清理,磁盘也不会被撑爆。生产环境另说,要根据数据量和磁盘容量算。
6. 装完之后还能做点什么
环境跑通只是起点。如果你想让这套 2.5.0 发挥更大价值,可以往几个方向延伸。一是用kafka-consumer-groups.sh观察消费者组的偏移量、lag 情况,理解消费进度的管理;二是故意 kill 掉 Broker 观察 Controller 重新选举的过程,加深对集群协调机制的理解;三是用kafka-run-class.sh配合kafka.tools.GetOffsetShell之类的工具,直接查分区的偏移量范围。
我个人在实际操作中的体会是,Kafka 2.5.0 这套 ZooKeeper 架构虽然"老",但它把分布式协调的很多概念暴露得很直白。你装一遍、配一遍、故意搞坏一遍再修好,对消息队列的理解会比单纯看文档深得多。尤其是伪集群那部分,三个 Broker 起起停停,Leader 切来切去,那种"原来副本是这么工作的"的感觉,是看多少篇文章都换不来的。
最后再分享一个小技巧:把启动和停止命令写成脚本,省得每次敲一长串。比如建一个start-all.sh,里面按顺序起 ZooKeeper 和 Kafka,再建一个stop-all.sh反着停。停的时候先停 Kafka 再停 ZooKeeper,顺序反了 Kafka 会因为找不到 ZooKeeper 报一堆错,虽然不影响数据,但日志很难看。养成这个习惯,日常折腾效率能高不少。