1. 写在前面:Kafka生产环境到底在难什么
做了几年Kafka相关的运维和架构工作,我最大的感受是:Kafka本身并不难上手,难的是它在生产环境里出的那些“奇奇怪怪”的问题。你单机跑个demo,生产者一发消费者一收,顺畅得像德芙。可一旦上了生产,Topic多了、分区多了、流量大了、上下游系统杂了,问题就接踵而来——消费滞后、分区失衡、磁盘写满、副本不同步、重复消费、消息延迟,有时候一天能碰到两三个。
这篇手册是我在实际运维过程中整理出来的排查经验,结合了几个比较典型的线上案例。里面没有太多教科书式的理论堆砌,重点放在“问题表现、排查路径、根因分析、解决方案”这条线上。如果你正在维护Kafka集群,或者你的业务对消息可靠性要求比较高,这篇内容应该能帮你少踩几个坑。就算你只是个初学者,把这些问题背后的排查思路看明白,对理解Kafka的运作机制也很有帮助。
另外说明一下,我这边环境以Kafka 2.x和3.0版本为主,部分命令在不同版本里可能有细微差异,但核心排查逻辑是通用的。
2. 排查前的准备:先把“现场”固定下来
2.1 排查问题前必做的三件事
很多人在Kafka出问题的时候,第一反应就是去翻日志、看监控、查消费组。这没错,但如果你没有提前做一些准备,排查效率会大打折扣。我整理了一下,有三件事最好在生产环境出事之前就准备好。
第一,确认你的监控体系覆盖了哪些Kafka指标。我见过不少团队,Kafka集群跑了大半年,监控面板上只有CPU、内存、磁盘这几个基础指标,连UnderReplicatedPartitions都没接。等到消费Lag涨了几百万,才后知后觉。至少要把 Broker 端的 UnderReplicatedPartitions、ActiveControllerCount、OfflinePartitionsCount,以及消费端的 Lag 和消费速率这些核心指标盯起来。
第二,留好排查工具链。Kafka 自带的命令行工具是排障的基础,kafka-topics.sh、kafka-consumer-groups.sh、kafka-configs.sh 这几个必须熟。再配合一些第三方工具,比如 Kafka Tool、Kafka UI、kafdrop 之类,可以直观地看到分区分布和消费进度。不过说实话,真到了排查问题的时候,命令行工具往往比图形界面还好用,因为它快、准、不受UI刷新限制。
第三,提前记录集群的基础信息。包括Broker节点数、每个节点的配置、Topic的分区数和副本数、关键的Broker参数设置等。这些看似琐碎的信息,在故障发生时能帮你快速缩小排查范围。比如你发现某个Broker的磁盘IO特别高,如果事先知道这个Broker上分配了哪些分区的Leader副本,就能快速判断是不是某个Topic的流量异常导致的。
提示:如果你现在还没接入Kafka监控,建议优先把UnderReplicatedPartitions和OfflinePartitionsCount这两个指标接上,它们是Kafka集群健康状况的两大命门。出了事再补监控,就太被动了。
2.2 定位问题前先看哪个指标
Kafka的问题类型其实可以粗略分成两类:一类是集群层面的问题,一类是客户端层面的问题。集群层面的问题通常会在Broker端指标上体现出来,比如磁盘满、网络IO高、副本同步异常;客户端层面的问题则主要体现在消费Lag、连接异常、请求超时等。
排查的时候,我先看的是“消费Lag”这个指标。它最直观——如果消费者处理不过来了,Lag就会持续上涨,这个信号基本不会骗人。但Lag涨不一定全是消费者的问题,也有可能是生产者端突然加大了写入量,或者某个分区所在的Broker出现了性能瓶颈。
所以我建议的定位顺序是:先看消费Lag是否异常,然后看Broker端的磁盘和网络IO,再看Topic的分区Leader分布是否均匀,最后看消费端的处理耗时和异常日志。按照这个顺序一层层往下剥,基本可以定位到问题的大致范围。
3. 集群层面高频故障:从“起不来”到“悄悄坏掉”
3.1 Controller频繁切换:集群抖动的隐形元凶
先聊聊一个比较隐蔽但影响很大的问题——Controller频繁切换。Kafka集群里有一个Controller角色,负责分区Leader的选举、元数据管理、Broker上下线处理等,可以说是集群的中枢神经。Controller挂了或者切换太频繁,整个集群的元数据就会不断震荡,表现就是客户端时不时的LeaderNotAvailable、NotLeaderForPartition之类的报错。
我之前遇到过一个案例,某集群一天内Controller切换了十几次,客户端频繁报错,但看Broker进程都还在,内存也没满,CPU也正常。当时排查了很久,最后在Controller的日志里发现了很多类似「Unable to connect to zookeeper」的异常。顺藤摸瓜查下去,发现是Controller与ZooKeeper之间的Session超时设置得太短,加上网络有轻微抖动,就触发了Controller的重新选举。
解决方式其实很简单——适当调大ZooKeeper的session.timeout.ms,并且把zookeeper.connection.timeout.ms也放宽一些。在稳定的内网环境里,这个参数设置成30秒甚至60秒都没有问题,不必为了“快速故障恢复”而把超时调得太激进。另外还有一个容易被忽略的点:Controller的所有Broker是共享的,如果集群里有一个Broker经常GC停顿太长,也可能会导致它与ZooKeeper的Session超时。所以排查Controller切换问题时,也要留意其他Broker的GC日志。
3.2 ISR收缩与副本同步异常:数据安全的警报
ISR是in-sync replicas的缩写,指的是与Leader保持同步的副本集合。如果某个Follower副本同步进度落后太多,或者长时间没有向Leader发起同步请求,它就会被踢出ISR。当ISR数量小于min.insync.replicas设置的值时,生产者那边如果配置了acks=all,就会直接报错,消息写入失败。
ISR收缩这个问题,常见原因有几种:一是Broker负载过高,导致Follower副本拉取消息的速度跟不上生产速度;二是网络问题,Follower与Leader之间的带宽不够或延迟太大;三是磁盘性能太差,Follower写入落盘太慢。
排查思路我一般这么走:先看是哪个Broker上的副本被踢出了ISR,然后看这个Broker的CPU、磁盘IO、网络流量,再结合当时的业务写入量判断是不是瞬时峰值导致的。如果是瞬时峰值导致的,通常ISR会自动恢复,不用太紧张。但如果是持续性的,就得考虑给对应Broker减负,比如把部分分区迁移到其他节点,或者升级硬件配置。
这里有一个实战经验要分享:不要一看到ISR缩小就急着把min.insync.replicas调小。调小意味着降低了数据可靠性,一旦Leader所在的Broker挂掉,这些数据可能就丢了。正确做法是先确认ISR收缩的原因,把根因解决掉,再观察ISR是否能自动恢复。
3.3 分区Leader不均衡:流量倾斜的隐形坑
Kafka的分区Leader会承担所有的读写请求,如果一个Broker上集中了太多分区的Leader,这个Broker的压力就会明显高于其他节点,形成热点。这种热点问题不会让集群直接挂掉,但会表现为单节点CPU偏高、磁盘IO偏高,而其他节点却很闲,整体资源利用率上不去。
分区Leader不均衡的原因有几种:一是新节点加入集群后,原有节点上的分区Leader不会自动迁移到新节点;二是某些Topic在创建时没有指定副本分布,默认策略可能会导致分区集中;三是之前某个Broker重启后,原本在该节点上的Leader被转移了,重启完成后不会自动迁回。
处理方式很简单,用Kafka自带的kafka-leader-election.sh或者通过kafka-reassign-partitions.sh重新均衡分区Leader分布即可。如果只是临时调整Leader位置,可以用kafka-preferred-replica-election.sh来触发Preferred Replica选举,把Leader切回到优先副本。
不过这里有一条经验:均衡Leader只是在解决问题,不是根治问题。真正要做的是在集群规划时,让分区数和Broker数合理搭配。比如3个Broker、6个分区的Topic,每个Broker理论上承担2个分区的Leader。如果分区数太少(比如分区数只有3而Broker有5个),天然就会有一些Broker空闲,这是设计阶段就该考虑清楚的。
4. 消费端问题排查:从Lag到重复消费
4.1 消费Lag居高不下:先分清是“生产太快”还是“消费太慢”
消费Lag升高是Kafka运维里最常遇到的问题,也是很多同学一看到就头大的问题。其实Lag升高的原因无非两类:生产速率超过了消费速率,或者消费端本身卡住了。
判断方法比较简单:看Lag的变化趋势,同时看消费者所在机器的资源使用情况。如果Lag在持续上涨,且消费者的CPU或内存已经很高了,那大概率是消费端处理能力不足;如果Lag上涨的同时消费者机器的资源还很空闲,那要检查一下消费者是不是在等待某些外部资源,比如数据库连接池满了、调用的下游接口变慢了、或者消费者线程被阻塞了。
我之前遇到过一个大促场景下的Lag问题。业务方反馈某核心Topic的消费Lag从几千一下子涨到了几十万,消费者服务也没报错,但消息就是消费不动。后来看了消费者线程的堆栈,发现大量线程阻塞在一个数据库批量插入的方法上——因为那天下游数据库出了慢查询,批量插入的SQL执行时间从几十毫秒变成了好几秒。数据库恢复之后,Lag又慢慢降下来了。
所以排查Lag问题时,不要只盯着Kafka本身。Kafka只是个管道,Lag升高的瓶颈经常出在管道出口——也就是消费逻辑所依赖的外部系统上。
还有一个点容易被忽略:如果消费者进程里的线程数小于Topic的分区数,同一个消费者实例内就会有部分分区得不到及时消费,这也会表现为某些分区的Lag特别高。检查一下消费线程数和分区数的关系,能做一次很简单的优化。
4.2 重复消费和消息丢失:数据准确性的两个极端
重复消费和消息丢失是Kafka使用中让人最头疼的两个问题,很多时候它们还是并存的——你想尽办法避免消息丢失,结果一不小心又把消息重复消费了。
先说说消息丢失是怎么发生的。最常见的原因是生产端配置不当。比如acks设置为0,生产者发送消息后不等任何确认,这种情况下Broker磁盘挂了或者网络闪断,消息就丢了。又比如acks设置为1,Leader写入成功就返回成功,但Follower还没来得及同步,Leader所在的Broker宕机了,这条消息一样会丢。要想最大程度避免消息丢失,生产端要配置acks=all,并且Topic的min.insync.replicas设置至少为2,同时副本数也要大于1。
再说重复消费。Kafka的消费语义是at least once,也就是说消息最少会被消费一次,但可能会被消费多次。触发重复消费的常见场景是:消费者处理完消息之后,还没提交offset就崩溃了,重启后Kafka会从上一次提交的offset重新消费,这就导致那部分消息被再次处理。
要彻底解决重复消费,最靠谱的办法是在消费端做幂等处理。这里说的幂等,是指在消费逻辑里加入去重机制——比如数据库表里用唯一索引,或者用Redis SETNX之类的操作保证同一笔业务只被处理一次。我见过很多团队试图通过调整Kafka的配置来避免重复消费,比如把enable.auto.commit设为false,然后精确控制提交时机,但这只能降低重复概率,没法从根上消除。
注意:不要指望Kafka帮你做到exactly-once。即使启用了Kafka的幂等性生产者(enable.idempotence=true),它保证的也只是生产端的幂等,也就是不会因为重试而重复写入消息,但消费端依然可能重复消费。消费端的幂等必须自己实现。
4.3 消费者Rebalance频繁:团队吵架导火索
Rebalance是Kafka消费组的一个核心机制——当消费组里的成员发生变化、订阅的Topic发生变化,或者分区数发生变化时,Kafka会触发Rebalance,把分区重新分配给各个消费者。Rebalance期间整个消费组会停止消费,如果Rebalance太频繁,消费就会断断续续,表现为消息处理有间歇性停顿。
我见过最夸张的一个场景:某个消费组有6个消费者实例,每个小时Rebalance十几次,每条消息的消费延迟经常超过1分钟。业务方一度怀疑是消费者处理的业务逻辑太慢,后来排查了很久才找到原因——消费者实例中有几个处理逻辑里包含了很长的阻塞操作,导致消费者与GroupCoordinator之间的心跳超时,被判定为已下线,于是触发了Rebalance;Rebalance完成之后,消费者恢复心跳,又加入消费组,又触发一次Rebalance,陷入恶性循环。
解决这类问题一般分两步:第一,把session.timeout.ms适当调大(比如从默认的10秒调到30秒或更长),给消费者更多的响应时间;第二,把max.poll.interval.ms也相应调大,避免消费者在处理批量消息耗时较长时被判定为“消费过慢”而踢出消费组。
另外还有一种Rebalance是“预期内”的,比如发布新版本时滚动重启消费者服务,每个实例重启都会触发Rebalance。对于这种情况,可以考虑在配置中心统一控制,让所有实例在短时间内完成重启,尽量减少Rebalance的次数,而不是每次都手动重启。
5. 消息延迟高:一个系统性排查案例
5.1 案例背景与问题表现
这个案例比较典型,我拿出来完整复盘一下。
背景是某业务有一个订单状态变更的Topic,下游负责把变更后的订单状态同步到搜索引擎和缓存系统,业务对延迟比较敏感,要求从订单变更到搜索引擎可查询,延迟不超过10秒。
某天突然接到业务反馈:搜索引擎里的订单状态更新明显变慢,最长的延迟达到了好几分钟。我第一时间看了消费Lag,发现这个Topic的Lag确实在上涨,虽然没有特别夸张,但一直维持在几万的水平,消费者的处理能力明显跟不上。
这里有个细节:Lag涨到几万,看似不是特别严重,但对于这个要求10秒内完成同步的业务来说,已经属于故障级别了。所以Lag的高低是相对业务需求而言的,没有一个绝对安全的数值。
5.2 逐步排查与根因定位
我按以下步骤做了排查。
第一步,看消费者实例的资源情况。CPU和内存都正常,没有明显的瓶颈。这一步基本排除了消费者本身处理能力不足的问题。
第二步,看消费者的日志。发现大量记录指向一个外部接口的调用超时,而且这个接口超时之后就重试,但重试也不一定成功。这下问题范围缩小了——不是Kafka吞不下消息,而是消费者调用的下游接口变慢了。
第三步,查看这个下游接口对应的服务。原来这个服务依赖一个数据库,而那台数据库的慢查询日志里出现了很多全表扫描的SQL——因为业务方某次发布上线了一个新功能,在查询条件里少加了一个索引字段,导致大量查询走了全表扫描,数据库CPU飙高,所有依赖这个库的服务都被拖慢了。
第四步,和业务方确认后,临时把这个新功能回滚掉,数据库压力立刻降了下来,消费Lag也在一两个小时内被追平了。
5.3 这个案例给到的启示
这个问题的根因不在Kafka,而是消费链路里的下游依赖。但如果不按照体系化的思路排查,很容易在Kafka集群本身绕圈圈——看Broker的IO、看磁盘、看网络,最后什么也没查到,白白耽误时间。
所以我在排查Kafka消息延迟高的问题时,有一个固定的checklist:
- 消费Lag是否在上涨?
- 消费者所在机器的CPU、内存、磁盘IO是否异常?
- 消费者日志里有没有外部依赖超时、报错?
- 消费者线程的堆栈有没有阻塞在某个操作上?
- 生产端的写入速率有没有突发增长?
- Topic的分区Leader分布是否均匀?
按照这个顺序排查,绝大多数问题都能在半小时内定位到大致范围。如果全部查完还没找到问题,再把目光投向集群本身——比如网络带宽、磁盘IOPS、GC频率这些更底层的指标。
6. 磁盘与Broker稳定性:被忽视的定时炸弹
6.1 磁盘写满之后会发生什么
Kafka的消息是持久化到磁盘的,而且默认不会自动清理已经消费过的数据,只根据保留策略(retention.ms或retention.bytes)来删除旧数据。如果磁盘空间规划不合理,或者业务增长太快,磁盘写满是早晚的事。
磁盘写满之后,Kafka Broker不会直接挂掉,但会变得非常诡异。表现为写入超时、副本同步失败、分区离线等。我印象最深的是某次磁盘满了之后,生产端的消息发送成功率掉到了不到50%,而且报的都是TimeoutException。当时还以为是网络问题,查了半天,最后发现所有出问题的Broker都指向同一块数据盘。
处理磁盘满的步骤是这样的:第一,先确认是不是有Topic的保留时间设置过长,或者积累了太多未消费的消息;第二,临时调整retention.ms把保留时间缩短一些,让Kafka尽快清理旧数据;第三,如果这些还不够,可以直接手动删除部分日志分区的段文件,但操作前必须确认这些数据已经处理完毕,否则会造成数据丢失;第四,长期来看,要么给集群加磁盘,要么把Topic的保留策略改小,或者引入冷数据归档机制。
关于手动删除日志段文件,我多说一句:虽然Kafka有log.segment.delete.delay.ms这个参数控制删除延迟,但在紧急情况下,直接去日志目录里删segment文件的做法并不推荐。因为在删除的时候如果Broker正在写这些段,可能会导致异常。缓冲区空间不足时,从容错的角度来说,先缩保留时间,再观察磁盘空间下降速度,这种操作更安全。
6.2 Broker长时间GC停顿的危害
JVM GC停顿是Kafka生产环境里一个“隐藏炸弹”。Kafka Broker本身是Java进程,JVM在做Full GC的时候,整进程会处于STW(Stop The World)状态,所有的请求处理都会暂停。如果一次Full GC持续了几秒钟,那么在这几秒里,这个Broker相当于失联了。
失联的后果是什么呢?第一,它上面的分区Leader会被其他Broker接管,产生Leader切换;第二,它与ZooKeeper之间的Session可能超时,导致Broker被判定为下线;第三,它作为Follower副本时,如果长时间没有向Leader同步,会被踢出ISR。
我之前遇到过一个案例:某个Broker每天固定时间点CPU飙高一次,伴随着消费Lag出现一个小尖峰。后来查GC日志,发现每天那个时间点都会有一次Full GC,耗时大概两三秒——刚好和Lag尖峰对上了。
排查GC问题,可以用jstat来看GC频率和耗时,或者开启GC日志。解决思路一般是:调整JVM堆内存大小,把初始堆和最大堆设为一致,避免动态伸缩;同时检查是否有大对象或内存泄漏问题。Kafka Broker的JVM参数其实默认值相对保守,如果业务量很大,建议用G1收集器,并且把-XX:MaxGCPauseMillis设置在一个合理的范围。
提示:你可以用jstat -gcutil {pid} 1000 查看GC实时情况。如果看到FGC(Full GC次数)涨得很快,或者FGCT(Full GC累计时间)在增长,说明JVM的GC压力已经比较大了,需要尽快处理。
6.3 网络延迟和带宽瓶颈:客户端视角不易察觉
Kafka是网络IO密集型的系统,生产和消费的数据都要经过网络传输。如果Broker之间的网络带宽不足,或者客户端所在机器和Broker之间的网络质量差,表现出的问题往往是“性能慢但没报错”。
客户端那边,生产者如果配置了压缩算法(比如lz4、zstd),会消耗一些CPU,但能显著降低网络传输的数据量。如果你的业务对带宽比较敏感,可以考虑开启压缩。Compression type选择上,我建议用lz4或者zstd,压缩比和CPU消耗相对均衡。
Broker端的网络问题,需要在Broker上抓包或用dstat、iftop看实时流量。如果是跨机房的场景,尤其要关注专线带宽是否打满。很多时候消费Lag升高,不是消费端处理不动,而是数据从Broker传输到消费端所在机房的带宽不够了。这种情况单纯优化消费端意义不大,得从网络规划上入手。
7. 一些配置参数与实操建议清单
7.1 生产端核心参数参考
生产端的参数配置直接决定了消息写入的可靠性和性能。我列几个重点参数和推荐配置:
- acks:建议设置为all。除非你对吞吐量要求极高且可以接受少量消息丢失,否则不要设成0或1。
- retries:建议设置一个较大的值,比如5到10,配合retry.backoff.ms一起使用。这样在网络抖动时,生产者会尽量重试,减少发送失败的概率。
- buffer.memory:默认32MB,如果单条消息比较大或者瞬时写入量高,建议调大。我一般设置成64MB或128MB。
- compression.type:生产环境建议开启压缩,推荐lz4或者zstd。压缩可以减少网络带宽和磁盘占用。
- linger.ms和batch.size:这两个参数影响消息发送的延迟和吞吐量。linger.ms默认是0,也就是有消息就发,不等待。在延迟要求不高的场景下,可以调大到5-10ms,配合batch.size(默认16KB)适当调大,吞吐量会有明显提升。
7.2 消费端核心参数参考
消费端的参数和它的消费模式紧密相关,这里给几组参考:
- enable.auto.commit:建议设置为false。手动控制offset提交时机,虽然代码里麻烦一点,但能减少消息丢失的概率。
- auto.offset.reset:根据业务需求设置。earliest代表从最早的offset开始消费,latest代表从最新的开始消费。对于需要全量补偿的场景用earliest,对于实时消费场景用latest。
- max.poll.records:默认500条。如果单条消息处理比较耗时,建议把这个值调小一些,避免单次轮询拿太多消息导致处理超时。
- session.timeout.ms:默认10秒。如果消费者处理逻辑偶尔有抖动,可以调大到30秒。
- max.poll.interval.ms:默认5分钟。如果消费者处理一批消息的时间可能超过5分钟,需要调大,否则会被认为消费过慢而触发Rebalance。
7.3 实用的Kafka命令速查
Kafka的命令行工具是排查问题的最强武器,我这里列几个高频使用的命令,方便大家直接抄作业。
查看Topic的详细信息,包括分区数、副本数、ISR情况:
bin/kafka-topics.sh --describe --bootstrap-server localhost:9092 --topic topic-name查看某个消费组的消费进度和Lag:
bin/kafka-consumer-groups.sh --describe --bootstrap-server localhost:9092 --group group-name手动重设消费组的offset到指定位置(需要谨慎操作):
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group group-name --reset-offsets --to-latest --execute --topic topic-name查看Broker上的分区Leader分布情况,用来判断是否存在Leader不均衡:
bin/kafka-topics.sh --describe --bootstrap-server localhost:9092 --under-replicated-partitions这条命令可以直接列出所有副本数小于配置值的分区,是检查集群健康状态的利器。
8. 常见问题速查表
把上面讲到的常见问题整理成一张速查表,方便大家在遇到问题时快速对照排查。
| 问题现象 | 常见原因 | 排查命令/工具 | 解决方案 |
|---|---|---|---|
| 消费Lag持续上涨 | 消费端处理能力不足或外部依赖变慢 | 查看消费组Lag,看消费者日志确认外部调用是否超时 | 优化消费逻辑,扩容消费者实例,排查下游依赖 |
| 消息发送超时 | 磁盘写满、网络异常、Broker负载过高 | 查看Broker磁盘空间、网络流量、JVM GC情况 | 清理磁盘、扩容带宽、优化GC参数 |
| 重复消费 | 消费者处理完成后commit失败或崩溃 | 查看消费者日志是否有崩溃、Rebalance记录 | 在消费端做幂等处理 |
| 分区Leader不均衡 | Topic分区数设计不合理或节点变更后未迁移 | kafka-topics.sh --describe | 执行reassign操作,长期方案是合理规划分区数 |
| Controller频繁切换 | ZooKeeper Session超时、网络抖动 | 查看Controller日志和ZooKeeper状态 | 调大session.timeout.ms,排查网络稳定性 |
| ISR收缩 | Follower同步速度慢、网络或磁盘性能差 | kafka-topics.sh --describe查看ISR状态 | 减负、升级硬件、调大min.insync.replicas的容忍度 |
| 消息延迟高 | 消费者阻塞、下游接口慢、网络带宽瓶颈 | 查看消费者堆栈、外部依赖耗时、带宽使用率 | 定位阻塞点,优化依赖,扩容带宽 |
| Rebalance频繁 | 会话超时、消费耗时过长 | 查看消费者心跳和Rebalance日志 | 调大session.timeout.ms和max.poll.interval.ms |
这张表能覆盖日常80%以上的问题场景。至于剩下的20%,往往需要结合具体业务逻辑去分析,这也是Kafka运维最有挑战性的地方。
9. 聊一点个人沉淀
写到这里,想再分享一点我个人的体会。Kafka生产环境的稳定性,其实三分靠运维,七分靠设计。一个Topic的分区数设计得合理不合理、消费组的并发策略对不对、生产端的可靠性参数有没有配好,这些在系统上线之前就已经决定了大部分问题的走向。如果前期设计做得扎实,后期运维会轻松很多。
另外,每次排查完问题,一定要把过程记录下来,形成自己的排查手册。Kafka的很多问题看似独立,但根因往往相似。我这边积累的这套排查路径,就是从一次次线上故障里总结出来的。你踩过的坑,记录下来就是经验;不记录,下次还得再踩一遍。
最后给大家一个建议:动手去搭一套Kafka环境,主动制造故障——模拟Broker宕机、手动扩大Lag、改错参数,看看系统会有什么表现。这种主动找事的方式,比你在生产环境里被故障折磨,要舒服得多,也更能加深理解。