RabbitMQ 学习笔记(四):Routing

Routing(按指定路线发送)

在之前的笔记中,我们构建了一个简单的日志记录系统,能够同时向许多接收器发送日志消息。

在本次笔记中,我们将为它添加一个新特性——我们将实现只订阅消息的一个子集。例如,我们只把关键错误消息(error)直接导向日志文件(以保存磁盘空间),同时仍然能够在控制台上打印出所有日志消息(包括error和warning,info)。

绑定

在前面的示例中,我们已经创建了绑定。你可能会记得:

channel.queueBind(queueName, EXCHANGE_NAME, "");

绑定是交换器(exchange)和队列(queue)之间的关系。这可以简单地看为成:队列对来自交换器的消息感兴趣

绑定可以采用额外的参数routingKey。为了避免与basic_publish的参数混淆,我们将把它称为 binding key(绑定键)。这就是我们如何创建一个binding key:

channel.queueBind(queueName, EXCHANGE_NAME, "black");

binding key的含义取决于交换器类型。我们之前使用的fanout交换器,简单地忽略掉它的值

Direct 交换器

我们上一个教程中的日志系统向所有用户广播所有的消息。我们希望扩展该功能,允许基于它们的性质来过滤消息。例如,我们可能需要一个将日志消息写入到磁盘的程序,它只接收关键错误(error),而不将警告或信息日志消息(warning ,info)写入而浪费磁盘空间。

之前,我们使用的是一个 fanout 交换器,这并没有给我们很大的灵活性——它只会无意识,无区分地广播消息

现在,我们将使用 direct 交换器。direct交换器背后的路线指定算法很简单——一条消息,若它的 routing key 与某一队列的 binding key 完全匹配,则将被传递给该队列

为了说明这一点,请参考下面的设置:

在这个设置中,我们可以看到与它绑定的两个队列的 direct 交换器 X 。第一个队列与 binding key 橙色绑定,第二个队列有两个绑定,一个是 binding key 黑色,另一个是绿色。

在这样的设置中,发送到交换器的消息中,带有橙色 routing key 的消息将被发送到队列 Q1 。带有黑色或绿色 routing key 的消息将被发送到 Q2 。所有其他所有的信息将被丢弃。

多个绑定

用相同的 binding key 绑定多个队列是完全合法的。在我们的示例中,我们可以在 X 和 Q1 之间添加 binding key 黑色。在这种情况下,direct 交换器将表现得像 fanout 交换器,并将消息广播到所有匹配的队列。带有黑色 routing key 的消息将被发送到 Q1 和 Q2 。

Emitting logs(发送日志)

我们将为我们的日志系统使用这个模型。我们不会向外发信息,而是将信息发送到 direct 交换器。我们将提供日志性质(error, warning, info)作为 routing key。这样,接收程序就能够选择它想要接收的性质。首先,让我们把注意力放在发送日志上。

一如既往,我们需要先创建一个交换器:

channel.exchangeDeclare(EXCHANGE_NAME, "direct");

我们准备发出一个信息:

channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());

Subscribing(订阅消息)

接收消息的代码将像之前的笔记一样,有一个不同——我们将为我们感兴趣的每个消息性质创建一个新的绑定

String queueName = channel.queueDeclare().getQueue();

for(String severity : argv){
  channel.queueBind(queueName, EXCHANGE_NAME, severity);
}

完成代码,编译运行

EmitLogDirect.java 的代码如下:

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;

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");
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();

    channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.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 + "'");

    channel.close();
    connection.close();
  }

  private static String getSeverity(String[] strings){
    if (strings.length < 1)
            return "info";
    return strings[0];
  }

  private static String getMessage(String[] strings){
    if (strings.length < 2)
            return "Hello World!";
    return joinStrings(strings, " ", 1);
  }

  private static String joinStrings(String[] strings, String delimiter, int startIndex) {
    int length = strings.length;
    if (length == 0 ) return "";
    if (length < startIndex ) return "";
    StringBuilder words = new StringBuilder(strings[startIndex]);
    for (int i = startIndex + 1; i < length; i++) {
        words.append(delimiter).append(strings[i]);
    }
    return words.toString();
  }
}

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, BuiltinExchangeType.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);
  }
}

像往常一样编译运行(请参阅学习笔记一中编译注意事项和类路径建议)。为了方便起见,在运行示例时,我们将使用环境变量$ CP。

javac -cp $CP ReceiveLogsDirect.java EmitLogDirect.java

如果你只想保存“error”和“warning”(而不是“info”)的日志信息到一个文件,只需打开控制台和类型:

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.’

相关链接

rabbitmq-c++(SimpleAmqpClient) 笔记代码系列:

rabbitmq-c++(SimpleAmqpClient) 笔记代码一
rabbitmq-c++(SimpleAmqpClient) 笔记代码二
rabbitmq-c++(SimpleAmqpClient) 笔记代码三
rabbitmq-c++(SimpleAmqpClient) 笔记代码四
rabbitmq-c++(SimpleAmqpClient) 笔记代码五
rabbitmq-c++(SimpleAmqpClient) 笔记代码六

RabbitMQ 学习笔记系列:

RabbitMQ 学习笔记(一):简单介绍及”Hello World”
RabbitMQ 学习笔记(二):work queues
RabbitMQ 学习笔记(三):Publish/Subscribe
RabbitMQ 学习笔记(四):Routing
RabbitMQ 学习笔记(五):Topics
RabbitMQ 学习笔记(六):RPC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值