RabbitMQ入门(5)——主题(Topic)
前面我们介绍了通过使用direct exchage,改善了fanout exchange只能进行虚拟广播的方式。尽管如此,直接交换也有自身的局限,它不能基于多个条件路由。
在我们的日志系统中,也许我们希望不仅要根据严重程度,而且要基于发送日志的源订阅日志。为了实现这个功能,我们需要学习更复杂的主题交换(topic exchange)。
主题交换(Topic exchange)
发送到主题交换机的消息不能随意设置路由键。它必须是由点分隔的一系列标识符。理论上可以是任何词,但最好见名知义,例如:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit",标识符最大长度为255个字节。
绑定键和路由键形式是一样的。主题类型的交换机背后的逻辑和直接类型的也很相似,使用特定的路由键发送的消息将被发送到绑定绑定键的所有队列中。
绑定键有两种特殊的情况:
*:匹配一个标识符
#:匹配0或多个标识符
图示如下:

在本例中,我们准备发送描述动物的消息,消息会附加一个路由键包含三个标识符。依次为速度、颜色、物种。
创建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的消息,虽然它有四个标识符,但是匹配了最后一个绑定,将会发送到Q2。
代码清单
Send:
package com.xxyh.rabbitmq;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
public class EmitLogsTopic {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 声明 topic类型的交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
String[] routingKeys = new String[]{"zhang.info", "li.warning", "wang.info", "zhang.error"};
for (String routingKey : routingKeys) {
String message = UUID.randomUUID().toString();
channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("utf-8"));
System.out.println(Thread.currentThread().getName() + " 发送消息: " + message);
}
channel.close();
connection.close();
}
}
Recv1:
package com.xxyh.rabbitmq;
import com.rabbitmq.client.*;
import sun.java2d.loops.TransformHelper;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ReceiveLogsTopic {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
String queue = channel.queueDeclare().getQueue();
// 接收所有zhang发出的消息
channel.queueBind(queue, EXCHANGE_NAME, "zhang.*");
System.out.println("准备接收所有zhang发出的消息----------------");
final 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(Thread.currentThread().getName() + " 接收到消息: " + message);
}
};
channel.basicConsume(queue, true, consumer);
}
}
Recv2:
package com.xxyh.rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ReceiveLogsTopicForError {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
String queue = channel.queueDeclare().getQueue();
// 接收所有error的消息
channel.queueBind(queue, EXCHANGE_NAME, "*.error");
System.out.println("准备接收error的消息----------------");
final 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(Thread.currentThread().getName() + " 接收到消息: " + message);
}
};
channel.basicConsume(queue, true, consumer);
}
}
接下来先运行两个接收端。运行结果如下:
运行发送端:
main 发送消息: 32292290-9775-4730-901a-f05272d2e242
main 发送消息: 2c4db38f-95c0-47b7-918f-b796b0ce1b33
main 发送消息: a8997aab-19f2-4b44-86a1-2721a87cc69d
main 发送消息: 2fa77a78-a06b-4015-9814-9167d5c727ac
Recv1:
准备接收所有zhang发出的消息----------------
pool-1-thread-4 接收到消息: 32292290-9775-4730-901a-f05272d2e242
pool-1-thread-5 接收到消息: 2fa77a78-a06b-4015-9814-9167d5c727ac
Recv2:
准备接收error的消息----------------
pool-1-thread-4 接收到消息: 2fa77a78-a06b-4015-9814-9167d5c727ac
RabbitMQ入门(5)——主题(Topic)的更多相关文章
- RabbitMQ入门:主题路由器(Topic Exchange)
上一篇博文中,我们使用direct exchange 代替了fanout exchange,这次我们来看下topic exchange. 一.Topic Exchange介绍 topic exchan ...
- (转)RabbitMQ学习之主题topic(java)
http://blog.csdn.net/zhu_tianwei/article/details/40887775 参考:http://blog.csdn.NET/lmj623565791/artic ...
- RabbitMQ入门_07_Fanout 与 Topic
A. 用广播的方式实现发布订阅 参考资料:https://www.rabbitmq.com/tutorials/tutorial-three-java.html Fanout 类型的 Exchange ...
- RabbitMQ入门:总结
随着上一篇博文的发布,RabbitMQ的基础内容我也学习完了,RabbitMQ入门系列的博客跟着收官了,以后有机会的话再写一些在实战中的应用分享,多谢大家一直以来的支持和认可. RabbitMQ入门系 ...
- RabbitMQ入门教程(七):主题交换机Topics
原文:RabbitMQ入门教程(七):主题交换机Topics 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...
- RabbitMQ入门-Topic模式
上篇<RabbitMQ入门-Routing直连模式>我们介绍了可以定向发送消息,并可以根据自定义规则派发消息.看起来,这个Routing模式已经算灵活的了,但是,这还不够,我们还有更加多样 ...
- (八)RabbitMQ消息队列-通过Topic主题模式分发消息
原文:(八)RabbitMQ消息队列-通过Topic主题模式分发消息 前两章我们讲了RabbitMQ的direct模式和fanout模式,本章介绍topic主题模式的应用.如果对direct模式下通过 ...
- 转载RabbitMQ入门(5)--主题
主题(topic) (使用Java客户端) 在先前的指南中我们改进了我们的日志系统.取代使用fanout类型的交易所,那个仅仅有能力实现哑的广播,我们使用一个direct类型的交易所,获得一个可以有选 ...
- RabbitMQ入门与使用篇
介绍 RabbitMQ是一个由erlang开发的基于AMQP(Advanced Message Queue)协议的开源实现.用于在分布式系统中存储转发消息,在易用性.扩展性.高可用性等方面都非常的优秀 ...
随机推荐
- virtio后端驱动详解
2016-10-08 virtIO是一种半虚拟化驱动,广泛用于在XEN平台和KVM虚拟化平台,用于提高客户机IO的效率,事实证明,virtIO极大的提高了VM IO 效率,配备virtIO前后端驱动的 ...
- CListCtrl控件使用方法总结
今天第一次用CListCtrl控件,遇到不少问题,查了许多资料,现将用到的一些东西总结如下: 以下未经说明,listctrl默认view 风格为report 相关类及处理函数 MFC:CListCtr ...
- 关于编译openjdk过程的一个编码错误
-Xlint:deprecation 重新编译 或者 错误: 编码ascii的不可映射字符 出现这种错误的时候 可以进行设置jdk编译编码即可解决 因为是编译openjdk 所以在环境变量中设置: ...
- request.getInputStream() 流只能读取一次问题
问题: 一次开发过程中同事在 sptring interceptor 中获取 request body 中值,以对数据的校验和预处理等操作 .导致之后spring 在读取request body 值做 ...
- MongoDB简单CRUD场景
MongoDB简单CRUD命令操作 (1)新建数据库:use 数据库名 (2)显示所有数据库:show dbs; (3)新建集合(两种方式) 隐式创建:在创建集合的同时往集合里面添加数据---db. ...
- 20165324《Java程序设计》第七周
20165324<Java程序设计>第七周 教材学习内容总结 第11章 JDBC与MySOLz数据库 MySQL数据库管理系统,简称MySQL. 使用步骤: 启动MySQL数据库服务 器建 ...
- C++基础之头文件和源文件的关系
今天找了个解析xml的开源C++项目tinyxml,按照网上的说法去编译,但是一直编译不通过,"无法打开头文件tinyxml.h",但是明明我在工程底下有了这个文件,对于我这种初学 ...
- 手把手教 GitHub + Hexo 搭建博客
前言 在很久以前,博主就想着要有自主的博客专栏或者网站.经历了博客园这个需要所谓的编辑审核,一直比较困惑,这些编辑是什么出身,怎么知道技术博客的价值性. 接下来找到了开源中国,这个可以自由发言的地方. ...
- Debian中安装使用sudo命令
Debian中安装使用sudo命令 sudo可以让非root用户具有管理员的权限,安装好的Debian后还不能使用sudo,需要使用root用户登陆后安装sudo命令.#apt-get insta ...
- web前端攻城狮整理的收藏夹
作为一名web前端开发工程师你的收藏夹存对了吗?下面是一份互联网上流传甚广的web前端开发收藏夹资源,包含学习网站.JS库.常用工具.常用插件.资讯书籍等资源.速速转存吧~ 一.学习网站 W3 ...