145 lines
3.2 KiB
Go
145 lines
3.2 KiB
Go
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
|
|
}
|