路由
在先前的教程中,我们创建一个简单的日志系统,我们能够广播日志消息给许多接受者。
在这个教程中,我们将添加一个特性--我们能够订阅消息的一个子集。例如,我们将可以直接传送危急的错误消息给日志文件(保存到硬盘中),能够打印所有的日志消息在控制台上。
绑定
在先前的教程中,我们已经创建了绑定,你可能想起这样的代码:
channel.queueBind(queueName, EXCHANGE_NAME, "");
绑定是交换机和队列之间的关系,这个可以简单地理解为:队列对交换机中的消息感兴趣
绑定会添加额外的routingkey参数。为了避免和basic_public 参数混淆,我们将叫它为binding key.这是我们创建一个带有key的绑定:
channel.queueBind(queueName, EXCHANGE_NAME, "black")
绑定key的含义取决于交换机的类型,fanout交换机,我们之前使用的,忽略了这个值。
direct 交换机
我们先前教程的日志系统将所有的消息广播给所有的消费者。我们想要扩展基于服务器允许过滤消息的功能。例如,我们可能需要一个写仅仅接受危急的错误日志信息到硬盘中,不为警告和消息日志信息浪费磁盘空间。
我们使用direct交换机,direct交换机背后的routing算法很简单-消息进入队列,它的bindingkey 和routing key匹配。
为了解释清楚,考虑以下的设置:
在这个设置中,我们可以看到direct交换机x,有两个队列和它绑定,第一个队列和binding key orange绑定,第二个有两个绑定,一个和binding key black绑定,另外一个和 green绑定。
在这样设置的消息发布给一个带有routing key orange的交换机将会路由到队列Q1。带有routing key black 或者green的消息将去Q2,所有其他的消息将会被丢弃。
多重绑定
多个队列绑定相同的binding key是完全合法的。在我们的例子中,我们能够将给交换机x队列Q1添加一个binding key black。在那个例子中,direct 交换机将和fanout交换机一样将消息、广播给所有匹配的队列。带有routing key black的消息将会传送给Q1和Q2.
发送日志
我们的日志系统使用这个模型,我们将发送消息给direct exchange而不是fanout 交换机。我们将提供日志严重性作为路由键。这样接受程序就可以接受它想接收的的严重性。让我们关注发送日志先。
一如既往的,我们需要先创建一个交换机。
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
我们准备好发送消息:
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
为了简化,我们假设严重性可以为 info warnning error这三个之一。
订阅
接受消息和先前的教程一样。除了一个例子外,我们将会为每一个我们感兴趣的严重消息创建一个新的绑定。
String queueName = channel.queueDeclare().getQueue();
for(String severity : argv){
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}
将所有代码放在一起。
EmitLogDirect.java类的代码。
import com.rabbitmq.client.*;
import java.io.IOException;
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("localhost");
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());
System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
channel.close();
connection.close();
}
//..
}
ReceiveLogsDirect.java的代码。
import com.rabbitmq.client.*;
import java.io.IOException;
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");
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);
}
}
像以前一样编译代码(看教程1的编译和类路径建议)。为了方便,在类路径上运行例子的时候,我们将使用一个
环境变量$CP(在windows为%CP%).
javac -cp $CP ReceiveLogsDirect.java EmitLogDirect.java
如果你想要仅仅保存warning和error的日志到一个文件中(而不是info)。仅仅是打开console和敲。
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.'
移动到教程5学习如何基于一个模式来监听消息。