go对接nats需复用连接、配置自动重连、按场景选同步/异步订阅,并显式管理jetstream消息ack,否则易触发文件描述符耗尽、消息重复或丢失。

Go 直接对接 nats-server 是可行且高效的,但“高性能”不等于“开箱即用”,关键在连接复用、订阅模型选择和错误恢复策略。
为什么 nats.Connect() 不能每次发消息都调用一次
频繁新建连接会迅速耗尽文件描述符,触发 too many open files 错误,同时 TLS 握手和连接建立延迟远高于消息本身处理时间。
- 始终复用单个
*nats.Conn实例,全局或按服务域初始化一次 - 使用
nats.ReconnectWait(2 * time.Second)和nats.MaxReconnects(-1)启用自动重连,避免手动轮询 - 若需隔离流量(如测试/生产环境),用不同
nats.URL初始化独立连接,而非反复Connect()
nats.Subscription 的两种模式:同步消费 vs 异步回调
同步消费(sub.NextMsg())适合控制节奏的批处理;异步回调(sub Chan 或 sub.AutoUnsubscribe())适合高吞吐、低延迟场景,但需自行管理 goroutine 生命周期。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 用
sub.Channel()时,必须启动 goroutine 消费 channel,否则消息堆积导致内存泄漏 - 用
sub.Subscribe("topic", handler)时,handler 函数内禁止阻塞操作(如长 SQL 查询),否则压垮整个 subscription 的内部 dispatcher - 若需精确控制并发数,优先选
sub.NextMsgWithContext(ctx)+ worker pool,而非无限制起 goroutine
发布消息时 nats.Publish() 和 nats.PublishAsync() 怎么选
Publish() 是同步阻塞调用,返回前确保消息已写入 socket 缓冲区;PublishAsync() 立即返回,靠 callback 或 nc.LastError() 检查失败,适合吞吐优先、允许少量丢失的场景。
- 金融类业务、状态同步等强一致性要求,必须用
Publish()并检查 error - 日志、埋点等容忍丢失的场景,可用
PublishAsync(),但务必设置nats.NoEcho()避免自己收到自己发的消息 - 无论哪种,都建议加
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)防止卡死
连接断开后,未确认的 JetStream 消息怎么办
JetStream 的 Consumer 默认启用 ack,但 Go 客户端不会自动重发未 ack 消息——它依赖你显式调用 msg.Ack() 或 msg.NakWithDelay()。断连期间消息仍保留在 stream 中,重连后需重新订阅并处理 pending 消息。
- 务必在 handler 中用
defer msg.Ack()或明确控制 ack 时机,避免漏 ack 导致重复投递 - 使用
js.GetConsumerInfo("stream", "consumer")查看NumAckPending,监控积压情况 - 不要依赖 “重连自动续传” —— NATS 不会帮你 replay,你得靠 consumer 的 durable name 和 deliver policy(如
DeliverAll())保证不丢起点
JetStream 的 ack 语义和重连行为容易被当成“自动可靠”,实际要靠 consumer 配置 + 显式 ack + 监控三者配合,缺一不可。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










