Kafka 作为分布式消息队列的“王者”,以其极高的吞吐量和优秀的持久化能力著称。但在实际生产环境中,如果配置或使用不当,很容易出现消息丢失、消息重复、消息积压、频繁 Rebalance 等严重问题。
以下是 Kafka 在实际使用和面试中必须掌握的核心注意事项,分为架构设计、生产者、消费者、消息可靠性、常见坑五个维度。
一、 架构与 Topic 设计注意事项
1. Partition(分区)数量的权衡
- 不要太少:Partition 数量决定了消费者的最大并发度。如果只有 3 个分区,你启动 10 个消费者,有 7 个也是闲置的。
- 不要太多:每个 Partition 对应磁盘上的一个目录和文件。分区过多会导致:
- Broker 打开的文件句柄过多,消耗内存。
- Controller 选举 Leader 时元数据同步变慢。
- 客户端(Producer/Consumer)需要维护更多的连接和状态。
- 建议:根据预估的吞吐量来定。一般单个 Partition 的写入吞吐量在 10MB/s~50MB/s 左右。如果是高吞吐场景,可以设置几十到上百个分区。
2. 副本因子(Replication Factor)
- 生产环境必须 ≥\ge≥ 3。
- 副本数决定了数据的高可用性。如果副本数为 3,允许 2 个 Broker 宕机而不丢数据(配合
min.insync.replicas=2)。
3. 命名规范
- 切忌随意命名。建议格式:
{环境}_{业务线}_{具体业务}_{版本},例如prod_order_payment_created_v1。方便后期运维和排查。
二、 生产者(Producer)注意事项
1. 保证消息不丢失(ACK 机制)
acks=0:生产者发完就忘,不等待 Broker 确认。(性能最高,极易丢消息)acks=1:Leader 副本写入成功就返回。(Leader 宕机且未同步给 Follower 时会丢消息)acks=all(或 -1):生产环境必选。要求所有 ISR(同步副本)集合中的副本都写入成功才返回。配合min.insync.replicas >= 2,可保证强可靠性。
2. 开启幂等性(Idempotence)与事务
- 幂等性:设置
enable.idempotence=true。Kafka 会为每个 Producer 分配一个 PID,并在消息中加入 Sequence Number。Broker 端会去重,防止网络抖动导致 Producer 重试时产生重复消息(解决单 Partition 内的 Exactly Once)。 - 事务:如果消息需要跨多个 Partition 发送,且要求要么全成功要么全失败,必须开启 Kafka 事务(
initTransactions())。
3. 提升吞吐量(批量与压缩)
- 批量发送:调整
batch.size(默认 16KB)和linger.ms(默认 0ms,建议设为 5~50ms)。让 Producer 稍微等一等,凑够一批再发,极大减少网络请求次数。 - 开启压缩:设置
compression.type=lz4或snappy。在 Producer 端压缩,Broker 端保持压缩,Consumer 端解压。能大幅节省网络带宽和磁盘 IO。
4. 顺序消费问题
- Kafka 只能保证单个 Partition 内的消息有序,无法保证全局有序。
- 如果业务要求同一个订单的消息必须有序,必须在发送时指定相同的 Key(如 OrderId),利用
Key的 Hash 值将消息路由到同一个 Partition。
三、 消费者(Consumer)注意事项
1. 消费组与 Partition 的关系
- 一个 Partition 只能被同一个 Consumer Group 中的一个 Consumer 消费。
- 避坑:Consumer 的数量不要超过 Partition 的数量。多出来的 Consumer 会处于闲置状态(Idle),白白浪费资源。
2. 偏移量(Offset)提交策略(核心)
- 自动提交(
enable.auto.commit=true):默认每 5 秒提交一次。极易导致消息丢失或重复消费。因为拉取消息和提交 offset 是异步的,如果拉取了消息还没处理完就宕机了,重启后会从旧 offset 开始,导致重复消费;如果处理完了但还没到提交时间就宕机了,会导致丢失。 - 手动提交(推荐):设置
enable.auto.commit=false。在业务逻辑完全处理成功后,再调用commitSync()或commitAsync()提交 offset。
3. 警惕 Rebalance(重平衡)
Rebalance 会导致所有消费者暂停消费(STW, Stop The World),对吞吐量影响极大。
- 触发条件:消费者组成员变化(宕机/新增)、订阅的 Topic 数量变化、Partition 数量变化。
- 避坑(假死导致的 Rebalance):
- 如果消费者处理消息太慢,超过了
max.poll.interval.ms(默认 5 分钟),Broker 会认为该消费者“死了”,将其踢出组,触发 Rebalance。 - 解决:适当调大
max.poll.interval.ms,或者减小max.poll.records(每次拉取的消息条数),让每次处理的任务量变小。
- 如果消费者处理消息太慢,超过了
四、 消息“零丢失”终极方案(面试必考)
要保证消息从生产到消费绝对不丢失,需要三管齐下:
- Producer 到 Broker 不丢:
acks=allretries = Integer.MAX_VALUE(无限重试)min.insync.replicas >= 2(至少 2 个副本写入成功)
- Broker 自身不丢:
- 副本数 ≥\ge≥ 3。
- 设置
unclean.leader.election.enable=false(关键)。默认情况下,如果 Leader 挂了,Kafka 会从 ISR 中选新 Leader。如果 ISR 全挂了,默认是不允许从非 ISR(可能数据有延迟)中选 Leader 的,宁可停机也不丢数据。如果设为 true,虽然能恢复服务,但会导致数据丢失。
- Broker 到 Consumer 不丢:
- 关闭自动提交 Offset。
- 先处理业务逻辑,成功后再手动提交 Offset。
- 消费端必须实现接口幂等性(防止重复消费)。
五、 常见坑与生产事故处理
1. 消息积压(几千万条堆积)怎么办?
原因:消费者处理太慢,或下游数据库/接口故障。
紧急处理方案(临时扩容):
- 修复消费者 Bug 或恢复下游服务。
- 如果原 Topic 只有 10 个 Partition,消费者已经拉满。此时新建一个 Topic,设置 60 个 Partition。
- 部署一个临时的“转发程序”(原 Consumer),它只负责从原 Topic 拉取消息,不做任何业务处理,直接轮询分发到新 Topic 的 60 个 Partition 中。
- 启动 60 个新的 Consumer 消费新 Topic,并行处理业务。
- 积压处理完毕后,恢复原状。
2. 消息体过大导致发送失败
- Kafka 默认限制单条消息大小为 1MB。如果发送 JSON 或包含图片 Base64 的大消息,会报错
RecordTooLargeException。 - 解决:
- 治本:大文件/大对象不要直接放 Kafka,应该上传到 OSS/MinIO,Kafka 里只传 URL。
- 治标:如果必须传,需要同步修改 Producer 的
max.request.size、Broker 的message.max.bytes和replica.fetch.max.bytes,以及 Consumer 的fetch.message.max.bytes。
3. 延迟消息支持
- Kafka 原生不支持延迟消息(像 RocketMQ 那样)。
- 替代方案:
- 使用多个不同延迟级别的 Topic(如
delay_1s,delay_5s),配合定时任务将消息转移到下一个级别的 Topic。 - 在 Consumer 端拉取后,判断时间戳,如果没到时间,利用线程池的
DelayQueue或ScheduledExecutorService在内存中延迟处理(不推荐,容易丢)。 - 引入外部组件(如结合 Redis 的 ZSet 或 RocketMQ)。
- 使用多个不同延迟级别的 Topic(如
六、 总结与选型建议
- Kafka:适合大数据日志收集、流式计算、超高吞吐的场景(如 ELK 架构、Flink 数据源)。它的强项是吞吐量,但在低延迟和复杂路由上不如 RocketMQ。
- RocketMQ:适合核心业务链路、金融级交易、需要丰富消息类型(延迟、事务、顺序) 的场景。
- RabbitMQ:适合中小型公司、对路由规则要求复杂、对消息可靠性要求极高但吞吐量要求不是千万级的场景。
在实际使用中,“手动提交 Offset + 消费端幂等” 是 Kafka 消费端的黄金法则;而 “acks=all + min.insync.replicas=2” 是生产端的黄金法则。牢记这两点,能避开 90% 的 Kafka 生产事故。
1352

被折叠的 条评论
为什么被折叠?



