虽然使用direct类型改良了我们的系统,但是仍然存在一些局限性:它不能够基于多重条件进行路由选择。 在我们的日志系统中,我们有可能希望不仅根据日志的级别而且想根据日志的来源进行订阅。这个概念类似unix工具:syslog,它转发日志基于严重性(info/warning/crit…)和设备(auth/cron/kern…) 这样可能给我们更多的灵活性:我们可能只想订阅来自’cron’的致命错误日志,而不是来自’kern’的。 为了在我们的系统中实现上述的需求,我们需要学习稍微复杂的主题类型的转发器(topic exchange)。

1、 主题转发(Topic Exchange)

发往主题类型的转发器的消息不能随意的设置选择键(routing_key),必须是由点隔开的一系列的标识符组成。标识符可以是任何东西,但是一般都与消息的某些特性相关。一些合法的选择键的例子:"stock.usd.nyse", "nyse.vmw","quick.orange.rabbit".你可以定义任何数量的标识符,上限为255个字节。 绑定键和选择键的形式一样。主题类型的转发器背后的逻辑和直接类型的转发器很类似:一个附带特殊的选择键将会被转发到绑定键与之匹配的队列中。需要注意的是:关于绑定键有两种特殊的情况。 *可以匹配一个标识符。 #可以匹配0个或多个标识符。

2、 图解:

我们准备发送关于动物的消息。消息会附加一个选择键包含3个标识符(两个点隔开)。第一个标识符描述动物的速度,第二个标识符描述动物的颜色,第三个标识符描述动物的物种:<speed>.<color>.<species>。 我们创建3个绑定键:Q1与*.orange.*绑定Q2与*.*.rabbit和lazy.#绑定。 可以简单的认为: Q1对所有的橙色动物感兴趣。 Q2想要知道关于兔子的一切以及关于懒洋洋的动物的一切。 一个附带quick.orange.rabbit的选择键的消息将会被转发到两个队列。附带lazy.orange.elephant的消息也会被转发到两个队列。另一方面quick.orange.fox只会被转发到Q1,lazy.brown.fox将会被转发到Q2。lazy.pink.rabbit虽然与两个绑定键匹配,但是也只会被转发到Q2一次。quick.brown.fox不能与任何绑定键匹配,所以会被丢弃。 如果我们违法我们的约定,发送一个或者四个标识符的选择键,类似:orange,quick.orange.male.rabbit,这些选择键不能与任何绑定键匹配,所以消息将会被丢弃。 另一方面,lazy.orange.male.rabbit,虽然是四个标识符,也可以与lazy.#匹配,从而转发至Q2。 注:主题类型的转发器非常强大,可以实现其他类型的转发器。 当一个队列与绑定键#绑定,将会收到所有的消息,类似fanout类型转发器。 当绑定键中不包含任何#与*时,类似direct类型转发器。

3、 完整的例子

发送端EmitLogTopic.java:

  1. package com.zhy.rabbit._05_topic_exchange;
  2. import java.util.UUID;
  3. import com.rabbitmq.client.Channel;
  4. import com.rabbitmq.client.Connection;
  5. import com.rabbitmq.client.ConnectionFactory;
  6. public class EmitLogTopic
  7. {
  8. private static final String EXCHANGE_NAME = "topic_logs";
  9. public static void main(String[] argv) throws Exception
  10. {
  11. // 创建连接和频道
  12. ConnectionFactory factory = new ConnectionFactory();
  13. factory.setHost("localhost");
  14. Connection connection = factory.newConnection();
  15. Channel channel = connection.createChannel();
  16. channel.exchangeDeclare(EXCHANGE_NAME, "topic");
  17. String[] routing_keys = new String[] { "kernal.info", "cron.warning",
  18. "auth.info", "kernel.critical" };
  19. for (String routing_key : routing_keys)
  20. {
  21. String msg = UUID.randomUUID().toString();
  22. channel.basicPublish(EXCHANGE_NAME, routing_key, null, msg
  23. .getBytes());
  24. System.out.println(" [x] Sent routingKey = "+routing_key+" ,msg = " + msg + ".");
  25. }
  26. channel.close();
  27. connection.close();
  28. }
  29. }
package com.zhy.rabbit._05_topic_exchange;

import java.util.UUID;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory; public class EmitLogTopic
{ private static final String EXCHANGE_NAME = "topic_logs"; public static void main(String[] argv) throws Exception
{
// 创建连接和频道
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, "topic"); String[] routing_keys = new String[] { "kernal.info", "cron.warning",
"auth.info", "kernel.critical" };
for (String routing_key : routing_keys)
{
String msg = UUID.randomUUID().toString();
channel.basicPublish(EXCHANGE_NAME, routing_key, null, msg
.getBytes());
System.out.println(" [x] Sent routingKey = "+routing_key+" ,msg = " + msg + ".");
} channel.close();
connection.close();
}
}

我们发送了4条消息,分别设置了不同的选择键。

接收端1,ReceiveLogsTopicForKernel.java

  1. package com.zhy.rabbit._05_topic_exchange;
  2. import com.rabbitmq.client.Channel;
  3. import com.rabbitmq.client.Connection;
  4. import com.rabbitmq.client.ConnectionFactory;
  5. import com.rabbitmq.client.QueueingConsumer;
  6. public class ReceiveLogsTopicForKernel
  7. {
  8. private static final String EXCHANGE_NAME = "topic_logs";
  9. public static void main(String[] argv) throws Exception
  10. {
  11. // 创建连接和频道
  12. ConnectionFactory factory = new ConnectionFactory();
  13. factory.setHost("localhost");
  14. Connection connection = factory.newConnection();
  15. Channel channel = connection.createChannel();
  16. // 声明转发器
  17. channel.exchangeDeclare(EXCHANGE_NAME, "topic");
  18. // 随机生成一个队列
  19. String queueName = channel.queueDeclare().getQueue();
  20. //接收所有与kernel相关的消息
  21. channel.queueBind(queueName, EXCHANGE_NAME, "kernel.*");
  22. System.out.println(" [*] Waiting for messages about kernel. To exit press CTRL+C");
  23. QueueingConsumer consumer = new QueueingConsumer(channel);
  24. channel.basicConsume(queueName, true, consumer);
  25. while (true)
  26. {
  27. QueueingConsumer.Delivery delivery = consumer.nextDelivery();
  28. String message = new String(delivery.getBody());
  29. String routingKey = delivery.getEnvelope().getRoutingKey();
  30. System.out.println(" [x] Received routingKey = " + routingKey
  31. + ",msg = " + message + ".");
  32. }
  33. }
  34. }
package com.zhy.rabbit._05_topic_exchange;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer; public class ReceiveLogsTopicForKernel
{ private static final String EXCHANGE_NAME = "topic_logs"; public static void main(String[] argv) throws Exception
{
// 创建连接和频道
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 声明转发器
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
// 随机生成一个队列
String queueName = channel.queueDeclare().getQueue(); //接收所有与kernel相关的消息
channel.queueBind(queueName, EXCHANGE_NAME, "kernel.*"); System.out.println(" [*] Waiting for messages about kernel. To exit press CTRL+C"); QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, true, consumer); while (true)
{
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
String routingKey = delivery.getEnvelope().getRoutingKey(); System.out.println(" [x] Received routingKey = " + routingKey
+ ",msg = " + message + ".");
}
}
}

直接收和Kernel相关的日志消息。

接收端2,ReceiveLogsTopicForCritical.java

  1. package com.zhy.rabbit._05_topic_exchange;
  2. import com.rabbitmq.client.Channel;
  3. import com.rabbitmq.client.Connection;
  4. import com.rabbitmq.client.ConnectionFactory;
  5. import com.rabbitmq.client.QueueingConsumer;
  6. public class ReceiveLogsTopicForCritical
  7. {
  8. private static final String EXCHANGE_NAME = "topic_logs";
  9. public static void main(String[] argv) throws Exception
  10. {
  11. // 创建连接和频道
  12. ConnectionFactory factory = new ConnectionFactory();
  13. factory.setHost("localhost");
  14. Connection connection = factory.newConnection();
  15. Channel channel = connection.createChannel();
  16. // 声明转发器
  17. channel.exchangeDeclare(EXCHANGE_NAME, "topic");
  18. // 随机生成一个队列
  19. String queueName = channel.queueDeclare().getQueue();
  20. // 接收所有与kernel相关的消息
  21. channel.queueBind(queueName, EXCHANGE_NAME, "*.critical");
  22. System.out
  23. .println(" [*] Waiting for critical messages. To exit press CTRL+C");
  24. QueueingConsumer consumer = new QueueingConsumer(channel);
  25. channel.basicConsume(queueName, true, consumer);
  26. while (true)
  27. {
  28. QueueingConsumer.Delivery delivery = consumer.nextDelivery();
  29. String message = new String(delivery.getBody());
  30. String routingKey = delivery.getEnvelope().getRoutingKey();
  31. System.out.println(" [x] Received routingKey = " + routingKey
  32. + ",msg = " + message + ".");
  33. }
  34. }
  35. }
package com.zhy.rabbit._05_topic_exchange;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer; public class ReceiveLogsTopicForCritical
{ private static final String EXCHANGE_NAME = "topic_logs"; public static void main(String[] argv) throws Exception
{
// 创建连接和频道
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 声明转发器
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
// 随机生成一个队列
String queueName = channel.queueDeclare().getQueue(); // 接收所有与kernel相关的消息
channel.queueBind(queueName, EXCHANGE_NAME, "*.critical"); System.out
.println(" [*] Waiting for critical messages. To exit press CTRL+C"); QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, true, consumer); while (true)
{
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
String routingKey = delivery.getEnvelope().getRoutingKey(); System.out.println(" [x] Received routingKey = " + routingKey
+ ",msg = " + message + ".");
}
}
}

只接收致命错误的日志消息。

运行结果:

[x] Sent routingKey = kernal.info ,msg = a7261f0d-18cc-4c85-ba80-5ecd9283dae7.  [x] Sent routingKey = cron.warning ,msg = 0c7e4484-66e0-4846-a869-a7a266e16281.  [x] Sent routingKey = auth.info ,msg = 3273f21f-6e6e-42f2-83df-1f2fafa7a19a.  [x] Sent routingKey = kernel.critical ,msg = f65d3e1a-0619-4f85-8b0d-59375380ecc9.

--------------------------------------------------------------------------------------------------------------------

[*] Waiting for messages about kernel. To exit press CTRL+C  [x] Received routingKey = kernel.critical,msg = f65d3e1a-0619-4f85-8b0d-59375380ecc9.

--------------------------------------------------------------------------------------------------------------------

[*] Waiting for critical messages. To exit press CTRL+C  [x] Received routingKey = kernel.critical,msg = f65d3e1a-0619-4f85-8b0d-59375380ecc9.

可以看到,我们通过使用topic类型的转发器,成功实现了多重条件选择的订阅。

RabbitMQ (五)主题(Topic) -摘自网络的更多相关文章

  1. RabbitMQ之主题(Topic)【译】

    在上一节中,我们改进了我们的日志系统,替换使用fanout exchange仅仅能广播消息,使得选择性的接收日志成为可能. 虽然使用direct exchange改进了我们的系统,但是它仍然由他的局限 ...

  2. Android:控件WebView显示网页 -摘自网络

    WebView可以使得网页轻松的内嵌到app里,还可以直接跟js相互调用. webview有两个方法:setWebChromeClient 和 setWebClient setWebClient:主要 ...

  3. Windows Azure Service Bus (5) 主题(Topic) 使用VS2013开发Service Bus Topic

    <Windows Azure Platform 系列文章目录> 项目文件,请在这里下载 在笔者之前的文章中Windows Azure Service Bus (1) 基础 介绍了Servi ...

  4. C# 中的内存管理,摘自网络

    C#编程的一个优点是程序员不需要关心具体的内存管理,尤其是垃圾收集器会处理所有的内存清理工作.虽然不必手工管理内存,但如果要编写高质量的代码,还是要理解后台发生的事情,理解C#的内存管理.本文主要介绍 ...

  5. Ubuntu的常用快捷键(摘自网络)

    篇一 : Ubuntu的复制粘贴操作及常用快捷键(摘自网络) Ubuntu的复制粘贴操作 1.最为简单,最为常用的应该是鼠标右键操作了,可以选中文件,字符等,右键鼠标,复制,到目的地右键鼠标,粘贴就结 ...

  6. MQTT主题Topic讲解

    文章转载于https://www.cnblogs.com/hayasi/p/7792191.html 我们已经把相关的连接报文搞定了.笔者想来想去还是决定先讲解一下订阅报文(SUBSCRIBE ).如 ...

  7. ionic3 教程(五)基本的网络请求

    链接: ionic3教程(一)安装和配置 ionic3教程(二)登录页制作 ionic3教程(三)设置页制作 ionic3教程(四)安卓硬件返回键处理ionic3 教程(五)基本的网络请求 这是最后一 ...

  8. RabbitMQ三种Exchange模式(fanout,direct,topic)的特性 -摘自网络

    RabbitMQ中,所有生产者提交的消息都由Exchange来接受,然后Exchange按照特定的策略转发到Queue进行存储 RabbitMQ提供了四种Exchange:fanout,direct, ...

  9. 【c#】RabbitMQ学习文档(五)Topic(主题。通配符模式)

    (本实例都是使用的Net的客户端,使用C#编写),说明,中文方括号[]表示名词. 在上一个教程中,我们改进了我们的日志记录系统. 没有使用只能够进行虚拟广播的[Fanout]交换机,而是使用了[Dir ...

随机推荐

  1. JS代码片段:appendHTML 和 insertAdjacentHTML

    function appendHTML(container,html,position){ position =position || 'after'; var objE = document.cre ...

  2. 关于矩阵最通俗的解释-超级经典zz

    线性代数课程,无论你从行列式入手还是直接从矩阵入手,从一开始就充斥着莫名其妙.比如说,在全国一般工科院系教学中应用最广泛的同济线性代数教材(现在到了第四版),一上来就介绍逆序数这个“前无古人,后无来者 ...

  3. 神经网络指南Hacker's guide to Neural Networks

    Hi there, I'm a CS PhD student at Stanford. I've worked on Deep Learning for a few years as part of ...

  4. 分布式ActiveMQ集群

    分布式ActiveMQ集群的部署配置细节: 官方资料:http://activemq.apache.org/clustering.html 基本上看这个就足够了,本文就不具体分析配置文件了. 1.Qu ...

  5. 自定义View(6)paint设置图图层重叠时的显示方式,包含清空canvas

    Paint.setXfermode 这个函数设置两个图层相交时的模式 在已有的图层上绘图将会在其上面添加一层新的图层. 如果新的图层是完全不透明的,那么它将完全遮挡住下面的图层,而setXfermod ...

  6. Android app Splash页的替代方案

    一般的App想要显示公司的log什么的,都会在启动的第一个页面显示,就是SplashActivity. 目前在看到一个替代SplashActivity的方案. 使用SplashActivity的时候, ...

  7. OLAP、OLTP的介绍和比较

    OLTP与OLAP的介绍 数据处理大致可以分成两大类:联机事务处理OLTP(on-line transaction processing).联机分析处理OLAP(On-Line Analytical ...

  8. UVa 1025 (动态规划) A Spy in the Metro

    题意: 有线性的n个车站,从左到右编号分别为1~n.有M1辆车从第一站开始向右开,有M2辆车从第二站开始向左开.在0时刻主人公从第1站出发,要在T时刻回见车站n 的一个间谍(忽略主人公的换乘时间).输 ...

  9. UVa 11389 (贪心) The Bus Driver Problem

    题意: 有司机,下午路线,晚上路线各n个.给每个司机恰好分配一个下午路线和晚上路线. 给出行驶每条路线的时间,如果司机开车时间超过d,则要付加班费d×r. 问如何分配路线才能使加班费最少. 分析: 感 ...

  10. [转]ASP.NET 页生命周期概述

    原文链接:http://msdn.microsoft.com/zh-cn/library/ms178472(v=vs.110).aspx  对应版本:.NET 4.0 ASP.NET 页运行时,此页将 ...