浅谈Go与RabbitMQ(四)

141 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情

今天接着来学习下RabbitMQ中另一个类型的交换器Direct,它只会将消息路由到那些 BindingKey 和 RoutingKey 完全匹配的队列中,结构如下所示:

image.png

绑定

err = ch.QueueBind(
  q.Name, // queue name
  "",     // routing key
  "logs", // exchange
  false,
  nil)

绑定是交换器和队列之间的关系,换句话说队列对这个交换器的信息比较感兴趣,绑定可以使用额外的routing key参数,为了避免与Publish参数混淆,这里可叫做binding key

err = ch.QueueBind(
  q.Name,    // queue name
  "warn",   // binding key
  "logs",    // exchange
  false,
  nil)

直连交换器

上一篇文章中提到的fanout类型交换器是将所有的消息广播到所有消费者,这次的direct交换器可以根据消息的严重性来过滤消息,希望日志信息写入文件只接收严重错误,不会接收warning或者info信息,使用fanout就不能做到这个事情

在这个模式中,使用warn路由键发布到交换器的消息会被路由到Q1,路由键debug,info消息将发送到Q2,所有其他键的消息将会被丢弃

多重绑定

使用相同的binding key绑定多个队列是完全可行,可以使用warn键在交换器和队列中进行绑定,这种情况类似于fanout类型的交换器,可以将信息广播到所有匹配的队列,带有warn的路由键将消息同时发送给Q1和Q2

image.png

发送日志

我们将在日志系统中使用这个模型。我们将发送消息到direct交换器,而不是fanout,通过日志等级来作为路由键,消费者可以根据需求来接受的日志等级

首先创建一个交换器

err = ch.ExchangeDeclare(
  "logs",        // name
  "direct",      // type
  true,          // durable
  false,         // auto-deleted
  false,         // internal
  false,         // no-wait
  nil,           // arguments
)

send.go代码

func getParam(args []string) (res string) {
   if (len(args) < 3) || os.Args[2] == "" {
      res = "YYQQ"
   } else {
      res = strings.Join(args[2:], "")
   }
   return
}
func getFrom(args []string) (res string) {
   if (len(args) < 2) || os.Args[1] == "" {
      res = "YYQQ info"
   } else {
      res = os.Args[1]
   }
   return
}

func main() {

   conn, err := amqp.Dial("amqp://admin:admin@localhost:5672/")
   DoError(err, "Failed connect to RabbitMQ!")
   defer conn.Close()
   ch, err := conn.Channel()
   DoError(err, "Failed to open a channel")
   defer ch.Close()

   err = ch.ExchangeDeclare(
      "logs_direct",
      "direct",
      true,
      false,
      false,
      false,
      nil)
   DoError(err, "Failed to declare a exchange")

   content := getParam(os.Args) //从命令行中返回数据
   err = ch.Publish(
      "logs_direct",
      getFrom(os.Args),
      false,
      false,
      amqp.Publishing{
         ContentType: "text/plain",
         Body:        []byte(content),
      })
   DoError(err, "Failed to publish a message")
}

receive.go代码

func main() {
   conn, err := amqp.Dial("amqp://admin:admin@localhost:5672/")
   DoError(err, "Failed to connect to RabbitMQ")
   defer conn.Close()

   ch, err := conn.Channel()
   DoError(err, "Failed to open a channel")
   defer ch.Close()

   err = ch.ExchangeDeclare(
      "logs_direct",
      "direct",
      true,
      false,
      false,
      false,
      nil)
   DoError(err, "Failed to declare a exchange")

   queue, err := ch.QueueDeclare(
      "",    // name
      false, // durable
      false, // delete when unused
      true,  // exclusive
      false, // no-wait
      nil,   // arguments
   )
   DoError(err, "Failed to declare a queue")
   fmt.Println("queue.name: ", queue.Name)

   if len(os.Args) < 2 {
      log.Printf("Usage: %s [info] [warning] [error]", os.Args[0])
      os.Exit(0)
   }
   
   //建议多个绑定关系
   for _, bindkey := range os.Args[1:] {
      log.Printf("Binding queue %s to exchange %s with routing key %s",
         queue.Name, "logs_direct", bindkey)
      err = ch.QueueBind(
         queue.Name,    // queue name
         bindkey,       // routing key
         "logs_direct", // exchange
         false,
         nil)
      DoError(err, "Failed to bind a queue")
   }
   content, err := ch.Consume(
      queue.Name, // queue
      "",         // consumer
      true,       // auto-ack
      false,      // exclusive
      false,      // no-local
      false,      // no-wait
      nil,        // args
   )
   DoError(err, "Failed to register a consumer")
   var wait chan struct{}
   go func() {
      for msg := range content {
         log.Printf("Received a message %s", msg.Body)
          }
   }()
   <-wait

}

如果你只想将warningerr(而不是info)级别的日志消息保存到文件中,只需打开控制台并输入:

go run receive.go warning error > logs_direct.log

如果要在控制台上输出Info的日志消息,请打开一个新终端并执行以下操作:

go run send.go info  "this is a info message"
go run receive.go info 

结果如图所示: image.png

例如,要发出errorwarning日志消息,只需输入:

go run send.go error warning "this is a error and warning message."
go run receive.go  warning  error

结果如图所示: image.png

image.png

绑定交换机与队列的关系---绑定时,明确绑定键 BindingKey

image.png

总结

今天浅谈了Go与RabbitMQ(四),还有很多相关的知识后面会继续深入,对于刚入门go语言的我来说,还有许多地方需要学习,有错误的地方欢迎大家指出,共同进步!!