原文链接:www.rabbitmq.com/tutorials/t…
在上一章教程中我们构建了一个简单的日志记录系统,我们能够通过它将日志消息广播给很多的接受者。
在本章中我们会给这个日志系统添加一个新特性——使它能够只订阅一部分日志消息。比如,只有一些严重的错误一直踩写入磁盘,并且所有的日志都会打印在控制台上。
绑定
在之前的代码中其实我们已经创建了一个绑定关系,代码是这样的:
channel.queueBind(queueName, EXCHANGE_NAME, "");
绑定指的是交换机和队列之间的一种关系,你可以理解为队列对交换机中的消息感兴趣。
绑定可以定义额外的参数routingKey,为了避免与basicPublish方法中的参数混淆,我们把它叫做绑定建。
channel.queueBind(queueName, EXCHANGE_NAME, "black");
绑定建的含义取决于交换机类型,像我们之前使用额fanout类型的交换机就会将这个参数给忽略掉。
Direct交换机
上一章中我们的日志系统会将所有的日志消息广播给所有的接受者,现在我们要把扩展成只接收经过严格过滤的日志消息。比如,只把error级别的日志写入磁盘,对于warning和ino级别的日志则不会写入磁盘以节省磁盘空间。
上图中我们可以看到,direct交换价x绑定了两个队列Q1和Q2,Q1的绑定的键是orange,Q2绑定的键是black和green。发布到交换机中消息,如果路由键为orange则消息将会被分发到Q1队列中,而路由键为back或green的消息会被分发到Q2队列中。其他的消息都会被忽略丢弃。
多重绑定
用同样的绑定建绑定多个多个队列时完全没问题的。我们可以在交换价x和Q1添加一个绑定建black,这样我们的direct交换机就像fanout交换机一样将消息广播到所有匹配的队列中。路由键为black的消息将会被分发到Q1和Q2中。
发送日志
我们会在日志系统中使用这种模式,把消息发送到direct交换机而不是fanout交换机。我们把日志消息的级别定义为路由键,这样接收程序就可以选择日志级别来接收消息了。现在我们现在看下如何发送日志消息。
按照惯例,先来定义交换机:
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
然后发送消息:
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
为了简化操作,我假设日志级别只有info、waring、error。
订阅
接收消息和之前的教程基本一致,唯一的不同就是,我们需要为每一个日志级别都创建一个绑定关系。
String queueName = channel.queueDeclare().getQueue();
for(String severity : argv){
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}
代码整合
EmitLogDirect.java代码:
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 Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String severity = getSeverity(argv);
String message = getMessage(argv);
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
}
}
//..
}
ReceiveLogsDirect.java代码:
mport com.rabbitmq.client.*;
public class ReceiveLogsDirect {
private static final String EXCHANGE_NAME = "direct_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, "direct");
String queueName = channel.queueDeclare().getQueue();
if (argv.length < 1) {
System.err.println("Usage: ReceiveLogsDirect [info] [warning] [error]");
System.exit(1);
}
for (String severity : argv) {
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" +
delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
};
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
}
}
编译方法和之前一样(参见第一章内容),为了方便起见,我们将在运行示例时为类路径使用环境变量$CP(在Windows上为%CP%)。
javac -cp $CP ReceiveLogsDirect.java EmitLogDirect.java
如果你想讲error和warning级别的日志写入文件,你可以指定参数来实现:
java -cp $CP ReceiveLogsDirect warning error > logs_from_rabbit.log
如果你想在屏幕上打印出所有的日志,你可以打开一个新终端:
java -cp $CP ReceiveLogsDirect info warning error
# => [*] Waiting for logs. To exit press CTRL+C
或者只发送error级别的日志:
java -cp $CP EmitLogDirect error "Run. Run. Or it will explode."
# => [x] Sent 'error':'Run. Run. Or it will explode.'
源代码可以查看EmitLogDirect.java和ReceiveLogDirect
下一章中我们将探索如何通过模式匹配来监听消息。