You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
4.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package hello_world
import (
amqp "github.com/rabbitmq/amqp091-go"
"goskeleton/app/global/variable"
"goskeleton/app/utils/rabbitmq/error_record"
"time"
)
func CreateConsumer() (*consumer, error) {
// 获取配置信息
conn, err := amqp.Dial(variable.ConfigYml.GetString("RabbitMq.HelloWorld.Addr"))
queueName := variable.ConfigYml.GetString("RabbitMq.HelloWorld.QueueName")
durable := variable.ConfigYml.GetBool("RabbitMq.HelloWorld.Durable")
chanNumber := variable.ConfigYml.GetInt("RabbitMq.HelloWorld.ConsumerChanNumber")
reconnectInterval := variable.ConfigYml.GetDuration("RabbitMq.HelloWorld.OffLineReconnectIntervalSec")
retryTimes := variable.ConfigYml.GetInt("RabbitMq.HelloWorld.RetryCount")
if err != nil {
//log.Println(err.Error())
return nil, err
}
cons := &consumer{
connect: conn,
queueName: queueName,
durable: durable,
chanNumber: chanNumber,
connErr: conn.NotifyClose(make(chan *amqp.Error, 1)),
offLineReconnectIntervalSec: reconnectInterval,
retryTimes: retryTimes,
receivedMsgBlocking: make(chan struct{}),
status: 1,
}
return cons, nil
}
// 定义一个消息队列结构体helloworld 模型
type consumer struct {
connect *amqp.Connection
queueName string
durable bool
chanNumber int
occurError error
connErr chan *amqp.Error
callbackForReceived func(receivedData string) // 断线重连,结构体内部使用
offLineReconnectIntervalSec time.Duration
retryTimes int
callbackOffLine func(err *amqp.Error) // 断线重连,结构体内部使用
receivedMsgBlocking chan struct{} // 接受消息时用于阻塞消息处理函数
status byte // 客户端状态1=正常0=异常
}
// Received 接收、处理消息
func (c *consumer) Received(callbackFunDealSmg func(receivedData string)) {
defer func() {
c.close()
}()
// 将回调函数地址赋值给结构体变量,用于掉线重连使用
c.callbackForReceived = callbackFunDealSmg
for i := 1; i <= c.chanNumber; i++ {
go func(chanNo int) {
ch, err := c.connect.Channel()
c.occurError = error_record.ErrorDeal(err)
defer func() {
_ = ch.Close()
}()
queue, err := ch.QueueDeclare(
c.queueName,
c.durable,
true,
false,
false,
nil,
)
c.occurError = error_record.ErrorDeal(err)
if err != nil {
return
}
msgs, err := ch.Consume(
queue.Name,
"", // 消费者标记,请确保在一个消息通道唯一
true, //是否自动确认,这里设置为 true自动确认
false, //是否私有队列false标识允许多个 consumer 向该队列投递消息true 表示独占
false, //RabbitMQ不支持noLocal标志。
false, // 队列如果已经在服务器声明,设置为 true ,否则设置为 false
nil,
)
c.occurError = error_record.ErrorDeal(err)
if err == nil {
for {
select {
case msg := <-msgs:
// 消息处理
if c.status == 1 && len(msg.Body) > 0 {
callbackFunDealSmg(string(msg.Body))
} else if c.status == 0 {
return
}
}
}
} else {
return
}
}(i)
}
if _, isOk := <-c.receivedMsgBlocking; isOk {
c.status = 0
close(c.receivedMsgBlocking)
}
}
// OnConnectionError 消费者端,掉线重连监听器
func (c *consumer) OnConnectionError(callbackOfflineErr func(err *amqp.Error)) {
c.callbackOffLine = callbackOfflineErr
go func() {
select {
case err := <-c.connErr:
var i = 1
for i = 1; i <= c.retryTimes; i++ {
// 自动重连机制
time.Sleep(c.offLineReconnectIntervalSec * time.Second)
// 发生连接错误时,中断原来的消息监听(包括关闭连接)
if c.status == 1 {
c.receivedMsgBlocking <- struct{}{}
}
conn, err := CreateConsumer()
if err != nil {
continue
} else {
go func() {
c.connErr = conn.connect.NotifyClose(make(chan *amqp.Error, 1))
go conn.OnConnectionError(c.callbackOffLine)
conn.Received(c.callbackForReceived)
}()
// 新的客户端重连成功后,释放旧的回调函数 - OnConnectionError
if c.status == 0 {
return
}
break
}
}
if i > c.retryTimes {
callbackOfflineErr(err)
// 如果超过最大重连次数,同样需要释放回调函数 - OnConnectionError
if c.status == 0 {
return
}
}
}
}()
}
// close 关闭连接
func (c *consumer) close() {
_ = c.connect.Close()
}