franz-go 需严格匹配 kafka broker 版本(如 3.6.0 对应 kgo.v3_6_0_0),显式配置 config.version 和 config.maxattempts,手动管理元数据、消息发送、offset 提交与连接生命周期,不自动重连且需调用 client.close()。

franz-go 不是“换包就能用”的替代品,它把 Kafka 协议细节暴露得更直接——用对了性能好、控制细;配错一个 config.Version 或漏掉 client.Close(),消息就静默失败或连接堆积。
franz-go 初始化必须显式设 config.Version 和 config.MaxAttempts
它不像 sarama 那样有模糊的默认版本,也不像 kafka-go 那样自动降级兼容。franz-go 的 config.Version 必须严格匹配 Kafka broker 实际版本(比如你用的是 Kafka 3.6.0,就得写 kgo.V3_6_0_0),否则会卡在元数据请求,日志里只显示 UNKNOWN_TOPIC_OR_PARTITION 或空响应。
config.MaxAttempts 默认是 1,意味着网络抖动时请求直接失败,不会重试。生产环境建议设为 3–5,并配合 config.RetryBackoff(如 2 * time.Second)避免雪崩。
- 别依赖
kgo.DefaultClient:它用的是V0_10_0_0,几乎肯定不兼容现代 Kafka - 初始化后务必调
client.OptTopicMetadata(topic)主动拉一次元数据,确认 topic 存在且分区可见 - 若用 KRaft 模式(无 ZooKeeper),
config.SeedBrokers必须填 controller 地址,不能只填普通 broker
发送消息要手动处理 ProduceRequest 和 ProduceResponse
franz-go 没有 “一行发消息” 的封装,client.ProduceSync 或 client.ProduceAsync 都需要你构造 *kgo.Record 并传入 ProduceRequest。这不是缺点,而是让你清楚每条消息的 key、value、headers、partition 甚至 timestamp。
关键点在于:成功返回 ≠ 消息已落盘。ProduceResponse 中每个 TopicPartition 的 Err 字段必须检查,Err == nil 才代表该分区写入成功;Err == kerr.NotLeaderForPartition 这类错误需触发元数据刷新再重试。
- 批量发送时,
kgo.Record切片长度建议 ≤ 100,太大容易触发 broker 的message.max.bytes限制 - 幂等性靠
config.EnableIdempotentWrite开启,但前提是 broker 已启用enable.idempotence=true且 client 使用了ProducerID - 不要在循环里反复 new
kgo.Record:复用结构体 +record.Reset()更省 GC
消费者需自己管理 offset 提交时机和 rebalance 逻辑
franz-go 没有内置 ConsumerGroup 抽象,kgo.ConsumerGroup 是实验性模块,生产环境推荐用底层 kgo.Client + 手动分配分区 + 自行提交 offset。这意味着你得自己监听 OnPartitionsAssigned 和 OnPartitionsRevoked 回调,做清理和 checkpoint。
offset 提交不是越勤越好:CommitOffsets 是同步阻塞调用,高频提交会拖慢消费速度;太懒又会导致重启后重复消费。合理做法是每处理 N 条(如 50)或每秒一次,取 max(record.Offset + 1, lastCommittedOffset) 提交。
- 首次消费用
kgo.OffsetOldest或kgo.OffsetNewest控制起点,别依赖 broker 默认策略 - 手动分配分区时,
client.FetchTopicPartitions(topic)必须在AssignPartitions前调用,否则 fetch 会报UNKNOWN_TOPIC_OR_PARTITION - 心跳超时由
config.HeartbeatInterval控制,默认 3s,云环境建议调到 5–10s 防止误踢出 group
别忽略 client.Close() 和连接健康检查
franz-go 的 client 不是无状态对象,它内部维护连接池、后台 goroutine 和 metadata cache。不调 client.Close() 会导致 TCP 连接泄漏、goroutine 泄露,Kafka broker 日志会出现大量 Connection reset by peer。
更隐蔽的问题是:它不自动重连。网络中断后,ProduceSync 会卡在 context.DeadlineExceeded,Fetch 可能永远阻塞。必须自己加一层健康检查 —— 比如定期调 client.Metadata(),失败则重建 client。
- 每次新建
client前,确保旧实例已Close(),否则新连接可能被 broker 拒绝(连接数超限) - 本地调试连不上?先用
telnet advertised.listeners地址 端口确认可达,franz-go 不会帮你解析localhost和 Docker 网络的地址映射差异 - 日志里出现
invalid request type?90% 是config.Version和 broker 版本差了一个小号,比如用了V3_5_0_0对接 3.6.0 broker
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











