1、主题

  在前面的例子中,我们对日志系统进行了改进。使用了direct交换机代替了fanout交换机,从只能盲目的广播消息改进为有可能选择性的接收日志。

  尽管直接交换机能够改善我们的日志系统,但是它也有它的限制——没办法基于多个标准执行路由操作。

  在我们的日志系统中,我们不只希望订阅基于日志级别,同时还希望订阅基于日志来源。其中unix工具syslog是同时基于日志的级别(info/warn/error)和设备-facility (auth/cron/kern...)来路由日志的。

  如果这样的话,将会给予我们非常大的灵活性,我们既可以监听来源于“cron”的严重程度为“critical errors”的日志,也可以监听来源于“kern”的所有日志。

  为了实现这个目的,接下来我们学习如何使用另一种更复杂的交换机 —— 主题交换机。

2、主题交换机

  发送到主题交换机(topic exchange)的消息不可以携带随意什么样子的路由键(routing_key),它的路由键必须是一个由.分隔开的词语列表。这些单词随便是什么都可以,但是最好是跟携带它们的消息有关系的词汇。以下是几个推荐的例子:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit"。词语的个数可以随意,但是不要超过255字节。

  绑定键也必须拥有同样的格式。主题交换机背后的逻辑跟直连交换机很相似 —— 一个携带着特定路由键的消息会被主题交换机投递给绑定键与之想匹配的队列。但是它的绑定键和路由键有两个特殊应用方式:

    * (星号) 用来表示一个单词.

      # (井号) 用来表示任意数量(零个或多个)单词

  下边用图说明:

                  

  这个例子里,我们发送的所有消息都是用来描述小动物的。发送的消息所携带的路由键是由三个单词所组成的,这三个单词被两个.分割开。路由键里的第一个单词描述的是动物的手脚的利索程度,第二个单词是动物的颜色,第三个是动物的种类。所以它看起来是这样的: <celerity>.<colour>.<species>

  我们创建了三个绑定:Q1的绑定键为 *.orange.*,Q2的绑定键为 *.*.rabbit 和 lazy.# 。

这三个绑定键被可以总结为:

    Q1 对所有的桔黄色动物都感兴趣。

    Q2 则是对所有的兔子所有懒惰的动物感兴趣。

  一个携带有 quick.orange.rabbit 的消息将会被分别投递给这两个队列。携带着 lazy.orange.elephant 的消息同样也会给两个队列都投递过去。另一方面携带有 quick.orange.fox 的消息会投递给第一个队列,携带有 lazy.brown.fox 的消息会投递给第二个队列。携带有 lazy.pink.rabbit 的消息只会被投递给第二个队列一次,即使它同时匹配第二个队列的两个绑定。携带着 quick.brown.fox 的消息不会投递给任何一个队列。

  如果我们违反约定,发送了一个携带有一个单词或者四个单词("orange" or "quick.orange.male.rabbit")的消息时,发送的消息不会投递给任何一个队列,而且会丢失掉。

  但是另一方面,即使 "lazy.orange.male.rabbit" 有四个单词,他还是会匹配最后一个绑定,并且被投递到第二个队列中。

  注意:

  主题交换机是很强大的,它可以表现出跟其他交换机类似的行为

  当一个队列的绑定键为 "#"(井号) 的时候,这个队列将会无视消息的路由键,接收所有的消息。

  当 * (星号) 和 # (井号) 这两个特殊字符都未在绑定键中出现的时候,此时主题交换机就拥有的直连交换机的行为。

3、组合在一起

  接下来我们会将主题交换机应用到我们的日志系统中。在开始工作前,我们假设日志的路由键由两个单词组成,路由键看起来是这样的:<facility>.<severity>。

  代码跟上一篇教程差不多。只是参数和交换机类型不一样。

EmitLogTopic.java代码如下:

 package rabbitmq.topic;

 import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection; import rabbitmq.utils.ConnectionUtils; /*
* 发送错误日志生产者
*/
public class EmitLogTopic {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//创建管道
Channel channel = connection.createChannel();
//创建直接交换器
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//获取日志级别
String severity = getSeverity(args);
//获取消息
String message = getMessage(args);
//发送消息
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
channel.close();
connection.close();
}
}

ReceiveLogsTopic.java代码如下:

 package rabbitmq.topic;

 import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope; import rabbitmq.utils.ConnectionUtils; public class ReceiveLogsTopic {
private static final String EXCHANGE_NAME = "topic_logs"; public static void main(String[] args) throws IOException, TimeoutException {
// 获取连接
Connection connection = ConnectionUtils.getConnection();
// 创建管道
Channel channel = connection.createChannel();
// 创建直接交换器
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String queueName = channel.queueDeclare().getQueue();
for (String severity : args) {
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}

ReceiveLogsTopic.java分别启动四次,参数分别为"#"、"kern.*"、"*.critical"、"kern.*" "*.critical"。

启动EmitLogTopic.java,参数为"kern.critical" "A critical kernel error",其中routing_key=“kern.critical”,那么绑定键为"#"、"kern.*"、"*.critical"都会收到日志信息

  

rabbitmq系列五 之主题交换机的更多相关文章

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

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

  2. RabbitMQ (五)主题(Topic)

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

  3. Go RabbitMQ(五)主题

    RabbitMQ topic 在之前我们将交换器的类型从fanout设置为direct后能够根据我们的选择获得响应的消息,虽然改良我们的消息日志系统,但是还有很多局限性,比如它不能基于多个标准进行路由 ...

  4. RabbitMQ系列(五)--高级特性

    在上一篇文章讲解MQ消息可靠性投递和幂等性中有提到confirm机制的重要性,现在更相信的说明一下 一.Confirm机制 Confirm就是消息确认,当Producer发送消息,如果Broker收到 ...

  5. rabbitmq系列五 之远程过程调用(RPC)

    1.远程过程调用(RPC) 在第二篇教程中我们介绍了如何使用工作队列(work queue)在多个工作者(woker)中间分发耗时的任务. 可是如果我们需要将一个函数运行在远程计算机上并且等待从那儿获 ...

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

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

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

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

  8. rabbitmq学习(三):rabbitmq之扇形交换机、主题交换机

    前言 上篇我们学习了rabbitmq的作用以及直连交换机的代码实现,这篇我们继续看如何用代码实现扇形交换机和主题交换机 一.扇形交换机 1.生产者 /** * 生产者 */ public class ...

  9. RabbitMQ入门教程(七):主题交换机Topics

    原文:RabbitMQ入门教程(七):主题交换机Topics 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...

随机推荐

  1. 2018.10.24 NOIP模拟 小 C 的序列(链表+数论)

    传送门 考虑到a[l],gcd(a[l],a[l+1]),gcd(a[l],a[l+1],a[l+2])....gcd(a[l]...a[r])a[l],gcd(a[l],a[l+1]),gcd(a[ ...

  2. hdu-1041(大数模板)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1041 题意:电脑中存在数字1,进行扩展操作,如果遇到1变为“01”,如果遇到0,变为“10”,经过一次 ...

  3. linux 安装 ORACLE JDK 8

    1.卸载默认的OPENJDK 查看 open jdk 的安装 rpm -qa | grep java 卸载 openjdk rpm -e --nodeps java-1.7.0-openjdk-1.7 ...

  4. Win7 VS2013环境编译Lua5.3.1

    主要参考这篇文章,原文有几个错误顺便改正了. 在Windows下使用Visual Studio编译Lua5.3 写本文时Lua官方网站放出的新版本为5.3.1,然后我不知道为啥,神奇的国内不能访问Lu ...

  5. 利用url传多个参数

    刚开始接触jsp,比较偏向于用button标签的onclick方法进行页面的跳转.但是关于页面跳转的各种问题真是叫人头大,以下记录,仅仅为自己以后查看. Qone 用url传参的时候遇到中文怎么办 编 ...

  6. hadoop集群的三种运行模式

    单机(本地)模式: 这种模式在一台单机上运行,没有分布式文件系统,而是直接读写本地操作系统的文件系统.在单机模式(standalone)中不会存在守护进程,所有东西都运行在一个JVM上.这里同样没有D ...

  7. DDR中的命令

    (3) 列地址选择(CAS#): 选择器件内感兴趣的地址列 行地址选择(RAS#): 选择设备中感兴趣的地址行. (4)(precharge)预充电 DRAM读取具有破坏性,也就是说,在读操作中会破坏 ...

  8. C#控件之:进度条(ProgressBar)

    一.重绘进度条 public class CustomProgressBar:ProgressBar { public CustomProgressBar() { this.SetStyle(Cont ...

  9. WMS和WMTS的区别

  10. HDOJ1024--Max Sum Plus Plus(动态规划)UnSolved

    Now I think you have got an AC in Ignatius.L's "Max Sum" problem. To be a brave ACMer, we ...