handler panic 会丢消息是因为 for range 读取 channel 时,若 handler panic 且未 recover,消费 goroutine 退出,后续事件全部卡在 channel 中无人处理;必须在每个 handler 外层加 defer recover 隔离错误。

用 channel 做本地事件总线时,为什么 handler panic 会丢消息
因为 Go 的 for range 读取 channel 时,一旦某个 handler 函数 panic 且没 recover,整个 goroutine 就退出,后续所有事件都卡在 channel 里没人收——不是“重试失败”,是直接停止消费。
- 必须在每个 handler 调用外层加
defer func() { recover() },否则一个 panic 拖垮全部监听器 - 别依赖全局 recover;panic 发生在哪个 handler 就该在哪个 goroutine 里捕获
- 带缓冲的
chan *Event(如容量 16)能缓解瞬时堆积,但不能替代错误隔离 - 事件结构体字段必须导出(首字母大写),否则
json.Marshal输出空对象,下游收不到有效数据
sarama.Consumer 收不到消息,大概率是这三个配置没改
sarama.Config 默认值对生产环境极不友好:Consumer 不报错、不设起始 offset、连接超时太短,导致“静默失联”。
-
Consumer.Return.Errors必须设为true,否则 offset 越界、rebalance 失败全无日志 -
Consumer.Offsets.Initial显式设成sarama.OffsetOldest或sarama.OffsetNewest,别信默认值 -
Net.DialTimeout和Net.ReadTimeout至少设为10 * time.Second,尤其连 Docker Kafka 时网络抖动常见 - 别用
sarama.SyncProducer发布事件——它阻塞主线程、难控制超时、不支持批量;改用sarama.AsyncProducer并监听Successes()和Errors()
Watermill 的 Publisher 为什么上线就丢订单事件
直接调 Publisher.Publish 是 fire-and-forget,Kafka broker 不可用、网络抖动、序列化失败全不反馈。关键事件(如支付成功)必须走 outbox.NewPublisher。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 先建 outbox 表,字段至少含
id、topic、payload、created_at、published_at -
outboxPub.Publish(ctx, "order.created", msg)必须在tx.Commit()之前调用,否则事务回滚时 outbox 记录也丢了 - 另起轮询协程查
published_at IS NULL,调真实 publisher 发送并更新该字段——别自己手写事务+defer rollback,outbox 包已处理幂等和并发安全 - Router 不是可选组件:所有 handler 共享一个 Subscriber 实例,靠内部 channel 分流,避免连接爆炸和 goroutine 泄漏
事件结构体怎么设计才不会在升级后崩掉消费者
Kafka 不管 schema,Go struct 却强类型,字段增删直接导致 json.Unmarshal panic 或静默丢字段。版本漂移比网络故障更隐蔽。
立即学习“go语言免费学习笔记(深入)”;
- 消息体最外层加
v字段,如{"v": "1.2", "data": {...}},消费者按 version 分支解析 -
time.Time别直塞 JSON;统一转成 RFC3339 字符串或 Unix 毫秒整数,避免时区/零值行为不一致 - topic 名带 service 和 domain event,如
order-service.order-created.v1,别用orders这种模糊名 - 若用 Protobuf,两端必须共用同一份
.proto文件和生成逻辑,别手写 Marshal/Unmarshal
真正麻烦的不是“怎么发”,而是“发完之后谁负责确保它被正确消费、不重复、不丢失、可演进”。每层抽象(channel / sarama / Watermill)都只解决一部分问题,混用时边界必须划清。


















