package message import ( "context" "fmt" "sync" "time" "github.com/gogf/gf/v2/frame/g" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" ) var ( nc *nats.Conn js jetstream.JetStream natsMu sync.RWMutex ) // natsConnect 建立 NATS 连接 func natsConnect(ctx context.Context) error { natsMu.Lock() defer natsMu.Unlock() // 安全地关闭旧连接 if oldConn := nc; oldConn != nil && !oldConn.IsClosed() { oldConn.Close() } // 从配置文件读取 NATS 地址 natsURL := g.Cfg().MustGet(ctx, "nats.url").String() if natsURL == "" { // 默认使用本地地址 natsURL = nats.DefaultURL } // 使用独立的日志上下文,避免使用外部可能被取消的上下文 logCtx := context.Background() // 连接选项配置 opts := []nats.Option{ nats.Name("goframe-nats-client"), nats.ReconnectWait(2 * time.Second), nats.MaxReconnects(-1), // 无限重连 nats.PingInterval(10 * time.Second), nats.MaxPingsOutstanding(5), nats.ReconnectHandler(func(nc *nats.Conn) { g.Log().Infof(logCtx, "✅ NATS 重连成功: %s", nc.ConnectedUrl()) natsMu.Lock() defer natsMu.Unlock() // 重新创建 JetStream 实例 if newJS, err := jetstream.New(nc); err == nil { js = newJS } }), nats.DisconnectErrHandler(func(nc *nats.Conn, err error) { g.Log().Warningf(logCtx, "⚠️ NATS 连接断开: %v, 准备重连...", err) }), nats.ClosedHandler(func(nc *nats.Conn) { g.Log().Infof(logCtx, "NATS 连接已关闭: %s", nc.ConnectedUrl()) }), nats.ErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { g.Log().Errorf(logCtx, "NATS 错误: %v", err) }), } var err error nc, err = nats.Connect(natsURL, opts...) if err != nil { return fmt.Errorf("NATS 连接失败: %w", err) } // 等待连接就绪 if nc.Status() != nats.CONNECTED { select { case <-time.After(5 * time.Second): // 连接超时,清理资源 if nc != nil { nc.Close() } return fmt.Errorf("NATS 连接超时") case <-nc.StatusChanged(nats.CONNECTED): // 连接成功 case <-ctx.Done(): // 外部上下文被取消,清理资源 if nc != nil { nc.Close() } return fmt.Errorf("NATS 连接被取消: %w", ctx.Err()) } } // 创建 JetStream 实例 js, err = jetstream.New(nc) if err != nil { // 创建 JetStream 失败,清理连接 if nc != nil { nc.Close() } return fmt.Errorf("创建 JetStream 失败: %w", err) } g.Log().Infof(ctx, "✅ NATS 连接成功: %s", nc.ConnectedUrl()) return nil } // natsPing 检测 NATS 连接状态 func natsPing() bool { natsMu.RLock() defer natsMu.RUnlock() if nc == nil || nc.IsClosed() { return false } // 使用 NATS 的状态检查 if nc.Status() != nats.CONNECTED { return false } return true } // natsReconnect 重连 NATS func natsReconnect(ctx context.Context) error { if err := natsConnect(ctx); err != nil { return fmt.Errorf("nats重连失败: %w", err) } return nil } // natsClose 关闭 NATS 连接 func natsClose(ctx context.Context) error { natsMu.Lock() defer natsMu.Unlock() if nc == nil || nc.IsClosed() { return nil // 连接已经关闭或不存在 } nc.Close() g.Log().Infof(ctx, "✅ NATS 连接已关闭") return nil }