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

在我们的日志系统中,也许我们希望不仅要根据严重程度,而且要基于发送日志的源订阅日志。为了实现这个功能,我们需要学习更复杂的主题交换(topic exchange)。

主题交换(Topic exchange)

发送到主题交换机的消息不能随意设置路由键。它必须是由点分隔的一系列标识符。理论上可以是任何词,但最好见名知义,例如:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit",标识符最大长度为255个字节。

绑定键和路由键形式是一样的。主题类型的交换机背后的逻辑和直接类型的也很相似,使用特定的路由键发送的消息将被发送到绑定绑定键的所有队列中。

绑定键有两种特殊的情况:

  1. *:匹配一个标识符
  2. #:匹配0或多个标识符

图示如下:

在本例中,我们准备发送描述动物的消息,消息会附加一个路由键包含三个标识符。依次为速度、颜色、物种。

创建3个绑定:Q1与*.orange.*绑定,Q2与*.*.rabbitlazy.#绑定。

可以简单的概述为:

  • Q1对所有的橙色动物感兴趣
  • Q2要知道关于兔子的一切和懒惰动物的一切

附带quick.orange.rabbit路由键的消息将发送到两个队列,附带lazy.orange.elephant的消息也会同时发送到两个队列,附带quick.orange.fox的消息只会发送到Q1,附带lazy.brown.fox的消息只会发送到Q2,附带lazy.pink.rabbit的消息尽管匹配了两个绑定,但只会发送到Q2一次,附带quick.brown.fox的消息会没有匹配任何一项会被丢弃。

那么如果违法协议,使用一或四个标识符作为路由键会发生什么呢?例如,orangequick.orange.male.rabbit,这些消息没有匹配任何绑定将会丢失。对于附带lazy.orange.male.rabbit的消息,虽然它有四个标识符,但是匹配了最后一个绑定,将会发送到Q2。

代码清单

Send:

  1. package com.xxyh.rabbitmq;
  2. import com.rabbitmq.client.BuiltinExchangeType;
  3. import com.rabbitmq.client.Channel;
  4. import com.rabbitmq.client.Connection;
  5. import com.rabbitmq.client.ConnectionFactory;
  6. import java.io.IOException;
  7. import java.util.UUID;
  8. import java.util.concurrent.TimeoutException;
  9. public class EmitLogsTopic {
  10. private static final String EXCHANGE_NAME = "topic_logs";
  11. public static void main(String[] args) throws IOException, TimeoutException {
  12. ConnectionFactory factory = new ConnectionFactory();
  13. factory.setHost("localhost");
  14. Connection connection = factory.newConnection();
  15. Channel channel = connection.createChannel();
  16. // 声明 topic类型的交换机
  17. channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
  18. String[] routingKeys = new String[]{"zhang.info", "li.warning", "wang.info", "zhang.error"};
  19. for (String routingKey : routingKeys) {
  20. String message = UUID.randomUUID().toString();
  21. channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("utf-8"));
  22. System.out.println(Thread.currentThread().getName() + " 发送消息: " + message);
  23. }
  24. channel.close();
  25. connection.close();
  26. }
  27. }

Recv1:

  1. package com.xxyh.rabbitmq;
  2. import com.rabbitmq.client.*;
  3. import sun.java2d.loops.TransformHelper;
  4. import java.io.IOException;
  5. import java.util.concurrent.TimeoutException;
  6. public class ReceiveLogsTopic {
  7. private static final String EXCHANGE_NAME = "topic_logs";
  8. public static void main(String[] args) throws IOException, TimeoutException {
  9. ConnectionFactory factory = new ConnectionFactory();
  10. factory.setHost("localhost");
  11. Connection connection = factory.newConnection();
  12. Channel channel = connection.createChannel();
  13. channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
  14. String queue = channel.queueDeclare().getQueue();
  15. // 接收所有zhang发出的消息
  16. channel.queueBind(queue, EXCHANGE_NAME, "zhang.*");
  17. System.out.println("准备接收所有zhang发出的消息----------------");
  18. final Consumer consumer = new DefaultConsumer(channel) {
  19. @Override
  20. public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
  21. String message = new String(body, "utf-8");
  22. System.out.println(Thread.currentThread().getName() + " 接收到消息: " + message);
  23. }
  24. };
  25. channel.basicConsume(queue, true, consumer);
  26. }
  27. }

Recv2:

  1. package com.xxyh.rabbitmq;
  2. import com.rabbitmq.client.*;
  3. import java.io.IOException;
  4. import java.util.concurrent.TimeoutException;
  5. public class ReceiveLogsTopicForError {
  6. private static final String EXCHANGE_NAME = "topic_logs";
  7. public static void main(String[] args) throws IOException, TimeoutException {
  8. ConnectionFactory factory = new ConnectionFactory();
  9. factory.setHost("localhost");
  10. Connection connection = factory.newConnection();
  11. Channel channel = connection.createChannel();
  12. channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
  13. String queue = channel.queueDeclare().getQueue();
  14. // 接收所有error的消息
  15. channel.queueBind(queue, EXCHANGE_NAME, "*.error");
  16. System.out.println("准备接收error的消息----------------");
  17. final Consumer consumer = new DefaultConsumer(channel) {
  18. @Override
  19. public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
  20. String message = new String(body, "utf-8");
  21. System.out.println(Thread.currentThread().getName() + " 接收到消息: " + message);
  22. }
  23. };
  24. channel.basicConsume(queue, true, consumer);
  25. }
  26. }

接下来先运行两个接收端。运行结果如下:

运行发送端:

  1. main 发送消息: 32292290-9775-4730-901a-f05272d2e242
  2. main 发送消息: 2c4db38f-95c0-47b7-918f-b796b0ce1b33
  3. main 发送消息: a8997aab-19f2-4b44-86a1-2721a87cc69d
  4. main 发送消息: 2fa77a78-a06b-4015-9814-9167d5c727ac

Recv1:

  1. 准备接收所有zhang发出的消息----------------
  2. pool-1-thread-4 接收到消息: 32292290-9775-4730-901a-f05272d2e242
  3. pool-1-thread-5 接收到消息: 2fa77a78-a06b-4015-9814-9167d5c727ac

Recv2:

  1. 准备接收error的消息----------------
  2. pool-1-thread-4 接收到消息: 2fa77a78-a06b-4015-9814-9167d5c727ac

RabbitMQ入门(5)——主题(Topic)的更多相关文章

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

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

  2. (转)RabbitMQ学习之主题topic(java)

    http://blog.csdn.net/zhu_tianwei/article/details/40887775 参考:http://blog.csdn.NET/lmj623565791/artic ...

  3. RabbitMQ入门_07_Fanout 与 Topic

    A. 用广播的方式实现发布订阅 参考资料:https://www.rabbitmq.com/tutorials/tutorial-three-java.html Fanout 类型的 Exchange ...

  4. RabbitMQ入门:总结

    随着上一篇博文的发布,RabbitMQ的基础内容我也学习完了,RabbitMQ入门系列的博客跟着收官了,以后有机会的话再写一些在实战中的应用分享,多谢大家一直以来的支持和认可. RabbitMQ入门系 ...

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

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

  6. RabbitMQ入门-Topic模式

    上篇<RabbitMQ入门-Routing直连模式>我们介绍了可以定向发送消息,并可以根据自定义规则派发消息.看起来,这个Routing模式已经算灵活的了,但是,这还不够,我们还有更加多样 ...

  7. (八)RabbitMQ消息队列-通过Topic主题模式分发消息

    原文:(八)RabbitMQ消息队列-通过Topic主题模式分发消息 前两章我们讲了RabbitMQ的direct模式和fanout模式,本章介绍topic主题模式的应用.如果对direct模式下通过 ...

  8. 转载RabbitMQ入门(5)--主题

    主题(topic) (使用Java客户端) 在先前的指南中我们改进了我们的日志系统.取代使用fanout类型的交易所,那个仅仅有能力实现哑的广播,我们使用一个direct类型的交易所,获得一个可以有选 ...

  9. RabbitMQ入门与使用篇

    介绍 RabbitMQ是一个由erlang开发的基于AMQP(Advanced Message Queue)协议的开源实现.用于在分布式系统中存储转发消息,在易用性.扩展性.高可用性等方面都非常的优秀 ...

随机推荐

  1. Xshell 连接虚拟机特别慢 解决方案

    由于各种原因,xshell连接虚拟机的rhel或者CentOS都几乎是龟速...... 今天专门查了一下解决方案: 原来是ssh的服务端在连接时会自动检测dns环境是否一致导致的,修改为不检测即可,操 ...

  2. java7(1)——反编译深入理解增强的switch(读字节命令实战)

    [本文介绍] 本文主要讲java_7 的改进switch的底层实现.反编译一个使用带String的switch的demo并一步步解析反编译出来的字节命令,从编译的角度解读switch的底层实现. [正 ...

  3. SyntaxError: Missing parentheses in call to 'print'. Did you mean print('XXXXXX')?

    因为Python3中取消了以前Python 2中的语法. 所以Python 3再使用python2的语法格式就会报错 错误信息中提示需要加上括号,字符串可以用单引号或双引号括起来 这样就不会报错了.

  4. 为什么使用Sails?

    http://sailsdoc.swift.ren/ 这里有 sails中文文档 http://www.jianshu.com/p/ac2da4142259 前言 入手Node.js半年,从用Expr ...

  5. HDFS集中式的缓存管理原理与代码剖析

    转载自:http://www.infoq.com/cn/articles/hdfs-centralized-cache/ HDFS集中式的缓存管理原理与代码剖析 Hadoop 2.3.0已经发布了,其 ...

  6. PHP用"字符串和变量"组成变量

    理论很简单,将字符串和变量组合在一起形成对另一个变量的操作.同样也可以是数组 例:   $FFabcd = '组合变量'; $a = 'abcd'; $ay = array('FF','abcd'); ...

  7. Parallel Programming-实现并行操作的流水线(生产者、消费者)

    本文介绍如何使用C#实现并行执行的流水线(生产者消费者): 1.流水线示意图 2.实现并行流水线 一.流水线示意图 上图演示了流水线,action1接收input,然后产生结果保存在buffer1中, ...

  8. win7 64bits下编译libjpeg库

    一.下载源代码.下载地址:http://www.ijg.org/.注意:一定要下载win32 版本二.编译源代码.       1.解压源代码,(不需要修改,修改报错)修改源代码中jconfig.vc ...

  9. Linux系统——磁盘管理

    磁盘结构 (1)硬盘的物理结构 磁头:每面一个磁盘 盘片:硬盘有多个盘片,每个盘片2面 (2)硬盘的数据结构 扇区:盘片被分为多个扇形区域,每个扇形区存放512字节的数据 磁道:统一盘片不同半径的同心 ...

  10. fake-useragent,python爬虫伪装请求头

    在编写爬虫进行网页数据的时候,大多数情况下,需要在请求是增加请求头,下面介绍一个python下非常好用的伪装请求头的库:fake-useragent,具体使用说明如下: 1.在scrapy中的使用 第 ...