轻松搞定RabbitMQ(六)——主题
转自 http://blog.csdn.net/xiaoxian8023/article/details/48806871
翻译地址:http://www.rabbitmq.com/tutorials/tutorial-five-java.html
在上一篇博文中,我们进一步改良了日志系统。使用Direct类型的转换器,使得接收者有能力进行选择性的接收日志,,而非fanout那样,只能够无脑的转发,如果你还不了解,请阅读:轻松搞定RabbitMQ(四)——发布/订阅。
虽然使用Direct类型的转换器改进了日志系统。但它仍然有一定的局限性——不能根据多重条件进行路由选择。
在我们的日志系统中,我们可能不仅仅根据日志严重性订阅日志,也想根据发送源订阅。你可能从unix工具syslog了解过这个概念,它可以根据严重性(info/warning/crit…)和设备(auth/cron/kern…)转发日志。
这将给我们更多的灵活性——我们可以订阅来自元‘cron’的致命错误日志,同时也可以订阅‘kern’的所有日志。
为了在我们的日志系统中实现上述需求,我们需要了解更复杂的主题类型的转发器——Topic Exchange。
Topic exchange(主题转发器)
发送给主题转发器的消息不能是任意设置的选择键,必须是用小数点隔开的一系列的标识符。这些标识符可以是随意,但是通常跟消息的某些特性相关联。一些合法的路由选择键比如“socket.usd.nyse”,"nyse.vmw","quick.orange.rabbit",你愿意用多少单词都可以,只要不超过上限的255个字节。
绑定键也必须以相同的格式。主题转发器的逻辑类似于direct类型的转发器。消息通过一个特定的路由键发送到所有与绑定键匹配的队列中。需要注意的是,关于绑定键有两种特殊的情况:*(星号)可以代替任意一个标识符 ;#(井号)可以代替零个或多个标识符。
举一个简单例子,如下图:
在上图例子中,我们发送描述动物的消息。消息会转发给包含3个单词(2个小数点)的路由键绑定的队列中。绑定键中的第一个单词描述的是速度,第二个是颜色,第三个是物种:“速度.颜色.物种”。
我们创建3个绑定:Q1绑定键是“*.orange.*”,Q2绑定键是“*.*.rabbit”,Q3绑定键是“lazy.#”。这些绑定可以概括为:Q1只对橙色的动物感兴趣。Q2则是关注兔子和所有懒的动物。
路由键为“quick.orange.rabbit”的消息会被路由到2个队列中去。而“lazy.orange.elephant”的消息同样会发往2个队列。另外“quick.orange.fox”
仅仅发往第一个队列,而"lazy.brown.fox"则只发往第二个队列。“quick.brown.fox”则所有的绑定键都不匹配而被丢弃。
如果我们违反约定,发送了只带1个或者4个标识符的选择键,像“orange”或者“quick.orange.male.rabbit”,会发生什么呢?这些消息都不匹配任何绑定,所以将被丢弃。
另外,“lazy.orange.male.rabbit”,尽管有4个标识符,但是仍然匹配最后一个绑定键,所以会发送到第二个队列中。
注:主题类型的转发器非常强大,可以实现其他类型的转发器。当队列绑定#绑定键,可以接受任何消息,类似于fanout转发器。当特殊字符*和#不包含在绑定键中,这个主题转发器就像一个direct类型的转发器。
完整实例
我们将主题类型的转发器应用到日志系统中,路由格式为:“<设备>.<严重级别>”。
发送端(EmitLogTopic.java)
- public class EmitLogDirect {
- private final static String EXCHANGE_NAME = "topic_logs";
- public static void main(String[] args) throws IOException {
- /**
- * 创建连接连接到MabbitMQ
- */
- ConnectionFactory factory = new ConnectionFactory();
- // 设置MabbitMQ所在主机ip或者主机名
- factory.setHost("127.0.0.1");
- // 创建一个连接
- Connection connection = factory.newConnection();
- // 创建一个频道
- Channel channel = connection.createChannel();
- // 指定转发——广播
- channel.exchangeDeclare(EXCHANGE_NAME, "topic");
- //所有设备和日志级别
- String[] facilities ={"auth","cron","kern","auth.A"};
- String[] severities={"error","info","warning"};
- for(int i=0;i<4;i++){
- for(int j=0;j<3;j++){
- //每一个设备,每种日志级别发送一条日志消息
- String routingKey = facilities[i]+"."+severities[j%3];
- // 发送的消息
- String message =" Hello World!"+Strings.repeat(".", i+1);
- //参数1:exchange name
- //参数2:routing key
- channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());
- System.out.println(" [x] Sent [" + routingKey +"] : '"+ message + "'");
- }
- }
- // 关闭频道和连接
- channel.close();
- connection.close();
- }
- }
消费者1(ReceiveLogs2Console.java)
- public class ReceiveLogs2Console {
- private static final String EXCHANGE_NAME = "topic_logs";
- public static void main(String[] argv) throws IOException, InterruptedException {
- ConnectionFactory factory = new ConnectionFactory();
- factory.setHost("127.0.0.1");
- // 打开连接和创建频道,与发送端一样
- Connection connection = factory.newConnection();
- final Channel channel = connection.createChannel();
- //声明exchange
- channel.exchangeDeclare(EXCHANGE_NAME, "topic");
- // 声明一个随机队列
- String queueName = channel.queueDeclare().getQueue();
- String[] routingKeys ={"auth.*","*.info","#.warning"};//关注所有的授权日志、所有info和waring级别的日志
- for (String routingKey : routingKeys) {
- //关注所有级别的日志(多重绑定)
- channel.queueBind(queueName, EXCHANGE_NAME, routingKey);
- }
- System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
- // 创建队列消费者
- 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(" [x] Received [" + envelope.getRoutingKey() + "] :'" + message + "'");
- }
- };
- channel.basicConsume(queueName, true, consumer);
- }
- }
消费者2(ReceiveLogs2File.java)
- public class ReceiveLogs2File {
- private static final String EXCHANGE_NAME = "topic_logs";
- public static void main(String[] argv) throws IOException, InterruptedException {
- ConnectionFactory factory = new ConnectionFactory();
- factory.setHost("127.0.0.1");
- // 打开连接和创建频道,与发送端一样
- Connection connection = factory.newConnection();
- final Channel channel = connection.createChannel();
- channel.exchangeDeclare(EXCHANGE_NAME, "topic");
- // 声明一个随机队列
- String queueName = channel.queueDeclare().getQueue();
- channel.queueBind(queueName, EXCHANGE_NAME, "");
- String severity="kern.error";//只关注核心错误级别的日志,然后记录到文件中去。
- channel.queueBind(queueName, EXCHANGE_NAME, severity);
- System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
- // 创建队列消费者
- 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");
- //记录日志到文件:
- print2File( "["+ envelope.getRoutingKey() + "] "+message);
- }
- };
- channel.basicConsume(queueName, true, consumer);
- }
- private static void print2File(String msg) {
- try {
- String dir = ReceiveLogs2File.class.getClassLoader().getResource("").getPath();
- String logFileName = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
- File file = new File(dir, logFileName + ".log");
- FileOutputStream fos = new FileOutputStream(file, true);
- fos.write((new SimpleDateFormat("HH:mm:ss").format(new Date())+" - "+msg + "\r\n").getBytes());
- fos.flush();
- fos.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
最终结果:
轻松搞定RabbitMQ(六)——主题的更多相关文章
- 轻松搞定RabbitMQ(五)——路由选择
转自 http://blog.csdn.net/xiaoxian8023/article/details/48733249 翻译地址:http://www.rabbitmq.com/tutorials ...
- 轻松搞定RabbitMQ(二)——工作队列之消息分发机制
转自 http://blog.csdn.net/xiaoxian8023/article/details/48681987 上一篇博文中简单介绍了一下RabbitMQ的基础知识,并写了一个经典语言入门 ...
- 轻松搞定RabbitMQ(一)——RabbitMQ基础知识+HelloWorld
转自 http://blog.csdn.net/xiaoxian8023/article/details/48679609 本文是简单介绍一下RabbitMQ,参考官网上的教程.同时加入了一些自己的理 ...
- 轻松搞定RabbitMQ(四)——发布/订阅
转自 http://blog.csdn.net/xiaoxian8023/article/details/48729479 翻译地址:http://www.rabbitmq.com/tutorials ...
- 轻松搞定RabbitMQ(三)——消息应答与消息持久化
转自 http://blog.csdn.net/xiaoxian8023/article/details/48710653 这个官网的第二个例子中的消息应答和消息持久化部分.我把它摘出来作为单独的一块 ...
- 使用BleLib的轻松搞定Android低功耗蓝牙Ble 4.0开发具体解释
转载请注明来源: http://blog.csdn.net/kjunchen/article/details/50909410 使用BleLib的轻松搞定Android低功耗蓝牙Ble 4.0开发具体 ...
- 【微服务】之七:轻松搞定SpringCloud微服务-API权限控制
权限控制,是一个系统当中必须的重要功能.张三只能访问输入张三的特定功能,李四不能访问属于赵六的特定菜单.这就要求对整个体系做一个完善的权限控制体系.该体系应该具备针区分用户.权限.角色等各种必须的功能 ...
- 【转】轻松搞定FTP之FlashFxp全攻略
转载网址:http://www.newhua.com/2008/0603/39163.shtml 轻松搞定FTP之FlashFxp全攻略 导读: FlashFXP是一款功能强大的FXP/FTP软件,融 ...
- 轻松搞定javascript变量(闭包,预解析机制,变量在内存的分配 )
变量: 存储数据的容器 1.声明 var 2.作用域 全局变量. 局部变量. 闭包(相对的全局变量): 3.类型 a.基本类型(undefi ...
随机推荐
- 【bzoj2338】[HNOI2011]数矩形 计算几何
题目描述 题解 计算几何 由于对角线平分且相等的四边形是矩形,因此我们可以把每条对角线存起来,按照对角线长度和中点位置为关键字排序,这样对于每个相同长度和中点的对角线就排到了一起. 于是对于每段可能形 ...
- 【bzoj3252】攻略 贪心+DFS序+线段树
题目描述 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏. 今天他得到了一款新游戏<XX半岛>,这款游戏有n个场景(scene),某 ...
- 【Luogu】P3155叶子的染色(树形DP)
题目链接 树形DP水题qwq. 设f[i][j]是以i为根的子树,染成j色,且满足内部需求的最少染色节点数. 设to是x的子节点,那么状态转移方程如此设计: 1.f[i][0] 这个状态表示i不染色, ...
- 【CCF】最优灌溉 最小生成树
[AC] #include<iostream> #include<cstdio> #include<string> #include<cstring> ...
- Linux System Programming 学习笔记(七) 线程
1. Threading is the creation and management of multiple units of execution within a single process 二 ...
- javaweb学习总结(九)—— 通过Servlet生成验证码图片(转)
(每天都会更新至少一篇以上,有兴趣的可以关注)转载自孤傲苍狼 一.BufferedImage类介绍 生成验证码图片主要用到了一个BufferedImage类,如下:
- SqlLite 安装与使用
一.安装文件 官方下载地址: http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki 选择要下载的类库文件:sqli ...
- HDU1071 The area
Ignatius bought a land last week, but he didn't know the area of the land because the land is enclos ...
- windows下webrtc的编译 2016(转)
====================20160124更新============================= 推荐一个FQ工具,shadowsocks,是需要付费的,一年也才不到100块,移 ...
- 《Linux命令行与shell脚本编程大全 第3版》
第一部分 Linux 命令行 第1章 初识Linux she1.1 什么是Linux 21.1.1 深入探究Linux 内核 31.1.2 GNU 工具 61.1.3 Linux 桌面环境 81 ...