(翻译)RabbitMQ官网教程(4)Routing

570 阅读3分钟

原文链接: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.javaReceiveLogDirect


下一章中我们将探索如何通过模式匹配来监听消息。