rabbitmq系列五 之主题交换机
1、主题
在前面的例子中,我们对日志系统进行了改进。使用了direct交换机代替了fanout交换机,从只能盲目的广播消息改进为有可能选择性的接收日志。
尽管直接交换机能够改善我们的日志系统,但是它也有它的限制——没办法基于多个标准执行路由操作。
在我们的日志系统中,我们不只希望订阅基于日志级别,同时还希望订阅基于日志来源。其中unix工具syslog是同时基于日志的级别(info/warn/error)和设备-facility (auth/cron/kern...)来路由日志的。
如果这样的话,将会给予我们非常大的灵活性,我们既可以监听来源于“cron”的严重程度为“critical errors”的日志,也可以监听来源于“kern”的所有日志。
为了实现这个目的,接下来我们学习如何使用另一种更复杂的交换机 —— 主题交换机。
2、主题交换机
发送到主题交换机(topic exchange)的消息不可以携带随意什么样子的路由键(routing_key),它的路由键必须是一个由.分隔开的词语列表。这些单词随便是什么都可以,但是最好是跟携带它们的消息有关系的词汇。以下是几个推荐的例子:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit"。词语的个数可以随意,但是不要超过255字节。
绑定键也必须拥有同样的格式。主题交换机背后的逻辑跟直连交换机很相似 —— 一个携带着特定路由键的消息会被主题交换机投递给绑定键与之想匹配的队列。但是它的绑定键和路由键有两个特殊应用方式:
* (星号) 用来表示一个单词.
# (井号) 用来表示任意数量(零个或多个)单词
下边用图说明:

这个例子里,我们发送的所有消息都是用来描述小动物的。发送的消息所携带的路由键是由三个单词所组成的,这三个单词被两个.分割开。路由键里的第一个单词描述的是动物的手脚的利索程度,第二个单词是动物的颜色,第三个是动物的种类。所以它看起来是这样的: <celerity>.<colour>.<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" or "quick.orange.male.rabbit")的消息时,发送的消息不会投递给任何一个队列,而且会丢失掉。
但是另一方面,即使 "lazy.orange.male.rabbit" 有四个单词,他还是会匹配最后一个绑定,并且被投递到第二个队列中。
注意:
主题交换机是很强大的,它可以表现出跟其他交换机类似的行为
当一个队列的绑定键为 "#"(井号) 的时候,这个队列将会无视消息的路由键,接收所有的消息。
当 * (星号) 和 # (井号) 这两个特殊字符都未在绑定键中出现的时候,此时主题交换机就拥有的直连交换机的行为。
3、组合在一起
接下来我们会将主题交换机应用到我们的日志系统中。在开始工作前,我们假设日志的路由键由两个单词组成,路由键看起来是这样的:<facility>.<severity>。
代码跟上一篇教程差不多。只是参数和交换机类型不一样。
EmitLogTopic.java代码如下:
package rabbitmq.topic; import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection; import rabbitmq.utils.ConnectionUtils; /*
* 发送错误日志生产者
*/
public class EmitLogTopic {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//创建管道
Channel channel = connection.createChannel();
//创建直接交换器
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//获取日志级别
String severity = getSeverity(args);
//获取消息
String message = getMessage(args);
//发送消息
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
channel.close();
connection.close();
}
}
ReceiveLogsTopic.java代码如下:
package rabbitmq.topic; import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope; import rabbitmq.utils.ConnectionUtils; public class ReceiveLogsTopic {
private static final String EXCHANGE_NAME = "topic_logs"; public static void main(String[] args) throws IOException, TimeoutException {
// 获取连接
Connection connection = ConnectionUtils.getConnection();
// 创建管道
Channel channel = connection.createChannel();
// 创建直接交换器
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String queueName = channel.queueDeclare().getQueue();
for (String severity : args) {
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}
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);
}
}
ReceiveLogsTopic.java分别启动四次,参数分别为"#"、"kern.*"、"*.critical"、"kern.*" "*.critical"。
启动EmitLogTopic.java,参数为"kern.critical" "A critical kernel error",其中routing_key=“kern.critical”,那么绑定键为"#"、"kern.*"、"*.critical"都会收到日志信息
rabbitmq系列五 之主题交换机的更多相关文章
- RabbitMQ (五)主题(Topic) -摘自网络
虽然使用direct类型改良了我们的系统,但是仍然存在一些局限性:它不能够基于多重条件进行路由选择. 在我们的日志系统中,我们有可能希望不仅根据日志的级别而且想根据日志的来源进行订阅.这个概念类似un ...
- RabbitMQ (五)主题(Topic)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37706355 上一篇博客中,我们进步改良了我们的日志系统.我们使用direct类 ...
- Go RabbitMQ(五)主题
RabbitMQ topic 在之前我们将交换器的类型从fanout设置为direct后能够根据我们的选择获得响应的消息,虽然改良我们的消息日志系统,但是还有很多局限性,比如它不能基于多个标准进行路由 ...
- RabbitMQ系列(五)--高级特性
在上一篇文章讲解MQ消息可靠性投递和幂等性中有提到confirm机制的重要性,现在更相信的说明一下 一.Confirm机制 Confirm就是消息确认,当Producer发送消息,如果Broker收到 ...
- rabbitmq系列五 之远程过程调用(RPC)
1.远程过程调用(RPC) 在第二篇教程中我们介绍了如何使用工作队列(work queue)在多个工作者(woker)中间分发耗时的任务. 可是如果我们需要将一个函数运行在远程计算机上并且等待从那儿获 ...
- 7.RabbitMQ系列之topic主题交换器
topic主题交换器它根据在队列绑定的路由键和路由模式通配符匹配将消息路由到队列. 生产者在消息头中添加路由键并将其发送到主题交换器. 收到消息后,exchange尝试将路由键与绑定到它的所有队列的绑 ...
- RabbitMQ系列教程之五:主题(Topic)(转载)
RabbitMQ系列教程之五:主题(Topic) (本实例都是使用的Net的客户端,使用C#编写),说明,中文方括号[]表示名词. 在上一个教程中,我们改进了我们的日志记录系统. 没有使用只能够进行虚 ...
- rabbitmq学习(三):rabbitmq之扇形交换机、主题交换机
前言 上篇我们学习了rabbitmq的作用以及直连交换机的代码实现,这篇我们继续看如何用代码实现扇形交换机和主题交换机 一.扇形交换机 1.生产者 /** * 生产者 */ public class ...
- RabbitMQ入门教程(七):主题交换机Topics
原文:RabbitMQ入门教程(七):主题交换机Topics 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...
随机推荐
- 菜品识别 SDK调用
from aip import AipImageClassify import os """ 填入参数 """ APP_ID = 'your ...
- C++STL queue
queue队列 先进先出 queue<int> q1; q1.push();//插入元素 q1.front();//求队头元素 q1.pop();//删除队头元素 q1.empty();/ ...
- 微信小程序的新的
app.request.get('http://ele.kassing.cn/v1/pois',this.data.city).then(res=>{ console.log(res) this ...
- 第31章:MongoDB-索引--复合索引
①复合索引 对多个字段创建索引被称为复合索引或者组合索引 ②创建组合索引 创建组合索引(以后台模式创建) db.user.ensureIndex({"username": 1, & ...
- 音频管理器(AudioManager)
MainActivity.java package com.wwj.serviceandboardcast; import android.app.Activity; import android ...
- php 微信登录 公众号 获取用户信息 微信网页授权
php 微信登录 公众号 获取用户信息 微信网页授权 先自己建立两个文件: index.php 和 getUserInfo.php index.php <?php //scope=snsap ...
- Spring通过注解配置Bean
@Component: 基本注解, 标识了一个受 Spring 管理的组件@Repository: 标识持久层组件@Service: 标识服务层(业务层)组件@Controller: 标识表现层组件 ...
- 2017-11-29 由runnable说起Android中的子线程和主线程
1.首先纠正一个观点,就是runnable运行在子线程中是错误的观念.runnable只是创建了一个执行任务的对象,但是它本身并不会创建一个新的子线程,Runable只是给你接口让你实现工作线程的工作 ...
- spoj high
matrixtree定理裸体,学了行列式的n^3解法,(应该是能应用于所有行列式): 代码是参考某篇题解的... #include<iostream> #include<cstrin ...
- redis之hello
1.创建一个maven工程 2.进入redis官网 https://github.com/xetorthio/jedis 3.找到 <!--导入到pom.xml文件中--><depe ...