RabbitMQ/JAVA (路由选择)
上篇博文中,我们建立了一个简单的日志系统。可以广播消息给多个消费者。本篇博文,我们将添加新的特性——我们可以只订阅部分消息。比如:我们可以接收Error级别的消息写入文件。同时仍然可以在控制台打印所有日志。
Bindings(绑定)
在上一篇博客中我们已经使用过绑定。类似下面的代码:
channel.queueBind(queueName, EXCHANGE_NAME, "");
绑定表示转换器与队列之间的关系。可以简单的认为:队列对该转发器上的消息感兴趣。
绑定可以设定额外的routingKey参数。为了与避免basicPublish方法(发布消息的方法)的参数混淆,我们准备把它称作绑定键(binding key)。下面展示如何使用绑定键(binding key)来创建一个绑定:
channel.queueBind(queueName, EXCHANGE_NAME, "black");
绑定键关键取决于转换器的类型。对于fanout类型,忽略此参数。
Direct exchange(直接转发)
前面讲到我们的日志系统广播消息给所有的消费者。我们想对其扩展,根据消息的严重性来过滤消息。例如:我们希望将致命错误的日志消息记录到文件,而不是把磁盘空间浪费在warn和info类型的日志上。我们使用的fanout转发器,不能给我们太多的灵活性。它仅仅只是盲目的广播而已。我们使用direct转发器进行代替,其背后的算法很简单——消息会被推送至绑定键(binding key)和消息发布附带的选择键(routing key)完全匹配的队列。

在上图中,我们可以看到direct类型的转发器与2个队列进行了绑定。第一个队列使用的绑定键是orange,第二个队列绑定键为black和green。这样当消息发布到转发器是,附带orange绑定键的消息将被路由到队列Q1中去。附带black和green绑定键的消息被路由到Q2中去。其他消息全部丢弃。
Multiple bindings(多重绑定)

使用一个绑定键绑定多个队列是完全合法的。如上图,绑定键black绑定了2个队列——Q1和Q2。
Emitting logs(发送日志)
我们将这种模式用于日志系统,发送消息给direct类型的转发器。我们将 提供日志严重性做为绑定键。那样,接收程序可以选择性的接收严重性的消息。首先关注发送日志的代码:
像往常一样首先创建一个转换器:
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
然后为发送消息做准备:
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
为了简化代码,我们假定日志的严重性是“info”、“warning”、“error”中之一。
Subscribing(订阅)
接收消息跟前面博文中的一样。我们仅需要修改一个地方:为每一个我们感兴趣的严重性的消息,创建一个新的绑定。
String queueName = channel.queueDeclare().getQueue();
for(String severity : argv){
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}
完整例子:

发送端代码(EmitLogDirect.java)
package ERROR;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class EmitLogDirect {
private static final String EXCHANGE_NAME = "direct_logs";
public static void main(String[] argv)
throws java.io.IOException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("115.159.181.204");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("admin");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String[] severities = {"error","info","warning"};
for(int i=0 ;i<3;i++){
String severity =severities[i%3];
String message="Hellp World!";
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
};
channel.close();
connection.close();
}
//..
}
消费者1(ReceiveLogs2Console.java)
package ERROR;
import java.io.IOException;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
public class ReceiveLogs2Console {
private static final String EXCHANGE_NAME = "direct_logs";
public static void main(String[] argv) throws IOException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("115.159.181.204");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("admin");
// 打开连接和创建频道,与发送端一样
Connection connection = factory.newConnection();
final Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
// 声明一个随机队列
String queueName = channel.queueDeclare().getQueue();
//所有日志严重性级别
String[] severities={"error","info","warning"};
for (String severity : severities) {
//关注所有级别的日志(多重绑定)
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
// 创建队列消费者
final Consumer consumer = new DefaultConsumer(channel) {
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)
package ERROR;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
public class ReceiveLogs2File {
private static final String EXCHANGE_NAME = "direct_logs";
public static void main(String[] argv) throws IOException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("115.159.181.204");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("admin");
// 打开连接和创建频道,与发送端一样
Connection connection = factory.newConnection();
final Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
// 声明一个随机队列
String queueName = channel.queueDeclare().getQueue();
String severity="error";//只关注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/JAVA (路由选择)的更多相关文章
- 译:1. RabbitMQ Java Client 之 "Hello World"
这些教程介绍了使用RabbitMQ创建消息传递应用程序的基础知识.您需要安装RabbitMQ服务器才能完成教程 1. 打造第一个Hello World 程序 RabbitMQ是一个消息代理:它接受和转 ...
- 五、RabbitMQ Java Client基本使用详解
Java Client的5.x版本系列需要JDK 8,用于编译和运行.在Android上,仅支持Android 7.0或更高版本.4.x版本系列支持7.0之前的JDK 6和Android版本. 加入R ...
- RABBITMQ/JAVA (主题)
上篇博文中,我们进一步改良了日志系统.即使用Direct类型的转换器,使得接受者有能力进行选择性的接收日志,而非fanout那样,只能够无脑的转发. 虽然使用Direct类型的转换器改进了日志系统.但 ...
- RabbitMQ/JAVA (发布/订阅模式)
发布/订阅模式即生产者将消息发送给多个消费者. 下面介绍几个在发布/订阅模式中的关键概念-- 1. Exchanges (转发器) 可能原来我们都是基于一个队列发送和接收消息.现在介绍一下完整的消息传 ...
- RABBITMQ/JAVA 客户端测试(再补:利用文件流)
(一)这里可以先复习一下java输入输出流和文件操作--- 1.File类保存文件或目录的各种元数据信息,包括文件名.文件长度.最后修改时间.是否可读.获取当前文件的路径名.判断指定文件是否存在.获取 ...
- RabbitMQ/JAVA 客户端测试(补:利用线程)
上次进行了简单的连接测试.这次主要进行一下小小的补充.利用线程将生产者消费者代码合到一个文件中. 我是将Recv.java(消费者)文件放在一个线程里添加到Send.java(生产者)中. 代码如下: ...
- RabbitMQ/JAVA 客户端连接测试
这里是一个简单的helloworld测试. 这里在eclipse平台中实现 细节不再赘述.重点是导入rabbitmq-java-client的jar包 下载地址:http://www.rabbitmq ...
- rabbitmq - java client lib一二事
由于不可抗因素, 需要给对接方撸一个client的demo.基于比较老的jdk. 所幸找到了这里:http://www.rabbitmq.com/releases/rabbitmq-java-clie ...
- 译:2. RabbitMQ Java Client 之 Work Queues (工作队列)
在上篇揭开RabbitMQ的神秘面纱一文中,我们编写了程序来发送和接收来自命名队列的消息. 本篇我们将创建一个工作队列,工作队列背后的假设是每个任务都交付给一个工作者 本篇是译文,英文原文请移步:ht ...
随机推荐
- [转]如何在Java中调用DLL方法
转载地址:http://developer.51cto.com/art/200906/129773.htm Java语言本身具有跨平台性,如果通过Java调用DLL的技术方便易用,使用Java开发前台 ...
- HBase Mac OSX 安装笔记
本次测试安装的机器为Mac Book Pro, 系统为 OS X 10.9.4.hbase版本0.98.6.1. 使用Java版本为Oracle的JDK 1.6.0_65. 1. 下载安装 hbase ...
- GMF:如何在不打开Editor的情况下生成图片
问题 GMF应用中,有时我们希望在不打开*DiagramEditor的情况下,从文件就能生成它的图片 解决方案 首先,从文件中构造DiagramImpl实例: TransactionalEditi ...
- R----ggplot2包介绍学习
分析数据要做的第一件事情,就是观察它.对于每个变量,哪些值是最常见的?值域是大是小?是否有异常观测? ggplot2图形之基本语法: ggplot2的核心理念是将绘图与数据分离,数据相关的绘图与数据无 ...
- 使用exec命令删除前几天产生的日志
上次学习了访问某个网站并产生相应的日志保存在指定位置,但是时间长了,日志会占用磁盘大量的空间,下面使用exec这个命令删除之前的日志: 命令格式: find 目录绝对路径 -mtime +n(时间) ...
- PHP 可变长度参数列表
In PHP 5.6 and later, argument lists may include the ... token to denote that the function accepts a ...
- python成长之路【第六篇】:python模块--time和datetime
1.时间表现形式 时间戳 (1970年1月1日之后的秒,即:time.time())格式化的时间字符串 (2014-11-11 11:11, 即:time.strftime('%Y-%m- ...
- linux 之SCP
一.从本地到远程复制 1.复制文件 * 命令格式: 1.scp -P remote_port local_file remote_username@remote_ip:remote_folder 或者 ...
- sqlAlchemy 按DateTime字段的年或月进行group_by查询
一.根据”create_date“查询每天的数据 1.查询2016年5月每天的数据 session.query(extract('day', User.create_date).label('day' ...
- HttpHelper类登录淘宝联盟并下载淘宝客订单xls
本次开发环境与工具如下:IE9.0浏览器 + IE抓包插件HttpWatch +WIN7 64位系统 + VS2005 IDE + .NET 2.0框架本想上传HttpWatch抓包插件,但由于文件超 ...