不能在 Iris handler 中直接调用 Kafka 客户端方法,因其非阻塞写入内存队列、不等待响应,导致日志丢失、HTTP 超时、panic 和内存上涨;应通过 logrus.Hook + 带缓冲 channel(如 10000 容量)解耦采集与发送,并由独立 goroutine 批量消费、序列化、投递。

Iris 框架本身不内置 Kafka 支持,集成 Kafka 的关键不在框架层,而在如何让 HTTP handler 不阻塞、不丢日志、不拖垮 Goroutine。直接在 iris.Context 里调 sarama.AsyncProducer.Input() 或 kafka-go.Writer.WriteMessages() 是最常见也最危险的做法——它会让日志或事件写入变成“不可控的内存排队”,一压就崩。
为什么不能在 Iris handler 里直接发 Kafka 消息
现象比原因更直观:panic: send on closed channel、HTTP 接口超时、内存持续上涨、UnknownTopicOrPartitionError 频发。根本不是 Iris 的问题,而是 Kafka 客户端和 Web 请求生命周期天然冲突:
-
sarama.AsyncProducer.Input()是非阻塞写入内存 buffer,不等 broker 响应就返回 —— handler 已 exit,消息还在队列里躺着 -
kafka-go.Writer默认异步写入,但若未设RequiredAcks或没处理WriteMessages返回 error,失败就静默丢弃 - handler 里开 goroutine 调用 producer?channel 泄漏、context 取消后 goroutine 无法回收,OOM 风险极高
推荐方案:logrus Hook + kafka-go Writer 异步中转
用 logrus.Hook 拦截日志,把 Entry 写进带缓冲的 chan *logrus.Entry;另起一个独立 goroutine 拉取、批量序列化、统一调 kafka-go.Writer.WriteMessages()。Iris 只负责快进快出,Kafka 只管稳发稳收。
- 定义缓冲 channel:
logChan := make(chan *logrus.Entry, 10000)—— 太小易阻塞 handler,太大吃内存;10k 是多数服务合理起点 - Hook 的
Fire()方法只做select { case logChan ,绝不碰网络或 producer 实例 - 消费 goroutine 按条数(如每 200 条)或大小(如累计 1MB)触发 flush,避免
MaxWait卡住小流量场景 - 序列化用
json.Marshal,别用fmt.Sprintf—— 后者注入空格换行,破坏 JSON 格式且逃逸严重
kafka-go Writer 必须显式配置的 4 项
默认配置只适合本地调试,生产环境不调等于裸奔:
立即学习“go语言免费学习笔记(深入)”;
-
RequiredAcks: kafka.RequiredAcksAll—— 确保所有 ISR 副本写入才返回,否则单点故障即丢消息 -
Async: false—— 同步写入,WriteMessages返回 error 才算失败,便于重试和告警 -
Timeout: 10 * time.Second—— 防止网络抖动时卡死,配合context.WithTimeout使用 -
CompressionCodec: kafka.Snappy(可选)—— 小消息多时能显著降低带宽和磁盘压力
消费者侧:别用 sarama.ConsumerGroup 做 Iris 流处理
如果你要用 Kafka 做事件驱动(比如订单创建后触发积分更新),消费者必须与 Iris 生命周期解耦。sarama 的 ConsumerGroup 在 Setup/Cleanup 里做 DB 查询或 HTTP 调用会卡死 rebalance,导致 CONSUMER-IDLE 状态;Errors channel 不消费则 goroutine 永久泄漏。
- 改用
kafka-go.Reader,它原生支持context.Context,ReadMessage可设 timeout,panic 后自动重试 - 必须显式设
CommitInterval: 1 * time.Second,否则 handler panic 时 offset 不提交,重启后重复消费 -
MinBytes: 1+MaxWait: 100 * time.Millisecond组合,保证低延迟不积压 - 业务逻辑里做幂等:用
msg.Key或msg.Headers中的"idempotency-id"做去重,不要指望客户端自动保证 Exactly-Once
真正难的从来不是连上 Kafka,而是当 consumer panic 后恢复、broker 临时不可用、或流量突增时,你的 offset 是否还准、消息是否仍在预期顺序里、下游系统会不会被同一条消息打穿。这些细节藏在 CommitInterval、MaxWait、错误重试策略和 handler 的 panic 捕获里。



















