Kafka Learning
把笔记总结上传一下
1.基础重要概念掌握:
Topic / Partition / Broker :
Broker 类似于 kafka 的服务器的一个 节点 负责存储消息并处理生产者和消费者的请求。
Topic 就是一个信息消费的逻辑分类 一个 Topic 里有一个或者多个 Partition 可供消费 可以并行处理 水平扩展 顺序保证(单分区内)
kafka 是 pull 模型 要靠拉取 在拉取时 消息位置由已经消费好的消费者提供的消息 去维护一个 offset 从而定位消息消费到了哪里 因为一个 Partition 在同一个 Consumer Group 中,一个 Partition 只能被一个 Consumer 消费 所以不会出现 offset 错位的问题。
kafka 的设计是 at-least-once 可能会有重复消费问题。
生产者与消费者的示例代码官网已给出,不再赘述。
2.生产者生产消息掌握:
首先要将消息序列化 在实际创建生产者对象的时候就已经配置好了序列化器
然后将消息传到分区器 如果指定了分区 那么分区器不会做什么 但是如果没有指定的话就会有balance 会根据 ProducerRecord 的键来选择一个分区
然后就是将消息批量发送到broker中 这条记录会被添加到一个记录批次中。这个批次中的所有消息都会被发送到相同的主题和分区上
最后 生产成功会收到一个响应对象 如果不成功会返回错误 有错误可重试规定次数
Kafka 生产者核心配置:
bootstrap.servers - 指定了 Producer 启动时要连接的 Broker 地址。注:如果你指定了 1000 个 Broker 连接信息,那么,Producer 启动时就会首先创建与这 1000 个 Broker 的 TCP 连接。在实际使用过程中,并不建议把集群中所有的 Broker 信息都配置到 bootstrap.servers 中,通常指定 3 ~ 4 台就足以了。因为 Producer 一旦连接到集群中的任一台 Broker,就能拿到整个集群的 Broker 信息,故没必要为 bootstrap.servers 指定所有的 Broker。
key.serializer - 键的序列化器。
value.serializer - 值的序列化器。
生产者生产信息 同步/异步/callback:
同步就是加了个阻塞等待 效率最慢 但是最保证消息的不丢失 强一致场景 金融
异步没有什么兜底策略 效率最快 但是可能丢 日志
callback就是先生成个callback对象接异步响应 折中方案 一般都用callback 订单
分区的概念:
Topic -> Partition -> Segment
个人看来 partition增强并发能力 segment对日志和文件定位有很大作用 offset和index的存在使得大文件可以被轻松定位读取
Producer 如何实现幂等性?
Kafka 通过 ProducerID + Sequence Number 实现幂等性。
Producer 启动时从 Broker 获取一个 ProducerID,每个 Partition 维护一个递增的 Sequence Number。
发送消息时携带 PID 和 Sequence。
Broker 会记录每个 Producer 在每个 Partition 的最新 Sequence。
如果收到重复的 Sequence,则直接丢弃,从而实现消息去重
然后内部还维护了一个预期的sequence 如果他知道有乱序且小于lastseq 那么直接抛乱序错误而不是重复 还会将此处的版本号(Epoch)更改 便于将之前版本的全部清空
为什么 Kafka 幂等 Producer 必须使用 acks=all?
Kafka 幂等性依赖 ProducerID 和 SequenceNumber 来做去重,但 Broker 必须保证这些状态不会因为 Leader 切换而丢失。
如果使用 acks=1,Leader 写入成功但 Follower 还未同步时 Leader 崩溃,新 Leader 可能没有该消息,从而导致 Producer 重试写入同一 Sequence 的消息,破坏幂等性。
使用 acks=all 可以保证 ISR 副本全部同步后才返回成功,即使 Leader 切换也不会丢失 Sequence 状态,从而保证幂等性。
Kafka 如何实现事务 Producer?
Kafka 事务 Producer 基于幂等 Producer 实现,通过 Transaction Coordinator 管理事务状态。
Producer 在事务开始时向 Coordinator 注册事务,发送消息时带上事务标识。
提交事务时 Coordinator 会向所有涉及的 Partition 写入 Commit Marker。
消费者在 read_committed 模式下只读取已提交事务的数据,从而实现 Exactly Once 语义。
Kafka 为什么不用传统两阶段提交,而使用 marker?
Kafka 为了保证高吞吐量,不会在事务期间锁住 Partition。
因此 Kafka 采用先写消息,再通过 Commit Marker 或 Abort Marker 来决定消息是否可见。
Consumer 在 read_committed 模式下只读取已提交事务的数据。
这种设计避免了传统两阶段提交带来的锁竞争和多次网络交互,从而保证 Kafka 的高性能。
3.kafka消费者消费信息知识掌握
基础简介:
Kafka 消费者(Consumer)以 pull 方式从 Broker 拉取消息。相比于 push 方式,pull 方式灵活度和扩展性更好,因为消费的主动性由消费者自身控制。
push 模式的缺点:
缺点:由 broker 决定消息推送的速率,对于不同消费速率的consumer就不太好处理了。push模式下,当broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了。
Pull 模式的优缺点:
优点:consumer可以根据自己的消费能力自主的决定消费策略
缺点:如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到新消息到达。为了避免这点,Kafka有个参数可以让consumer阻塞直到新消息到达
由于是读已提交 所以它只会读marker可以读得到的部分
提交也有同步/异步/同步和异步组合提交
一般情况下,针对偶尔出现的提交失败,不进行重试不会有太大问题,因为如果提交失败是因为临时问题导致的,那么后续的提交总会有成功的。但如果这是发生在关闭消费者或再均衡前的最后一次提交,就要确保能够提交成功。
因此,在消费者关闭前一般会组合使用 commitSync() 和 commitAsync()。挂一个finally块塞sync即可。
Kafka 的分区再均衡有什么作用?
Kafka 的 Rebalance 用于在消费者组成员变化时,
重新分配 partition 与 consumer 的映射关系。
它主要解决三个问题:
1 保证所有 partition 都能被消费
2 在 consumer 增减时动态调整任务分配
3 实现故障恢复和系统扩展
Rebalance 会尽量保持任务分配均衡,
但是否完全均衡取决于 partition 数量和分配策略。
为什么 Kafka 可以从任意 offset 消费,而 RocketMQ 不太鼓励?
Kafka 的 Topic 本质是一个 append-only 的日志系统,每个 partition 都是顺序日志,通过 segment 和 index 可以快速定位任意 offset,因此 Kafka 天然支持从任意 offset 回放数据,非常适合流处理和事件回放场景。
而 RocketMQ 的存储结构是 CommitLog + ConsumeQueue,offset 是逻辑队列位置,主要设计目标是保证消息可靠投递和顺序消费,因此虽然也可以指定 offset,但通常不鼓励频繁回溯消费。
笔者感觉RocketMQ是为业务而生 保障业务是第一准则 kafka反倒长得不像是一个消息 “队列” 了
Kafka 更像:Producer → Log → Consumer
而 传统MQ 更像:Producer → Queue → Consumer
kafka 通过 Customer Group 和 一个 partition 只被一个 consumer 读 把它弄的像个 Queue
KRaft算法
KRaft(Kafka Raft)是 Kafka 在新版本中引入的一种 基于 Raft 共识算法的元数据管理机制,它的主要目的就是 替代早期 Kafka 依赖的 ZooKeeper。
在早期架构中,Kafka 需要通过 ZooKeeper 来完成一些核心功能,比如 Broker 注册、Controller 选举、Topic 元数据管理以及配置变更等。这种架构需要同时维护 Kafka 和 ZooKeeper 两套分布式系统,部署复杂,而且 ZooKeeper 在大规模集群下会成为扩展瓶颈。
KRaft 的核心思想是:Kafka 自己实现一套基于 Raft 的元数据管理系统。在 KRaft 模式下,Kafka 会运行一个 Controller Quorum,这些 Controller 节点通过 Raft 算法选举出一个 Leader Controller,并通过 日志复制机制(metadata log) 来管理整个集群的元数据。
当发生元数据变更,比如 创建 Topic、修改分区、副本变化等操作时,请求会先写入 Controller Leader 的元数据日志,然后通过 Raft 的方式复制到其他 Controller 节点,当多数节点确认后才提交,从而保证元数据的一致性。
KRaft 带来的优势主要有三个:
去掉 ZooKeeper,Kafka 架构更简单,部署和运维成本降低。
扩展性更好,元数据通过日志复制管理,吞吐能力比 ZooKeeper 更强。
故障恢复更快,Controller 选举基于 Raft,一般在毫秒到百毫秒级完成。
简单来说,KRaft 就是 Kafka 使用 Raft 算法构建的一套内部元数据管理机制,用来替代 ZooKeeper,使 Kafka 成为一个完全自管理的分布式系统。
为什么最好禁用自动提交?
Kafka 自动提交 offset 时,并不知道业务是否真正处理完成,它提交的是 poll 到消息后的消费位置。如果消费者在处理消息过程中宕机,就可能出现 offset 已提交但消息未处理的情况,导致消息丢失。因此生产环境通常关闭自动提交,在业务处理成功后手动提交 offset,以保证至少一次(At least once)的消费语义。
在hmdp中的应用
为什么这样选型
秒杀是突发流量,订单创建不适合全走同步 DB 写入,Kafka 先承接流量、后异步落库。
业务里有“Redis 先扣减、DB 后落单”的天然分布式一致性问题,需要 MQ 做解耦和补偿闭环。
相比旧方案里提到的 Redis Stream,Kafka 在持久化、副本、重放、消费组扩展、DLQ治理上更成熟。
Kafka 在项目里的作用
作用1:秒杀下单主链路异步化
请求线程只做资格校验 + Redis Lua 扣减,然后发 seckill_voucher_topic,消费者异步建单。
作用2:缓存失效广播
券变更后发 seckill_voucher_cache_invalidation_topic,所有实例都消费并删本地缓存+Redis缓存。
作用3:失败隔离与可追溯
发送失败重试,超限入 .DLQ,配合审计日志和指标做后续排查/补偿。
具体用了哪些 Kafka 特性、怎么用的
特性1:Topic + Consumer Group
下单 topic 用共享消费组做负载均衡;缓存失效 consumer 用随机 groupId,让每台实例都收到一份(广播语义)。
特性2:异步生产发送回调
生产端 send() 后在成功/失败回调里分别打点、记录日志、触发重试或回滚。
特性3:消息封装与业务元数据
用 MessageExtend 包装 uuid、producerTime、headers、key,方便幂等、延迟判断、重试计数。
特性4:重试 + 指数退避 + DLQ
缓存失效 producer 失败后按退避重试,超过阈值投递 topic.DLQ。
特性5:消费侧业务校验与补偿
秒杀下单 consumer 会检查消息延迟,超阈值直接丢弃并执行 Redis 回滚与对账记录;消费异常也会回滚并记日志。
特性6:可观测性
用 Micrometer 统计成功、失败、重试、DLQ 等指标,日志里带 uuid/trace 信息便于追链路。
消息丢失怎么办?
分两层看:技术层防丢 + 业务层补偿。
- 生产端防丢
acks=all+enable.idempotence=true,避免 leader 切换时丢消息/重发重复。- 发送失败必须重试,重试超限进
DLQ(缓存失效链路已经这么做了)。 - 下单链路里发送失败已做 Redis 回滚,这很好,但建议再加“本地消息表/outbox”做兜底补发。
- Broker 端防丢
- topic 至少
replication.factor=3,并设min.insync.replicas=2。 - 禁止不安全 leader 选举(避免脏 leader 导致已 ack 消息丢失)。
- 监控 ISR 缩容和 under-replicated partitions。
- 消费端防丢
- 关闭自动提交 offset,改“业务成功后再提交”。
enable-auto-commit: true,存在“先提交后失败”的丢消息窗口,这是需要优先改的点。- 消费异常要重试,超限进 DLQ;消费逻辑保持幂等(你们已有去重/一人一单校验)。
- 业务补偿防丢
- 用对账任务扫描“Redis 扣减成功但订单未落库”的差异,自动回滚或补单。
- 项目已经有对账日志与回滚链路,这是最终兜底。
消息真丢了时,靠 Kafka 配置只能“尽量不丢”;真正把损失收敛到可控,必须靠这套“幂等 + DLQ + 对账补偿 + 回滚”闭环。
讲一下你们项目的最难点?
这个项目最难的点,不是“秒杀并发”本身,而是这条链路的一致性闭环:
Redis原子扣减成功 -> Kafka异步下单 -> DB分片落单 -> 缓存/通知副作用
任何一个环节失败,都会出现“库存、订单、用户感知”不一致。
最难点(按优先级):
Redis 与 DB 的跨系统一致性
Redis 已扣了,Kafka 可能发送失败/消费超时,DB 没订单。
消息可靠性与消费语义
当前有自动提交 offset,存在“业务失败但位点已提交”的丢单窗口。
并发下幂等与重复处理
重试、重复投递、并发消费可能重复建单或重复回滚。
缓存失效广播的边界
广播删除是最终一致,不能当强事务一致来用。
现在已做的部分:
Redis Lua 前置扣减 + trace 记录
消费失败回滚 Redis + 对账日志
缓存失效链路有重试和 DLQ
订单侧有幂等约束(message uuid/一人一单检查)