在上一章中,我们完善了我们的日志系统,用direct交换器替换了fanout交换器,使得我们可以有选择性地接收消息。尽管如此,仍然还有限制:不能基于多个标准进行路由。在我们的日志系统中,我们可能不仅希望根据日志等级订阅日志,还希望根据日志来源订阅日志。这个概念来自于unix工具syslog,它不仅可以根据日志等级(info/warn/crit...)来路由日志,同时还可以根据设备(auth/cron/kern...)来路由日志。这将更加灵活,我们可能希望只监听来自'cron'的error级别日志,同时又要接收来自'kern'的所有级别的日志。我们的日志系统如果要实现这个功能,就需要使用到另外一种交换器:主题交换器(Topic Exchange)。

1、主题交换器(Topic Exchange)

  发送到主题交换器的消息不能有任意的routing key,必须是由点号分开的一串单词,这些单词可以是任意的,但通常是与消息相关的一些特征。比如以下是几个有效的routing key: "stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit",routing key的单词可以有很多,最大限制是255 bytes。

  binding key必须与routing key模式一样。Topic交换器的逻辑与direct交换器有点相似:使用特定路由键发送的消息将被发送到所有使用匹配绑定键绑定的队列,然而,绑定键有两个特殊的情况,如下:

  • * 表示匹配任意一个单词
  • # 表示匹配任意一个或多个单词

  下图很好地表示这这两个通配符的用法:

  

  在这个例子中,我们将发送所有跟动物有关的消息,这些消息将会发送到由三个单词,两个点号组成的routing key,第一个单词了表示的是速度,第二个单词表示颜色,第三个单词表示种类:

  "<speed>.<colour>.<species>"。

  我们创建三个绑定关系:队列Q1绑定到绑定键*.orange.* ,队列Q2绑定到*.*.rabbit和lazy.#。

  总结下来就是:

  • 队列Q1对橘黄色(orange)颜色的所有动物感兴趣;
  • 队列Q2对所有的兔子(rabbit)和所有慢吞吞(lazy)的动物感兴趣。

  一个路由为 "quick.orange.rabbit"的消息,将会被转发到这两个队列,路由为"lazy.orange.elephant"的消息也被转发给这两个队列,路由为 "quick.orange.fox"的消息将只被转发到Q1队列,路由为 "lazy.brown.fox"的消息将只被转发到Q2队列。"lazy.pink.rabbit" 只被转发到Q2队列一次(虽然它匹配绑定键*.*.rabbit和lazy.#),路由为 "quick.brown.fox"的消息与任何一个绑定键都不匹配,因此将会被丢弃。

  如果我们发送的消息的的路由是由一个单词“orangle"或4个单词”quick.orangle.male.rabbit“将会怎样?会因为与任何一个绑定键不匹配而被丢弃。

  另一方面,路由为 "lazy.orange.male.rabbit"的消息,因为匹配"lazy.#"绑定键,因而会被转发到Q2队列。

  Topic交换器非常强大,可以像其他类型的交换器一样工作:

  当一个队列的绑定键是"#"是,它将会接收所有的消息,而不再考虑所接收消息的路由键,就像是fanout交换器一样;

  当一个队列的绑定键没有用到”#“和”*“时,它又像direct交换一样工作。

2、完整的代码

  下面是在我们日志系统中采用Topic交换器的完整代码,我们要发送的日志消息的路由由两个单词组成:"<facility>.<severity>"。

  EmitLogTopic.java

 import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory; public class EmitLogTopic { private final static String EXCHANGE_NAME = "topic_logs"; public static void main(String[] args) throws Exception { ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost); try(Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) { channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC); String message = "A critical kernel error";
String routingKey = "kern.critical"; channel.basicPublish(EXCHANGE_NAME,routingKey,null,message.getBytes("utf-8")); System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'");
}
}
}

  ReceiveLogsTopic.java

 import com.rabbitmq.client.*;

 public class ReceiveLogsTopic {

     private final static String EXCHANGE_NAME = "topic_logs";

     public static void main(String[] args) throws Exception {

         ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC); String queueName = channel.queueDeclare().getQueue(); if (args.length < 1) {
System.err.println("Usage: ReceiveLogsTopic [binding_key]...");
System.exit(1);
} for (String bindingKey : args) {
channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
} System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" +
delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
};
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
}
}

  启动4个接收者,分别传入绑定键:#、kern.*、*.critical、kern.* *.critical。

  启动生产者:发送一条路由为“kern.critical”的消息,消息内容为:“A critical kernel error”,分别查看接收情况:

   

   

  可以看到,所有绑定键的队列都正常接收到了消息。

3、SpringBoot实现

  工程如下图:

  

  一、生产者

  application.properties

#RabbitMq
spring.rabbitmq.host=localhost
rabbitmq.exchange.topic=topic_logs2
rabbitmq.exchange.topic.routing.key=kern.critical

  EmitLogTopic.java

 import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; @Component
public class EmitLogTopic { @Value("${rabbitmq.exchange.topic}")
private String exchangeName; @Value("${rabbitmq.exchange.topic.routing.key}")
private String routingKey; @Autowired
private AmqpTemplate template; public void sendMessage(Object message) {
System.out.println("发送消息:" + message);
template.convertAndSend(exchangeName,routingKey,message);
}
}

  EmitLogTopicRunner.java

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component; @Component
public class EmitLogTopicRunner implements ApplicationRunner { @Autowired
private EmitLogTopic emitLogTopic; @Override
public void run(ApplicationArguments args) throws Exception {
emitLogTopic.sendMessage("A critical kernel error");
}
}

  二、消费者

  application.properties

#RabbitMq
spring.rabbitmq.host=localhost
rabbitmq.exchange.topic=topic_logs2
rabbitmq.topic.queue=topic_queue
rabbitmq.exchange.topic.binding.key=kern.critical server.port=8081

  ReceiveLogsTopic.java

 import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component; @Component
@RabbitListener(
bindings = @QueueBinding(
value = @Queue(value = "${rabbitmq.topic.queue}",autoDelete = "true"),
exchange = @Exchange(value = "${rabbitmq.exchange.topic}",type = ExchangeTypes.TOPIC),
key = {"#","kern.*","*.critical"}
)
)
public class ReceiveLogsTopic { @RabbitHandler
public void reeive(Object message) {
System.out.println("接收到消息:" + message);
}
}

  启动查看控制台输出:

  生者产输出:

  

  消费者输出:

  

  至此,RabbitMq的主题交换器讲解完了,在下一章中将会讲解RabbitMq的RPC。

RabbitMQ指南之五:主题交换器(Topic Exchange)的更多相关文章

  1. RabbitMQ入门:主题路由器(Topic Exchange)

    上一篇博文中,我们使用direct exchange 代替了fanout exchange,这次我们来看下topic exchange. 一.Topic Exchange介绍 topic exchan ...

  2. PHP 下基于 php-amqp 扩展的 RabbitMQ 简单用例 (二) -- Topic Exchange 和 Fanout Exchange

    Topic Exchange 此模式下交换机,在推送消息时, 会根据消息的主题词和队列的主题词决定将消息推送到哪个队列. 交换机只会为 Queue 分发符合其指定的主题的消息. 向交换机发送消息时,消 ...

  3. rabbitmq 交换机模式 -主题模式 topic

    建立一个交换机 tpc 并且绑定了各自的路由到 Q1 Q2 <?php require_once "./vendor/autoload.php"; use PhpAmqpLi ...

  4. RabbitMQ入门(5)——主题(Topic)

    前面我们介绍了通过使用direct exchage,改善了fanout exchange只能进行虚拟广播的方式.尽管如此,直接交换也有自身的局限,它不能基于多个条件路由. 在我们的日志系统中,也许我们 ...

  5. RabbitMQ系列教程之五:主题(Topic)(转载)

    RabbitMQ系列教程之五:主题(Topic) (本实例都是使用的Net的客户端,使用C#编写),说明,中文方括号[]表示名词. 在上一个教程中,我们改进了我们的日志记录系统. 没有使用只能够进行虚 ...

  6. 7.RabbitMQ系列之topic主题交换器

    topic主题交换器它根据在队列绑定的路由键和路由模式通配符匹配将消息路由到队列. 生产者在消息头中添加路由键并将其发送到主题交换器. 收到消息后,exchange尝试将路由键与绑定到它的所有队列的绑 ...

  7. RabbitMQ (五)主题(Topic) -摘自网络

    虽然使用direct类型改良了我们的系统,但是仍然存在一些局限性:它不能够基于多重条件进行路由选择. 在我们的日志系统中,我们有可能希望不仅根据日志的级别而且想根据日志的来源进行订阅.这个概念类似un ...

  8. RabbitMQ (五)主题(Topic)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37706355 上一篇博客中,我们进步改良了我们的日志系统.我们使用direct类 ...

  9. RabbitMQ简单应用の主题模式(topic)

    Topic exchange(主题转发器) 发送给主题转发器的消息不能是任意设置的选择键,必须是用小数点隔开的一系列的标识符.这些标识符可以是随意,但是通常跟消息的某些特性相关联.一些合法的路由选择键 ...

随机推荐

  1. [bzoj2453]维护队列_带修改莫队

    维护队列 bzoj-2453 题目大意:给定一个n个数序列,支持查询区间数的种类数,单点修改.不强制在线. 注释:$1\le n,m\le 10^5$. 想法: 带修改莫队裸题. 如果没有修改操作的话 ...

  2. MongoDB小结07 - update【$pop】

    如果将数组看做队列,可以用$pop方法删除第一个或者最后一个元素 {$pop:{"key":-1}},{$pop:{"key":1}}

  3. muduo定时器、多线程模型及epoll的封装

    timerfd是Linux为用户程序提供的一个定时器接口,这个接口基于文件描述符. clock_gettime函数可以获取系统时钟,精确到纳秒.需要在编译时指定库:-lrt.可以获取两种类型时间: C ...

  4. Xcode中git的用法介绍与&quot;Please tell me who you are&quot;问题的解决方式

    我在之前多篇博客中解说了怎样使用命令行操作git,能够大大提高我们的工作效率.详细能够參考<Git学习札记><Git学习札记--进阶>等文章.事实上对于同一个工具,我们有不同的 ...

  5. Azure Pack演示样例缩放部署架构

  6. 图像处理之基础---滤波器之高斯低通滤波器的高斯模板生成c实现

    ()代码实现 对原图进行高斯平滑,去除图像中的计算噪声void Bmp::MakeGauss(double sigma,double **pdKernel,int *pnWindowSize){ // ...

  7. hdu 5256 序列变换

    最长上升子序列 nlogn;也是从别人的博客学来的 #include<iostream> #include<algorithm> #define maxn 100000+5 u ...

  8. SDK Manager配置

    改Host的都是扯淡,现在不好使了.. 还是使用东软的国内镜像好使,打开SDK Manager Tools - Options Http proxy Server:  mirrors.neusoft. ...

  9. iOS开发——基础篇——iOS开发 Xcode8中遇到的问题及改动

      iOS开发 Xcode8中遇到的问题及改动 新版本发布总会有很多坑,也会有很多改动. 一个一个填吧... 一.遇到的问题 1.权限以及相关设置 iOS10系统下调用系统相册.相机功能,或者苹果健康 ...

  10. 步长为float

    import numpy as np for i in np.arange(0.005, 0.05, 1): print(i)