译:5.RabbitMQ Java Client 之 Topics (主题)
在 上篇博文 译:4.RabbitMQ 之Routing(路由) 中,我们改进了日志系统。
我们使用的是direct(直接交换),而不是使用只能进行虚拟广播的 fanout(扇出交换) ,并且有可能选择性地接收日志。
虽然使用direct(直接交换)改进了我们的系统,但它仍然有局限性 - 它不能基于多个标准进行路由。
在我们的日志系统中,我们可能不仅要根据严重性订阅日志,还要根据发出日志的源来订阅日志。您可能从syslog unix工具中了解这个概念,该 工具根据严重性(info / warn / crit ...)和facility(auth / cron / kern ...)来路由日志。
这会给我们带来很大的灵活性 - 我们可能想听听来自'cron'的关键错误以及来自'kern'的所有日志。
要在我们的日志系统中实现这一点,我们需要了解更复杂的topic (主题交换)。
本篇为译文,英文原文请移步:https://www.rabbitmq.com/tutorials/tutorial-five-java.html
Topic exchange 主题交换
发送到主题交换的消息不能具有任意 routing_key - 它必须是由点分隔的单词列表。单词可以是任何内容,但通常它们指定与消息相关的一些功能。一些有效的路由密钥示例:“ stock.usd.nyse ”,“ nyse.vmw ”,“ quick.orange.rabbit ”。路由密钥中可以包含任意数量的单词,最多可达255个字节。
绑定密钥也必须采用相同的形式。主题交换背后的逻辑 类似于直接交换- 使用特定路由密钥发送的消息将被传递到与匹配绑定密钥绑定的所有队列。但是,绑定键有两个重要的特殊情况:
- *(星号)可以替代一个单词。
- #(hash)可以替换零个或多个单词。
在一个例子中解释这个是最容易的:

在这个例子中,我们将发送所有描述动物的消息。消息将与包含三个单词(两个点)的路由键一起发送。
路由键中的第一个单词将描述速度,第二个是颜色,第三个是物种:“ <speed>。<color>。<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 ”或“ quick.orange.male.rabbit”,会发生什么?好吧,这些消息将不匹配任何绑定,将丢失。
另一方面,“ lazy.orange.male.rabbit ”,即使它有四个单词,也会匹配最后一个绑定,并将被传递到第二个队列。
主题交流
主题交换功能强大,可以像其他交易所一样。
当队列与“ # ”(哈希)绑定密钥绑定时 - 它将接收所有消息,而不管路由密钥 - 如扇出交换。
当特殊字符“ * ”(星号)和“ # ”(哈希)未在绑定中使用时,主题交换的行为就像直接交换一样
把它们放在一起
我们将在日志记录系统中使用主题交换。我们将首先假设日志的路由键有两个词:“ <facility>。<severity> ”。
EmitLogTopic.java
import com.rabbitmq.client.BuiltinExchangeType;
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) {
Connection connection = null;
Channel channel = null;
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); connection = factory.newConnection();
channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC); String routingKey = getRouting(argv);
String message = getMessage(argv); channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'"); } catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (Exception ignore) {
}
}
}
} private static String getRouting(String[] strings) {
if (strings.length < 1)
return "anonymous.info";
return strings[0];
} private static String getMessage(String[] strings) {
if (strings.length < 2)
return "Hello World!";
return joinStrings(strings, " ", 1);
} private static String joinStrings(String[] strings, String delimiter, int startIndex) {
int length = strings.length;
if (length == 0)
return "";
if (length < startIndex)
return "";
StringBuilder words = new StringBuilder(strings[startIndex]);
for (int i = startIndex + 1; i < length; i++) {
words.append(delimiter).append(strings[i]);
}
return words.toString();
}
}
ReceiveLogsTopic.java
import java.io.IOException; import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope; public class ReceiveLogsTopic {
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, BuiltinExchangeType.TOPIC);
String queueName = channel.queueDeclare().getQueue(); if (argv.length < 1) {
System.err.println("Usage: ReceiveLogsTopic [binding_key]...");
System.exit(1);
} for (String bindingKey : argv) {
channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
} System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 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);
}
}
译:5.RabbitMQ Java Client 之 Topics (主题)的更多相关文章
- 译:1. RabbitMQ Java Client 之 "Hello World"
这些教程介绍了使用RabbitMQ创建消息传递应用程序的基础知识.您需要安装RabbitMQ服务器才能完成教程 1. 打造第一个Hello World 程序 RabbitMQ是一个消息代理:它接受和转 ...
- 译:4.RabbitMQ Java Client 之 Routing(路由)
在上篇博文 译:3.RabbitMQ 之Publish/Subscribe(发布和订阅) 我们构建了一个简单的日志系统 我们能够向许多接收者广播日志消息. 在本篇博文中,我们将为其添加一个功能 - ...
- 译:6.RabbitMQ Java Client 之 Remote procedure call (RPC,远程过程调用)
在 译:2. RabbitMQ 之Work Queues (工作队列) 我们学习了如何使用工作队列在多个工作人员之间分配耗时的任务. 但是如果我们需要在远程计算机上运行一个函数并等待结果呢?嗯,这 ...
- 译:2. RabbitMQ Java Client 之 Work Queues (工作队列)
在上篇揭开RabbitMQ的神秘面纱一文中,我们编写了程序来发送和接收来自命名队列的消息. 本篇我们将创建一个工作队列,工作队列背后的假设是每个任务都交付给一个工作者 本篇是译文,英文原文请移步:ht ...
- 译:3.RabbitMQ Java Client 之 Publish/Subscribe(发布和订阅)
在上篇 RabbitMQ 之Work Queues (工作队列)教程中,我们创建了一个工作队列,工作队列背后的假设是每个任务都交付给一个工作者. 在这一部分,我们将做一些完全不同的事情 - 我们将向多 ...
- 五、RabbitMQ Java Client基本使用详解
Java Client的5.x版本系列需要JDK 8,用于编译和运行.在Android上,仅支持Android 7.0或更高版本.4.x版本系列支持7.0之前的JDK 6和Android版本. 加入R ...
- rabbitmq - java client lib一二事
由于不可抗因素, 需要给对接方撸一个client的demo.基于比较老的jdk. 所幸找到了这里:http://www.rabbitmq.com/releases/rabbitmq-java-clie ...
- 译: 5. RabbitMQ Spring AMQP 之 Topic 主题
在上一个教程中,我们提高了消息传递的灵活 我们使用direct交换而不是使用仅能够进行虚拟广播的fanout交换, 并且获得了基于路由key 有选择地接收消息的可能性. 虽然使用direct 交换改进 ...
- RabbitMQ的使用(五)RabbitMQ Java Client简单生产者、消费者代码示例
pom文件: <dependencies> <dependency> <groupId>com.rabbitmq</groupId> <artif ...
随机推荐
- POJ1487 Single-Player Games 高斯消元
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ1487 题解概括 给出多个树形结构,由小写字母和数字表示,每个小写字母表示一棵小树.现在,以a为根节点 ...
- Unity 之 添加背景音乐 以及 Slider控制
游戏音频分为背景音乐与环境音乐两种.Audio Clip(音频剪辑)有四种音乐格式.MP3:适合较长音频,作为背景音乐.Ogg:适合较长音频,作为背景音乐.Wav:适合较短音频,作为环境音乐.Ai ...
- hdu 2005 求第几天(水题)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2005 转载于:https://blog.csdn.net/tigerisland45/article/ ...
- Angular 个人深究(三)【由Input&Output引起的】
Angular 个人深究(三)[由Input&Output引起的] 注:最近项目在做别的事情,angular学习停滞了 1.Angular 中 @Input与@Output的使用 //test ...
- vue+axios实现移动端图片上传
在利用vue做一些H5页面时,或多或少会遇到有图片上传的操作,主要是运用html5里面的input[type=file]来实现,传递到后端的数据是以二进制的格式传递,所以上传图片的请求与普通的请求稍微 ...
- NTFS的交换数据流ADS应用
NTFS的交换数据流ADS应用 NTFS是Windows常用的文件系统格式.该格式支持交换数据流(Alternate Data Streams,缩写ADS)特性.该特性可以让多个文件流使用同一个文 ...
- MySQL中查询获取每个班级成绩前三名的学生信息
CREATE TABLE t_testscore( pk_id INT PRIMARY KEY, c_name VARCHAR(50) , c_score INT, c_class INT )DEFA ...
- Open Source Book For ML
The following is a list of free, open source books on machine learning, statistics, data-mining, etc ...
- SimpleDateFormat 格式化参数说明
字母 日期或时间元素 表示 示例 G Era 标志符 Text AD y 年 Year 1996; 96 M 年中的月份 Month July; Jul; 07 w 年中的周数 Number 27 W ...
- 微信小程序 —— 动态决定页面元素显示或隐藏的技巧
在微信小程序开发中,经常遇到一些由后台控制显示(is_open : 1)或者隐藏(is_open : 0),有俩种办法: 复杂办法 1.先在元素的class中 class=’{{show?’true’ ...