fox/rmq/connection.go
2025-09-03 22:21:30 +08:00

159 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package rmq
import (
"context"
"errors"
"github.com/fox/fox/log"
amqp "github.com/rabbitmq/amqp091-go"
"time"
)
type Connection struct {
conn *amqp.Connection
channel *amqp.Channel
config *RabbitMQConfig
isConnected bool
done chan bool
notifyClose chan *amqp.Error
notifyConfirm chan amqp.Confirmation
}
func NewConnection(config *RabbitMQConfig) (*Connection, error) {
conn := &Connection{
config: config,
done: make(chan bool),
}
if err := conn.Connect(); err != nil {
return nil, err
}
return conn, nil
}
func (c *Connection) Connect() error {
var err error
// 建立连接
c.conn, err = amqp.Dial(c.config.URL)
if err != nil {
return errors.Join(err, errors.New("failed to connect to RabbitMQ"))
}
// 创建通道
c.channel, err = c.conn.Channel()
if err != nil {
return errors.Join(err, errors.New("failed to open channel"))
}
// 设置QoS
err = c.channel.Qos(c.config.PrefetchCount, 0, false)
if err != nil {
return errors.Join(err, errors.New("failed to set QoS"))
}
// 声明交换器
err = c.channel.ExchangeDeclare(
c.config.ExchangeName,
"direct",
true,
false,
false,
false,
nil,
)
if err != nil {
return errors.Join(err, errors.New("failed to declare exchange"))
}
// 声明队列
_, err = c.channel.QueueDeclare(
c.config.QueueName, // 队列名称
c.config.Durable, // 持久化 true队列元数据名称、属性和消息会写入磁盘RabbitMQ 重启后仍存在。false队列存在于内存重启后丢失。
false, // 不自动删除 true当最后一个消费者断开连接后队列自动删除。
false, // 非独占队列
false, // 等待服务器确认
nil,
)
if err != nil {
return errors.Join(err, errors.New("failed to declare queue"))
}
// 绑定队列
err = c.channel.QueueBind(
c.config.QueueName,
c.config.RoutingKey,
c.config.ExchangeName,
false,
nil,
)
if err != nil {
return errors.Join(err, errors.New("failed to bind queue"))
}
// 设置确认通道
c.notifyClose = make(chan *amqp.Error)
c.notifyConfirm = make(chan amqp.Confirmation)
c.channel.NotifyClose(c.notifyClose)
c.channel.NotifyPublish(c.notifyConfirm)
c.isConnected = true
log.Info("RabbitMQ connection established successfully")
return nil
}
func (c *Connection) Reconnect(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case <-c.done:
return
case err := <-c.notifyClose:
if err != nil {
log.ErrorF("RabbitMQ connection closed: %v", err)
c.isConnected = false
// 重连逻辑
for {
if err := c.Connect(); err != nil {
log.ErrorF("Failed to reconnect: %v. Retrying in %vs", err, c.config.ReconnectInterval)
time.Sleep(time.Duration(c.config.ReconnectInterval) * time.Second)
continue
}
break
}
}
}
}
}
func (c *Connection) Close() error {
close(c.done)
if c.channel != nil {
if err := c.channel.Close(); err != nil {
return err
}
}
if c.conn != nil {
if err := c.conn.Close(); err != nil {
return err
}
}
c.isConnected = false
log.Info("RabbitMQ connection closed")
return nil
}
func (c *Connection) IsConnected() bool {
return c.isConnected
}
func (c *Connection) GetChannel() *amqp.Channel {
return c.channel
}