RabbitMQ教程——路由选择

259 阅读4分钟

路由选择

原文链接

(使用 amqp.node 客户端)

上一节的教程中,我们搭建了一个简易的日志系统。我们可以向许多接收者广播日志消息。

在这一节的教程中,我们准备往里面添加一些功能——我们准备让它可以仅仅订阅消息的子集。例如,我们将可以把关键的错误消息直接指向日志文件(为了节省磁盘空间),然而却仍然可以在控制台上输入打印所有的日志消息。

连接器

在之前的例子中,我们已经创建了连接器。你可以这样调用代码:

channel.bindQueue(q.queue, exchange, '');

一个连接器是一个交换器和一个队列之间的关系。你可以这样简单的理解为:队列对来此这个交换器的消息感兴趣。

连接器可以携带一个额外的绑定键的参数(也就是上面代码中的空字符串)。这也就是我们怎么使用一个键来创建一个连接:

channel.bindQueue(queue_name, exchange_name, 'black');

连接键的含义取决于交换器的类型。这个fanout交换器,在我们之前的教程中是会忽略它的值的。

Direct Exchange

我们上一节教程中的日志系统是会广播所有的消息给所有的消费者。我们想要去扩展,让消息基于严重程度允许进行消息的过滤。举个例子,我们想要写日志消息到磁盘上的脚本仅仅只接收严重的错误,并不会在警告或者信息级日志消息上浪费磁盘空间。

我们之前使用的是fanout交换器,但没有给我们太多的灵活性——它只会盲目广播。

我们将会使用direct交换器来代替它。direct交换器背后的路由算法是简单的——一条消息只会走到那些值为binding key的队列并和值为routing key的消息完全匹配才行。

为了说明这个,来看一下下面的图:

img

在上图中,我们看到direct交换器X有着两个连接着的队列。第一个队列是由连接键为orange连接的,第二个有两个连接键,一个连接键是black,还有一个是green

在上图的结构中,一条带有路由键orange的发布到交换器的消息将会被路由到队列Q1。带有black或者green路由键的消息将会走到Q2。所有其他的消息将会被丢弃。

Multiple bindings

img

使用相同的连接键来绑定多个队列是完全合法的。在我们的例子中,我们可以在XQ1之间添加一个带有连接键black的连接。在这种情况下,direct交换器的行为将会和fanout交换器的行为很像,并且会给所有匹配的队列广播消息。一条带有black路由键的消息将会被发送到Q1Q2两个队列。

发出日志(Emitting logs)

我们将会把这个模型使用在我们的日志系统上。取而代之,我们将会使用direct交换器,而不是fanout交换器。我们将提供日志严重性来作为routing key。这种方式下接收脚本可以知道如何去选择它想要接收的严重程度的消息。让我们先专注于发出日志。

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

var exchange = 'direct_logs';

channel.assertExchange(exchange, 'direct', {
  durable: false
});

并且我们已经准备好发送一条消息:

var exchange = 'direct_logs';

channel.assertExchange(exchange, 'direct', {
  durable: false
});
channel.publish(exchange, severity, Buffer.from(msg));

为了简化理解,我们假设severity只有以下三种情况:infowarningerror

订阅(Subscribing)

接收消息的代码还是和之前的教程差不多,但是有一个地方不一样——我们将位我们感兴趣的每一个严重程度创建一个新的连接。

args.forEach(function(severity) {
  channel.bindQueue(q.queue, exchange, severity);
});

把他们合到一起(Putting it all together)

img

emit_log_direct.js脚本的代码如下:

#!/usr/bin/env node

var amqp = require('amqplib/callback_api');

amqp.connect('amqp://localhost', function(error0, connection) {
  if (error0) {
    throw error0;
  }
  connection.createChannel(function(error1, channel) {
    if (error1) {
      throw error1;
    }
    var exchange = 'direct_logs';
    var args = process.argv.slice(2);
    var msg = args.slice(1).join(' ') || 'Hello World!';
    var severity = (args.length > 0) ? args[0] : 'info';

    channel.assertExchange(exchange, 'direct', {
      durable: false
    });
    channel.publish(exchange, severity, Buffer.from(msg));
    console.log(" [x] Sent %s: '%s'", severity, msg);
  });

  setTimeout(function() {
    connection.close();
    process.exit(0)
  }, 500);
});

receive_logs_direct.js脚本的代码如下:

#!/usr/bin/env node

var amqp = require('amqplib/callback_api');

var args = process.argv.slice(2);

if (args.length == 0) {
  console.log("Usage: receive_logs_direct.js [info] [warning] [error]");
  process.exit(1);
}

amqp.connect('amqp://localhost', function(error0, connection) {
  if (error0) {
    throw error0;
  }
  connection.createChannel(function(error1, channel) {
    if (error1) {
      throw error1;
    }
    var exchange = 'direct_logs';

    channel.assertExchange(exchange, 'direct', {
      durable: false
    });

    channel.assertQueue('', {
      exclusive: true
      }, function(error2, q) {
        if (error2) {
          throw error2;
        }
      console.log(' [*] Waiting for logs. To exit press CTRL+C');

      args.forEach(function(severity) {
        channel.bindQueue(q.queue, exchange, severity);
      });

      channel.consume(q.queue, function(msg) {
        console.log(" [x] %s: '%s'", msg.fields.routingKey, msg.content.toString());
      }, {
        noAck: true
      });
    });
  });
});

如果你想仅仅只保存warningerror(不包含info)的日志消息,只需要打开终端并输入:

./receive_logs_direct.js warning error > logs_from_rabbit.log

如果你想要在屏幕上看所有的日志消息,打开一个新的终端并且输入:

./receive_logs_direct.js info warning error
# => [*] Waiting for logs. To exit press CTRL+C

举例,如果你想要发出日志消息只需要输入:

./emit_log_direct.js error "Run. Run. Or it will explode."
# => [x] Sent 'error':'Run. Run. Or it will explode.'

emit_log_direct.js 源码receive_logs_direct.js——所有的源码

继续前进到教程5来了解如何基于一个模式来监听消息。