Kratos 项目应选 kafka-go 而非 sarama,因其更轻量、默认行为合理、错误感知直接、配置简洁,契合 Kratos 的简洁与 error-driven 风格。

Kratos 本身不内置 Kafka 支持,必须靠第三方库手动集成;选 kafka-go 比 sarama 更轻、更稳、配置更少,尤其适合 Kratos 的简洁风格。
为什么不用 sarama 而选 kafka-go
sarama 功能全但配置极重:Consumer 需显式设 Consumer.Return.Errors = true、Consumer.Offsets.Initial 必须为 sarama.OffsetOldest 或 sarama.OffsetNewest,否则新消费者组静默失败;Net 超时若小于 10s,本地 Docker 环境极易断连不重试。而 kafka-go 默认行为更合理:Reader 自动管理 offset 提交,Writer 默认支持批量、重试、背压,Balancer 可直接选 LeastBytes 或 Hash,不用手写分区逻辑。
- 如果你项目已用 Kratos 且不需要 Kafka 0.11+ 的事务 API,
kafka-go是更省心的选择 -
sarama.AsyncProducer虽比 Sync 好,但仍需手动监听Successes()和Errors()通道,漏监听就等于“发了但不知道成败” -
kafka-go.Writer写入后返回error,失败可立即感知,更适合 Kratos 的 error-driven 风格
在 Kratos 中注册 kafka-go 客户端
别把 Kafka 客户端塞进 data 层只当生产者用——Kratos 的 server 层才是消费逻辑的自然落点。正确做法是:把 kafka-go.Reader 封装成一个自定义 server,和 HTTP / gRPC server 并列启动。
- 在
cmd/{service}/main.go的app.New中,调用kratos_kafka.NewKafkaServer(你自己写的封装)传入kafka-go.Reader实例 - 该 server 的
Start方法里启动一个 goroutine 循环调用reader.ReadMessage,收到消息后交由业务 handler 处理 - 务必设置
reader.Config.MaxWait = 1 * time.Second和MinBytes = 1,否则小流量下会卡住几秒才拉到消息 - 不要在 handler 里直接 commit:除非你明确需要精确控制,否则让
kafka-go自动 commit(不调reader.CommitMessages即可)
Kafka 消息体结构设计不能偷懒
Go struct 直接 json.Marshal 塞进 Kafka,上线后加字段或改类型,消费者立刻 panic——这不是 Kafka 的锅,是序列化契约失控。
- 消息最外层必须带版本字段,例如
{"v":"1.2","data":{"user_id":123}},消费者按v分支解析 - 禁止直接序列化
time.Time:不同 Go 版本对零值处理不一致,跨语言消费者(如 Java/Python)更难对齐;统一转成time.Format(time.RFC3339)字符串或UnixMilli()整数 - Topic 名要带 service 和 event 语义,比如
user-service.user-registered.v1,方便后续做权限隔离、监控聚合、schema 演进
本地调试时最容易忽略的网络配置
Docker 启的 Kafka(如 confluentinc/cp-kafka)暴露的是容器内网地址,Kratos 服务若也跑在 Docker 里,bootstrap.servers 必须填 kafka:9092(不是 localhost:9092);若 Kratos 在宿主机运行,则 Kafka 的 advertised.listeners 必须设为宿主机可访问的地址,比如 PLAINTEXT://127.0.0.1:9092。
- 用
kafka-go连接时,kafka.Reader的Addr参数填的是 broker 地址,不是 ZooKeeper;ZooKeeper 已被 KRaft 淘汰,新集群无需它 - 连接失败时,
kafka-go报的错误通常是"dial tcp: i/o timeout"或"failed to resolve address",先检查网络通路,再查 DNS 解析,最后看 broker 是否真正监听了 advertised 地址 - 别依赖默认超时:显式设
ReadTimeout: 10 * time.Second、WriteTimeout: 10 * time.Second,避免因网络抖动导致 reader 卡死
真正麻烦的从来不是连上 Kafka,而是消息体结构一旦定型就很难动,consumer 的容错解析逻辑和 producer 的版本发布节奏必须同步推进——这点在 Kratos 这种强调清晰契约的框架里,反而更容易被忽视。



















