Kafka:位移、积压与恢复练习
运行诊断与恢复
本轮仅静态审阅,以下命令与练习均未执行;预期结果不是本轮运行证据。早期真实服务验证仅为历史记录,不能代替当前环境验证。
先定位不可达层次
连接拒绝先检查容器是否运行和本机端口;bootstrap 成功但后续失败,检查 advertised.listeners 的返回地址是否可达;CLI 有权限错误时再检查认证与 ACL。不要通过删除卷“修复”网络错误。日志可能包含业务数据,外发前应脱敏。
docker logs --tail 80 showdocs-m4b-kafka-broker
docker exec showdocs-m4b-kafka-broker /opt/kafka/bin/kafka-topics.sh --bootstrap-server broker:9092 --describe --topic orders
docker exec showdocs-m4b-kafka-broker /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server broker:9092 --describe --group accounting观察分区 Leader、Replicas、Isr 与组 CURRENT-OFFSET、LOG-END-OFFSET、LAG。单节点 Leader=1 不等于故障转移演练通过。组不存在可能是从未提交,不要立即重置位移。
重启保留
本实验将 log.dirs=/var/lib/kafka/data 放入 showdocs-m4b-kafka-data 命名卷,容器重启后仍使用同一目录。先完成 完整项目前三步,仅写入一条 order-created,记录生产结果中的 partition/offset/value,然后重启并用从未使用过的新组重读;同一已提交组可能读不到旧消息,这是位移恢复的正常行为。
docker restart showdocs-m4b-kafka-broker
docker exec showdocs-m4b-kafka-broker /opt/kafka/bin/kafka-topics.sh --bootstrap-server broker:9092 --list
python examples/kafka/main.py --topic course-orders consume --group course-recovery-new --count 1 --committopic 必须仍只有一条保留记录;额外生产过消息时按实际数量调整 count。管理请求成功后再读取,不靠固定 sleep 判断恢复。容器重启实验只证明同一卷下的受控服务恢复;它不是备份、异机灾难恢复或断电持久性测试。单副本卷损坏仍可能丢数据。
位移重置的影响
重置消费组位移会改变下一次读取位置,可能重复业务或者跳过业务。先停该组全部消费者,保存当前 --describe 输出,确认数据仍在保留窗口,再 dry-run;审阅新 offset 后才 --execute。
docker exec showdocs-m4b-kafka-broker /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server broker:9092 --group accounting --topic orders --reset-offsets --to-earliest --dry-run本课程只展示预演,不自动执行改写。若需要精确修复,优先按分区目标 offset 制订方案;时间戳重置不代表外部数据库能一起回滚。
资源与清理
只对本任务运行下面命令。down 保留命名卷,适合下一次复习;添加 -v 会删除本实验全部日志与组位移,必须先确定无保留需要。原 showdocs-showdocs-1 与其他产品不在命令作用范围内。
docker compose -p showdocs-m4b-kafka -f examples/kafka/compose.yaml down本篇的机制与失败分支可按综合练习书面推演;提示与参考答案分开提供,无需执行示例。
练习与参考解法
本轮仅静态审阅,以下命令与练习均未执行;预期结果不是本轮运行证据。早期真实服务验证仅为历史记录,不能代替当前环境验证。
练习:区分日志与队列
前置:完成 依赖与服务准备,以下是未执行的实验步骤。使用从未用过的 exercise-orders;重做时同时替换所有 topic/组名。
python examples/kafka/main.py --topic exercise-orders create
python examples/kafka/main.py --topic exercise-orders produce --value order-0 --partition 0
python examples/kafka/main.py --topic exercise-orders produce --value order-1 --partition 1
python examples/kafka/main.py --topic exercise-orders produce --value order-2 --partition 2
python examples/kafka/main.py --topic exercise-orders consume --group exercise-A --count 3
python examples/kafka/main.py --topic exercise-orders consume --group exercise-A --count 3 --commit
python examples/kafka/main.py --topic exercise-orders consume --group exercise-B --count 3 --commit
python examples/kafka/main.py --topic exercise-orders produce --value after-commit --partition 0
python examples/kafka/main.py --topic exercise-orders consume --group exercise-A --count 1 --commit提示:按 partition/offset 比较两次结果,不比较跨分区数组顺序。每次读取都需达到 count 才输出;组名复用、消息残留或提交失败都会破坏预期。每条命令预期退出 0,最后一次只返回 after-commit。
用新 topic 创建三个分区,在每个分区写入不同订单值。组 A 不提交读三条后关闭,再用 A 提交读取;组 B 也提交读取三条。记录 value/partition/offset,解释为何 A 重读而 B 也能读。最后向分区 0 加一条,验证 A 只恢复新消息。
进阶问题:把三个不同用户改成同一个 key,去掉 --partition。观察分区一致性;解释为何把 topic 扩到六个分区后不能继续承诺所有旧 key 路由保持不变。不要在共享数据上做扩分区实验。
参考解法
完成练习后展开参考解法
参考答案:组 A 第一次没有提交,下次仍没有有效恢复位移,所以 earliest 从各分区起点重放;第二次处理后提交的分别是下一条位置。组 B 的位移独立,A 的消费不会删除日志。分区 0 原始 offset 0,新增消息是 offset 1;A 在另外两个分区没有新数据。
同一 key 可以把相关事件放进同一分区,但映射依赖分区数和驱动分区器。Kafka 保证分区内日志次序,业务线程池的完成顺序是另外的合同。增加消费者数只能利用已有分区并行度,不会自动提高单分区串行处理能力。
失败练习与答案
先自行回答两个问题:①把项目生产命令的 --server 改为 127.0.0.1:1,为什么必须同时检查 delivery callback 与 flush?②先写数据库再提交位移,如果在两者之间退出会怎样,反过来又怎样?
提示:把“进入本地缓冲”“broker 确认”“副作用完成”“提交位移”画成四个事件。
展开失败题参考答案
离线发送预期约五秒达到消息超时并非零退出;socket/关闭等额外耗时不属于精确五秒保证。若只调用 produce 后返回 0,只能证明进入本地缓冲区。callback 检查交付错误,flush 检查未排空记录,必须一起判断。
先写数据库后提交,故障重放可能再次执行,需订单唯一键等去重;先提交则可能漏处理。单节点、acks=all、幂等生产者也不能消除外部副作用原子性缺口。
验收边界
课程交付 KRaft 单节点、分区与组、确认与提交、重放与重启的示例步骤(历史曾运行,本轮未执行)、积压诊断和有界 CLI。没有覆盖生产多节点扩缩容、SASL/TLS、事务流处理、跨地域复制或外部数据库的 exactly-once。完成这些练习后再进入相应专门设计,而不是把实验配置复制到公网。
继续阅读
- 完整实践
- 知识专题
- 知识专题