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() }