go rabbitmq topic模式

本文详细解释了在RabbitMQ中如何使用topic交换机进行消息路由,包括路由键和bindingkey的规则,以及如何通过不同的绑定方式让多个队列接收不同模式的消息。生产者和消费者的示例代码展示了实际操作过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

发送到主题交换的消息不能有任意的路由关键字,它必须是由点分隔的单词列表。单词可以是任何东西,但通常它们指定了与消息相关的一些特性。一些有效的路由密钥示例:“stock.usd.nyse", “nyse.vmw”, “quick.orange.rabbit”. 路由 key 中可以有任意多个字,最多 255 字节。

binding key 的格式也必须相同。主题交换机背后的逻辑类似于直接交换机,使用特定 routing key 发送的消息将被传递到使用匹配 binding key 绑定的所有队列。但是,binding key有两种重要的特殊情况:

  • * 只能代替一个单词。
  • # 可以代替零个或多个单词。

用一个例子来解释这一点:

21_Go语言操作RabbitMQ Topic模式.png

在这个例子中,我们将发送所有描述动物的信息。消息将使用由三个单词(两点)组成的路由键发送。路由键中的第一个词将描述速度,第二个是颜色,第三个是种类:“”。

我们创建了三个绑定:Q1 用绑定键 *.orange.* 绑定,Q2 用 “..rabbit” 和 “lazy.#” 绑定。这些绑定可以概括为:

  • Q1 对所有的橙色动物都感兴趣。
  • Q2 想听到关于兔子的一切,以及关于懒惰动物的一切。

routing key 设置为 “quick.orange.rabbit” 的消息将传递到所有队列。routing key 为 “lazy.orange.elephant” 的消息同样会被传递到所有的队列,但 “quick.orange.fox” 将只会发送到第一个队列,“lazy.brown.fox” 将只会发送到第二个队列。

routing key 为 “lazy.pink.rabbit” 的消息将只会发送到第二个队列一次,尽管他可以匹配两次,同样,routing key 为 “quick.brown.fox” 的消息将被丢弃,因为它不满足任何 topic。

如果我们设置了一个四个单词的 routing key,比如 “quick.orange.male.rabbit”,同样会被丢失,因为不匹配任何的队列。

说明

Topic 交换机功能非常强大,当队列用 # 绑定键绑定时,它将接收所有消息,而不管路由键是什么,就像在扇形交换机中一样。当绑定中不使用特殊字符 * 和 # 时,Topic 交换机的行为将与直接交换机一样。

生产者代码

package main

import (
   "fmt"
   "github.com/streadway/amqp"
   "time"
)

const (
   AddrServer         = "amqp://lisus2000:lisus2000@192.168.66.130:5672/"
   ExchangeNameServer = "logs_topic"
)

var Log_Levels = []string{"error", "info", "error", "warning"}

func main() {
   fmt.Println("生产者生产消息")
   var (
      conn    *amqp.Connection
      channel *amqp.Channel
      err     error
   )
   //连接MQServer
   if conn, err = amqp.Dial(AddrServer); err != nil {
      fmt.Println("Connect RabbitMQ Err =", err, "Addr =", AddrServer)
      return
   }
   //需要关闭
   defer conn.Close()
   //创建一个Channel,所有的连接都是通过Channel管理的
   if channel, err = conn.Channel(); err != nil {
      fmt.Println("Create Channel Err =", err)
      return
   }
   defer channel.Close()
   //创建交换机
   if err = channel.ExchangeDeclare(
      ExchangeNameServer, //声名交换机名称
      "topic",            //交换机类型为topic
      true,
      false,
      false,
      false,
      nil,
   ); err != nil {
      fmt.Println("ExchangeDeclare Err =", err)
      return
   }
   //直接向交换机发送数据即可
   for i := 0; i < 100; i++ {
      logLevel := Log_Levels[i%4]
      msg := fmt.Sprintf("Msg Level %s", logLevel)
      if err = channel.Publish(ExchangeNameServer, "lazy.brown.fox", false, false, amqp.Publishing{
         ContentType: "text/plain",
         Body:        []byte(msg),
      }); err != nil {
         fmt.Println("Publish Err =", err)
         return
      }
      fmt.Println("Send msg ok, msg =", msg)
      time.Sleep(5 * time.Second)
   }
}

消费者代码

package main

import (
   "fmt"
   "github.com/streadway/amqp"
   "time"
)

const (
   RecvAddr1            = "amqp://lisus2000:lisus2000@192.168.66.130:5672/"
   ReceiveExchangeName1 = "logs_topic"
)

func main() {
   fmt.Println("消费端1启动")
   var (
      conn    *amqp.Connection
      channel *amqp.Channel
      queue   amqp.Queue
      msgs    <-chan amqp.Delivery
      err     error
   )
   //连接MQServer
   if conn, err = amqp.Dial(RecvAddr1); err != nil {
      fmt.Println("Connect RabbitMQ Err =", err, "Addr =", RecvAddr1)
      return
   }
   //需要关闭
   defer conn.Close()
   //创建一个Channel,所有的连接都是通过Channel管理的
   if channel, err = conn.Channel(); err != nil {
      fmt.Println("Create Channel Err =", err)
      return
   }
   defer channel.Close()
   //创建交换机
   if err = channel.ExchangeDeclare(
      ReceiveExchangeName1, //交换机名称
      "topic",              // 交换机类型
      true,                 // 是否持久化
      false,                // 是否自动删除
      false,                // internal
      false,                // 如果为 True,那么其他连接尝试修改该队列,将会触发异常。
      nil,                  // 额外参数
   ); err != nil {
      fmt.Println("ExchangeDeclare Err =", err)
      return
   }
   //创建队列
   if queue, err = channel.QueueDeclare(
      "",    //队列名
      false, //持久的
      false, //是否自动删除
      true,  //独占的
      false,
      nil,
   ); err != nil {
      fmt.Println("QueueDeclare Err =", err)
      return
   }
   //交换机绑定队列
   if err = channel.QueueBind(queue.Name, "lazy.#", ReceiveExchangeName1, false, nil); err != nil {
      fmt.Println("QueueBind Err =", err)
      return
   }
   //读取数据
   if msgs, err = channel.Consume(
      queue.Name, // queue
      "",         // consumer
      true,       // 自动消息确认
      false,      // exclusive
      false,      // no-local
      false,      // no-wait
      nil,
   ); err != nil {
      fmt.Println("Consume Err =", err)
      return
   }
   go func() {
      for msg := range msgs {
         fmt.Printf("Received a message: %s\n", msg.Body)
      }
   }()
   time.Sleep(100 * time.Second)
}

消费端代码2

package main

import (
   "fmt"
   "github.com/streadway/amqp"
   "time"
)

const (
   RecvAddr2            = "amqp://lisus2000:lisus2000@192.168.66.130:5672/"
   ReceiveExchangeName2 = "logs_topic"
)

func main() {
   fmt.Println("消费端2启动")
   var (
      conn    *amqp.Connection
      channel *amqp.Channel
      queue   amqp.Queue
      msgs    <-chan amqp.Delivery
      err     error
   )
   //连接MQServer
   if conn, err = amqp.Dial(RecvAddr2); err != nil {
      fmt.Println("Connect RabbitMQ Err =", err, "Addr =", RecvAddr2)
      return
   }
   //需要关闭
   defer conn.Close()
   //创建一个Channel,所有的连接都是通过Channel管理的
   if channel, err = conn.Channel(); err != nil {
      fmt.Println("Create Channel Err =", err)
      return
   }
   defer channel.Close()
   //创建交换机
   if err = channel.ExchangeDeclare(
      ReceiveExchangeName2,
      "topic",
      true,
      false,
      false,
      false,
      nil,
   ); err != nil {
      fmt.Println("ExchangeDeclare Err =", err)
      return
   }
   //创建队列
   if queue, err = channel.QueueDeclare(
      "",    //队列名
      false, //持久的
      false, // delete when unused
      true,  //独占的
      false,
      nil,
   ); err != nil {
      fmt.Println("QueueDeclare Err =", err)
      return
   }
   //交换机绑定队列
   if err = channel.QueueBind(queue.Name, "*.brown.*", ReceiveExchangeName2, false, nil); err != nil {
      fmt.Println("QueueBind Err =", err)
      return
   }
   //读取数据
   if msgs, err = channel.Consume(
      queue.Name, // queue
      "",         // consumer
      true,       // 自动消息确认
      false,      // exclusive
      false,      // no-local
      false,      // no-wait
      nil,
   ); err != nil {
      fmt.Println("Consume Err =", err)
      return
   }
   go func() {
      for msg := range msgs {
         fmt.Printf("Received a message: %s\n", msg.Body)
      }
   }()
   time.Sleep(100 * time.Second)
}

测试

启动生产者

启动消费者1

启动消费者2

从图中可以看到通过TOP模式匹配的方式,两个消费者都有消息消费了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值