重构消息队列连接管理,支持多数据源配置
主要变更: 1. 重构NATS、RabbitMQ和Redis连接管理模块,支持多数据源配置 2. 统一连接管理接口,增加数据源名称参数 3. 优化连接状态检查和错误处理 4. 增加连接池管理和资源清理机制 5. 改进日志输出格式和内容
This commit is contained in:
@@ -7,103 +7,158 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
conn *amqp.Connection
|
||||
channel *amqp.Channel
|
||||
rabbitmqMu sync.RWMutex
|
||||
muRabbitMQ sync.RWMutex
|
||||
rabbitmqConns map[string]*amqp.Connection
|
||||
rabbitmqChannels map[string]*amqp.Channel
|
||||
)
|
||||
|
||||
// config RabbitMQ 配置
|
||||
type config struct {
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
VHost string
|
||||
func init() {
|
||||
rabbitmqConns = make(map[string]*amqp.Connection)
|
||||
rabbitmqChannels = make(map[string]*amqp.Channel)
|
||||
}
|
||||
|
||||
func rabbitmqConnect(ctx context.Context) error {
|
||||
rabbitmqMu.Lock()
|
||||
defer rabbitmqMu.Unlock()
|
||||
|
||||
LOOP:
|
||||
cfg := &config{
|
||||
Host: g.Cfg().MustGet(ctx, "rabbitmq.host").String(),
|
||||
Port: g.Cfg().MustGet(ctx, "rabbitmq.port").Int(),
|
||||
Username: g.Cfg().MustGet(ctx, "rabbitmq.username").String(),
|
||||
Password: g.Cfg().MustGet(ctx, "rabbitmq.password").String(),
|
||||
VHost: g.Cfg().MustGet(ctx, "rabbitmq.vhost", "/").String(),
|
||||
// rabbitmqConnect 建立 RabbitMQ 连接
|
||||
func rabbitmqConnect(ctx context.Context, name string) error {
|
||||
if g.Cfg().MustGet(ctx, "rabbitmq").IsEmpty() {
|
||||
g.Log().Errorf(ctx, "❌ RabbitMQ 配置不存在")
|
||||
return fmt.Errorf("RabbitMQ Configuration does not exist")
|
||||
}
|
||||
// 确定数据源名称
|
||||
dsName := "default"
|
||||
if !g.IsEmpty(name) {
|
||||
dsName = name
|
||||
}
|
||||
|
||||
url := "amqp://" + cfg.Username + ":" + cfg.Password + "@" + cfg.Host + ":" + gconv.String(cfg.Port) + "/" + cfg.VHost
|
||||
g.Log().Infof(ctx, "🔔 RabbitMQ [%s] 开始创建连接", dsName)
|
||||
muRabbitMQ.Lock()
|
||||
defer muRabbitMQ.Unlock()
|
||||
|
||||
var err error
|
||||
conn, err = amqp.Dial(url)
|
||||
// 安全地关闭旧连接(仅针对该数据源)
|
||||
if oldConn, exists := rabbitmqConns[dsName]; exists && oldConn != nil && !oldConn.IsClosed() {
|
||||
oldConn.Close()
|
||||
}
|
||||
if oldChannel, exists := rabbitmqChannels[dsName]; exists && oldChannel != nil && !oldChannel.IsClosed() {
|
||||
oldChannel.Close()
|
||||
}
|
||||
delete(rabbitmqConns, dsName)
|
||||
delete(rabbitmqChannels, dsName)
|
||||
|
||||
// 从配置文件读取 RabbitMQ 配置
|
||||
host := g.Cfg().MustGet(ctx, fmt.Sprintf("rabbitmq.%s.host", dsName)).String()
|
||||
port := g.Cfg().MustGet(ctx, fmt.Sprintf("rabbitmq.%s.port", dsName)).Int()
|
||||
username := g.Cfg().MustGet(ctx, fmt.Sprintf("rabbitmq.%s.username", dsName)).String()
|
||||
password := g.Cfg().MustGet(ctx, fmt.Sprintf("rabbitmq.%s.password", dsName)).String()
|
||||
vHost := g.Cfg().MustGet(ctx, fmt.Sprintf("rabbitmq.%s.vhost", dsName), "/").String()
|
||||
if g.IsEmpty(host) {
|
||||
return fmt.Errorf("❌ RabbitMQ 配置错误: host 不能为空 (数据源: %s)", dsName)
|
||||
}
|
||||
if g.IsEmpty(port) {
|
||||
return fmt.Errorf("❌ RabbitMQ 配置错误: port 不能为空 (数据源: %s)", dsName)
|
||||
}
|
||||
if g.IsEmpty(username) {
|
||||
return fmt.Errorf("❌ RabbitMQ 配置错误: username 不能为空 (数据源: %s)", dsName)
|
||||
}
|
||||
if g.IsEmpty(password) {
|
||||
return fmt.Errorf("❌ RabbitMQ 配置错误: password 不能为空 (数据源: %s)", dsName)
|
||||
}
|
||||
// 构建连接 URL
|
||||
url := "amqp://" + username + ":" + password + "@" + host + ":" + gconv.String(port) + "/" + vHost
|
||||
|
||||
// 创建连接
|
||||
newConn, err := amqp.Dial(url)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "重连失败: %v", err)
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
goto LOOP
|
||||
g.Log().Errorf(ctx, "❌ RabbitMQ [%s] 连接失败: %v", dsName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
channel, err = conn.Channel()
|
||||
// 创建 Channel
|
||||
newChannel, err := newConn.Channel()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "创建 Channel 失败: %v", err)
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
goto LOOP
|
||||
g.Log().Errorf(ctx, "❌ RabbitMQ [%s] 创建 Channel 失败: %v", dsName, err)
|
||||
newConn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
g.Log().Info(ctx, "RabbitMQ 重连成功")
|
||||
return nil
|
||||
}
|
||||
// 保存连接和 Channel
|
||||
rabbitmqConns[dsName] = newConn
|
||||
rabbitmqChannels[dsName] = newChannel
|
||||
|
||||
// rabbitmqReconnect 重新连接
|
||||
func rabbitmqReconnect(ctx context.Context) error {
|
||||
if err := rabbitmqConnect(ctx); err != nil {
|
||||
return fmt.Errorf("nats重连失败: %w", err)
|
||||
}
|
||||
g.Log().Infof(ctx, "✅ RabbitMQ [%s] 连接成功", dsName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// rabbitmqPing 检测 RabbitMQ 连接状态
|
||||
func rabbitmqPing() bool {
|
||||
rabbitmqMu.RLock()
|
||||
defer rabbitmqMu.RUnlock()
|
||||
func rabbitmqPing(ctx context.Context, name string) bool {
|
||||
// 确定数据源名称
|
||||
dsName := "default"
|
||||
if !g.IsEmpty(name) {
|
||||
dsName = name
|
||||
}
|
||||
|
||||
if conn == nil || conn.IsClosed() {
|
||||
muRabbitMQ.RLock()
|
||||
defer muRabbitMQ.RUnlock()
|
||||
|
||||
conn, exists := rabbitmqConns[dsName]
|
||||
channel, channelExists := rabbitmqChannels[dsName]
|
||||
if !exists || conn == nil || conn.IsClosed() || !channelExists || channel == nil || channel.IsClosed() {
|
||||
g.Log().Errorf(ctx, "❌ RabbitMQ [%s] 连接已关闭或不可用", dsName)
|
||||
return false
|
||||
}
|
||||
|
||||
g.Log().Infof(ctx, "📊 RabbitMQ [%s] 连接正常", dsName)
|
||||
return true
|
||||
}
|
||||
|
||||
// rabbitmqClose 关闭连接
|
||||
func rabbitmqClose(ctx context.Context) error {
|
||||
rabbitmqMu.Lock()
|
||||
defer rabbitmqMu.Unlock()
|
||||
// rabbitmqClose 关闭 RabbitMQ 连接
|
||||
func rabbitmqClose(ctx context.Context, name string) error {
|
||||
// 确定数据源名称
|
||||
dsName := "default"
|
||||
if !g.IsEmpty(name) {
|
||||
dsName = name
|
||||
}
|
||||
|
||||
muRabbitMQ.Lock()
|
||||
defer muRabbitMQ.Unlock()
|
||||
|
||||
var lastErr error
|
||||
|
||||
if channel != nil {
|
||||
if channel, exists := rabbitmqChannels[dsName]; exists && channel != nil && !channel.IsClosed() {
|
||||
if err := channel.Close(); err != nil {
|
||||
g.Log().Errorf(ctx, "关闭 RabbitMQ Channel 失败: %v", err)
|
||||
g.Log().Errorf(ctx, "❌ RabbitMQ [%s] 关闭 Channel 失败: %v", dsName, err)
|
||||
lastErr = err
|
||||
}
|
||||
channel = nil
|
||||
}
|
||||
delete(rabbitmqChannels, dsName)
|
||||
|
||||
if conn != nil {
|
||||
if conn, exists := rabbitmqConns[dsName]; exists && conn != nil && !conn.IsClosed() {
|
||||
if err := conn.Close(); err != nil {
|
||||
g.Log().Errorf(ctx, "关闭 RabbitMQ 连接失败: %v", err)
|
||||
g.Log().Errorf(ctx, "❌ RabbitMQ [%s] 关闭连接失败: %v", dsName, err)
|
||||
lastErr = err
|
||||
}
|
||||
conn = nil
|
||||
}
|
||||
delete(rabbitmqConns, dsName)
|
||||
|
||||
g.Log().Info(ctx, "RabbitMQ 连接已关闭")
|
||||
g.Log().Infof(ctx, "✅ RabbitMQ [%s] 连接已关闭", dsName)
|
||||
return lastErr
|
||||
}
|
||||
|
||||
// getRabbitMQConn 获取 RabbitMQ 连接(内部使用)
|
||||
func getRabbitMQConn(name string) *amqp.Connection {
|
||||
dsName := "default"
|
||||
if !g.IsEmpty(name) {
|
||||
dsName = name
|
||||
}
|
||||
return rabbitmqConns[dsName]
|
||||
}
|
||||
|
||||
// getRabbitMQChannel 获取 RabbitMQ Channel(内部使用)
|
||||
func getRabbitMQChannel(name string) *amqp.Channel {
|
||||
dsName := "default"
|
||||
if !g.IsEmpty(name) {
|
||||
dsName = name
|
||||
}
|
||||
return rabbitmqChannels[dsName]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user