Go RabbitMQ(五)主题
RabbitMQ topic
在之前我们将交换器的类型从fanout设置为direct后能够根据我们的选择获得响应的消息,虽然改良我们的消息日志系统,但是还有很多局限性,比如它不能基于多个标准进行路由
在我们的日志系统中我们可能不仅仅是依据消息的严重性进行订阅,还有可能同时基于消息的危险等级和消息来源,比如我们监听来自cron的危险错误和来自kern的所有日志。通过topic我们可以来实现以上功能
主题交换器(topic exchange)
消息如果发送到主题交换器的话不能使用任何的routing_key,它必须是由点分隔的单词列表。单词可以是任意的,但通常它们是与消息相关的一些特性
binding key必须是具有相同格式,topic交换器背后的逻辑跟direct交换器的逻辑类似,一个指定了routing key的消息将会被投递到所有使用binding key并与routing key 相匹配的队列中。binding key有两种特殊情况:
*可以代表代替一个单词#可以代替0个或多个单词

在本例子中,我们将发送所有描述动物的消息,这些消息将使用由三个单词(两个点)组成的routing_key发送。routing_key第一个单词描述速度,第二个表示颜色,第三个表示物种
队列Q1使用binding_key:*.orange.*,队列Q2使用binding_key*.*.rabbit和lazy.#。总结如下:
- Q1队列将会接收所有orange的动物,比如
quick.orange.rabbit,lazy.orange.elephant,quick.orange.fox, - Q2队列会接收所有跟rabbit相关和lazy类型的动物,比如
quick.orange.rabbit,lazy.orange.elephant,lazy.brown.fox,lazy.pink.rabbit - 像
quick.brown.fox跟以上两个队列的routing_key都不匹配所以该消息会被丢弃 lazy.orange.male.rabbit则只能匹配队列Q2
topic 交换器非常灵活并且可以表现为其他交换器
比如设置队列的binding_key为#,则队列会接收所有的消息不管routing_key是什么
当binding_key不使用特殊字段`*`和`#`的时候,此时topic交换器跟direct交换器一样
完整代码如下:
- emitLogsTopic.go
 
func main() {
        conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
        failOnError(err, "Failed to connect to RabbitMQ")
        defer conn.Close()
        ch, err := conn.Channel()
        failOnError(err, "Failed to open a channel")
        defer ch.Close()
        err = ch.ExchangeDeclare(
                "logs_topic", // name
                "topic",      // type
                true,         // durable
                false,        // auto-deleted
                false,        // internal
                false,        // no-wait
                nil,          // arguments
        )
        failOnError(err, "Failed to declare an exchange")
        body := bodyFrom(os.Args)
        err = ch.Publish(
                "logs_topic",          // exchange
                severityFrom(os.Args), // routing key
                false, // mandatory
                false, // immediate
                amqp.Publishing{
                        ContentType: "text/plain",
                        Body:        []byte(body),
                })
        failOnError(err, "Failed to publish a message")
        log.Printf(" [x] Sent %s", body)
}
func bodyFrom(args []string) string {
        var s string
        if (len(args) < 3) || os.Args[2] == "" {
                s = "hello"
        } else {
                s = strings.Join(args[2:], " ")
        }
        return s
}
func severityFrom(args []string) string {
        var s string
        if (len(args) < 2) || os.Args[1] == "" {
                s = "anonymous.info"
        } else {
                s = os.Args[1]
        }
        return s
}
- receiveLogsTopic.go
 
func main() {
        conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
        failOnError(err, "Failed to connect to RabbitMQ")
        defer conn.Close()
        ch, err := conn.Channel()
        failOnError(err, "Failed to open a channel")
        defer ch.Close()
        err = ch.ExchangeDeclare(
                "logs_topic", // name
                "topic",      // type
                true,         // durable
                false,        // auto-deleted
                false,        // internal
                false,        // no-wait
                nil,          // arguments
        )
        failOnError(err, "Failed to declare an exchange")
        q, err := ch.QueueDeclare(
                "",    // name
                false, // durable
                false, // delete when usused
                true,  // exclusive
                false, // no-wait
                nil,   // arguments
        )
        failOnError(err, "Failed to declare a queue")
        if len(os.Args) < 2 {
                log.Printf("Usage: %s [binding_key]...", os.Args[0])
                os.Exit(0)
        }
        for _, s := range os.Args[1:] {
                log.Printf("Binding queue %s to exchange %s with routing key %s",
                        q.Name, "logs_topic", s)
                err = ch.QueueBind(
                        q.Name,       // queue name
                        s,            // routing key
                        "logs_topic", // exchange
                        false,
                        nil)
                failOnError(err, "Failed to bind a queue")
        }
        msgs, err := ch.Consume(
                q.Name, // queue
                "",     // consumer
                true,   // auto ack
                false,  // exclusive
                false,  // no local
                false,  // no wait
                nil,    // args
        )
        failOnError(err, "Failed to register a consumer")
        forever := make(chan bool)
        go func() {
                for d := range msgs {
                        log.Printf(" [x] %s", d.Body)
                }
        }()
        log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
        <-forever
}
- 接收所有的日志:
go run receiveLogsTopic.go "#" - 接收来自kern的所有日志:
go run receiveLogsTopic.go "kern.*" - 接收关于critical的日志:
go run receiveLogsTopic.go "*.critical" - 创建多个binding:
go run receiveLogsTopic.go "kern.*" "*.critical" - 启动发送消息的脚本:
go run receiveLogsTopic.go "kern.critical" "A critical kernel error" 
Go RabbitMQ(五)主题的更多相关文章
- RabbitMQ之主题(Topic)【译】
		
在上一节中,我们改进了我们的日志系统,替换使用fanout exchange仅仅能广播消息,使得选择性的接收日志成为可能. 虽然使用direct exchange改进了我们的系统,但是它仍然由他的局限 ...
 - rabbitmq五种模式详解(含实现代码)
		
一.五种模式详解 1.简单模式(Queue模式) 当生产端发送消息到交换机,交换机根据消息属性发送到队列,消费者监听绑定队列实现消息的接收和消费逻辑编写.简单模式下,强调的一个队列queue只被一个消 ...
 - RABBITMQ/JAVA  (主题)
		
上篇博文中,我们进一步改良了日志系统.即使用Direct类型的转换器,使得接受者有能力进行选择性的接收日志,而非fanout那样,只能够无脑的转发. 虽然使用Direct类型的转换器改进了日志系统.但 ...
 - RabbitMQ 使用主题进行消息分发
		
在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity的log.但是,这也是它之所以叫做简单日志系统的原因, ...
 - RabbitMQ 五种工作模式
		
官网介绍:https://www.rabbitmq.com/getstarted.html 五种工作模式的主要特点 简单模式:一个生产者,一个消费者 work模式:一个生产者,多个消费者,每个消费者获 ...
 - rabbitmq (五)RPC
		
Remote Procedure Call or RPC(远程函数调用) 当我们需要在远程计算机上运行一个函数,并且等待结果的时候,我们用到RPC 在rabbitmq客户端使用call函数,发送RPC ...
 - Rabbitmq(6) 主题模式
		
* 匹配1个 # 匹配所有 发送者: package com.aynu.bootamqp.service; import com.aynu.bootamqp.commons.utils.Amqp; i ...
 - RabbitMQ (五) 订阅者模式之分发模式 ( fanout )
		
前面讲到了简单队列和工作队列. 这两种队列有个非常明显的缺点 : 生产者发送的消息,只能进入到一个队列. 消息只能进入到一个队列就意味着消息只能被一个消费者消费. 尽管工作队列模式中,一个队列中的消息 ...
 - 快速掌握RabbitMQ(五)——搭建高可用的RabbitMQ集群
		
RabbitMQ的集群是依赖erlang集群的,而erlang集群是通过.erlang.cookie文件进行通信认证的,所以我们使用RabbitMQ集群时只需要配置一下.erlang.cookie文件 ...
 - RabbitMQ五:生产者--队列--多消费者
		
一.生成者-队列-多消费者(前言) 上篇文章,我们做了一个简单的Demo,一个生产者对应一个消费者,本篇文章就介绍 生产者-队列-多个消费者,下面简单示意图 P 生产者 C 消费者 中间队列 ...
 
随机推荐
- Git Note - Branch
			
1. add a new branch cd workspace git branch user1/newbranch1 git checkout user1/newbranch1 or git ch ...
 - Angular之constructor和ngOnInit差异及适用场景
			
constructor会在类生成实例时调用,Angular无法控制constructor,constructor中应该只进行依赖注入而不是进行真正的业务操作 ngOnInit属于Angular生命周期 ...
 - Hibernate继承映射(@Inheritance)
			
继承映射在 Annotation 中使用 @Inheritance 注解,并且需要使用 strategy 属性指定继承策略,继承策略有 SINGLE_TABLE.TABLE_PER_CLASS 和 J ...
 - Verify the Developer App certificate for your account is trusted on your device.
			
1.报错内容 Could not launch "CH5203" Verify the Developer App certificate for your account is ...
 - @media媒体查询
			
@media媒体查询 @media screen and (min-width:640px) and (max-width:1920px){/*当屏幕尺寸大于640px时与小于1920时*/ .pub ...
 - grunt 常用插件
			
grunt-contrib-uglify:代码压缩 grunt-contrib-jshint:检查js拼写错误 csslint:检查css语法错误
 - mysql常用日期、时间查询
			
好记性不如烂笔头 select curdate(); --获取当前日期 select last_day(curdate()); --获取本月最后一天. day); -- 获取本月第一天 ,interv ...
 - vs2010和opencv2.4.9配置
			
1.下载安装opencv2.4.9 直接到官网上下载opencv,我下载的是opencv2.4.9版本,然后进行安装,opencv的安装其实就是解压,我解压的路径是D:\opencv249.解压完成后 ...
 - Vue 不睡觉教程1-从最土开始
			
目标最近在学习vue的过程中发现网上的vue教程总有些不同的问题,有的教程上来只说语法,有的教程上来就用vue-cli来建项目,但是vue-cli是整合了webpack等多个插件的工具,不利于我们学习 ...
 - [性能测试]:关于MQ协议脚本开发
			
消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接它们. 银行脚本使用MQ通信的较多,下面介绍一个MQ的脚本: M ...