刚接触消息队列的人,十个里有九个会被Kafka那一堆名词劝退:Broker、Topic、Partition、Consumer Group、Offset……听着像天书,翻官方文档又全是英文术语,看完更懵。
这篇不是官方文档的翻译,就是一个用Kafka做过几个真实项目的老开发,把入门路上最该知道的东西给你捋一遍。我尽量用大白话讲清楚Kafka到底解决了什么问题、核心概念怎么理解、环境怎么搭、代码怎么写、哪些坑必须避开。你会跟着我一起,从零搭出一个能跑的Kafka环境,并用Java写一个最简的订单消息推送Demo,把生产者、消费者、主题、消费组这些概念全部落到代码里。
Kafka不是某个公司专属的组件,而是一个开源分布式消息流平台,初学不用追求把每个源码细节都搞懂,先把“消息从哪来、存到哪、谁去取”这条主链路跑通,后面再逐步深入。
1. 先搞清楚Kafka到底解决什么问题
1.1 没有消息队列的时候,系统是怎么崩的
想象一下某电商平台的订单系统。用户下单后,订单服务要干很多事情:更新库存、发短信通知、给用户积分、推送物流信息、更新数据分析报表。一开始订单量小,同步调用也没问题,一个请求进来,挨个调用下游服务就行。
但订单量一上来,情况就变了。库存服务响应慢一点,整个下单接口就跟着卡;短信服务偶尔超时,下单直接失败;大促期间流量暴涨,下游服务全被拖垮,订单服务也跟着雪崩。更头疼的是,如果某个下游逻辑要改,订单服务就得跟着改代码重新上线,耦合得死死的。
这时候你需要一层“缓冲垫”——把订单服务产生的消息先放到一个中间地带,下游各服务自己去取,谁有空谁处理,互不拖累,也不直接依赖。
这就是消息队列的核心价值:解耦、削峰、异步。
Kafka就是这样一个中间地带,而且它不只是简单存消息,它把消息按顺序写到磁盘里,靠顺序读写和零拷贝搞出了极高的吞吐。你把它理解成一个超级大的、分布式的“消息快递中转站”,生产者往里面扔包裹,消费者按自己的节奏来取件。
1.2 和RabbitMQ、RocketMQ相比,Kafka的定位差异
肯定有人问:消息队列不是有RabbitMQ、RocketMQ吗,为什么还要学Kafka?打个比方:
RabbitMQ像一个小而精的社区快递站,功能丰富,路由规则灵活,适合业务系统内部各种复杂的消息路由场景,但吞吐量有上限。
RocketMQ更像是产自阿里的重型快递分拣中心,功能全面,金融级别的可靠性,在很多大厂内部广泛使用。
Kafka则像一个巨型的城市级物流转运枢纽,牺牲了一些路由灵活性,换来的是极致的吞吐量和海量消息堆积能力。它可以轻松扛住每秒几十万甚至上百万条消息的写入,并且消息堆积几亿条都不会把磁盘搞死——这也是它成为大数据生态标配的原因,Flink、Spark要实时消费数据,都优先找Kafka要。
所以Kafka的核心使用场景,可以总结成三类:
- 大数据实时数据管道:各系统产生的日志、埋点、业务数据,统一进Kafka,再由实时计算框架消费分析。
- 异步削峰:大促、秒杀场景,请求先打到Kafka,后端按自己的处理能力慢慢消费,避免系统被瞬时流量冲垮。
- 系统间解耦:上游只管把消息丢进Kafka,下游需要数据了自己来拉取,上下游互不知道对方的存在,系统间零耦合。
1.3 我刚入门的时候是怎么理解的
我第一次接触Kafka是接收一个日志采集系统。当时看到“Broker”“Topic”“Partition”“Consumer Group”这些词,完全不知道怎么串起来。后来我找到一个很土但很管用的理解方式:把Kafka想象成一个快递中转站。
Broker就是快递中转站本身,一台机器就是一个站点。 Topic就是不同的运输线路,比如“订单专线”“日志专线”,消息按线路存放。 Partition就是同一条线路上的多个分拣口。一条线路只有一个分拣口的话,快递只能一个一个过,效率太低;多开几个分拣口,多个工人同时处理,吞吐就上来了。 Consumer Group就是一组负责取件的快递员。一条线路上的快件,多个快递员分工取,每个人负责不同的分拣口,互相不重复取同一件。
这套类比帮我搞懂了80%的核心概念。接下来你要做的,就是亲手把环境跑起来,把概念对应到实操里。
2. 本地搭建Kafka环境,从一个单节点开始
2.1 版本选型:我用的是Kafka 3.x,别再纠结ZK还是KRaft
如果你去搜Kafka安装教程,会发现网上大量教程还在教你装ZooKeeper,原因是老版本Kafka依赖ZooKeeper存元数据、做Leader选举。
但从Kafka 3.3开始,官方正式推出KRaft模式,直接用Kafka内置的Raft协议来管理元数据,不再需要单独的ZooKeeper集群。到3.5之后KRaft已经进入生产可用状态,2.8之后自带的ZooKeeper已经变得多余。我建议新手直接学KRaft模式,少装一个组件,少踩一堆坑,也更贴近新版本的生态方向。
版本上我推荐用3.6.x或3.7.x的稳定版。太老的2.x教程虽然多,但配置方式和新版差异大,容易看着教程走到一半发现参数对不上。
2.2 安装和启动:整个过程需要装好JDK,然后下载解压改配置
前置条件就一个:确保机器上有JDK 8以上的版本,推荐JDK 11或17。我本机用的是JDK 17,没遇到兼容问题。
然后到Apache Kafka官网下载二进制包。下载的时候注意选带bin的二进制包,别下载源码包。我用的是3.6.1版本,解压后目录结构大概是:
kafka_2.13-3.6.1/ ├── bin/ # 启动脚本,含Windows的.bat版本 ├── config/ # 配置文件,server.properties、producer.properties等 ├── libs/ # 依赖jar包 └── logs/ # 运行时日志进入解压目录,先看配置文件config/server.properties。KRaft模式需要重点改这几个地方:
# 每个Broker的唯一ID broker.id=0 # 监听地址,9092是Kafka默认端口 listeners=PLAINTEXT://localhost:9092 # KRaft模式必须配置的日志目录 log.dirs=/tmp/kraft-combined-logs # KRaft模式需要的控制器配置 process.roles=broker,controller controller.quorum.voters=0@localhost:9093 controller.listener.names=CONTROLLER listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT如果是第一次运行KRaft模式,需要先格式化存储目录,生成一个集群ID。新版提供了一个脚本,一条命令搞定:
KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)" bin/kafka-storage.sh format -t $KAFKA_CLUSTER_ID -c config/server.properties看到输出类似“Formatting /tmp/kraft-combined-logs with metadata.version XX”就说明格式化成功。注意这一步只要在节点第一次启动前做一次,重复格式化会清空已有数据。
然后启动Kafka:
bin/kafka-server-start.sh config/server.properties看到日志里出现“Kafka Server started”就说明起来了。如果你只想做入门测试,单节点完全够用。后面要模拟集群,复制几份配置改端口、改broker.id就行,但那是后话。
2.3 第一条命令:创建主题、控制台生产和消费
环境跑起来之后,用命令行跑通全链路是最直观的。我建议新手先别写代码,用Kafka自带的命令行脚本把消息流转看一遍,比任何文档都有说服力。
开一个终端,创建一个名为“test-topic”的主题,指定1个副本、3个分区:
bin/kafka-topics.sh --create --topic test-topic --bootstrap-server localhost:9092 --partitions 3 --replication-factor 1这里--bootstrap-server填的就是你配置文件中listeners地址。--replication-factor 1表示每个分区只存一份数据,单节点只能这么配,多节点集群里可以设为2或3,为的是冗余容灾。
创建完之后,查看主题信息:
bin/kafka-topics.sh --describe --topic test-topic --bootstrap-server localhost:9092你能看到类似这样的输出:
Topic: test-topic TopicId: xxx PartitionCount: 3 Replication Factor: 1 Partition: 0 Leader: 0 Replicas: 0 Isr: 0 Partition: 1 Leader: 0 Replicas: 0 Isr: 0 Partition: 2 Leader: 0 Replicas: 0 Isr: 0再开一个终端当消费者:
bin/kafka-console-consumer.sh --topic test-topic --bootstrap-server localhost:9092 --from-beginning再开一个终端当生产者:
bin/kafka-console-producer.sh --topic test-topic --bootstrap-server localhost:9092在生产者终端里随便输几行内容,回车,你会在消费者终端里看到同样的内容原样输出。恭喜你,Kafka的消息通道已经通了。
这一步把最核心的链路跑通之后,心里就有底了:Broker起来了,主题建了,生产者能写,消费者能读。接下来你要弄明白的,就是这条链路里几个关键参数到底影响什么。
3. 核心概念与配置参数,这里只说入门必须懂的
3.1 主题与分区:消息是按顺序存的,但别过度解读“顺序”
主题(Topic)是消息的逻辑分类,分区(Partition)是物理存储的单元。生产者发来的消息会被分配到某个分区里,追加写入该分区的日志文件末尾。同一个分区内的消息是有序的(按Offset递增),但不同分区之间不保证全局顺序。
如果你理解成“Kafka能保证所有消息的顺序”,那就错了。它只保证分区内的顺序。如果你有一个业务场景强依赖消息顺序,比如订单状态流转:创建→支付→完成,那这些消息必须进同一个分区,并且通常要按业务主键选分区。
分区的数量直接决定消费的并行度,一个分区同时只能被同一个消费组里的一个消费者实例消费。分区太少,消费者多也跑不满;分区太多,又会增加Broker的元数据管理和文件句柄开销。入门阶段,主题默认创3个分区就够,随着业务量增长再调整,或者从一开始就留出扩展余地。
3.2 副本机制:为什么Kafka数据不会轻易丢
Kafka的高可靠,核心靠的是副本机制。每个分区可以配置多个副本,副本分两种角色:
- Leader副本:负责处理读写请求。
- Follower副本:从Leader同步数据,不对外服务。
生产者在写消息时,默认只要Leader收到消息并写入日志就算成功。要更高的可靠性,可以设置acks=all,表示所有ISR(同步中的副本)都写入后才返回成功。ISR就是跟Leader保持同步的副本集合。
我见过一个真实事故:某业务为了极致性能,配置了acks=0(不等待任何确认),结果Broker宕机时消息直接丢了一批,业务方还没察觉。这种配置风险极高,新手千万别学。入门阶段生产环境建议统一用acks=all,并配合min.insync.replicas=2来兜底,即在至少两个副本同步的情况下才接受写入。
3.3 消费者组:同一分组分工消费,跨分组重复消费
消费者组(Consumer Group)是Kafka里最核心也最容易混淆的概念。
一组的多个消费者共同消费一个主题的所有分区,每条消息只会被组内一个消费者消费。分区与消费者的分配关系是:分区数除以消费者数,尽可能均分。如果消费者数大于分区数,有的消费者会空闲,白白占着资源不干活。
不同消费者组消费同一个主题,互不影响。比如订单服务组拿订单消息去发通知,数据分析组拿同样的订单消息去做统计,两边各建一个组,都能完整收到所有消息。
入门期最容易犯的错是:同一个消费者组里开了好几个消费者实例,以为能加快消费速度,结果发现消息不重复了但有的实例一直在空闲。排查半天才发现分区数小于消费者数。
3.4 最常用的Broker和客户端参数速查
Broker端(server.properties)常用参数:
| 参数 | 作用 | 我的建议值 |
|---|---|---|
broker.id | 节点唯一ID | 0, 1, 2... |
listeners | 客户端连接地址 | PLAINTEXT://内网IP:9092 |
log.dirs | 消息日志目录 | 多块磁盘用逗号分隔 |
num.partitions | 自动创建主题的默认分区数 | 3 |
log.retention.hours | 消息保留时间 | 168(7天) |
auto.create.topics.enable | 是否自动创建主题 | 生产环境设false |
生产者端(producer.properties)常用参数:
| 参数 | 作用 | 我的建议值 |
|---|---|---|
acks | 确认级别 | all |
retries | 发送失败重试次数 | Integer.MAX_VALUE |
batch.size | 批次大小,字节 | 16384(16KB) |
linger.ms | 发送前等待时间 | 5~20 |
buffer.memory | 发送缓冲区 | 33554432(32MB) |
batch.size和linger.ms是性能调优的两大法宝。消息攒一批再发,网络请求次数大幅减少,吞吐能提升一个数量级。但注意,如果linger.ms设太大,消息延迟会升高。实时性要求高的场景,linger.ms不要超过20。
消费者端常用参数:
| 参数 | 作用 | 我的建议值 |
|---|---|---|
enable.auto.commit | 是否自动提交偏移量 | 学习期true,生产建议手动 |
auto.commit.interval.ms | 自动提交间隔 | 1000~5000 |
auto.offset.reset | 无初始偏移量时从哪开始 | earliest(数据处理场景) |
max.poll.records | 单次poll返回最大条数 | 500 |
session.timeout.ms | 会话超时时间 | 45000 |
auto.offset.reset是最容易导致“消费不到消息”或“重复消费一堆老消息”的元凶。新消费组首次启动时,如果主题里已经有历史消息,设earliest会从头开始消费所有消息,设latest则只消费之后的新消息。这个坑我踩过很多次,调试的时候经常要反复确认自己到底设的哪个值。
4. 动手写一个入门Demo:模拟订单消息推送
4.1 工程搭建与依赖引入
环境通了,概念顺了,接下来就是把链路用代码串起来。我用Java写一个最简单的Kafka生产者消费者Demo,模拟订单支付后发送消息的场景。不会用Spring,就用原生客户端,这样对底层机制更清楚,换框架时也更容易理解。
新建一个Maven工程,在pom.xml里加入:
<dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-clients</artifactId> <version>3.6.1</version> </dependency>这个依赖就是Kafka官方Java客户端,所有生产者消费者的API都在里面。注意版本尽量和Broker版本保持一致,跨大版本有协议兼容问题,虽然通常向后兼容,但没必要给自己找麻烦。
我建了一个OrderMessage类,用来模拟一条订单消息,包含订单号、用户ID、支付金额、时间戳几个字段,后面用JSON序列化传消息。
4.2 生产者代码:核心逻辑与关键参数落位
下面的代码是一个标准的Kafka生产者,往order-topic里发订单消息:
import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; import java.util.Properties; public class OrderMessageProducer { public static void main(String[] args) throws Exception { Properties props = new Properties(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ProducerConfig.ACKS_CONFIG, "all"); props.put(ProducerConfig.RETRIES_CONFIG, 3); props.put(ProducerConfig.LINGER_MS_CONFIG, 10); props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer"); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer"); KafkaProducer<String, String> producer = new KafkaProducer<>(props); for (int i = 0; i < 10; i++) { String orderId = "ORDER-" + System.currentTimeMillis() + "-" + i; String message = "{\"orderId\":\"" + orderId + "\",\"userId\":" + (1000 + i) + ",\"amount\":" + (99.9 + i) + "}"; ProducerRecord<String, String> record = new ProducerRecord<>( "order-topic", i % 3, orderId, message); RecordMetadata metadata = producer.send(record).get(); System.out.printf("消息发送成功: partition=%d, offset=%d, value=%s%n", metadata.partition(), metadata.offset(), message); } producer.flush(); producer.close(); } }ProducerConfig类里全是官方常量,用常量不会拼错字符串。有几个细节值得注意:
第一,发送record时我指定了分区序号i % 3,目的是让消息落在3个不同分区上,演示分区分配效果。生产中通常传订单ID当key,让同一个订单的消息都进同一个分区,保证这个订单的状态流转消息是顺序的。
第二,metadata是发送结果,包含了分区分区和offset信息,打印出来能非常直观地看到消息落在哪个分区。
第三,producer.close()会先flush再关闭,一般不用显式调flush,但如果消息量大且想确认都发送完了,手动flush一次更稳妥。
4.3 消费者代码:消费组和偏移量提交
消费者同样只用原生API。下面的代码把刚才生产的订单消息拉取下来:
import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; import java.time.Duration; import java.util.Collections; import java.util.Properties; public class OrderMessageConsumer { public static void main(String[] args) { Properties props = new Properties(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ConsumerConfig.GROUP_ID_CONFIG, "order-consumer-group"); props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true"); props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "3000"); props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer"); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer"); KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Collections.singletonList("order-topic")); try { while (true) { ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000)); for (ConsumerRecord<String, String> record : records) { System.out.printf("收到消息: partition=%d, offset=%d, key=%s, value=%s%n", record.partition(), record.offset(), record.key(), record.value()); // 实际业务中在这里写订单消息的处理逻辑 } } } finally { consumer.close(); } } }新手对消费者最困惑的是poll这个循环。Kafka消费者不是服务端给你推消息,而是自己主动去拉,poll(Duration.ofMillis(1000))是“最多等一秒钟,没有消息就返回空集合”。
循环里那行注释很重要。真实项目中,消息处理逻辑就写在for循环里,比如更新数据库、调外部接口、发短信。处理完成后才有偏移量提交。
enable.auto.commit=true时,Kafka会定期自动提交你已拉取消息的偏移量。这在学习阶段很省心,但生产环境中如果处理消息耗时长,容易出问题:消费者已经拉取了消息但还没处理完,自动提交却已经提交了偏移量,这时如果进程崩溃,重启后会从已提交的偏移量继续消费,没处理完的那批消息就丢了。
4.4 运行验证:生产者消费者一起跑
先创建主题:
bin/kafka-topics.sh --create --topic order-topic --bootstrap-server localhost:9092 --partitions 3 --replication-factor 1启动消费者,再启动生产者。你会看到消费者终端陆续打印出生产者发送的10条消息,并带分区和偏移量信息。
这里建议做一个验证重复消费的经典小实验:启动两个消费者实例,代码里GROUP_ID一样,然后生产一批消息,观察两个实例的消息分配情况。你会发现每条消息都只会被其中一个实例消费,验证了“组内不重复消费”的机制。
5. 常见报错与排查手段,这些坑我替你踩过了
5.1 消费者连不上Broker:Connection refused
新手最容易遇到,启动消费者报:
org.apache.kafka.common.errors.TimeoutException: Topic order-topic not present in metadata after 60000 ms或者直接ConnectException。先别急着怀疑代码,90%是Broker监听地址配置问题。
如果你用的server.properties里配的listeners=PLAINTEXT://localhost:9092,那就只能用localhost连。如果客户端在另一台机器,就要把这里改成内网IP,比如PLAINTEXT://192.168.1.100:9092,另外别忘了看防火墙有没有放行9092端口。改完配置需要重启Kafka才生效。
注意:Kafka有个“被广告的地址”概念(advertised.listeners),如果配了复杂网络环境,客户端拿到的是错误地址,也会连不上。本地测试尽量让listeners、advertised.listeners保持一致。
5.2 反复消费,消息处理完又出现
排查顺序是这样的:
第一,确认是不是消费组首次启动,并且auto.offset.reset=earliest。首次消费会把所有历史消息全部读一遍,这不算故障,只是参数语义。
第二,确认enable.auto.commit是不是为true,以及消息处理耗时是否超过提交间隔。如果消息处理要5秒,自动提交间隔是3秒,那就会在没处理完时提前提交,崩溃恢复后offset已经前进,消息丢失;反过来,如果处理逻辑抛异常了而提交成功了,重启就看不到那条消息,但下次消费者启动时earliest配合新的组ID才会重读历史。
第三,检查自己是不是每次调试都用同一个GROUP_ID_CONFIG,但重启时又手动重置了偏移量,导致看起来像重复消费。
5.3 消息发送失败:RecordTooLargeException
默认Broker端单条消息大小上限是1MB(message.max.bytes),生产端也有max.request.size限制。如果你发送大对象或大文件内容,会直接报RecordTooLargeException。
调整方式:Broker的server.properties里设message.max.bytes=10485760,生产者端设props.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, 10485760),两边都改才有效。但我更建议别动不动调大消息上限,Kafka本来就不适合传大文件,真要传,把文件放对象存储,消息里只放文件路径。
5.4 消费延迟突然变大:排查思路
先看消费者组是不是有实例挂掉,导致分区rebalance;再看生产者的linger.ms、batch.size是不是设置不合理;再看消费逻辑里有没有外部调用阻塞。消费延迟的核心就是两个:生产者吞吐卡了,或者消费者处理能力卡了。
排查时可以借助命令行脚本看消费组情况:
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group order-consumer-group --describe输出里的LAG列代表消费者落后多少条消息。如果LAG持续增长,说明消费速度跟不上生产速度,这时候加消费者实例不一定有用,要看分区数是否够分。
6. 入门后的下一步:序列化、事务与流处理
6.1 不要只发String消息,试试Avro和Schema Registry
入门Demo里消息是手拼的JSON字符串,字段一多就会出错,而且Java对象和JSON转换很容易踩坑。真实项目里,推荐用Avro或Protobuf做序列化,配合Schema Registry管理消息格式。
Avro最大的价值在于Schema演化兼容:订单消息里增加一个字段,老消费者不会因为不认识新字段就报错,新消费者又能读到新字段。这对“上游改了消息格式,下游一堆系统要跟着改”的痛点是降维打击。
我们公司之前有一次,上游订单服务在消息里加了一个优惠券字段,没做兼容处理,下游三个数据管道全炸了。后来迁移到Avro+Schema Registry,同样加字段,下游无感。这个经验惨痛且真实。
6.2 精确一次语义:事务与幂等生产者
入门阶段你可能只关心“消息发了、收到了”,但在金融、交易类场景,“不重不丢”是底线。Kafka提供了幂等生产者和事务API,可以实现端到端的精确一次处理语义。
简单说,幂等生产者(enable.idempotence=true)保证同一条消息在Producer内部重试时不会重复写入。事务则保证一组消息要么都写成功,要么都写失败。配合消费者的“读处理写”流程做成事务,就能实现真正的不重不丢。
这块内容对新手来说偏难,入门阶段不用深入,但你要知道Kafka有这个能力,等真正遇到交易类需求时,知道往哪个方向学。
6.3 从消费者到流处理:Kafka Streams和ksqlDB
入门完消费者,你会发现一条消息从进Kafka到被消费,中间还能做很多事:过滤、转换、聚合、关联,这就是流处理。
Kafka Streams是官方流处理库,直接用Java写,算子风格跟Flink很像但轻量得多,可以嵌入你的Java应用里,不依赖独立集群。如果不想写代码,用ksqlDB写SQL就能对流数据做实时统计,比如“每5分钟统计每个商品的订单量”。
我建议入门Kafka后,选一个场景练手,比如做一个“订单实时统计”:用Kafka Streams消费订单消息,按时间窗口统计每分钟的交易额。做完这个,你对Kafka的认知会从一个消息管道升级成实时计算平台,也算是最热门的实时数仓链路的一个最小闭环。
7. 写在最后:入门Kafka,我的一点实在建议
如果你问我入门Kafka最绕过的弯是什么,我的回答是:别只盯着API背参数,一定要把环境搭起来,用命令行亲手发一条消息、收一条消息、看一次消费组LAG。很多东西光看书是记不住的,一旦你亲手操作过,那些名词就从抽象的概念变成了你手里的工具。
写这篇的起因是项目组来了个实习生,我让他两周内跑通Kafka全链路并做一个订单消息Demo,他第一周问我最多的问题是“这个参数不设行不行”“为什么不自动创建主题”“消费组到底是什么”。我把回答整理成了这篇文章的思路,里面有我的踩坑史,也有我们线上环境里真实发生过的事故教训。
最后再分享一个小技巧:学Kafka时,无论环境多简单,都养成用命令行脚本查看信息的习惯。kafka-topics.sh和kafka-consumer-groups.sh这两个脚本是你排查问题最快的工具,比什么监控面板都直接。等你把这条链路玩熟了,拉集群、接流处理、上生产环境,都会水到渠成。