消息队列-Kafka的安装(二)从下载解压到生产消费全流程实测上一回我把Kafka的基本概念和消息队列的使用场景梳理了一遍说实话理论聊得再多都不如自己在服务器上把一套环境跑起来来得踏实。这一篇不讲虚的直接带上操作记录把Kafka从下载解压、配置文件调整、启动服务到生产消费消息验证的完整流程走一遍并把我在实际操作中遇到的坑、排查思路、调参经验一并记录下来给正准备动手装Kafka的朋友做一个可以直接照着操作的参考。需要提前说明一点Kafka目前的主流版本已经演进到3.x2.8版本之后引入了KRaft模式也就是不再强制依赖ZooKeeper了。但考虑到生产环境里大量存量集群仍然跑在ZooKeeper模式上而且很多教程和运维工具都基于这套架构本篇实操以Kafka 2.13-3.4.0版本为主同时会在后面单独说明KRaft模式的差异两套方案都覆盖免得你装完发现跟网上资料对不上。1. 安装之前的准备先把版本搭配关系搞明白1.1 为什么版本组合会决定安装成败很多新手上来就下载最新的Kafka包解压之后启动直接报错查来查去发现是JDK版本不兼容或者ZooKeeper版本对不上。Kafka本身是一个Java进程它对JDK版本有硬性要求底层依赖的ZooKeeper也需要匹配版本这三者之间的搭配关系直接决定了安装过程是否顺利。以我这次安装的Kafka 3.4.0为例官方文档明确要求JDK 8到JDK 17之间的版本都能运行但如果你用的是JDK 7或更老的JDK 6连启动脚本都跑不起来会直接抛出UnsupportedClassVersionError。而ZooKeeper这边Kafka 3.4.0配套使用的是ZooKeeper 3.8.x版本我实测用3.7.x也能跑但用3.5.x就会在创建Topic时遇到SessionTimeoutException之类的问题排查起来很绕。1.2 安装前的三个硬性检查项在动手之前我习惯先做一个三连检查这三个检查项缺一个后面都会出问题检查JDK版本执行java -version确认版本号同时确认JAVA_HOME环境变量已经配置好。很多启动脚本是通过JAVA_HOME去找Java的你以为装了Java但启动脚本找不到照样报错。检查服务器内存Kafka Broker默认的堆内存设置是1G如果你的机器只有512M内存启动后很容易出现OutOfMemoryError建议至少2G内存起步生产环境建议独立部署不要跟数据库、应用服务挤在同一台机器上。检查端口占用情况Kafka默认监听9092端口ZooKeeper默认监听2181端口实测过程中这两个端口被占用的情况特别多尤其是2181端口很多监控组件默认也会用它。提示安装前先把这三个项目确认好能避免后续百分之八十的报错。我在第一次安装时就是没查端口结果ZooKeeper起了一个旧的实例Kafka连上了错误的ZooKeeperTopic怎么都创建不出来。2. 下载与解压Kafka发行包里到底装了什么2.1 下载渠道与版本选择思路Kafka的官方下载页面其实很容易找到但我建议不要直接点最大的那个版本号而是找到对应的Scala版本和Kafka版本组合。比如Kafka 3.4.0会提供kafka_2.13-3.4.0.tgz和kafka_2.12-3.4.0.tgz两种包这里的2.13和2.12指的是编译Kafka时使用的Scala语言版本。除非你有特殊的客户端兼容需求否则我建议直接用2.13的包它在性能和功能上都比2.12的更新一些。下载完成后先做一件事校验文件完整性。官方页面提供了SHA512校验值用sha512sum命令比对一下再解压避免下载了损坏的包导致后面各种诡异问题。我这个人心比较急曾经跳过了这一步结果在启动时出现了莫名其妙的类加载错误折腾了半天才发现是包损坏。2.2 解压后的目录结构解读用tar -xzf kafka_2.13-3.4.0.tgz命令解压后会得到一个kafka_2.13-3.4.0目录这里面的结构很有必要搞清楚因为它决定了你后续所有配置文件要往哪里放bin目录存放所有可执行脚本。包括kafka-server-start.sh启动Broker、kafka-topics.sh管理Topic、kafka-console-producer.sh命令行生产者、kafka-console-consumer.sh命令行消费者等这是Kafka运维用的核心工具集合。config目录存放所有配置文件。server.properties是Broker的核心配置zookeeper.properties是ZooKeeper的配置producer.properties和consumer.properties分别是生产者和消费者的默认配置还有log4j.properties日志配置。libs目录存放Kafka运行所需的全部依赖Jar包这里面的jar包数量极多不需要手动管理启动脚本会自动加载。licenses目录第三方许可证信息一般不用管。我建议额外做一步操作把这个目录重命名成不带版本号的kafka比如mv kafka_2.13-3.4.0 /opt/kafka后续写脚本、配置环境变量时能少打很多字符也不容易因为版本号导致路径错误。3. 核心配置详解server.properties里那些决定命运的参数3.1 Broker基础配置broker.id、listeners和log.dirs进入config目录打开server.properties文件这是Kafka Broker最核心的配置文件。我逐行过一下必须改的参数其他参数保持默认可以先跑起来。第一个是broker.id这个参数是Broker在整个集群中的唯一编号默认值是0。单机部署时不用改但你如果在同一台机器上模拟集群环境跑多个Broker每个实例的broker.id必须不同否则后启动的Broker会因为ID冲突连不上ZooKeeper直接启动失败。第二个是listeners这是Kafka对外提供服务的地址和端口。默认配置是PLAINTEXT://:9092如果你监听在localhost上外边用IP访问会连不上这点特别容易踩坑。我推荐直接改成listenersPLAINTEXT://0.0.0.0:9092表示监听所有网卡地址这样无论是本机访问还是远程访问都能连上。生产环境为了安全一定要配置认证和加密这里单机测试先用明文监听。第三个是log.dirs这个参数指定Kafka消息数据实际落盘的目录。默认值是/tmp/kafka-logs这个目录极其不适合生产使用因为/tmp目录在系统重启时可能被清空而且它通常空间有限。我建议改为独立的数据目录比如/data/kafka/logs并且提前用mkdir命令创建好目录同时确保该分区有足够的磁盘空间。注意log.dirs配置的目录如果不存在Kafka启动时会自动创建但如果你配置了多个目录并用逗号分隔其中任何一个目录创建失败都会导致Broker启动失败。3.2 ZooKeeper连接配置与关键性能参数下一步是配置zookeeper.connect参数。单机模式下默认是localhost:2181看起来没问题但这里有个细节如果你的ZooKeeper设置了chroot路径也就是说在ZooKeeper中为Kafka单独划分了一个命名空间那这个参数要写成localhost:2181/kafka。这样做的目的是让Kafka的元数据跟其他ZooKeeper使用方的数据隔离避免互相干扰。还有两个参数很容易被忽略但实际使用中直接影响性能和稳定性。第一个是num.partitions默认值是1它决定了新创建的Topic默认有几个分区。生产环境我建议改成3或者更多因为分区数是Kafka并行度的上限消费者组的并发消费能力直接受分区数限制只有1个分区的话消费者再多也只能有一个消费者在消费。第二个是log.retention.hours默认值是168小时也就是7天。这个参数决定消息在磁盘上保留多久如果消息量特别大7天可能占用大量磁盘空间可以根据业务需要调整成72小时或更短。3.3 Zookeeper的配置文件也需要过一眼很多人装Kafka的时候只顾着看server.properties把zookeeper.properties忽略了。其实ZooKeeper的配置同样需要关注两个点。一个是dataDir参数它指定ZooKeeper的数据存储目录默认在/tmp/zookeeper这个目录同样不适合生产使用建议改到/data/zookeeper。另一个是clientPort参数默认2181确保这个端口没有被其他进程占用。这里顺带分享一个我在实操中总结的小经验ZooKeeper的内存配置Kafka无法控制它在conf/zookeeper-env.sh中配置ZOO_MAIN但实际上ZooKeeper的JVM内存由zookeeper-server-start.sh脚本中的KAFKA_HEAP_OPTS环境变量控制默认值是512M消息量大的环境下建议调高到1G或2G否则后续集群节点多、Topic多的时候ZooKeeper会成为性能瓶颈。这是我踩过坑之后才翻源码看明白的读起来有点绕但理解了就好办了。4. 启动与验证如何确认Kafka真的装好了4.1 三步启动流程先ZooKeeper后Broker启动顺序是有讲究的必须先启动ZooKeeper等它正常对外提供服务之后再启动Kafka Broker。虽然Kafka启动脚本在连不上ZooKeeper时会自动重试但如果你先启动Kafka日志里会刷出一堆连接异常给人的感觉很慌实际上等ZooKeeper就绪之后它会自动连上但没必要这么折腾。首先启动ZooKeeper进入Kafka目录执行bin/zookeeper-server-start.sh -daemon config/zookeeper.properties这里的-daemon参数表示后台启动加了之后日志不会再打在前台想看日志得去logs目录翻。如果你是第一次调试不建议加-daemon直接前台启动能看到启动细节方便判断是否有报错。然后启动Kafka Brokerbin/kafka-server-start.sh -daemon config/server.properties启动完成后用jps命令检查Java进程应该能看到两个进程一个是QuorumPeerMain这是ZooKeeper的主类另一个是Kafka这是Broker的主类。两个进程都在说明基础启动成功了。4.2 创建Topic并验证Broker状态Kafka提供了一个非常简单的方式查看Broker是否正常工作那就是创建一个Topic。执行bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic test-topic --partitions 3 --replication-factor 1这条命令直接创建了一个名为test-topic的Topic分区数3副本数1。单机环境副本数只能配1如果你强行配3创建时会直接报错提示副本数不能超过Broker数量。创建成功后查看Topic状态bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic test-topic正常输出会列出这个Topic的分区信息、Leader分布、Replicas分布等元数据能看到Partition 0、1、2三个分区Leader都指向当前的Broker编号就说明Broker和ZooKeeper之间的元数据读写一切正常。4.3 生产消费全链路最真实的安装验证标准Topic创建成功只能说明Broker能启动但能不能真正收发消息还需要做一次完整的生产消费测试。这一步极其重要因为有些情况下Broker能起来但是数据写入和读取链路有问题比如磁盘权限不对、网络配置有问题。先启动一个消费者监听test-topicbin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test-topic注意消费者启动后不要急着在同一个终端里输入消息我们需要另开一个终端窗口执行生产者命令bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic test-topic这时生产者终端会进入一个交互模式输入一行字符回车发送一条消息。比如输入hello kafka再输入第二条消息welcome to mq两条消息发送完毕后回到消费者终端你会看到这两条消息马上显示出来。看到这个消息出现在消费者终端就说明Kafka的完整链路已经彻底跑通了生产者把消息发送到BrokerBroker把消息写入磁盘分区消费者从Broker拉取到消息并输出。以我个人经验来看这套标准验证方法是排查安装问题的最快路径只要这个流程能走通Kafka安装就完全没有问题。5. 常见问题与排查技巧实录5.1 启动时端口被占用实际安装中最常遇见的问题是端口被占用。Kafka启动时报Address already in use或者连接ZooKeeper时超时绝大多数情况是这个原因。排查方法很简单执行netstat -tlnp | grep 9092和netstat -tlnp | grep 2181查看端口是否被其他进程占用。如果确实被占了要么杀掉占用的进程要么改Kafka或ZooKeeper的端口配置二选一不要硬等。5.2 创建Topic时报错LeaderNotAvailableException这个问题特别典型报错信息大致是Error while executing topic command: LeaderNotAvailableException。第一次遇到时以为Topic创建失败了但实际上你再用--describe去看Topic可能已经创建出来了只是暂时没有Leader选举完成。这个问题的本质是Broker跟ZooKeeper之间的会话还没有完全建立或者说控制器Controller还没有激活完成。解决办法有三个一是等10到20秒再执行--describe确认状态二是检查server.properties里的broker.id是否存在冲突同一个集群内两个Broker用了相同ID就会导致Leader选举异常三是检查ZooKeeper的dataDir目录是否有残留数据如果有之前测试留下的旧元数据建议备份后清空重来。5.3 消费者连不上Broker但Kafka明明启动了这个问题的隐蔽性非常强。你在服务器上用localhost测试完全正常但换到另一台机器用IP地址访问却连不上比如报Connection refused或SocketTimeoutException。大多数人会以为是防火墙问题当然防火墙确实要检查防火墙也要确保9092和2181端口放通但更常见的原因其实是listeners配置。我在3.1里强调过listeners要配成PLAINTEXT://0.0.0.0:9092如果你保持默认的PLAINTEXT://:9092且主机名解析到127.0.0.1那么客户端用IP访问就会失败。排查方法在server.properties里加上advertised.listenersPLAINTEXT://你的IP:9092这个参数专门用来告诉客户端Broker对外公布的地址然后重启Broker再测试。5.4 日志刷屏和磁盘占用过高Kafka跑了一段时间之后你可能发现磁盘空间莫名其妙少了几个G日志目录里全是*.log和*.index文件。这是因为Kafka默认不会立刻删除旧消息只有在log.retention.hours到期后才会触发清理。有一个很容易忽略的点Kafka的日志清理是分时间阈值和大小阈值双重控制的你在server.properties里只看到log.retention.hours但其实还可以配置log.retention.bytes参数两者谁先触发就按谁执行。我建议给log.retention.bytes设置一个上限值比如1073741824也就是1G这样即使时间没到容量超限也会自动清理避免磁盘被写满。还有一个配合参数是log.cleanup.policydelete这是默认值。如果你把cleanup.policy改成compact那消息不会被删除而是对相同Key的消息做合并这种模式适合保存Key-Value类型的最新状态数据不适合普通日志消息不要随意去改。6. 两种部署模式的差异与选型建议6.1 ZooKeeper模式与KRaft模式的本质区别前面所有的操作都是基于ZooKeeper模式这是Kafka 2.x时代的标准架构。在这套架构里ZooKeeper负责元数据管理也就是Broker的注册、Topic的元数据、分区的Leader和ISR信息等等。Broker处理消息的读写但它的很多决定都要跟ZooKeeper商量比如选举Controller、分配分区Leader。KRaft模式是Kafka 3.x引入的新架构最大的变化是把元数据管理收回了Kafka自身内部不再依赖外部的ZooKeeper。Kafka内部新增了一个元数据日志机制通过Raft协议来保证多个Broker之间的元数据一致性。带来的好处是部署架构简化了少维护一个分布式系统运维成本明显降低同时元数据变更的时延也更低踩过ZooKeeper扩容坑的人应该能明白这个喜讯的分量。6.2 KRaft模式的安装实操差异如果你选择KRaft模式配置流程会简单不少。首先需要一个集群ID执行bin/kafka-storage.sh random-uuid拿到UUID后格式化存储目录bin/kafka-storage.sh format -t UUID -c config/kraft/server.properties然后启动Kafka单节点bin/kafka-server-start.sh config/kraft/server.properties对比ZooKeeper模式少了一步启动ZooKeeper配置文件中没有zookeeper.connect参数取而代之的是process.roles、controller.quorum.voters等参数。对于单机学习环境KRaft模式更清爽少一个进程就少一个排查点。但这个模式在3.x版本中还在快速迭代如果你不是对ZooKeeper模式有历史包袱新环境直接用KRaft也是一种合理选择。6.3 我推荐怎么选我个人建议分场景来选如果是学习Kafka的基本原理、写Demo、做集成测试直接上KRaft模式省事干净如果是在公司里搭生产集群而且团队已有ZooKeeper运维经验那么沿用ZooKeeper模式会更稳因为相关坑网上都有对应解决方案而KRaft模式在超大规模集群下的表现还需要更多实践验证。如果你的生产环境是全新的、没有人维护过ZooKeeper那可以直接评估KRaft模式毕竟它代表着Kafka未来的架构方向。我装Kafka安装这一路走下来最大的体会是安装本身不难难的是版本选型和配置细节。很多报错都是因为官方文档读得不细、参数含义理解不到位导致的。只要把版本组合确认好、三个核心参数broker.id、listeners、log.dirs配清楚、启动顺序理顺再用生产消费验证一把整个安装过程其实二十分钟就能完成。以后你再去排查别人的Kafka环境也能从这些细节快速定位问题而不是一头扎进日志里翻半天。