虽然使用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. android系统中使用TelephonyManager类来获取imei号和其他手机信息

    在AndroidManifest.xml文件中增加<!--允许读取电话状态SIM的权限--><uses-permission android:name="android.p ...

  2. hive环境的搭建

    hive 默认用的是derby数据库存储源数据,在这改为 mysql来存储: 1.hive和关系数据库的对照关系 hive 所要查询的数据保存在HDFS中: hive 中的数据库和表对应HDFS中的文 ...

  3. 设置MySQL主从同步

    1. 配置主服务器 1.1 编辑my.cnf文件,配置主服务器ID. [mysqld] log-bin=mysql-bin server-id=1relay-log = relay-bin relay ...

  4. 1287. Mars Canals(DP)

    1287 水DP #include <iostream> #include<cstdio> #include<cstring> #include<algori ...

  5. Codeforces Round #239 (Div. 2)

    做了三个题,先贴一下代码...终于涨分了 A. Line to Cashier 水题 #include <iostream> #include <cstdio> #includ ...

  6. fil_system_struct

    /** The tablespace memory cache */ typedef struct fil_system_struct fil_system_t; /** The tablespace ...

  7. BZOJ2298: [HAOI2011]problem a

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2298 题解:刚开始思考的方向错了...一直在想LIS什么的,又发现不合法的情况不好判断,真是个 ...

  8. Android下fragment切换的动画效果

    网上看到的例子,转过来记录一下,学习一下,感谢原作者的辛勤编码,效果非常好 基于Android3.0新增的动画api,效果很赞 共21种动画效果: <item>X轴缩放</item& ...

  9. JAVA JDK1.5-1.9新特性

    1.51.自动装箱与拆箱:2.枚举(常用来设计单例模式)3.静态导入4.可变参数5.内省 1.61.Web服务元数据2.脚本语言支持3.JTable的排序和过滤4.更简单,更强大的JAX-WS5.轻量 ...

  10. Informatica元数据库解析

    Informatica所有的元数据信息均以数据库表的方式存到了元数据库中.当然Infa本身工具提供了很多的人性化的功能,使我们在开发时可以很方便的进行操作,但人们的需求总是万变的,需要方便的取到自己需 ...