学习rabbitmq在Go中使用时的一些想法

853 阅读3分钟

学习rabbitmq在Go中使用时的一些想法:

在学习过程中对代码组织的一些想法
相关环境:
// 使用docker镜像选择带management的tag的版本,这个版本带管理页面,方便调试
docker:
docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:management

项目目录如下

--mq
  d--consumers 		//文件夹,此文件夹里面定义消费者需要处理的业务
  f--consumer.go    // 返回一个消费者对象,需要自己实现消费函数
  f--exchange.go 	// 多交换机定义
  f--name.go		// 所有的队列,交换机以及路由键在此定义,不同组之间使用空行隔开
  f--publish.go 	// 生产者定义
  f--queue.go  		// 多队列定义
  f--rabbitmq.go 	// 初始化
  f--rabbitmq_test.go 	//测试文件

首先明确:

全局只能有一个Conn和Channel

针对不同功能:

file: consumer.go

consumer.go文件中主要是消费者的定义:

type Consume struct {  
   Queue string  
  Consume string  
  AutoAck bool  
  Exclusive bool  
  NoLocal bool  
  NoWait bool  
  Arg amqp.Table  
}  
  
func Consumer(c Consume) (msg <-chan amqp.Delivery, err error) {  
   return RabbitMqCh.Consume(  
      c.Queue,  
      c.Consume,  
      c.AutoAck,  
      c.Exclusive,  
      c.NoLocal,  
      c.NoWait,  
      c.Arg,  
   )  
}

在获取消费者对象后,可以在consumers文件中创建相关文件实现对应的业务
file: publish.go

publish文件中主要是生产者相关的定义

// 此结构体可扩充,根据不同需求,可以扩充以满足amqp.Publishing结构体  
type Publish struct {  
   Exchange string  
  QueueName string  
  Mandatory bool  
  Immediate bool  
  ContentType string  
  Body []byte  
}  
  
func Publisher(p Publish) error {  
   fmt.Println(p)  
   return RabbitMqCh.Publish(  
      p.Exchange,  
      p.QueueName,  
      p.Mandatory,  
      p.Immediate,  
      amqp.Publishing{  
         ContentType: p.ContentType,  
         Body:        p.Body,  
      },  
   )  
}

考虑到可能有在项目中多个地方进行推送,所以可以定义实现一个项目的默认Publish结构,
在做好合适的规范后,只用关注Body即可(适用于单条队列)
如果是多条队列,需要发送到不同的交换机和队列,可以在此声明一个独立的结构体,传入必要的参数,以替换默认值
file exchange.go

exchange主要定义的是交换机相关:

const (  
   Fanout = exchangeType("fanout")  
   Direct = exchangeType("direct")  
   Topic = exchangeType("topic")  
   Headers = exchangeType("headers")  
)  
  
type exchangeType string  
  
type ExchangeDeclare struct {  
   Name string  
  ExchangeType exchangeType  
  Durable bool  
  AutoDelete bool  
  Internal bool  
  NoWait bool  
  Arg amqp.Table  
}  
  
func RegisterisExchangeDeclare(exs []ExchangeDeclare) {  
   for _, ex := range exs {  
      RabbitMqCh.ExchangeDeclare(  
         ex.Name,  
         string(ex.ExchangeType),  
         ex.Durable,  
         ex.AutoDelete,  
         ex.Internal,  
         ex.NoWait,  
         ex.Arg,  
      )  
   }  
}  
  
func exchangeInit() {  
   var exchanges []ExchangeDeclare  
  exchanges = append(exchanges, ExchangeDeclare{  
      Name:         TestExchange,  
      ExchangeType: Direct,  
      Durable:      false,  
      AutoDelete:   false,  
      Internal:     false,  
      NoWait:       false,  
      Arg:          nil,  
   })  
  
   RegisterisExchangeDeclare(exchanges)  
}


通过数组对象的形式,进行批量注册
file queue.go

queue文件主要定义队列相关:

type QueueDeclare struct {  
   Name string  
  Durable bool  
  DelWnUnused bool  
  Exclusive bool  
  NoWait bool  
  Arg amqp.Table  
  
  // 扩充参数  
  ExchangeName string  
  RouteKey string  
}  
  
func RegisterisQueueDeclare(Qs []QueueDeclare) {  
   for _, q := range Qs {  
      _, err := RabbitMqCh.QueueDeclare(  
         q.Name,  
         q.Durable,  
         q.DelWnUnused,  
         q.Exclusive,  
         q.NoWait,  
         q.Arg,  
      )  
      if err != nil {  
         fmt.Println("queue err ", err)  
      }  
      fmt.Println("bingbing")  
      err = RabbitMqCh.QueueBind(q.Name, q.RouteKey, q.ExchangeName, false, nil)  
      if err != nil {  
         fmt.Println("QueueBind err ", err)  
      }  
   }  
}  
  
func queueInit() {  
   var qs []QueueDeclare  
  qs = append(qs, QueueDeclare{  
      Name:         TestQueue,  
      Durable:      false,  
      DelWnUnused:  false,  
      Exclusive:    false,  
      NoWait:       false,  
      Arg:          nil,  
      ExchangeName: TestExchange,  
      RouteKey:     TestRouteKey,  
   })  
  
   RegisterisQueueDeclare(qs)  
}

和exchange相同,通过数组方式统一注册
file name.go

name文件主要是交换机,队列,路由键的常量定义

// 此文件书写所有的队列,交换机,路由键名称  
// LogExchange = "log.exchange"  
  
const (  
   TestExchange = "test.exchange"  
   TestQueue = "test.queue"  
   TestRouteKey = "test.route.key"  
)
file rabbitmq.go

rabbitmq文件主要是连接的初始化,以及交换机和队列的初始化

// 全局唯一的连接  
var RabiitMqConn *amqp.Connection  
var RabbitMqCh *amqp.Channel  
  
func Conn() {  
  
   User := "guest"  
  Pwd := "guest"  
  Host := "localhost"  
  Port := "5672"  
  
  url := "amqp://" + User + ":" + Pwd + "@" + Host + ":" + Port + "/"  
  
  conn, err := amqp.Dial(url)  
   if err != nil {  
      panic(fmt.Sprintf("get mq conn err %v \n", err))  
   }  
   RabiitMqConn = conn  
  
  ch, err := conn.Channel()  
   if err != nil {  
      panic(fmt.Sprintf("get mq ch err %v \n", err))  
   }  
   RabbitMqCh = ch  
}  
  
func Init() {  
   Conn()  
   exchangeInit()  
   queueInit()  
}

在项目开始运行时调用Init()函数进行mq的初始化
当我们需要定义多个交换机和队列时,只需要在exchange和queue注册的地方append对应的结构体,在项目下一次运行时即可加载
同时,在对路由和交换机的名称进行设计后,可以快速的找到对应的交换机和路由,以及相关消费者和生产者的实现
维护方便

以上就是昨天学习的一些想法,欢迎大家评论批评指正,蟹蟹~